From 0458df83da7c850ff6c25556a35d19aeed8387e3 Mon Sep 17 00:00:00 2001 From: Benjamin Dupont <4503241+Benjozork@users.noreply.github.com> Date: Tue, 26 Mar 2024 19:29:38 -0400 Subject: [PATCH] chore: lint overhaul (#458) * refactor: remove antd and antd icons * fix: further styling fixes * fix: further styling fixes * fix: further styling fixes * fix: broken changelog * fix: further styling fixes * fix: lint * fix: remove testing code * refactor: move lint to prettier * style: run lint * refactor: add react-hooks and tailwind lint * style: run lint * refactor: fix lint issues * style: run lint * chore: fix lint * fix: simplebar import * fix: selected track styling --- .eslintrc.js | 39 + .eslintrc.json | 89 - .prettierrc | 6 + package-lock.json | 6161 +++++++++-------- package.json | 23 +- src/common/channels.ts | 44 +- src/common/settings.ts | 425 +- src/main/InstallManager.ts | 253 +- src/main/SentryClient.ts | 77 +- src/main/index.ts | 451 +- src/react-app-env.d.ts | 10 +- src/renderer/actions/install-path.utils.tsx | 106 +- .../Configure/ConfigurationAspectDisplay.tsx | 68 +- .../AddonSection/Configure/TrackSelector.tsx | 66 +- .../Configure/YesNoOptionToggle.tsx | 37 +- .../AddonSection/Configure/index.tsx | 242 +- src/renderer/components/AddonSection/Enums.ts | 92 +- .../AddonSection/MainActionButton.tsx | 154 +- .../AddonSection/MyInstall/index.tsx | 168 +- .../AddonSection/ReleaseNotes/index.tsx | 260 +- .../components/AddonSection/StateSection.tsx | 580 +- .../components/AddonSection/index.tsx | 1018 +-- src/renderer/components/App/AddonBar.tsx | 305 +- src/renderer/components/App/NavBar.tsx | 117 +- src/renderer/components/App/SideBar.tsx | 212 +- src/renderer/components/App/index.tsx | 325 +- src/renderer/components/Button.tsx | 72 +- .../components/DebugSection/index.tsx | 68 +- src/renderer/components/ErrorModal/index.tsx | 117 +- .../components/InstallerUpdate/index.tsx | 126 +- .../components/InstallerUpdate/styles.tsx | 24 +- .../components/LocalApiConfigEditUI/index.tsx | 447 +- src/renderer/components/Logo/index.tsx | 6 +- .../components/Modal/AutostartDialog.tsx | 205 +- .../components/Modal/CannotInstallDialog.tsx | 181 +- .../components/Modal/DependencyDialog.tsx | 57 +- src/renderer/components/Modal/ErrorDialog.tsx | 274 +- .../Modal/IncompatibleAddonDialog.tsx | 49 +- .../components/Modal/InstallSizeDialog.tsx | 222 +- src/renderer/components/Modal/index.tsx | 498 +- .../NoAvailableAddonsSection/index.tsx | 6 +- .../components/PublisherSection/index.tsx | 22 +- src/renderer/components/SentrySessionCard.tsx | 83 +- .../components/SettingsSection/About.tsx | 120 +- .../SettingsSection/Customization.tsx | 98 +- .../components/SettingsSection/Developer.tsx | 83 +- .../components/SettingsSection/Download.tsx | 211 +- .../components/SettingsSection/General.tsx | 132 +- .../components/SettingsSection/index.tsx | 231 +- src/renderer/components/SideBar/index.tsx | 113 +- src/renderer/components/Toggle.tsx | 31 +- .../components/WindowActionButtons/index.tsx | 102 +- src/renderer/data.ts | 1151 +-- src/renderer/index.tsx | 129 +- .../redux/features/applicationStatus.ts | 21 +- src/renderer/redux/features/configuration.ts | 18 +- src/renderer/redux/features/downloads.ts | 121 +- src/renderer/redux/features/installStatus.ts | 55 +- src/renderer/redux/features/installedTrack.ts | 18 +- .../redux/features/latestVersionNames.ts | 29 +- src/renderer/redux/features/releaseNotes.ts | 24 +- src/renderer/redux/features/selectedTrack.ts | 20 +- .../redux/features/sentrySessionID.ts | 18 +- src/renderer/redux/reducer.ts | 40 +- src/renderer/redux/store.ts | 39 +- src/renderer/redux/types.ts | 34 +- src/renderer/style/theme.ts | 66 +- src/renderer/utils/AddonData.ts | 302 +- src/renderer/utils/BackgroundServices.ts | 224 +- src/renderer/utils/DataCache.ts | 42 +- src/renderer/utils/Directories.ts | 226 +- src/renderer/utils/ExternalApps.ts | 112 +- src/renderer/utils/ExternalAppsUI.tsx | 64 +- src/renderer/utils/FreeDiskSpace.ts | 127 +- src/renderer/utils/IncompatibleAddOnsCheck.ts | 112 +- src/renderer/utils/InstallManager.tsx | 1173 ++-- src/renderer/utils/InstallerConfiguration.ts | 642 +- src/renderer/utils/McduServer.ts | 30 +- src/renderer/utils/Msfs.ts | 28 +- src/renderer/utils/Resolver.ts | 109 +- tailwind.config.js | 3 - 81 files changed, 10342 insertions(+), 9541 deletions(-) create mode 100644 .eslintrc.js delete mode 100644 .eslintrc.json create mode 100644 .prettierrc diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..a140d5d0 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,39 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'prettier', 'react', 'tailwindcss'], + extends: [ + 'eslint:recommended', + 'plugin:prettier/recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + 'plugin:react/recommended', + ], + env: { + browser: true, + es2021: true, + node: true, + }, + parserOptions: { + sourceType: 'module', + }, + settings: { + react: { version: 'detect' }, + }, + rules: { + 'prettier/prettier': [ + 'error', + {}, + { + usePrettierrc: true, + }, + ], + 'no-console': 'off', + + 'tailwindcss/classnames-order': 'error', + 'tailwindcss/enforces-negative-arbitrary-values': 'error', + 'tailwindcss/enforces-shorthand': 'error', + 'tailwindcss/migration-from-tailwind-2': 'error', + 'tailwindcss/no-contradicting-classname': 'error', + }, +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 608f748a..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "plugins": [ - "react", - "@typescript-eslint" - ], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "env": { - "browser": true, - "es2021": true, - "node": true - }, - "parserOptions": { - "sourceType": "module" - }, - - // DO NOT CHANGE THIS FILE (unless you are an admin) (if admin, make sure you change the same file in the latest asobo branch as well) - "rules": { - "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/no-var-requires": "off", - "indent": [ - "error", - 4, - { - "SwitchCase": 1 - } - ], - "linebreak-style": "off", - // No quotes option is really fully consistent, so probably best to jsut not enforce either type - "quotes": "off", - "semi": ["error", "always"], - "curly": ["error", "all"], - "object-curly-spacing": ["error", "always"], - "brace-style": ["error", "1tbs"], - "space-before-blocks": "error", - "space-before-function-paren": [ - "error", - { - "anonymous": "always", - "named": "never", - "asyncArrow": "always" - } - ], - "space-in-parens": "error", - "space-infix-ops": "error", - "space-unary-ops": "error", - "keyword-spacing": "error", - "no-irregular-whitespace": "error", - "no-trailing-spaces": "error", - "semi-spacing": "error", - "no-mixed-spaces-and-tabs": "error", - "no-multi-spaces": "error", - "prefer-const": [ - "error", - { - "destructuring": "all" - } - ], - "no-console": "off", - "eol-last": ["error", "always"], - // Fix false positives on enums - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": "error", - // Why... - "@typescript-eslint/no-empty-function": "off", - "no-multiple-empty-lines": [ - "error", - { - "max": 1, - "maxBOF": 0, - "maxEOF": 0 - } - ], - // require trailing commas in multiline object literals - "comma-dangle": ["error", { - "arrays": "always-multiline", - "objects": "always-multiline", - "imports": "always-multiline", - "exports": "always-multiline", - "functions": "always-multiline" - }], - // enforce spacing before and after comma - "comma-spacing": ["error", { "before": false, "after": true }] - } -} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..c9751b21 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "printWidth": 120, + "parser": "typescript", + "endOfLine": "auto" +} diff --git a/package-lock.json b/package-lock.json index 236e4629..28435be9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "GPL-3.0", "dependencies": { "@flybywiresim/fragmenter": "^0.7.4", - "@flybywiresim/react-components": "^0.2.5", "@reduxjs/toolkit": "^1.7.1", "@sentry/cli": "^2.5.0", "@sentry/electron": "^3.0.7", @@ -36,7 +35,6 @@ "react-detect-offline": "^2.4.1", "react-dom": "^17.0.1", "react-hot-loader": "^4.13.0", - "react-html-parser": "^2.0.2", "react-intersection-observer": "^8.33.1", "react-markdown": "^7.0.1", "react-redux": "^7.2.2", @@ -46,9 +44,10 @@ "redux": "^4.0.5", "remark-gfm": "^2.0.0", "semver": "7.3.5", - "simplebar-react": "^2.3.0", + "simplebar-react": "^3.2.4", "styled-components": "^5.2.1", "tabler-icons-react": "~1.33.0", + "tailwind-merge": "^2.2.1", "walkdir": "^0.4.1", "winreg": "^1.2.4", "ws": "^8.8.0" @@ -75,8 +74,8 @@ "@types/styled-components": "^5.1.7", "@types/webpack": "^5.28.0", "@types/webpack-env": "^1.16.3", - "@typescript-eslint/eslint-plugin": "^4.14.2", - "@typescript-eslint/parser": "^4.14.2", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", "@webpack-cli/serve": "^1.6.0", "babel-loader": "^8.2.2", "concurrently": "^6.2.1", @@ -84,10 +83,12 @@ "electron": "^25.2.0", "electron-builder": "^24.4.0", "electron-devtools-installer": "^3.2.0", - "eslint": "^7.19.0", - "eslint-plugin-import": "^2.20.0", - "eslint-plugin-react": "^7.27.1", - "eslint-plugin-tailwindcss": "^1.17.2", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-tailwindcss": "^3.14.1", "fork-ts-checker-webpack-plugin": "^6.4.0", "html-webpack-plugin": "^5.5.0", "inspectpack": "^4.7.1", @@ -103,10 +104,10 @@ "react-svg-loader": "^3.0.3", "redux-devtools-extension": "^2.13.9", "style-loader": "^1.3.0", - "tailwindcss": "^2.2.19", + "tailwindcss": "^3.4.1", "ts-loader": "^8.0.15", "ts-node": "^9.1.1", - "typescript": "^4.1.3", + "typescript": "^5.3.3", "url-loader": "^4.1.1", "webpack": "^5.62.1", "webpack-cli": "^4.9.1", @@ -117,6 +118,27 @@ "utf-8-validate": "^5.0.9" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@babel/code-frame": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", @@ -1411,11 +1433,14 @@ } }, "node_modules/@babel/runtime": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", - "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/template": { @@ -1978,42 +2003,60 @@ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "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, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "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, "dependencies": { - "sprintf-js": "~1.0.2" + "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": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" @@ -2022,32 +2065,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/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==", + "node_modules/@eslint/eslintrc/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, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "engines": { + "node": ">=10" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/@eslint/eslintrc/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==", + "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, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@flybywiresim/api-client": { @@ -2067,18 +2103,6 @@ "zip-lib": "^0.7.3" } }, - "node_modules/@flybywiresim/react-components": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@flybywiresim/react-components/-/react-components-0.2.5.tgz", - "integrity": "sha512-SRCeC58kYcxqEk2CKt5sUsp9UhtmdkQwe5bTdgDTAbS4o3LhKQfeB1SYXqBoh6CQyOdwzFRSd5gTqAYIcbPTtQ==", - "dependencies": { - "tabler-icons-react": "~1.33.0" - }, - "peerDependencies": { - "react": ">=17.0.1", - "react-dom": ">=17.0.1" - } - }, "node_modules/@flybywiresim/tailwind-config": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@flybywiresim/tailwind-config/-/tailwind-config-0.3.1.tgz", @@ -2094,6 +2118,39 @@ "purgecss": "^3.1.3" } }, + "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==", + "dev": true, + "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, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2190,6 +2247,54 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz", + "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "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, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz", + "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -2273,12 +2378,12 @@ } }, "node_modules/@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "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, "dependencies": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" }, "engines": { @@ -2286,21 +2391,21 @@ } }, "node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "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, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "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, "dependencies": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" }, "engines": { @@ -2329,6 +2434,18 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@reduxjs/toolkit": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.7.1.tgz", @@ -2477,11 +2594,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/@sentry/electron/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, "node_modules/@sentry/hub": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-7.8.0.tgz", @@ -3167,9 +3279,9 @@ "dev": true }, "node_modules/@types/eslint": { - "version": "7.2.8", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.8.tgz", - "integrity": "sha512-RTKvBsfz0T8CKOGZMfuluDNyMFHnu5lvNr4hWEsQeHXH6FcmIDIozOyWMh36nLGMwVd5UFNXC2xztA8lln22MQ==", + "version": "8.56.3", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.3.tgz", + "integrity": "sha512-PvSf1wfv2wJpVIFUMSb+i4PvqNYkB9Rkp9ZDO3oaWzq4SKhsQk4mrMBr3ZH06I0hKrVGLBacmgl8JM4WVjb9dg==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -3248,15 +3360,9 @@ "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/keyv": { "version": "3.1.4", @@ -3270,8 +3376,15 @@ "node_modules/@types/lodash": { "version": "4.14.168", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz", - "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==", - "dev": true + "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dependencies": { + "@types/lodash": "*" + } }, "node_modules/@types/mdast": { "version": "3.0.10", @@ -3402,9 +3515,9 @@ "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" }, "node_modules/@types/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-4g1jrL98mdOIwSOUh6LTlB0Cs9I0dQPwINUhBg7C6pN4HLr8GS8xsksJxilW6S6dQHVi2K/o+lQuQcg7LroCnw==" + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" }, "node_modules/@types/styled-components": { "version": "5.1.9", @@ -3479,30 +3592,33 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.20.0.tgz", - "integrity": "sha512-sw+3HO5aehYqn5w177z2D82ZQlqHCwcKSMboueo7oE4KU9QiC0SAgfS/D4z9xXvpTc8Bt41Raa9fBR8T2tIhoQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "4.20.0", - "@typescript-eslint/scope-manager": "4.20.0", - "debug": "^4.1.1", - "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -3510,50 +3626,42 @@ } } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.20.0.tgz", - "integrity": "sha512-sQNlf6rjLq2yB5lELl3gOE7OuoA/6IVXJUJ+Vs7emrQMva14CkOwyQwD7CW+TkmOJ4Q/YGmoDLmbfFrpGmbKng==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.20.0", - "@typescript-eslint/types": "4.20.0", - "@typescript-eslint/typescript-estree": "4.20.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "lru-cache": "^6.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "bin": { + "semver": "bin/semver.js" }, - "peerDependencies": { - "eslint": "*" + "engines": { + "node": ">=10" } }, "node_modules/@typescript-eslint/parser": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.20.0.tgz", - "integrity": "sha512-m6vDtgL9EABdjMtKVw5rr6DdeMCH3OA1vFb0dAyuZSa3e5yw1YRzlwFnm9knma9Lz6b2GPvoNSa8vOXrqsaglA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "4.20.0", - "@typescript-eslint/types": "4.20.0", - "@typescript-eslint/typescript-estree": "4.20.0", - "debug": "^4.1.1" + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -3562,29 +3670,56 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.20.0.tgz", - "integrity": "sha512-/zm6WR6iclD5HhGpcwl/GOYDTzrTHmvf8LLLkwKqqPKG6+KZt/CfSgPCiybshmck66M2L5fWSF/MKNuCwtKQSQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.20.0", - "@typescript-eslint/visitor-keys": "4.20.0" + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@typescript-eslint/types": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.20.0.tgz", - "integrity": "sha512-cYY+1PIjei1nk49JAPnH1VEnu7OYdWRdJhYI5wiKOUMhLTG1qsx5cQxCUTuwWCmQoyriadz3Ni8HZmGSofeC+w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3592,21 +3727,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.20.0.tgz", - "integrity": "sha512-Knpp0reOd4ZsyoEJdW8i/sK3mtZ47Ls7ZHvD8WVABNx5Xnn7KhenMTRGegoyMTx6TiXlOVgMz9r0pDgXTEEIHA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.20.0", - "@typescript-eslint/visitor-keys": "4.20.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3618,23 +3754,108 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/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, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.20.0.tgz", - "integrity": "sha512-NXKRM3oOVQL8yNFDNCZuieRIwZ5UtjNLYtmMx2PacEAGmbaEYtGgVHUHVyZvU/0rYZcizdrWjDo+WBtRPSgq+A==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.20.0", - "eslint-visitor-keys": "^2.0.0" + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "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 + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -3859,9 +4080,9 @@ } }, "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -4060,6 +4281,12 @@ "node": ">=4" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -4231,6 +4458,22 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "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, + "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-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -4247,15 +4490,15 @@ "dev": true }, "node_modules/array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "is-string": "^1.0.7" }, "engines": { @@ -4274,15 +4517,16 @@ "node": ">=8" } }, - "node_modules/array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "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, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "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" @@ -4291,15 +4535,33 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", - "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", + "node_modules/array.prototype.tosorted": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", + "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.1.0", + "es-shim-unscopables": "^1.0.2" + } + }, + "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, + "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" @@ -4348,6 +4610,15 @@ "node": ">=0.12.0" } }, + "node_modules/asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4370,32 +4641,19 @@ "node": ">=10.12.0" } }, - "node_modules/autoprefixer": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.0.tgz", - "integrity": "sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA==", + "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, - "peer": true, "dependencies": { - "browserslist": "^4.17.5", - "caniuse-lite": "^1.0.30001272", - "fraction.js": "^4.1.1", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.1.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/axios": { @@ -5081,13 +5339,19 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "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" @@ -5141,7 +5405,7 @@ "node_modules/can-use-dom": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz", - "integrity": "sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo=" + "integrity": "sha512-ceOhN1DL7Y4O6M0j9ICgmTYziV89WMd96SvSl0REd8PMgrY0B/WBOPoed5S1KUmJqXgUXh8gzSe6E3ae27upsQ==" }, "node_modules/caniuse-lite": { "version": "1.0.30001284", @@ -5210,9 +5474,9 @@ } }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "dependencies": { "anymatch": "~3.1.2", @@ -5226,6 +5490,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -5750,6 +6017,19 @@ "typescript": "^4.0.2" } }, + "node_modules/config-file-ts/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/config-ini-parser": { "version": "1.5.9", "resolved": "https://registry.npmjs.org/config-ini-parser/-/config-ini-parser-1.5.9.tgz", @@ -5769,15 +6049,6 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, - "node_modules/contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -5832,16 +6103,6 @@ "is-what": "^3.12.0" } }, - "node_modules/core-js": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.9.1.tgz", - "integrity": "sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-js-compat": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.9.1.tgz", @@ -5972,15 +6233,6 @@ "node": ">=4" } }, - "node_modules/css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/css-has-pseudo": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", @@ -6406,9 +6658,9 @@ } }, "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "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 }, "node_modules/deepmerge": { @@ -6496,6 +6748,23 @@ "node": ">=10" } }, + "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, + "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-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -6506,15 +6775,20 @@ } }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "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/defined": { @@ -6786,6 +7060,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, "dependencies": { "domelementtype": "^2.0.1", "entities": "^2.0.0" @@ -6795,6 +7070,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "dev": true, "funding": [ { "type": "github", @@ -6806,6 +7082,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -6818,20 +7095,14 @@ "node_modules/domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "node_modules/domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dependencies": { - "domelementtype": "1" - } + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true }, "node_modules/domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, "dependencies": { "dom-serializer": "0", "domelementtype": "1" @@ -7353,11 +7624,6 @@ "node": ">=8.6" } }, - "node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -7406,31 +7672,52 @@ } }, "node_modules/es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz", + "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.7", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.2", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.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.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.1", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "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.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.0", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.1", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -7439,11 +7726,81 @@ "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, + "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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz", + "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==", + "dev": true, + "dependencies": { + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.4", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.2", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" }, + "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, + "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, + "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", @@ -7491,187 +7848,124 @@ } }, "node_modules/eslint": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz", - "integrity": "sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", - "ajv": "^6.10.0", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "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.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.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", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "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", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", - "minimatch": "^3.0.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "dependencies": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "dependencies": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/eslint-module-utils/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/eslint-plugin-import": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", - "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, "dependencies": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" }, "engines": { - "node": ">=4" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "dependencies": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" }, - "engines": { - "node": ">=0.10.0" + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "node_modules/eslint-plugin-react": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.27.1.tgz", - "integrity": "sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA==", + "version": "7.33.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", + "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", "dev": true, "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flatmap": "^1.2.5", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.12", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.0", - "object.values": "^1.1.5", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.6" + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.8" }, "engines": { "node": ">=4" @@ -7680,6 +7974,18 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "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/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -7702,39 +8008,45 @@ } }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "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, "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "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-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/eslint-plugin-tailwindcss": { - "version": "1.17.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-1.17.2.tgz", - "integrity": "sha512-cQWP7bSdJHcnBIv/eCXqerMYiwn7qfpyRvqzCiGhttWBhjr+h5q6XsAgg967mdUeJzzQr+68ETXNHLkCK7KJQA==", + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.14.3.tgz", + "integrity": "sha512-1MKT8CrVuqVJleHxb7ICHsF2QwO0G+VJ28athTtlcOkccp0qmwK7nCUa1C9paCZ+VVgQU4fonsjLz/wUxoMHJQ==", "dev": true, "dependencies": { "fast-glob": "^3.2.5", - "postcss": "^8.3.0", - "tailwindcss": "^2.1.2" + "postcss": "^8.4.4" }, "engines": { "node": ">=12.13.0" + }, + "peerDependencies": { + "tailwindcss": "^3.4.0" } }, "node_modules/eslint-scope": { @@ -7749,46 +8061,16 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "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, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ansi-styles": { @@ -7806,15 +8088,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/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, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -7849,10 +8122,59 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/eslint/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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, + "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/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, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/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, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/eslint/node_modules/globals": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.7.0.tgz", - "integrity": "sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -7873,25 +8195,6 @@ "node": ">=8" } }, - "node_modules/eslint/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, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7917,26 +8220,32 @@ } }, "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/espree/node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=4" + "node": ">=0.4.0" } }, "node_modules/esprima": { @@ -7952,9 +8261,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -7964,9 +8273,9 @@ } }, "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -8162,10 +8471,16 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -8175,7 +8490,7 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -8406,6 +8721,22 @@ "node": ">=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, + "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.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -8451,6 +8782,15 @@ } } }, + "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, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -8686,20 +9026,6 @@ "integrity": "sha512-MiHrA5teO6t8zKArE3DdMPT/Db6v2GUt5yfWnhBTrrsVfeCJUUnV6sgFvjGNBKDmEMqVwRFkEePL7wPwqrLKKA==", "dev": true }, - "node_modules/fraction.js": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", - "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -8761,16 +9087,40 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true + "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, + "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, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gauge": { "version": "4.0.4", @@ -8809,14 +9159,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "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" @@ -8844,13 +9199,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "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, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { "node": ">= 0.4" @@ -8936,7 +9292,6 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, - "optional": true, "dependencies": { "define-properties": "^1.1.3" }, @@ -8948,16 +9303,16 @@ } }, "node_modules/globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -8967,13 +9322,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, - "engines": { - "node": ">= 4" + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/got": { @@ -9006,24 +9364,18 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -9046,9 +9398,9 @@ } }, "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "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, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9062,10 +9414,34 @@ "node": ">=4" } }, - "node_modules/has-symbols": { + "node_modules/has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "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, + "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, "engines": { "node": ">= 0.4" @@ -9075,12 +9451,12 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -9094,6 +9470,18 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dev": true, + "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", @@ -9103,12 +9491,6 @@ "he": "bin/he" } }, - "node_modules/hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", - "dev": true - }, "node_modules/history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -9154,18 +9536,6 @@ "wbuf": "^1.1.0" } }, - "node_modules/hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", - "dev": true - }, - "node_modules/hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", - "dev": true - }, "node_modules/html-entities": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", @@ -9234,32 +9604,6 @@ "node": ">=6" } }, - "node_modules/htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dependencies": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } - }, - "node_modules/htmlparser2/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -9481,9 +9825,9 @@ ] }, "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -9523,18 +9867,6 @@ "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", "dev": true }, - "node_modules/import-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", - "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dev": true, - "dependencies": { - "import-from": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -9551,27 +9883,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-from/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, - "engines": { - "node": ">=8" - } - }, "node_modules/import-local": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", @@ -9806,13 +10117,13 @@ } }, "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "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, "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -9909,17 +10220,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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, + "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": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "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, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-bigint": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", - "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9937,12 +10282,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", - "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "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, "dependencies": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -9952,9 +10298,9 @@ } }, "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "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, "engines": { "node": ">= 0.4" @@ -9975,37 +10321,26 @@ "is-ci": "bin.js" } }, - "node_modules/is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dev": true, - "dependencies": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - } - }, "node_modules/is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "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, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -10046,6 +10381,18 @@ "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, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "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", @@ -10054,6 +10401,21 @@ "node": ">=8" } }, + "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, + "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", @@ -10102,10 +10464,19 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "engines": { "node": ">= 0.4" @@ -10124,10 +10495,13 @@ } }, "node_modules/is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -10210,11 +10584,26 @@ "node": ">=0.10.0" } }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "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, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10258,6 +10647,21 @@ "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, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -10270,13 +10674,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "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, "dependencies": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10332,10 +10758,23 @@ "node": ">=0.10.0" } }, + "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, + "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/jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -10426,18 +10865,6 @@ "node": ">=8" } }, - "node_modules/jake/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/jake/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -10485,6 +10912,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-base64": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", @@ -10886,9 +11322,9 @@ } }, "node_modules/lilconfig": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", - "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", "dev": true, "engines": { "node": ">=10" @@ -11101,42 +11537,6 @@ "node": ">=8" } }, - "node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/load-json-file/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -11158,11 +11558,31 @@ "node": ">=8.9.0" } }, + "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, + "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-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -11172,19 +11592,14 @@ "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", - "dev": true - }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -11196,26 +11611,10 @@ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", "dev": true }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" - }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" - }, - "node_modules/lodash.topath": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", - "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "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 }, "node_modules/log-symbols": { @@ -12333,13 +12732,13 @@ ] }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -12421,9 +12820,9 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -12635,6 +13034,17 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz", @@ -12977,9 +13387,9 @@ } }, "node_modules/object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13011,14 +13421,14 @@ } }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "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, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -13029,28 +13439,28 @@ } }, "node_modules/object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -13077,27 +13487,27 @@ } }, "node_modules/object.hasown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", - "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", "dev": true, "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -13182,17 +13592,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "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.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -13366,6 +13776,21 @@ "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, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -13530,13 +13955,13 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.0.tgz", - "integrity": "sha512-tZFEaRQbMLjwrsmidsGJ6wDMv0iazJWk6SfIKnY4Xru8auXgmJkOBa5DUbYFcFD2Rzk2+KDlIiF0GVXNCbgC7g==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "dependencies": { "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2" + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -13585,9 +14010,9 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -13608,83 +14033,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { - "node": ">=4" + "node": ">= 6" } }, "node_modules/pkg-up": { @@ -13809,10 +14164,19 @@ "ms": "^2.1.1" } }, + "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, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { - "version": "8.4.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", - "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "funding": [ { @@ -13822,10 +14186,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.1", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -15010,31 +15378,64 @@ } }, "node_modules/postcss-load-config": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", - "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "import-cwd": "^3.0.0", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" }, "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">= 14" }, "peerDependencies": { + "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "peerDependenciesMeta": { + "postcss": { + "optional": true + }, "ts-node": { "optional": true } } }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz", + "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/postcss-loader": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.2.0.tgz", @@ -15895,9 +16296,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -15927,10 +16328,16 @@ } }, "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -15998,6 +16405,34 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", @@ -16062,13 +16497,13 @@ } }, "node_modules/prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "node_modules/property-information": { @@ -16388,17 +16823,6 @@ "node": ">= 8" } }, - "node_modules/react-html-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/react-html-parser/-/react-html-parser-2.0.2.tgz", - "integrity": "sha512-XeerLwCVjTs3njZcgCOeDUqLgNIt/t+6Jgi5/qPsO/krUWl76kWKXMeVs2LhY2gwM6X378DkhLjur0zUQdpz0g==", - "dependencies": { - "htmlparser2": "^3.9.0" - }, - "peerDependencies": { - "react": "^0.14.0 || ^15.0.0 || ^16.0.0-0" - } - }, "node_modules/react-intersection-observer": { "version": "8.33.1", "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.33.1.tgz", @@ -16660,121 +17084,6 @@ "npm-normalize-package-bin": "^1.0.0" } }, - "node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -16867,6 +17176,27 @@ "redux": "^4" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", + "integrity": "sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.0.0", + "get-intrinsic": "^1.2.3", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -16886,9 +17216,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.14.5", @@ -16900,13 +17230,15 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "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, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "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" @@ -16915,18 +17247,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/regexpu-core": { "version": "4.7.1", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", @@ -17188,19 +17508,18 @@ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz", "integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==" }, - "node_modules/resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" - }, "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "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" @@ -17291,18 +17610,6 @@ "node": ">=0.10.0" } }, - "node_modules/rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", - "dev": true - }, - "node_modules/rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", - "dev": true - }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -17377,11 +17684,52 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/safe-array-concat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/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 + }, "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==" }, + "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, + "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/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -17655,6 +18003,38 @@ "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.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "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, + "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/set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -17763,30 +18143,26 @@ "semver": "bin/semver.js" } }, - "node_modules/simplebar": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/simplebar/-/simplebar-5.3.3.tgz", - "integrity": "sha512-OfuSX47Axq9aR6rp9WK3YefAg+1Qw3UKKxS46PdElPpd+FWXMj17/nispYxsHtU3F7mv+ilmqELWmRt7KUgHgg==", + "node_modules/simplebar-core": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/simplebar-core/-/simplebar-core-1.2.4.tgz", + "integrity": "sha512-P+Sqshef4fq3++gQ82TgNYcgl3qZFSCP5jS2/8NMmw18oagXOijMzs1G+vm6RUY3oMvpwH3wGoqh9u6SyDjHfQ==", "dependencies": { + "@types/lodash-es": "^4.17.6", "can-use-dom": "^0.1.0", - "core-js": "^3.0.1", - "lodash.debounce": "^4.0.8", - "lodash.memoize": "^4.1.2", - "lodash.throttle": "^4.1.1", - "resize-observer-polyfill": "^1.5.1" + "lodash": "^4.17.21", + "lodash-es": "^4.17.21" } }, "node_modules/simplebar-react": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/simplebar-react/-/simplebar-react-2.3.3.tgz", - "integrity": "sha512-hfyRz3MVSAoyZfTB2zZcpzVtz+BQ7mMBVN+BNMgaAKujKyIrBQ69xIcwrZpBRWRqPd4WHmM0x6DMQpRGPJa7MQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/simplebar-react/-/simplebar-react-3.2.4.tgz", + "integrity": "sha512-ogLN79e7JUm82wJChD7NSUB+4EHCFvDkjXpiu8hT1Alk7DnCekUWds61NXcsP9jC97KOgF5To/AVjYFbX0olgg==", "dependencies": { - "prop-types": "^15.6.1", - "simplebar": "^5.3.3" + "simplebar-core": "^1.2.4" }, "peerDependencies": { - "react": "^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0", - "react-dom": "^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0" + "react": ">=16.8.0" } }, "node_modules/slash": { @@ -18168,45 +18544,65 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", "side-channel": "^1.0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -18259,15 +18655,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -18346,6 +18733,83 @@ "react-is": ">= 16.8.0" } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/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, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -18369,6 +18833,18 @@ "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, + "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", @@ -18430,96 +18906,20 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "node_modules/table": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz", - "integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "lodash.clonedeep": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.0.1.tgz", - "integrity": "sha512-46ZA4TalFcLLqX1dEU3dhdY38wAtDydJ4e7QQTVekLUTzXkb1LfqU6VOBXC/a9wiv4T094WURqJH6ZitF92Kqw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/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==", + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/table/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/table/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 - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/table/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://opencollective.com/unts" } }, "node_modules/tabler-icons-react": { @@ -18530,136 +18930,61 @@ "react": ">= 16.8.0" } }, + "node_modules/tailwind-merge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.1.tgz", + "integrity": "sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==", + "dependencies": { + "@babel/runtime": "^7.23.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { - "version": "2.2.19", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.19.tgz", - "integrity": "sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", "dev": true, "dependencies": { - "arg": "^5.0.1", - "bytes": "^3.0.0", - "chalk": "^4.1.2", - "chokidar": "^3.5.2", - "color": "^4.0.1", - "cosmiconfig": "^7.0.1", - "detective": "^5.2.0", + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.2.7", - "fs-extra": "^10.0.0", - "glob-parent": "^6.0.1", - "html-tags": "^3.1.0", - "is-color-stop": "^1.1.0", - "is-glob": "^4.0.1", - "lodash": "^4.17.21", - "lodash.topath": "^4.5.2", - "modern-normalize": "^1.1.0", - "node-emoji": "^1.11.0", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", "normalize-path": "^3.0.0", - "object-hash": "^2.2.0", - "postcss-js": "^3.0.3", - "postcss-load-config": "^3.1.0", - "postcss-nested": "5.0.6", - "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0", - "pretty-hrtime": "^1.0.3", - "purgecss": "^4.0.3", - "quick-lru": "^5.1.1", - "reduce-css-calc": "^2.1.8", - "resolve": "^1.20.0", - "tmp": "^0.2.1" + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" }, "engines": { - "node": ">=12.13.0" - }, - "peerDependencies": { - "autoprefixer": "^10.0.2", - "postcss": "^8.0.9" - } - }, - "node_modules/tailwindcss/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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=14.0.0" } }, "node_modules/tailwindcss/node_modules/arg": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", - "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==", - "dev": true - }, - "node_modules/tailwindcss/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, - "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/tailwindcss/node_modules/color": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/color/-/color-4.1.0.tgz", - "integrity": "sha512-o2rkkxyLGgYoeUy1OodXpbPAQNmlNBrirQ8ODO8QutzDiDMNdezSOZLNnusQ6pUpCQJUsaJIo9DZJKqa2HgH7A==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - } - }, - "node_modules/tailwindcss/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/tailwindcss/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==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "dev": true }, - "node_modules/tailwindcss/node_modules/fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/tailwindcss/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -18672,51 +18997,58 @@ "node": ">=10.13.0" } }, - "node_modules/tailwindcss/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==", + "node_modules/tailwindcss/node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/tailwindcss/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/tailwindcss/node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "dev": true, "dependencies": { - "universalify": "^2.0.0" + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" } }, "node_modules/tailwindcss/node_modules/postcss-js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", - "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", "dev": true, "dependencies": { - "camelcase-css": "^2.0.1", - "postcss": "^8.1.6" + "camelcase-css": "^2.0.1" }, "engines": { - "node": ">=10.0" + "node": "^12 || ^14 || >= 16" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" } }, "node_modules/tailwindcss/node_modules/postcss-nested": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", - "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.6" + "postcss-selector-parser": "^6.0.11" }, "engines": { "node": ">=12.0" @@ -18729,42 +19061,6 @@ "postcss": "^8.2.14" } }, - "node_modules/tailwindcss/node_modules/purgecss": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.1.3.tgz", - "integrity": "sha512-99cKy4s+VZoXnPxaoM23e5ABcP851nC2y2GROkkjS8eJaJtlciGavd7iYAw2V84WeBqggZ12l8ef44G99HmTaw==", - "dev": true, - "dependencies": { - "commander": "^8.0.0", - "glob": "^7.1.7", - "postcss": "^8.3.5", - "postcss-selector-parser": "^6.0.6" - }, - "bin": { - "purgecss": "bin/purgecss.js" - } - }, - "node_modules/tailwindcss/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -18972,6 +19268,27 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -19085,6 +19402,24 @@ "utf8-byte-length": "^1.0.1" } }, + "node_modules/ts-api-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", + "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, "node_modules/ts-loader": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.1.0.tgz", @@ -19201,56 +19536,10 @@ "typescript": ">=2.7" } }, - "node_modules/tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/type-check": { "version": "0.4.0", @@ -19288,17 +19577,90 @@ "node": ">= 0.6" } }, + "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, + "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, + "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, + "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.5", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", + "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", + "dev": true, + "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/typescript": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", - "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/un-eval": { @@ -19307,14 +19669,14 @@ "integrity": "sha512-Wlj/pum6dQtGTPD/lclDtoVPkSfpjPfy1dwnnKw/sZP5DpBH9fLhBgQfsqNhe5/gS1D+vkZUuB771NRMUPA5CA==" }, "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { @@ -19706,12 +20068,6 @@ "uuid": "bin/uuid" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -20285,6 +20641,72 @@ "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, + "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-builtin-type/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 + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", + "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.5", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -20304,15 +20726,6 @@ "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.4.tgz", "integrity": "sha512-IHpzORub7kYlb8A43Iig3reOvlcBJGX9gZ0WycHhghHtA65X0LYnMRuJs+aH1abVnMJztQkvQNlltnbPi5aGIA==" }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -20574,6 +20987,18 @@ } }, "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, + "@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true + }, "@babel/code-frame": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", @@ -21635,11 +22060,11 @@ } }, "@babel/runtime": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", - "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" } }, "@babel/template": { @@ -22072,65 +22497,61 @@ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" }, + "@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, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, "@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "requires": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "type-fest": "^0.20.2" } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "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 } } }, + "@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 + }, "@flybywiresim/api-client": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@flybywiresim/api-client/-/api-client-0.15.0.tgz", @@ -22148,14 +22569,6 @@ "zip-lib": "^0.7.3" } }, - "@flybywiresim/react-components": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@flybywiresim/react-components/-/react-components-0.2.5.tgz", - "integrity": "sha512-SRCeC58kYcxqEk2CKt5sUsp9UhtmdkQwe5bTdgDTAbS4o3LhKQfeB1SYXqBoh6CQyOdwzFRSd5gTqAYIcbPTtQ==", - "requires": { - "tabler-icons-react": "~1.33.0" - } - }, "@flybywiresim/tailwind-config": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@flybywiresim/tailwind-config/-/tailwind-config-0.3.1.tgz", @@ -22171,6 +22584,29 @@ "purgecss": "^3.1.3" } }, + "@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + } + }, + "@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 + }, + "@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -22236,6 +22672,45 @@ } } }, + "@jridgewell/gen-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz", + "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@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 + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz", + "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -22297,28 +22772,28 @@ } }, "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "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, "requires": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "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 }, "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "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, "requires": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, @@ -22338,6 +22813,12 @@ "dev": true, "optional": true }, + "@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true + }, "@reduxjs/toolkit": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.7.1.tgz", @@ -22457,11 +22938,6 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" } } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" } } }, @@ -22957,9 +23433,9 @@ "dev": true }, "@types/eslint": { - "version": "7.2.8", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.8.tgz", - "integrity": "sha512-RTKvBsfz0T8CKOGZMfuluDNyMFHnu5lvNr4hWEsQeHXH6FcmIDIozOyWMh36nLGMwVd5UFNXC2xztA8lln22MQ==", + "version": "8.56.3", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.3.tgz", + "integrity": "sha512-PvSf1wfv2wJpVIFUMSb+i4PvqNYkB9Rkp9ZDO3oaWzq4SKhsQk4mrMBr3ZH06I0hKrVGLBacmgl8JM4WVjb9dg==", "requires": { "@types/estree": "*", "@types/json-schema": "*" @@ -23038,15 +23514,9 @@ "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "@types/keyv": { "version": "3.1.4", @@ -23060,8 +23530,15 @@ "@types/lodash": { "version": "4.14.168", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz", - "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==", - "dev": true + "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==" + }, + "@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "requires": { + "@types/lodash": "*" + } }, "@types/mdast": { "version": "3.0.10", @@ -23192,9 +23669,9 @@ "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" }, "@types/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-4g1jrL98mdOIwSOUh6LTlB0Cs9I0dQPwINUhBg7C6pN4HLr8GS8xsksJxilW6S6dQHVi2K/o+lQuQcg7LroCnw==" + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" }, "@types/styled-components": { "version": "5.1.9", @@ -23268,88 +23745,163 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.20.0.tgz", - "integrity": "sha512-sw+3HO5aehYqn5w177z2D82ZQlqHCwcKSMboueo7oE4KU9QiC0SAgfS/D4z9xXvpTc8Bt41Raa9fBR8T2tIhoQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.20.0", - "@typescript-eslint/scope-manager": "4.20.0", - "debug": "^4.1.1", - "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, - "@typescript-eslint/experimental-utils": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.20.0.tgz", - "integrity": "sha512-sQNlf6rjLq2yB5lELl3gOE7OuoA/6IVXJUJ+Vs7emrQMva14CkOwyQwD7CW+TkmOJ4Q/YGmoDLmbfFrpGmbKng==", + "@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.20.0", - "@typescript-eslint/types": "4.20.0", - "@typescript-eslint/typescript-estree": "4.20.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" } }, - "@typescript-eslint/parser": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.20.0.tgz", - "integrity": "sha512-m6vDtgL9EABdjMtKVw5rr6DdeMCH3OA1vFb0dAyuZSa3e5yw1YRzlwFnm9knma9Lz6b2GPvoNSa8vOXrqsaglA==", + "@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.20.0", - "@typescript-eslint/types": "4.20.0", - "@typescript-eslint/typescript-estree": "4.20.0", - "debug": "^4.1.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" } }, - "@typescript-eslint/scope-manager": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.20.0.tgz", - "integrity": "sha512-/zm6WR6iclD5HhGpcwl/GOYDTzrTHmvf8LLLkwKqqPKG6+KZt/CfSgPCiybshmck66M2L5fWSF/MKNuCwtKQSQ==", + "@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "requires": { - "@typescript-eslint/types": "4.20.0", - "@typescript-eslint/visitor-keys": "4.20.0" + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.20.0.tgz", - "integrity": "sha512-cYY+1PIjei1nk49JAPnH1VEnu7OYdWRdJhYI5wiKOUMhLTG1qsx5cQxCUTuwWCmQoyriadz3Ni8HZmGSofeC+w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.20.0.tgz", - "integrity": "sha512-Knpp0reOd4ZsyoEJdW8i/sK3mtZ47Ls7ZHvD8WVABNx5Xnn7KhenMTRGegoyMTx6TiXlOVgMz9r0pDgXTEEIHA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "requires": { - "@typescript-eslint/types": "4.20.0", - "@typescript-eslint/visitor-keys": "4.20.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "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, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@typescript-eslint/visitor-keys": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.20.0.tgz", - "integrity": "sha512-NXKRM3oOVQL8yNFDNCZuieRIwZ5UtjNLYtmMx2PacEAGmbaEYtGgVHUHVyZvU/0rYZcizdrWjDo+WBtRPSgq+A==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "requires": { - "@typescript-eslint/types": "4.20.0", - "eslint-visitor-keys": "^2.0.0" + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" } }, + "@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 + }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -23549,9 +24101,9 @@ "dev": true }, "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, @@ -23694,6 +24246,12 @@ "color-convert": "^1.9.0" } }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, "anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -23840,6 +24398,16 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "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, + "requires": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + } + }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -23853,15 +24421,15 @@ "dev": true }, "array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "is-string": "^1.0.7" } }, @@ -23871,26 +24439,45 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "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, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" } }, - "array.prototype.flatmap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", - "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", + "array.prototype.tosorted": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", + "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.1.0", + "es-shim-unscopables": "^1.0.2" + } + }, + "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, + "requires": { + "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" } }, "asap": { @@ -23924,6 +24511,15 @@ "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", "dev": true }, + "asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.3" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -23940,19 +24536,13 @@ "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==" }, - "autoprefixer": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.0.tgz", - "integrity": "sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA==", + "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, - "peer": true, "requires": { - "browserslist": "^4.17.5", - "caniuse-lite": "^1.0.30001272", - "fraction.js": "^4.1.1", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.1.0" + "possible-typed-array-names": "^1.0.0" } }, "axios": { @@ -24482,13 +25072,16 @@ } }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "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" } }, "callsites": { @@ -24527,7 +25120,7 @@ "can-use-dom": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz", - "integrity": "sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo=" + "integrity": "sha512-ceOhN1DL7Y4O6M0j9ICgmTYziV89WMd96SvSl0REd8PMgrY0B/WBOPoed5S1KUmJqXgUXh8gzSe6E3ae27upsQ==" }, "caniuse-lite": { "version": "1.0.30001284", @@ -24570,9 +25163,9 @@ "integrity": "sha512-iOrT8yCZjSnyNZ43476FE2rnssvgw5hnuwOM0hm8Nj1qa0v4ieUUEbCyxxsEliaoDUb/75yCOL71zkDiDBLbMQ==" }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -24984,6 +25577,14 @@ "requires": { "glob": "^7.1.6", "typescript": "^4.0.2" + }, + "dependencies": { + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true + } } }, "config-ini-parser": { @@ -25002,12 +25603,6 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -25053,11 +25648,6 @@ "is-what": "^3.12.0" } }, - "core-js": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.9.1.tgz", - "integrity": "sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg==" - }, "core-js-compat": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.9.1.tgz", @@ -25160,12 +25750,6 @@ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, "css-has-pseudo": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", @@ -25473,9 +26057,9 @@ } }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "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 }, "deepmerge": { @@ -25538,6 +26122,17 @@ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true }, + "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, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, "define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -25545,12 +26140,14 @@ "dev": true }, "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "defined": { @@ -25775,6 +26372,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, "requires": { "domelementtype": "^2.0.1", "entities": "^2.0.0" @@ -25783,12 +26381,14 @@ "domelementtype": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", - "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==" + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "dev": true }, "entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true } } }, @@ -25800,20 +26400,14 @@ "domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" - } + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true }, "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, "requires": { "dom-serializer": "0", "domelementtype": "1" @@ -26225,11 +26819,6 @@ "ansi-colors": "^4.1.1" } }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, "env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -26266,31 +26855,90 @@ } }, "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz", + "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.7", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.2", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.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.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.1", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "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.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.0", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.1", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.14" + } + }, + "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, + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "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 + }, + "es-iterator-helpers": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz", + "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==", + "dev": true, + "requires": { + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.4", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.2", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.0" } }, "es-module-lexer": { @@ -26298,6 +26946,26 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" }, + "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, + "requires": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + } + }, + "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, + "requires": { + "hasown": "^2.0.0" + } + }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -26333,59 +27001,51 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz", - "integrity": "sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", - "ajv": "^6.10.0", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "requires": { + "@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.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.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", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "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", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", - "minimatch": "^3.0.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -26395,15 +27055,6 @@ "color-convert": "^2.0.1" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -26429,35 +27080,50 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "globals": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.7.0.tgz", - "integrity": "sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA==", + "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 + }, + "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, "requires": { - "type-fest": "^0.20.2" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, - "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==", + "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, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "is-glob": "^4.0.3" } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "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 }, "supports-color": { @@ -26477,128 +27143,45 @@ } } }, - "eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } + "requires": {} }, - "eslint-plugin-import": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", - "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, "requires": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" } }, "eslint-plugin-react": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.27.1.tgz", - "integrity": "sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA==", + "version": "7.33.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", + "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", "dev": true, "requires": { - "array-includes": "^3.1.4", - "array.prototype.flatmap": "^1.2.5", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.12", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.0", - "object.values": "^1.1.5", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.6" + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.8" }, "dependencies": { "doctrine": { @@ -26617,32 +27200,39 @@ "dev": true }, "resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "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, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } }, + "eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "requires": {} + }, "eslint-plugin-tailwindcss": { - "version": "1.17.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-1.17.2.tgz", - "integrity": "sha512-cQWP7bSdJHcnBIv/eCXqerMYiwn7qfpyRvqzCiGhttWBhjr+h5q6XsAgg967mdUeJzzQr+68ETXNHLkCK7KJQA==", + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.14.3.tgz", + "integrity": "sha512-1MKT8CrVuqVJleHxb7ICHsF2QwO0G+VJ28athTtlcOkccp0qmwK7nCUa1C9paCZ+VVgQU4fonsjLz/wUxoMHJQ==", "dev": true, "requires": { "fast-glob": "^3.2.5", - "postcss": "^8.3.0", - "tailwindcss": "^2.1.2" + "postcss": "^8.4.4" } }, "eslint-scope": { @@ -26654,44 +27244,27 @@ "estraverse": "^4.1.1" } }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "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 }, "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true } } @@ -26702,18 +27275,18 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" }, "dependencies": { "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } @@ -26874,10 +27447,16 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -27076,6 +27655,16 @@ } } }, + "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, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -27103,6 +27692,15 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" }, + "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, + "requires": { + "is-callable": "^1.1.3" + } + }, "foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -27267,13 +27865,6 @@ "integrity": "sha512-MiHrA5teO6t8zKArE3DdMPT/Db6v2GUt5yfWnhBTrrsVfeCJUUnV6sgFvjGNBKDmEMqVwRFkEePL7wPwqrLKKA==", "dev": true }, - "fraction.js": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", - "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==", - "dev": true, - "peer": true - }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -27319,15 +27910,27 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "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, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + } + }, + "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 }, "gauge": { @@ -27358,14 +27961,16 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-own-enumerable-property-symbols": { @@ -27384,13 +27989,14 @@ } }, "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "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, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" } }, "glob": { @@ -27455,31 +28061,31 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, - "optional": true, "requires": { "define-properties": "^1.1.3" } }, "globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" } }, "got": { @@ -27506,21 +28112,18 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -27539,9 +28142,9 @@ } }, "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "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 }, "has-flag": { @@ -27549,19 +28152,34 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, - "has-symbols": { + "has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0" + } + }, + "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 + }, + "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 }, "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "requires": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" } }, "has-unicode": { @@ -27569,18 +28187,21 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "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 }, - "hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", - "dev": true - }, "history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -27623,18 +28244,6 @@ "wbuf": "^1.1.0" } }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", - "dev": true - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", - "dev": true - }, "html-entities": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", @@ -27683,31 +28292,6 @@ } } }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, "http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -27876,9 +28460,9 @@ "dev": true }, "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true }, "image-size": { @@ -27905,15 +28489,6 @@ "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", "dev": true }, - "import-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", - "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dev": true, - "requires": { - "import-from": "^3.0.0" - } - }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -27924,23 +28499,6 @@ "resolve-from": "^4.0.0" } }, - "import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "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 - } - } - }, "import-local": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", @@ -28116,13 +28674,13 @@ } }, "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "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, "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "hasown": "^2.0.0", "side-channel": "^1.0.4" } }, @@ -28188,17 +28746,39 @@ "has-tostringtag": "^1.0.0" } }, + "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, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "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, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-bigint": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", - "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==", - "dev": true + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } }, "is-binary-path": { "version": "2.1.0", @@ -28210,18 +28790,19 @@ } }, "is-boolean-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", - "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "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, "requires": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "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 }, "is-ci": { @@ -28233,35 +28814,24 @@ "ci-info": "^3.2.0" } }, - "is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "requires": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" + "hasown": "^2.0.0" } }, - "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "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, "requires": { - "has": "^1.0.3" + "has-tostringtag": "^1.0.0" } }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, "is-decimal": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.0.tgz", @@ -28279,11 +28849,29 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, + "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, + "requires": { + "call-bind": "^1.0.2" + } + }, "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==" }, + "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, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -28319,10 +28907,16 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, + "is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true + }, "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true }, "is-number": { @@ -28332,10 +28926,13 @@ "dev": true }, "is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-obj": { "version": "2.0.0", @@ -28385,12 +28982,21 @@ "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", "dev": true }, - "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", "dev": true }, + "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, + "requires": { + "call-bind": "^1.0.7" + } + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -28415,19 +29021,44 @@ "has-symbols": "^1.0.1" } }, + "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, + "requires": { + "which-typed-array": "^1.1.14" + } + }, "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 }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true + }, "is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "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, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", "dev": true, "requires": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" } }, "is-what": { @@ -28468,10 +29099,23 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "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, + "requires": { + "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" + } + }, "jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "requires": { "@isaacs/cliui": "^8.0.2", @@ -28530,15 +29174,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -28575,6 +29210,12 @@ } } }, + "jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true + }, "js-base64": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", @@ -28898,9 +29539,9 @@ } }, "lilconfig": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", - "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", "dev": true }, "lines-and-columns": { @@ -29057,35 +29698,6 @@ } } }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, "loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -29101,11 +29713,25 @@ "json5": "^2.1.2" } }, + "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, + "requires": { + "p-locate": "^5.0.0" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -29115,19 +29741,14 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true }, "lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", - "dev": true - }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -29139,26 +29760,10 @@ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", "dev": true }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" - }, - "lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" - }, - "lodash.topath": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", - "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "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 }, "log-symbols": { @@ -29886,13 +30491,13 @@ "integrity": "sha512-UT0ylWEEy80RFYzK9pEaugTqaxoD/j0Y9WhHpSyitxd99zjoQz7JJ+iKuhPAgOW2MiPSUAx+c09dcqokeyaROA==" }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "mime": { @@ -29949,9 +30554,9 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -30117,6 +30722,17 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "nanoid": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz", @@ -30375,9 +30991,9 @@ "dev": true }, "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true }, "object-is": { @@ -30397,37 +31013,37 @@ "dev": true }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "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, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, "object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "object.getownpropertydescriptors": { @@ -30442,24 +31058,24 @@ } }, "object.hasown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", - "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "obuf": { @@ -30519,17 +31135,17 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", "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.3" + "type-check": "^0.4.0" } }, "ora": { @@ -30651,6 +31267,15 @@ "yocto-queue": "^0.1.0" } }, + "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, + "requires": { + "p-limit": "^3.0.2" + } + }, "p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -30775,13 +31400,13 @@ "dev": true }, "path-scurry": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.0.tgz", - "integrity": "sha512-tZFEaRQbMLjwrsmidsGJ6wDMv0iazJWk6SfIKnY4Xru8auXgmJkOBa5DUbYFcFD2Rzk2+KDlIiF0GVXNCbgC7g==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "requires": { "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2" + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "dependencies": { "lru-cache": { @@ -30820,9 +31445,9 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { @@ -30831,65 +31456,11 @@ "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", "dev": true }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } + "pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true }, "pkg-up": { "version": "3.1.0", @@ -30990,21 +31561,27 @@ } } }, + "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 + }, "postcss": { - "version": "8.4.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", - "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "requires": { - "nanoid": "^3.3.1", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, "dependencies": { "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true } } @@ -31912,14 +32489,27 @@ } }, "postcss-load-config": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", - "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", "dev": true, "requires": { - "import-cwd": "^3.0.0", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "dependencies": { + "lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "dev": true + }, + "yaml": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz", + "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==", + "dev": true + } } }, "postcss-loader": { @@ -32589,9 +33179,9 @@ } }, "postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", "dev": true, "requires": { "cssesc": "^3.0.0", @@ -32659,6 +33249,22 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "peer": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", @@ -32710,13 +33316,13 @@ } }, "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "property-information": { @@ -32940,14 +33546,6 @@ } } }, - "react-html-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/react-html-parser/-/react-html-parser-2.0.2.tgz", - "integrity": "sha512-XeerLwCVjTs3njZcgCOeDUqLgNIt/t+6Jgi5/qPsO/krUWl76kWKXMeVs2LhY2gwM6X378DkhLjur0zUQdpz0g==", - "requires": { - "htmlparser2": "^3.9.0" - } - }, "react-intersection-observer": { "version": "8.33.1", "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.33.1.tgz", @@ -33168,95 +33766,6 @@ "npm-normalize-package-bin": "^1.0.0" } }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "dependencies": { - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -33341,6 +33850,21 @@ "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==", "requires": {} }, + "reflect.getprototypeof": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", + "integrity": "sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.0.0", + "get-intrinsic": "^1.2.3", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -33357,9 +33881,9 @@ } }, "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "regenerator-transform": { "version": "0.14.5", @@ -33371,21 +33895,17 @@ } }, "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "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, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" } }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, "regexpu-core": { "version": "4.7.1", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", @@ -33581,19 +34101,15 @@ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz", "integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==" }, - "resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" - }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-alpn": { @@ -33661,18 +34177,6 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", - "dev": true - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", - "dev": true - }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -33723,11 +34227,42 @@ } } }, + "safe-array-concat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "dev": true, + "requires": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, "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==" }, + "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, + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + } + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -33960,6 +34495,32 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, + "set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dev": true, + "requires": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + } + }, + "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, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + } + }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -34051,26 +34612,23 @@ } } }, - "simplebar": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/simplebar/-/simplebar-5.3.3.tgz", - "integrity": "sha512-OfuSX47Axq9aR6rp9WK3YefAg+1Qw3UKKxS46PdElPpd+FWXMj17/nispYxsHtU3F7mv+ilmqELWmRt7KUgHgg==", + "simplebar-core": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/simplebar-core/-/simplebar-core-1.2.4.tgz", + "integrity": "sha512-P+Sqshef4fq3++gQ82TgNYcgl3qZFSCP5jS2/8NMmw18oagXOijMzs1G+vm6RUY3oMvpwH3wGoqh9u6SyDjHfQ==", "requires": { + "@types/lodash-es": "^4.17.6", "can-use-dom": "^0.1.0", - "core-js": "^3.0.1", - "lodash.debounce": "^4.0.8", - "lodash.memoize": "^4.1.2", - "lodash.throttle": "^4.1.1", - "resize-observer-polyfill": "^1.5.1" + "lodash": "^4.17.21", + "lodash-es": "^4.17.21" } }, "simplebar-react": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/simplebar-react/-/simplebar-react-2.3.3.tgz", - "integrity": "sha512-hfyRz3MVSAoyZfTB2zZcpzVtz+BQ7mMBVN+BNMgaAKujKyIrBQ69xIcwrZpBRWRqPd4WHmM0x6DMQpRGPJa7MQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/simplebar-react/-/simplebar-react-3.2.4.tgz", + "integrity": "sha512-ogLN79e7JUm82wJChD7NSUB+4EHCFvDkjXpiu8hT1Alk7DnCekUWds61NXcsP9jC97KOgF5To/AVjYFbX0olgg==", "requires": { - "prop-types": "^15.6.1", - "simplebar": "^5.3.3" + "simplebar-core": "^1.2.4" } }, "slash": { @@ -34390,39 +34948,53 @@ } }, "string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", "side-channel": "^1.0.4" } }, + "string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "stringify-object": { @@ -34461,12 +35033,6 @@ "ansi-regex": "^5.0.1" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -34514,6 +35080,60 @@ "supports-color": "^5.5.0" } }, + "sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "dependencies": { + "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, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -34531,6 +35151,12 @@ "has-flag": "^3.0.0" } }, + "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 + }, "svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", @@ -34585,76 +35211,14 @@ } } }, - "table": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz", - "integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==", + "synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", "dev": true, "requires": { - "ajv": "^8.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "lodash.clonedeep": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "ajv": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.0.1.tgz", - "integrity": "sha512-46ZA4TalFcLLqX1dEU3dhdY38wAtDydJ4e7QQTVekLUTzXkb1LfqU6VOBXC/a9wiv4T094WURqJH6ZitF92Kqw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" } }, "tabler-icons-react": { @@ -34663,107 +35227,50 @@ "integrity": "sha512-9E2J6QgjgQPTOTIAure3Mh7nIgqsidlvugCoX7E7sSQjsBjFPlzCLflFcvSaw7duzqPJzir9SvpvPdlYh4IS4w==", "requires": {} }, + "tailwind-merge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.1.tgz", + "integrity": "sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==", + "requires": { + "@babel/runtime": "^7.23.7" + } + }, "tailwindcss": { - "version": "2.2.19", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.19.tgz", - "integrity": "sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", "dev": true, "requires": { - "arg": "^5.0.1", - "bytes": "^3.0.0", - "chalk": "^4.1.2", - "chokidar": "^3.5.2", - "color": "^4.0.1", - "cosmiconfig": "^7.0.1", - "detective": "^5.2.0", + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.2.7", - "fs-extra": "^10.0.0", - "glob-parent": "^6.0.1", - "html-tags": "^3.1.0", - "is-color-stop": "^1.1.0", - "is-glob": "^4.0.1", - "lodash": "^4.17.21", - "lodash.topath": "^4.5.2", - "modern-normalize": "^1.1.0", - "node-emoji": "^1.11.0", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", "normalize-path": "^3.0.0", - "object-hash": "^2.2.0", - "postcss-js": "^3.0.3", - "postcss-load-config": "^3.1.0", - "postcss-nested": "5.0.6", - "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0", - "pretty-hrtime": "^1.0.3", - "purgecss": "^4.0.3", - "quick-lru": "^5.1.1", - "reduce-css-calc": "^2.1.8", - "resolve": "^1.20.0", - "tmp": "^0.2.1" + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" }, "dependencies": { - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, "arg": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", - "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/color/-/color-4.1.0.tgz", - "integrity": "sha512-o2rkkxyLGgYoeUy1OodXpbPAQNmlNBrirQ8ODO8QutzDiDMNdezSOZLNnusQ6pUpCQJUsaJIo9DZJKqa2HgH7A==", - "dev": true, - "requires": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "dev": true }, - "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -34773,67 +35280,40 @@ "is-glob": "^4.0.3" } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "dev": true }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "dev": true, "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" } }, "postcss-js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", - "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", "dev": true, "requires": { - "camelcase-css": "^2.0.1", - "postcss": "^8.1.6" + "camelcase-css": "^2.0.1" } }, "postcss-nested": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", - "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.6" - } - }, - "purgecss": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.1.3.tgz", - "integrity": "sha512-99cKy4s+VZoXnPxaoM23e5ABcP851nC2y2GROkkjS8eJaJtlciGavd7iYAw2V84WeBqggZ12l8ef44G99HmTaw==", - "dev": true, - "requires": { - "commander": "^8.0.0", - "glob": "^7.1.7", - "postcss": "^8.3.5", - "postcss-selector-parser": "^6.0.6" - } - }, - "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==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "postcss-selector-parser": "^6.0.11" } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true } } }, @@ -34988,6 +35468,24 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -35079,6 +35577,19 @@ "utf8-byte-length": "^1.0.1" } }, + "ts-api-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", + "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "dev": true, + "requires": {} + }, + "ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, "ts-loader": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.1.0.tgz", @@ -35157,51 +35668,10 @@ "yn": "3.1.1" } }, - "tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "type-check": { "version": "0.4.0", @@ -35227,10 +35697,62 @@ "mime-types": "~2.1.24" } }, + "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, + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + } + }, + "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, + "requires": { + "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" + } + }, + "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, + "requires": { + "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" + } + }, + "typed-array-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", + "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", + "dev": true, + "requires": { + "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" + } + }, "typescript": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", - "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true }, "un-eval": { @@ -35239,14 +35761,14 @@ "integrity": "sha512-Wlj/pum6dQtGTPD/lclDtoVPkSfpjPfy1dwnnKw/sZP5DpBH9fLhBgQfsqNhe5/gS1D+vkZUuB771NRMUPA5CA==" }, "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" } }, @@ -35526,12 +36048,6 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -35927,6 +36443,59 @@ "is-symbol": "^1.0.3" } }, + "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, + "requires": { + "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" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, + "which-typed-array": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", + "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.5", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.1" + } + }, "wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -35946,12 +36515,6 @@ "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.4.tgz", "integrity": "sha512-IHpzORub7kYlb8A43Iig3reOvlcBJGX9gZ0WycHhghHtA65X0LYnMRuJs+aH1abVnMJztQkvQNlltnbPi5aGIA==" }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index 0b3ef95c..97653a0c 100644 --- a/package.json +++ b/package.json @@ -69,8 +69,8 @@ "@types/styled-components": "^5.1.7", "@types/webpack": "^5.28.0", "@types/webpack-env": "^1.16.3", - "@typescript-eslint/eslint-plugin": "^4.14.2", - "@typescript-eslint/parser": "^4.14.2", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", "@webpack-cli/serve": "^1.6.0", "babel-loader": "^8.2.2", "concurrently": "^6.2.1", @@ -78,10 +78,12 @@ "electron": "^25.2.0", "electron-builder": "^24.4.0", "electron-devtools-installer": "^3.2.0", - "eslint": "^7.19.0", - "eslint-plugin-import": "^2.20.0", - "eslint-plugin-react": "^7.27.1", - "eslint-plugin-tailwindcss": "^1.17.2", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-tailwindcss": "^3.14.1", "fork-ts-checker-webpack-plugin": "^6.4.0", "html-webpack-plugin": "^5.5.0", "inspectpack": "^4.7.1", @@ -97,10 +99,10 @@ "react-svg-loader": "^3.0.3", "redux-devtools-extension": "^2.13.9", "style-loader": "^1.3.0", - "tailwindcss": "^2.2.19", + "tailwindcss": "^3.4.1", "ts-loader": "^8.0.15", "ts-node": "^9.1.1", - "typescript": "^4.1.3", + "typescript": "^5.3.3", "url-loader": "^4.1.1", "webpack": "^5.62.1", "webpack-cli": "^4.9.1", @@ -108,7 +110,6 @@ }, "dependencies": { "@flybywiresim/fragmenter": "^0.7.4", - "@flybywiresim/react-components": "^0.2.5", "@reduxjs/toolkit": "^1.7.1", "@sentry/cli": "^2.5.0", "@sentry/electron": "^3.0.7", @@ -134,7 +135,6 @@ "react-detect-offline": "^2.4.1", "react-dom": "^17.0.1", "react-hot-loader": "^4.13.0", - "react-html-parser": "^2.0.2", "react-intersection-observer": "^8.33.1", "react-markdown": "^7.0.1", "react-redux": "^7.2.2", @@ -144,9 +144,10 @@ "redux": "^4.0.5", "remark-gfm": "^2.0.0", "semver": "7.3.5", - "simplebar-react": "^2.3.0", + "simplebar-react": "^3.2.4", "styled-components": "^5.2.1", "tabler-icons-react": "~1.33.0", + "tailwind-merge": "^2.2.1", "walkdir": "^0.4.1", "winreg": "^1.2.4", "ws": "^8.8.0" diff --git a/src/common/channels.ts b/src/common/channels.ts index d8899c22..d7fbab54 100644 --- a/src/common/channels.ts +++ b/src/common/channels.ts @@ -1,24 +1,24 @@ export default { - window: { - minimize: 'window/minimize', - maximize: 'window/maximize', - close: 'window/close', - isMaximized: 'window/isMaximized', - }, - update: { - error: 'update/error', - available: 'update/available', - downloaded: 'update/downloaded', - }, - checkForInstallerUpdate: 'checkForInstallerUpdate', - installManager: { - fragmenterEvent: 'installManager/fragmenterEvent', - installFromUrl: 'installManager/installFromUrl', - cancelInstall: 'installManager/cancelInstall', - uninstall: 'installManager/uninstall', - }, - sentry: { - requestSessionID: 'sentry/requestSessionID', - provideSessionID: 'sentry/provideSessionID', - }, + window: { + minimize: 'window/minimize', + maximize: 'window/maximize', + close: 'window/close', + isMaximized: 'window/isMaximized', + }, + update: { + error: 'update/error', + available: 'update/available', + downloaded: 'update/downloaded', + }, + checkForInstallerUpdate: 'checkForInstallerUpdate', + installManager: { + fragmenterEvent: 'installManager/fragmenterEvent', + installFromUrl: 'installManager/installFromUrl', + cancelInstall: 'installManager/cancelInstall', + uninstall: 'installManager/uninstall', + }, + sentry: { + requestSessionID: 'sentry/requestSessionID', + provideSessionID: 'sentry/provideSessionID', + }, }; diff --git a/src/common/settings.ts b/src/common/settings.ts index 60fb0774..8c11f7ca 100644 --- a/src/common/settings.ts +++ b/src/common/settings.ts @@ -1,255 +1,258 @@ -import Store, { Schema } from "electron-store"; -import * as fs from "fs-extra"; -import walk from "walkdir"; -import * as path from "path"; +import Store, { Schema } from 'electron-store'; +import * as fs from 'fs-extra'; +import walk from 'walkdir'; +import * as path from 'path'; import * as os from 'os'; -import { Dispatch, SetStateAction, useEffect, useState } from "react"; +import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import * as packageInfo from '../../package.json'; const defaultCommunityDir = (): string => { - if (os.platform().toString() === 'linux') { - return 'linux'; - } + if (os.platform().toString() === 'linux') { + return 'linux'; + } - // Ensure proper functionality in main- and renderer-process - let msfsConfigPath = null; + // Ensure proper functionality in main- and renderer-process + let msfsConfigPath = null; - const steamPath = path.join(process.env.APPDATA, "\\Microsoft Flight Simulator\\UserCfg.opt"); - const storePath = path.join(process.env.LOCALAPPDATA, "\\Packages\\Microsoft.FlightSimulator_8wekyb3d8bbwe\\LocalCache\\UserCfg.opt"); + const steamPath = path.join(process.env.APPDATA, '\\Microsoft Flight Simulator\\UserCfg.opt'); + const storePath = path.join( + process.env.LOCALAPPDATA, + '\\Packages\\Microsoft.FlightSimulator_8wekyb3d8bbwe\\LocalCache\\UserCfg.opt', + ); - if (fs.existsSync(steamPath)) { - msfsConfigPath = steamPath; - } else if (fs.existsSync(storePath)) { - msfsConfigPath = storePath; - } else { - walk(process.env.LOCALAPPDATA, (path) => { - if (path.includes("Flight") && path.includes("UserCfg.opt")) { - msfsConfigPath = path; - } - }); - } + if (fs.existsSync(steamPath)) { + msfsConfigPath = steamPath; + } else if (fs.existsSync(storePath)) { + msfsConfigPath = storePath; + } else { + walk(process.env.LOCALAPPDATA, (path) => { + if (path.includes('Flight') && path.includes('UserCfg.opt')) { + msfsConfigPath = path; + } + }); + } - if (!msfsConfigPath) { - return 'C:\\'; - } + if (!msfsConfigPath) { + return 'C:\\'; + } - try { - const msfsConfig = fs.readFileSync(msfsConfigPath).toString(); - const msfsConfigLines = msfsConfig.split(/\r?\n/); - const packagesPathLine = msfsConfigLines.find(line => line.includes('InstalledPackagesPath')); - const communityDir = path.join(packagesPathLine.split(" ").slice(1).join(" ").replaceAll('"', ''), "\\Community"); + try { + const msfsConfig = fs.readFileSync(msfsConfigPath).toString(); + const msfsConfigLines = msfsConfig.split(/\r?\n/); + const packagesPathLine = msfsConfigLines.find((line) => line.includes('InstalledPackagesPath')); + const communityDir = path.join(packagesPathLine.split(' ').slice(1).join(' ').replaceAll('"', ''), '\\Community'); - return fs.existsSync(communityDir) ? communityDir : 'C:\\'; - } catch (e) { - console.warn('Could not parse community dir from file', msfsConfigPath); - console.error(e); - return 'C:\\'; - } + return fs.existsSync(communityDir) ? communityDir : 'C:\\'; + } catch (e) { + console.warn('Could not parse community dir from file', msfsConfigPath); + console.error(e); + return 'C:\\'; + } }; export const persistWindowSettings = (window: Electron.BrowserWindow): void => { - store.set('cache.main.maximized', window.isMaximized()); + store.set('cache.main.maximized', window.isMaximized()); - const winSize = window.getSize(); - store.set('cache.main.lastWindowX', winSize[0]); - store.set('cache.main.lastWindowY', winSize[1]); + const winSize = window.getSize(); + store.set('cache.main.lastWindowX', winSize[0]); + store.set('cache.main.lastWindowY', winSize[1]); }; export const useSetting = (key: string, defaultValue?: T): [T, Dispatch>] => { - const [storedValue, setStoredValue] = useState(store.get(key, defaultValue)); + const [storedValue, setStoredValue] = useState(store.get(key, defaultValue)); - useEffect(() => { - setStoredValue(store.get(key, defaultValue)); + useEffect(() => { + setStoredValue(store.get(key, defaultValue)); - const cancel = store.onDidChange(key as never, (val) => { - setStoredValue(val as T); - }); + const cancel = store.onDidChange(key as never, (val) => { + setStoredValue(val as T); + }); - return () => { - cancel(); - }; - }, [key]); - - const setValue = (newVal: T) => { - store.set(key, newVal); + return () => { + cancel(); }; + }, [defaultValue, key]); + + const setValue = (newVal: T) => { + store.set(key, newVal); + }; - return [storedValue, setValue]; + return [storedValue, setValue]; }; export const useIsDarkTheme = (): boolean => { - return true; + return true; }; interface Settings { - mainSettings: { - autoStartApp: boolean, - disableExperimentalWarning: boolean, - disableDependencyPrompt: { [k: string]: { [k: string]: boolean } }, - disableBackgroundServiceAutoStartPrompt: { [k: string]: { [k: string]: boolean } }, - useCdnCache: boolean, - dateLayout: string, - useLongDateFormat: boolean, - useDarkTheme: boolean, - allowSeasonalEffects: boolean, - msfsPackagePath: string, - configDownloadUrl: string, - }, - cache: { - main: { - lastWindowX: number, - lastWindowY: number, - maximized: boolean, - lastShownSection: string, - lastShownAddonKey: string, - }, - }, - metaInfo: { - lastVersion: string, - lastLaunch: number, - }, + mainSettings: { + autoStartApp: boolean; + disableExperimentalWarning: boolean; + disableDependencyPrompt: { [k: string]: { [k: string]: boolean } }; + disableBackgroundServiceAutoStartPrompt: { [k: string]: { [k: string]: boolean } }; + useCdnCache: boolean; + dateLayout: string; + useLongDateFormat: boolean; + useDarkTheme: boolean; + allowSeasonalEffects: boolean; + msfsPackagePath: string; + configDownloadUrl: string; + }; + cache: { + main: { + lastWindowX: number; + lastWindowY: number; + maximized: boolean; + lastShownSection: string; + lastShownAddonKey: string; + }; + }; + metaInfo: { + lastVersion: string; + lastLaunch: number; + }; } const schema: Schema = { - mainSettings: { - type: "object", - // Empty defaults are required when using type: "object" (https://github.com/sindresorhus/conf/issues/85#issuecomment-531651424) + mainSettings: { + type: 'object', + // Empty defaults are required when using type: "object" (https://github.com/sindresorhus/conf/issues/85#issuecomment-531651424) + default: {}, + properties: { + autoStartApp: { + type: 'boolean', + default: false, + }, + disableExperimentalWarning: { + type: 'boolean', + default: false, + }, + disableDependencyPrompt: { + type: 'object', default: {}, - properties: { - autoStartApp: { - type: "boolean", - default: false, - }, - disableExperimentalWarning: { - type: "boolean", - default: false, - }, - disableDependencyPrompt: { - type: "object", - default: {}, - additionalProperties: { - type: "object", - default: {}, - additionalProperties: { - type: "object", - default: {}, - additionalProperties: { - type: "boolean", - default: false, - }, - }, - }, - }, - disableBackgroundServiceAutoStartPrompt: { - type: "object", - default: {}, - additionalProperties: { - type: "object", - default: {}, - additionalProperties: { - type: "boolean", - default: false, - }, - }, - }, - disableAddonDiskSpaceModal: { - type: "object", - default: {}, - additionalProperties: { - type: "object", - default: {}, - additionalProperties: { - type: "boolean", - default: false, - }, - }, - }, - useCdnCache: { - type: "boolean", - default: true, - }, - dateLayout: { - type: "string", - default: "yyyy/mm/dd", - }, - useLongDateFormat: { - type: "boolean", - default: false, - }, - useDarkTheme: { - type: "boolean", - default: false, - }, - allowSeasonalEffects: { - type: "boolean", - default: true, - }, - msfsCommunityPath: { - type: "string", - default: defaultCommunityDir(), - }, - installPath: { - type: "string", - default: defaultCommunityDir(), - }, - separateTempLocation: { - type: "boolean", - default: false, - }, - tempLocation: { - type: "string", - default: defaultCommunityDir(), - }, - configDownloadUrl: { - type: "string", - default: packageInfo.configUrls.production, + additionalProperties: { + type: 'object', + default: {}, + additionalProperties: { + type: 'object', + default: {}, + additionalProperties: { + type: 'boolean', + default: false, }, + }, }, - }, - cache: { - type: "object", + }, + disableBackgroundServiceAutoStartPrompt: { + type: 'object', default: {}, - properties: { - main: { - type: "object", - default: {}, - properties: { - lastWindowX: { - type: "integer", - }, - lastWindowY: { - type: "integer", - }, - maximized: { - type: "boolean", - default: false, - }, - lastShownSection: { - type: "string", - default: "", - }, - lastShownAddonKey: { - type: "string", - default: "", - }, - }, - }, + additionalProperties: { + type: 'object', + default: {}, + additionalProperties: { + type: 'boolean', + default: false, + }, + }, + }, + disableAddonDiskSpaceModal: { + type: 'object', + default: {}, + additionalProperties: { + type: 'object', + default: {}, + additionalProperties: { + type: 'boolean', + default: false, + }, }, + }, + useCdnCache: { + type: 'boolean', + default: true, + }, + dateLayout: { + type: 'string', + default: 'yyyy/mm/dd', + }, + useLongDateFormat: { + type: 'boolean', + default: false, + }, + useDarkTheme: { + type: 'boolean', + default: false, + }, + allowSeasonalEffects: { + type: 'boolean', + default: true, + }, + msfsCommunityPath: { + type: 'string', + default: defaultCommunityDir(), + }, + installPath: { + type: 'string', + default: defaultCommunityDir(), + }, + separateTempLocation: { + type: 'boolean', + default: false, + }, + tempLocation: { + type: 'string', + default: defaultCommunityDir(), + }, + configDownloadUrl: { + type: 'string', + default: packageInfo.configUrls.production, + }, }, - metaInfo: { - type: "object", + }, + cache: { + type: 'object', + default: {}, + properties: { + main: { + type: 'object', default: {}, properties: { - lastVersion: { - type: "string", - default: "", - }, - lastLaunch: { - type: "integer", - default: 0, - }, + lastWindowX: { + type: 'integer', + }, + lastWindowY: { + type: 'integer', + }, + maximized: { + type: 'boolean', + default: false, + }, + lastShownSection: { + type: 'string', + default: '', + }, + lastShownAddonKey: { + type: 'string', + default: '', + }, }, + }, + }, + }, + metaInfo: { + type: 'object', + default: {}, + properties: { + lastVersion: { + type: 'string', + default: '', + }, + lastLaunch: { + type: 'integer', + default: 0, + }, }, + }, }; const store = new Store({ schema, clearInvalidConfig: true }); diff --git a/src/main/InstallManager.ts b/src/main/InstallManager.ts index fde31421..a59a28d1 100644 --- a/src/main/InstallManager.ts +++ b/src/main/InstallManager.ts @@ -1,139 +1,148 @@ import { - FragmenterContext, - FragmenterContextEvents, - FragmenterInstaller, - FragmenterInstallerEvents, -} from "@flybywiresim/fragmenter"; -import channels from "common/channels"; -import { ipcMain, WebContents } from "electron"; -import fs from "fs"; -import { promisify } from "util"; -import path from "path"; + FragmenterContext, + FragmenterContextEvents, + FragmenterInstaller, + FragmenterInstallerEvents, +} from '@flybywiresim/fragmenter'; +import channels from 'common/channels'; +import { ipcMain, WebContents } from 'electron'; +import fs from 'fs'; +import { promisify } from 'util'; +import path from 'path'; let lastProgressSent = 0; export class InstallManager { - static async install( - sender: WebContents, - ourInstallID: number, - url: string, - tempDir: string, - destDir: string, - ): Promise { - const abortController = new AbortController(); - - const fragmenterContext = new FragmenterContext({ useConsoleLog: true }, abortController.signal); - const fragmenterInstaller = new FragmenterInstaller(fragmenterContext, url, destDir, { temporaryDirectory: tempDir, forceManifestCacheBust: true }); - - const forwardFragmenterInstallerEvent = (event: keyof FragmenterInstallerEvents) => { - fragmenterInstaller.on(event, (...args: unknown[]) => { - if (event === 'downloadProgress' || event === 'unzipProgress' || event === 'copyProgress') { - const currentTime = performance.now(); - const timeSinceLastProgress = currentTime - lastProgressSent; - - if (timeSinceLastProgress > 25) { - sender.send(channels.installManager.fragmenterEvent, ourInstallID, event, ...args); - - lastProgressSent = currentTime; - } - } else { - sender.send(channels.installManager.fragmenterEvent, ourInstallID, event, ...args); - } - }); - }; - - const forwardFragmenterContextEvent = (event: keyof FragmenterContextEvents) => { - fragmenterContext.on(event, (...args: unknown[]) => { - sender.send(channels.installManager.fragmenterEvent, ourInstallID, event, ...args); - }); - }; - - const handleCancelInstall = (_: unknown, installID: number) => { - if (installID !== ourInstallID) { - return; - } - - abortController.abort(); - }; - - // Setup cancel event listener - ipcMain.on(channels.installManager.cancelInstall, handleCancelInstall); - - forwardFragmenterInstallerEvent('error'); - forwardFragmenterInstallerEvent('downloadStarted'); - forwardFragmenterInstallerEvent('downloadProgress'); - forwardFragmenterInstallerEvent('downloadInterrupted'); - forwardFragmenterInstallerEvent('downloadFinished'); - forwardFragmenterInstallerEvent('unzipStarted'); - forwardFragmenterInstallerEvent('unzipProgress'); - forwardFragmenterInstallerEvent('unzipFinished'); - forwardFragmenterInstallerEvent('copyStarted'); - forwardFragmenterInstallerEvent('copyProgress'); - forwardFragmenterInstallerEvent('copyFinished'); - forwardFragmenterInstallerEvent('retryScheduled'); - forwardFragmenterInstallerEvent('retryStarted'); - forwardFragmenterInstallerEvent('fullDownload'); - forwardFragmenterInstallerEvent('cancelled'); - forwardFragmenterInstallerEvent('logInfo'); - forwardFragmenterInstallerEvent('logWarn'); - forwardFragmenterInstallerEvent('logError'); - forwardFragmenterContextEvent('phaseChange'); - - let ret = false; - - try { - await fragmenterInstaller.install(); - - ret = true; - } catch (e) { - if (e.message.startsWith('FragmenterError')) { - ret = e; - } else { - throw e; - } + static async install( + sender: WebContents, + ourInstallID: number, + url: string, + tempDir: string, + destDir: string, + ): Promise { + const abortController = new AbortController(); + + const fragmenterContext = new FragmenterContext({ useConsoleLog: true }, abortController.signal); + const fragmenterInstaller = new FragmenterInstaller(fragmenterContext, url, destDir, { + temporaryDirectory: tempDir, + forceManifestCacheBust: true, + }); + + const forwardFragmenterInstallerEvent = (event: keyof FragmenterInstallerEvents) => { + fragmenterInstaller.on(event, (...args: unknown[]) => { + if (event === 'downloadProgress' || event === 'unzipProgress' || event === 'copyProgress') { + const currentTime = performance.now(); + const timeSinceLastProgress = currentTime - lastProgressSent; + + if (timeSinceLastProgress > 25) { + sender.send(channels.installManager.fragmenterEvent, ourInstallID, event, ...args); + + lastProgressSent = currentTime; + } + } else { + sender.send(channels.installManager.fragmenterEvent, ourInstallID, event, ...args); } + }); + }; + + const forwardFragmenterContextEvent = (event: keyof FragmenterContextEvents) => { + fragmenterContext.on(event, (...args: unknown[]) => { + sender.send(channels.installManager.fragmenterEvent, ourInstallID, event, ...args); + }); + }; + + const handleCancelInstall = (_: unknown, installID: number) => { + if (installID !== ourInstallID) { + return; + } + + abortController.abort(); + }; + + // Setup cancel event listener + ipcMain.on(channels.installManager.cancelInstall, handleCancelInstall); + + forwardFragmenterInstallerEvent('error'); + forwardFragmenterInstallerEvent('downloadStarted'); + forwardFragmenterInstallerEvent('downloadProgress'); + forwardFragmenterInstallerEvent('downloadInterrupted'); + forwardFragmenterInstallerEvent('downloadFinished'); + forwardFragmenterInstallerEvent('unzipStarted'); + forwardFragmenterInstallerEvent('unzipProgress'); + forwardFragmenterInstallerEvent('unzipFinished'); + forwardFragmenterInstallerEvent('copyStarted'); + forwardFragmenterInstallerEvent('copyProgress'); + forwardFragmenterInstallerEvent('copyFinished'); + forwardFragmenterInstallerEvent('retryScheduled'); + forwardFragmenterInstallerEvent('retryStarted'); + forwardFragmenterInstallerEvent('fullDownload'); + forwardFragmenterInstallerEvent('cancelled'); + forwardFragmenterInstallerEvent('logInfo'); + forwardFragmenterInstallerEvent('logWarn'); + forwardFragmenterInstallerEvent('logError'); + forwardFragmenterContextEvent('phaseChange'); + + let ret = false; + + try { + await fragmenterInstaller.install(); + + ret = true; + } catch (e) { + if (e.message.startsWith('FragmenterError')) { + ret = e; + } else { + throw e; + } + } - // Tear down cancel event listener - ipcMain.removeListener(channels.installManager.cancelInstall, handleCancelInstall); + // Tear down cancel event listener + ipcMain.removeListener(channels.installManager.cancelInstall, handleCancelInstall); - return ret; - } + return ret; + } - static async uninstall( - sender: WebContents, - communityPackageDir: string, - packageCacheDirs: string, - ): Promise { - const communityPackageDirExists = await promisify(fs.exists)(communityPackageDir); + static async uninstall( + sender: WebContents, + communityPackageDir: string, + packageCacheDirs: string, + ): Promise { + const communityPackageDirExists = await promisify(fs.exists)(communityPackageDir); - if (communityPackageDirExists) { - await fs.promises.rm(communityPackageDir, { recursive: true }); - } + if (communityPackageDirExists) { + await fs.promises.rm(communityPackageDir, { recursive: true }); + } - for (const packageCacheDir of packageCacheDirs) { - const packageCacheDirExists = await promisify(fs.exists)(packageCacheDir); + for (const packageCacheDir of packageCacheDirs) { + const packageCacheDirExists = await promisify(fs.exists)(packageCacheDir); - if (packageCacheDirExists) { - const dirents = await fs.promises.readdir(packageCacheDir); + if (packageCacheDirExists) { + const dirents = await fs.promises.readdir(packageCacheDir); - for (const dirent of dirents) { - if (dirent !== 'work') { - await fs.promises.unlink(path.join(packageCacheDir, dirent)); - } - } - } + for (const dirent of dirents) { + if (dirent !== 'work') { + await fs.promises.unlink(path.join(packageCacheDir, dirent)); + } } - - return true; + } } - static setupIpcListeners(): void { - ipcMain.handle(channels.installManager.installFromUrl, async (event, installID: number, url: string, tempDir: string, destDir: string) => { - return InstallManager.install(event.sender, installID, url, tempDir, destDir); - }); - - ipcMain.handle(channels.installManager.uninstall, async (event, communityPackageDir: string, packageCacheDirs: string) => { - return InstallManager.uninstall(event.sender, communityPackageDir, packageCacheDirs); - }); - } + return true; + } + + static setupIpcListeners(): void { + ipcMain.handle( + channels.installManager.installFromUrl, + async (event, installID: number, url: string, tempDir: string, destDir: string) => { + return InstallManager.install(event.sender, installID, url, tempDir, destDir); + }, + ); + + ipcMain.handle( + channels.installManager.uninstall, + async (event, communityPackageDir: string, packageCacheDirs: string) => { + return InstallManager.uninstall(event.sender, communityPackageDir, packageCacheDirs); + }, + ); + } } diff --git a/src/main/SentryClient.ts b/src/main/SentryClient.ts index 9725a88e..0751078c 100644 --- a/src/main/SentryClient.ts +++ b/src/main/SentryClient.ts @@ -1,53 +1,54 @@ -import { ipcMain } from "electron"; +import { ipcMain } from 'electron'; import fs from 'fs'; -import * as Sentry from "@sentry/electron/main"; +import * as Sentry from '@sentry/electron/main'; import { customAlphabet } from 'nanoid'; -import packageJson from "../../package.json"; -import channels from "common/channels"; -import path from "path"; +import packageJson from '../../package.json'; +import channels from 'common/channels'; +import path from 'path'; const ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const SESSION_ID_LENGTH = 14; export class SentryClient { - static initialize(): void { - const nanoid = customAlphabet(ALPHABET, SESSION_ID_LENGTH); - const generatedSessionID = nanoid(); - - let dsn; - try { - const extraResourcesPath = process.env.NODE_ENV === 'production' ? path.join(process.resourcesPath, 'extraResources') : 'extraResources'; - - dsn = fs.readFileSync(path.join(extraResourcesPath, '.sentrydsn'), { encoding: 'utf-8' }); - } catch (e) { - console.error('Could not read extraResources/sentry.json. See exception below'); - console.error(e); - } + static initialize(): void { + const nanoid = customAlphabet(ALPHABET, SESSION_ID_LENGTH); + const generatedSessionID = nanoid(); + + let dsn; + try { + const extraResourcesPath = + process.env.NODE_ENV === 'production' ? path.join(process.resourcesPath, 'extraResources') : 'extraResources'; + + dsn = fs.readFileSync(path.join(extraResourcesPath, '.sentrydsn'), { encoding: 'utf-8' }); + } catch (e) { + console.error('Could not read extraResources/sentry.json. See exception below'); + console.error(e); + } - if (!dsn) { - console.warn('Sentry not initialized - dsn not loaded'); + if (!dsn) { + console.warn('Sentry not initialized - dsn not loaded'); - // Send an session ID when requested - ipcMain.handle(channels.sentry.requestSessionID, () => { - return ''; - }); + // Send an session ID when requested + ipcMain.handle(channels.sentry.requestSessionID, () => { + return ''; + }); - return; - } + return; + } - // Send the session ID when requested - ipcMain.handle(channels.sentry.requestSessionID, () => { - return generatedSessionID; - }); + // Send the session ID when requested + ipcMain.handle(channels.sentry.requestSessionID, () => { + return generatedSessionID; + }); - Sentry.setTag('session.id', generatedSessionID); + Sentry.setTag('session.id', generatedSessionID); - Sentry.init({ - dsn, - release: packageJson.version, - // sampleRate: 0.1, - }); + Sentry.init({ + dsn, + release: packageJson.version, + // sampleRate: 0.1, + }); - console.log('Sentry initialized'); - } + console.log('Sentry initialized'); + } } diff --git a/src/main/index.ts b/src/main/index.ts index e1410a19..0b097f5d 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,255 +1,258 @@ import { app, BrowserWindow, Menu, globalShortcut, shell, ipcMain } from 'electron'; -import { NsisUpdater } from "electron-updater"; +import { NsisUpdater } from 'electron-updater'; import * as path from 'path'; import installExtension, { REDUX_DEVTOOLS, REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer'; import * as packageInfo from '../../package.json'; -import settings, { persistWindowSettings } from "common/settings"; -import channels from "common/channels"; -import * as remote from "@electron/remote/main"; -import { InstallManager } from "main/InstallManager"; -import { SentryClient } from "main/SentryClient"; +import settings, { persistWindowSettings } from 'common/settings'; +import channels from 'common/channels'; +import * as remote from '@electron/remote/main'; +import { InstallManager } from 'main/InstallManager'; +import { SentryClient } from 'main/SentryClient'; function initializeApp() { - function createWindow() { - // Create the browser window. - mainWindow = new BrowserWindow({ - width: 1280, - height: 800, - minWidth: 1280, - minHeight: 800, - frame: false, - icon: 'src/main/icons/icon.ico', - backgroundColor: '#1b2434', - show: false, - webPreferences: { - nodeIntegration: true, - contextIsolation: false, - }, - }); - - remote.enable(mainWindow.webContents); - - const UpsertKeyValue = (header: Record | Record, keyToChange: string, value: string | string[]) => { - for (const key of Object.keys(header)) { - if (key.toLowerCase() === keyToChange.toLowerCase()) { - header[key] = value; - return; - } - } - header[keyToChange] = value; - }; + function createWindow() { + // Create the browser window. + mainWindow = new BrowserWindow({ + width: 1280, + height: 800, + minWidth: 1280, + minHeight: 800, + frame: false, + icon: 'src/main/icons/icon.ico', + backgroundColor: '#1b2434', + show: false, + webPreferences: { + nodeIntegration: true, + contextIsolation: false, + }, + }); - mainWindow.webContents.session.webRequest.onBeforeSendHeaders( - (details, callback) => { - const { requestHeaders } = details; - UpsertKeyValue(requestHeaders, 'Access-Control-Allow-Origin', '*'); - callback({ requestHeaders }); - }, - ); - - mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => { - const { responseHeaders } = details; - UpsertKeyValue(responseHeaders, 'Access-Control-Allow-Origin', ['*']); - UpsertKeyValue(responseHeaders, 'Access-Control-Allow-Headers', ['*']); - callback({ - responseHeaders, - }); - }); - - mainWindow.once('ready-to-show', () => { - mainWindow.show(); - }); - - mainWindow.on('closed', () => { - mainWindow.removeAllListeners(); - app.quit(); - }); - - ipcMain.on(channels.window.minimize, () => { - mainWindow.minimize(); - }); - - ipcMain.on(channels.window.maximize, () => { - mainWindow.isMaximized() ? mainWindow.unmaximize() : mainWindow.maximize(); - }); - - ipcMain.on(channels.window.close, () => { - persistWindowSettings(mainWindow); - mainWindow.destroy(); - }); - - ipcMain.on(channels.window.isMaximized, (event) => { - event.sender.send(channels.window.isMaximized, mainWindow.isMaximized()); - }); - - ipcMain.on('request-startup-at-login-changed', (_, value: boolean) => { - app.setLoginItemSettings({ - openAtLogin: value, - }); - }); - - /* - * Setting the value of the program's taskbar progress bar. - * value: The value to set the progress bar to. ( [0 - 1.0], -1 to hide the progress bar ) - */ - ipcMain.on('set-window-progress-bar', (_, value: number) => { - mainWindow.setProgressBar(value); - }); - - const lastX = settings.get('cache.main.lastWindowX'); - const lastY = settings.get('cache.main.lastWindowY'); - const shouldMaximize = settings.get('cache.main.maximized'); - - if (shouldMaximize) { - mainWindow.maximize(); - } else if (lastX && lastY) { // 0 width and height should be reset to defaults - mainWindow.setBounds({ - width: lastX, - height: lastY, - }); + remote.enable(mainWindow.webContents); + + const UpsertKeyValue = ( + header: Record | Record, + keyToChange: string, + value: string | string[], + ) => { + for (const key of Object.keys(header)) { + if (key.toLowerCase() === keyToChange.toLowerCase()) { + header[key] = value; + return; } + } + header[keyToChange] = value; + }; + + mainWindow.webContents.session.webRequest.onBeforeSendHeaders((details, callback) => { + const { requestHeaders } = details; + UpsertKeyValue(requestHeaders, 'Access-Control-Allow-Origin', '*'); + callback({ requestHeaders }); + }); - mainWindow.center(); + mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => { + const { responseHeaders } = details; + UpsertKeyValue(responseHeaders, 'Access-Control-Allow-Origin', ['*']); + UpsertKeyValue(responseHeaders, 'Access-Control-Allow-Headers', ['*']); + callback({ + responseHeaders, + }); + }); - // and load the index.html of the app. - if (serve) { - mainWindow.loadURL('http://localhost:8080/index.html').then(); - } else { - mainWindow.loadFile(path.join(__dirname, '../renderer/index.html')).then(); - } + mainWindow.once('ready-to-show', () => { + mainWindow.show(); + }); - mainWindow.webContents.setWindowOpenHandler(({ url }) => { - shell.openExternal(url).then(); - return { action: 'deny' }; - }); - - if (process.env.NODE_ENV === 'development') { - // Open the DevTools. - settings.openInEditor(); - mainWindow.webContents.once('dom-ready', () => { - mainWindow.webContents.openDevTools(); - }); - } + mainWindow.on('closed', () => { + mainWindow.removeAllListeners(); + app.quit(); + }); - globalShortcut.register('CmdOrCtrl+R', () => { - mainWindow.isFocused() && mainWindow.reload(); - }); - - globalShortcut.register('CmdOrCtrl+F5', () => { - mainWindow.isFocused() && mainWindow.reload(); - }); - - globalShortcut.register('CmdOrCtrl+F12', () => { - mainWindow.isFocused() && mainWindow.webContents.toggleDevTools(); - }); - - // Auto updater - if (process.env.NODE_ENV !== 'development') { - let updateOptions; - if (packageInfo.version.includes('dev')) { - updateOptions = { - provider:'generic' as const, - url: 'https://cdn.flybywiresim.com/installer/dev', - }; - } else if (packageInfo.version.includes('rc')) { - updateOptions = { - provider:'generic' as const, - url: 'https://cdn.flybywiresim.com/installer/rc', - }; - } else { - updateOptions = { - provider:'generic' as const, - url: 'https://cdn.flybywiresim.com/installer/release', - }; - } - - const autoUpdater = new NsisUpdater(updateOptions); - - autoUpdater.addListener('update-downloaded', (event, releaseNotes, releaseName) => { - mainWindow.webContents.send(channels.update.downloaded, { event, releaseNotes, releaseName }); - }); - - autoUpdater.addListener('update-available', () => { - mainWindow.webContents.send(channels.update.available); - }); - - autoUpdater.addListener('error', (error) => { - mainWindow.webContents.send(channels.update.error, { error }); - }); - - // tell autoupdater to check for updates - mainWindow.once('show', () => { - autoUpdater.checkForUpdates().then(); - }); - - ipcMain.on(channels.checkForInstallerUpdate, () => { - autoUpdater.checkForUpdates().then(); - }); - - ipcMain.on('restartAndUpdate', () => { - autoUpdater.quitAndInstall(); - app.exit(); - }); - } - } + ipcMain.on(channels.window.minimize, () => { + mainWindow.minimize(); + }); - if (!app.requestSingleInstanceLock()) { - app.quit(); - } + ipcMain.on(channels.window.maximize, () => { + mainWindow.isMaximized() ? mainWindow.unmaximize() : mainWindow.maximize(); + }); - remote.initialize(); + ipcMain.on(channels.window.close, () => { + persistWindowSettings(mainWindow); + mainWindow.destroy(); + }); - app.setAppUserModelId('FlyByWire Installer'); + ipcMain.on(channels.window.isMaximized, (event) => { + event.sender.send(channels.window.isMaximized, mainWindow.isMaximized()); + }); - let mainWindow: BrowserWindow; + ipcMain.on('request-startup-at-login-changed', (_, value: boolean) => { + app.setLoginItemSettings({ + openAtLogin: value, + }); + }); - Menu.setApplicationMenu(null); + /* + * Setting the value of the program's taskbar progress bar. + * value: The value to set the progress bar to. ( [0 - 1.0], -1 to hide the progress bar ) + */ + ipcMain.on('set-window-progress-bar', (_, value: number) => { + mainWindow.setProgressBar(value); + }); - const serve = process.argv.slice(1).some((arg) => arg === "--serve"); + const lastX = settings.get('cache.main.lastWindowX'); + const lastY = settings.get('cache.main.lastWindowY'); + const shouldMaximize = settings.get('cache.main.maximized'); + + if (shouldMaximize) { + mainWindow.maximize(); + } else if (lastX && lastY) { + // 0 width and height should be reset to defaults + mainWindow.setBounds({ + width: lastX, + height: lastY, + }); + } - // This method will be called when Electron has finished - // initialization and is ready to create browser windows. - // Some APIs can only be used after this event occurs. - app.on('ready', () => { - createWindow(); + mainWindow.center(); - if (process.env.NODE_ENV === 'development') { - installExtension(REACT_DEVELOPER_TOOLS) - .then((name) => console.log(`Added Extension: ${name}`)) - .catch((err) => console.log('An error occurred: ', err)); + // and load the index.html of the app. + if (serve) { + mainWindow.loadURL('http://localhost:8080/index.html').then(); + } else { + mainWindow.loadFile(path.join(__dirname, '../renderer/index.html')).then(); + } - installExtension(REDUX_DEVTOOLS) - .then((name) => console.log(`Added Extension: ${name}`)) - .catch((err) => console.log('An error occurred: ', err)); - } + mainWindow.webContents.setWindowOpenHandler(({ url }) => { + shell.openExternal(url).then(); + return { action: 'deny' }; }); - // Quit when all windows are closed, except on macOS. There, it's common - // for applications and their menu bar to stay active until the user quits - // explicitly with Cmd + Q. - app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { - app.quit(); - } + if (process.env.NODE_ENV === 'development') { + // Open the DevTools. + settings.openInEditor(); + mainWindow.webContents.once('dom-ready', () => { + mainWindow.webContents.openDevTools(); + }); + } + + globalShortcut.register('CmdOrCtrl+R', () => { + mainWindow.isFocused() && mainWindow.reload(); }); - app.on('activate', () => { - // On OS X it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) { - createWindow(); - } + globalShortcut.register('CmdOrCtrl+F5', () => { + mainWindow.isFocused() && mainWindow.reload(); }); - // Someone tried to run a second instance, we should focus our window. - app.on('second-instance', () => { - if (mainWindow) { - if (mainWindow.isMinimized()) { - mainWindow.restore(); - } - mainWindow.focus(); - } + globalShortcut.register('CmdOrCtrl+F12', () => { + mainWindow.isFocused() && mainWindow.webContents.toggleDevTools(); }); + + // Auto updater + if (process.env.NODE_ENV !== 'development') { + let updateOptions; + if (packageInfo.version.includes('dev')) { + updateOptions = { + provider: 'generic' as const, + url: 'https://cdn.flybywiresim.com/installer/dev', + }; + } else if (packageInfo.version.includes('rc')) { + updateOptions = { + provider: 'generic' as const, + url: 'https://cdn.flybywiresim.com/installer/rc', + }; + } else { + updateOptions = { + provider: 'generic' as const, + url: 'https://cdn.flybywiresim.com/installer/release', + }; + } + + const autoUpdater = new NsisUpdater(updateOptions); + + autoUpdater.addListener('update-downloaded', (event, releaseNotes, releaseName) => { + mainWindow.webContents.send(channels.update.downloaded, { event, releaseNotes, releaseName }); + }); + + autoUpdater.addListener('update-available', () => { + mainWindow.webContents.send(channels.update.available); + }); + + autoUpdater.addListener('error', (error) => { + mainWindow.webContents.send(channels.update.error, { error }); + }); + + // tell autoupdater to check for updates + mainWindow.once('show', () => { + autoUpdater.checkForUpdates().then(); + }); + + ipcMain.on(channels.checkForInstallerUpdate, () => { + autoUpdater.checkForUpdates().then(); + }); + + ipcMain.on('restartAndUpdate', () => { + autoUpdater.quitAndInstall(); + app.exit(); + }); + } + } + + if (!app.requestSingleInstanceLock()) { + app.quit(); + } + + remote.initialize(); + + app.setAppUserModelId('FlyByWire Installer'); + + let mainWindow: BrowserWindow; + + Menu.setApplicationMenu(null); + + const serve = process.argv.slice(1).some((arg) => arg === '--serve'); + + // This method will be called when Electron has finished + // initialization and is ready to create browser windows. + // Some APIs can only be used after this event occurs. + app.on('ready', () => { + createWindow(); + + if (process.env.NODE_ENV === 'development') { + installExtension(REACT_DEVELOPER_TOOLS) + .then((name) => console.log(`Added Extension: ${name}`)) + .catch((err) => console.log('An error occurred: ', err)); + + installExtension(REDUX_DEVTOOLS) + .then((name) => console.log(`Added Extension: ${name}`)) + .catch((err) => console.log('An error occurred: ', err)); + } + }); + + // Quit when all windows are closed, except on macOS. There, it's common + // for applications and their menu bar to stay active until the user quits + // explicitly with Cmd + Q. + app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } + }); + + app.on('activate', () => { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } + }); + + // Someone tried to run a second instance, we should focus our window. + app.on('second-instance', () => { + if (mainWindow) { + if (mainWindow.isMinimized()) { + mainWindow.restore(); + } + mainWindow.focus(); + } + }); } SentryClient.initialize(); diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index d1fe9899..116013cb 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -40,16 +40,14 @@ declare module '*.png' { } declare module '*.webp' { - const src: string; - export default src; + const src: string; + export default src; } declare module '*.svg' { import * as React from 'react'; - export const ReactComponent: React.FunctionComponent & { title?: string }>; + export const ReactComponent: React.FunctionComponent & { title?: string }>; const src: string; export default src; @@ -69,3 +67,5 @@ declare module '*.module.sass' { const classes: { readonly [key: string]: string }; export default classes; } + +declare module '*.yaml' {} diff --git a/src/renderer/actions/install-path.utils.tsx b/src/renderer/actions/install-path.utils.tsx index 7960a942..3d511df5 100644 --- a/src/renderer/actions/install-path.utils.tsx +++ b/src/renderer/actions/install-path.utils.tsx @@ -1,74 +1,74 @@ -import settings from "common/settings"; -import { Directories } from "renderer/utils/Directories"; -import { dialog } from "@electron/remote"; +import settings from 'common/settings'; +import { Directories } from 'renderer/utils/Directories'; +import { dialog } from '@electron/remote'; export const setupMsfsCommunityPath = async (): Promise => { - const currentPath = Directories.installLocation(); + const currentPath = Directories.installLocation(); - const path = await dialog.showOpenDialog({ - title: 'Select your MSFS community directory', - defaultPath: typeof currentPath === 'string' ? currentPath : '', - properties: ['openDirectory'], - }); + const path = await dialog.showOpenDialog({ + title: 'Select your MSFS community directory', + defaultPath: typeof currentPath === 'string' ? currentPath : '', + properties: ['openDirectory'], + }); - if (path.filePaths[0]) { - settings.set('mainSettings.msfsCommunityPath', path.filePaths[0]); - return path.filePaths[0]; - } else { - return ""; - } + if (path.filePaths[0]) { + settings.set('mainSettings.msfsCommunityPath', path.filePaths[0]); + return path.filePaths[0]; + } else { + return ''; + } }; export const setupInstallPath = async (): Promise => { - const currentPath = Directories.installLocation(); + const currentPath = Directories.installLocation(); - const path = await dialog.showOpenDialog({ - title: 'Select your install directory', - defaultPath: typeof currentPath === 'string' ? currentPath : '', - properties: ['openDirectory'], - }); + const path = await dialog.showOpenDialog({ + title: 'Select your install directory', + defaultPath: typeof currentPath === 'string' ? currentPath : '', + properties: ['openDirectory'], + }); - if (path.filePaths[0]) { - settings.set('mainSettings.installPath', path.filePaths[0]); - if (!settings.get('mainSettings.separateLiveriesPath')) { - settings.set('mainSettings.liveriesPath', path.filePaths[0]); - } - return path.filePaths[0]; - } else { - return ""; + if (path.filePaths[0]) { + settings.set('mainSettings.installPath', path.filePaths[0]); + if (!settings.get('mainSettings.separateLiveriesPath')) { + settings.set('mainSettings.liveriesPath', path.filePaths[0]); } + return path.filePaths[0]; + } else { + return ''; + } }; export const setupTempLocation = async (): Promise => { - const currentPath = Directories.tempLocation(); + const currentPath = Directories.tempLocation(); - const path = await dialog.showOpenDialog({ - title: 'Select a location for temporary folders', - defaultPath: typeof currentPath === 'string' ? currentPath : '', - properties: ['openDirectory'], - }); + const path = await dialog.showOpenDialog({ + title: 'Select a location for temporary folders', + defaultPath: typeof currentPath === 'string' ? currentPath : '', + properties: ['openDirectory'], + }); - if (path.filePaths[0]) { - settings.set('mainSettings.tempLocation', path.filePaths[0]); - return path.filePaths[0]; - } else { - return ""; - } + if (path.filePaths[0]) { + settings.set('mainSettings.tempLocation', path.filePaths[0]); + return path.filePaths[0]; + } else { + return ''; + } }; export const setupLiveriesPath = async (): Promise => { - const currentPath = Directories.liveries(); + const currentPath = Directories.liveries(); - const path = await dialog.showOpenDialog({ - title: 'Select your liveries directory', - defaultPath: typeof currentPath === 'string' ? currentPath : '', - properties: ['openDirectory'], - }); + const path = await dialog.showOpenDialog({ + title: 'Select your liveries directory', + defaultPath: typeof currentPath === 'string' ? currentPath : '', + properties: ['openDirectory'], + }); - if (path.filePaths[0]) { - settings.set('mainSettings.liveriesPath', path.filePaths[0]); - return path.filePaths[0]; - } else { - return ""; - } + if (path.filePaths[0]) { + settings.set('mainSettings.liveriesPath', path.filePaths[0]); + return path.filePaths[0]; + } else { + return ''; + } }; diff --git a/src/renderer/components/AddonSection/Configure/ConfigurationAspectDisplay.tsx b/src/renderer/components/AddonSection/Configure/ConfigurationAspectDisplay.tsx index 1d195c24..96a8325f 100644 --- a/src/renderer/components/AddonSection/Configure/ConfigurationAspectDisplay.tsx +++ b/src/renderer/components/AddonSection/Configure/ConfigurationAspectDisplay.tsx @@ -1,46 +1,42 @@ -import React, { FC, useState } from "react"; -import ReactMarkdown from "react-markdown"; -import { ConfigurationAspect } from "renderer/utils/InstallerConfiguration"; -import { YesNoOptionToggle } from "renderer/components/AddonSection/Configure/YesNoOptionToggle"; +import React, { FC, useState } from 'react'; +import ReactMarkdown from 'react-markdown'; +import { ConfigurationAspect } from 'renderer/utils/InstallerConfiguration'; +import { YesNoOptionToggle } from 'renderer/components/AddonSection/Configure/YesNoOptionToggle'; export interface ConfigurationAspectDisplayProps { - aspect: ConfigurationAspect, + aspect: ConfigurationAspect; } export const ConfigurationAspectDisplay: FC = ({ aspect }) => { - const [on, setOn] = useState(false); + const [on, setOn] = useState(false); - if (!aspect?.choices) { - throw new Error('Invalid configuration aspect: choices array is falsy.'); - } else if (aspect.choices?.length === 0) { - throw new Error('Invalid configuration aspect: 0 choices.'); - } + if (!aspect?.choices) { + throw new Error('Invalid configuration aspect: choices array is falsy.'); + } else if (aspect.choices?.length === 0) { + throw new Error('Invalid configuration aspect: 0 choices.'); + } - const chosen = aspect.choices[0]; + const chosen = aspect.choices[0]; - return ( -
-

- {aspect.title} -

-
- {/* noop */} - {aspect.choiceKind === 'yesNo' && ( - setOn(old => !old)} downloadSize={'287.5 MB'} /> - )} -
- {chosen && chosen.description && -
-

Description

-

- -

-
- } + return ( +
+

{aspect.title}

+
+ {/* noop */} + {aspect.choiceKind === 'yesNo' && ( + setOn((old) => !old)} downloadSize={'287.5 MB'} /> + )} +
+ {chosen && chosen.description && ( +
+

Description

+

+ + {chosen.description} + +

- ); + )} +
+ ); }; diff --git a/src/renderer/components/AddonSection/Configure/TrackSelector.tsx b/src/renderer/components/AddonSection/Configure/TrackSelector.tsx index 5c43e587..94d1fcaa 100644 --- a/src/renderer/components/AddonSection/Configure/TrackSelector.tsx +++ b/src/renderer/components/AddonSection/Configure/TrackSelector.tsx @@ -1,44 +1,48 @@ -import React from "react"; -import { useSelector } from "react-redux"; -import { InstallerStore } from "renderer/redux/store"; +import React from 'react'; +import { useSelector } from 'react-redux'; +import { InstallerStore } from 'renderer/redux/store'; import { Check } from 'tabler-icons-react'; -import { Addon, AddonTrack } from "renderer/utils/InstallerConfiguration"; +import { Addon, AddonTrack } from 'renderer/utils/InstallerConfiguration'; import '../index.css'; +import { twMerge } from 'tailwind-merge'; export const Tracks: React.FC = ({ children }) => ( -
- {children} -
+
{children}
); type TrackProps = { - className?: string, - addon: Addon, - track: AddonTrack, - isSelected: boolean, - isInstalled: boolean, - handleSelected(track: AddonTrack): void, + className?: string; + addon: Addon; + track: AddonTrack; + isSelected: boolean; + isInstalled: boolean; + handleSelected(track: AddonTrack): void; }; export const Track: React.FC = ({ isSelected, isInstalled, handleSelected, addon, track }) => { - const latestVersionName = useSelector((state) => ( - state.latestVersionNames[addon.key]?.[track.key]?.name ?? '' - )); + const latestVersionName = useSelector( + (state) => state.latestVersionNames[addon.key]?.[track.key]?.name ?? '', + ); - return ( -
handleSelected(track)} - > -
-
- {track.name} - {latestVersionName} -
- {isInstalled && ( - - )} -
- ); + return ( +
handleSelected(track)} + > +
+
+ {track.name} + + {latestVersionName} + +
+ {isInstalled && } +
+ ); }; diff --git a/src/renderer/components/AddonSection/Configure/YesNoOptionToggle.tsx b/src/renderer/components/AddonSection/Configure/YesNoOptionToggle.tsx index 2a01d5d6..5b7f7feb 100644 --- a/src/renderer/components/AddonSection/Configure/YesNoOptionToggle.tsx +++ b/src/renderer/components/AddonSection/Configure/YesNoOptionToggle.tsx @@ -1,28 +1,29 @@ import React, { FC } from 'react'; -import { Toggle } from "renderer/components/Toggle"; +import { Toggle } from 'renderer/components/Toggle'; export interface YesNoOptionToggleProps { - enabled: boolean, - onToggle: () => void - downloadSize?: string, + enabled: boolean; + onToggle: () => void; + downloadSize?: string; } export const YesNoOptionToggle: FC = ({ enabled, onToggle, downloadSize }) => { - const handleClick = onToggle; + const handleClick = onToggle; - const bgColor = enabled ? 'bg-utility-green' : 'bg-navy-light'; - const titleColor = enabled ? 'text-navy' : 'text-quasi-white'; + const bgColor = enabled ? 'bg-utility-green' : 'bg-navy-light'; + const titleColor = enabled ? 'text-navy' : 'text-quasi-white'; - return ( -
- + return ( +
+ - - Terrain Database - {downloadSize && ( - {downloadSize} - )} - -
- ); + + Terrain Database + {downloadSize && {downloadSize}} + +
+ ); }; diff --git a/src/renderer/components/AddonSection/Configure/index.tsx b/src/renderer/components/AddonSection/Configure/index.tsx index becb9582..9fb5e098 100644 --- a/src/renderer/components/AddonSection/Configure/index.tsx +++ b/src/renderer/components/AddonSection/Configure/index.tsx @@ -1,136 +1,146 @@ -import React, { FC } from "react"; -import { useHistory, useParams } from "react-router-dom"; -import ReactMarkdown from "react-markdown"; -import { Addon, AddonTrack, ConfigurationAspect } from "renderer/utils/InstallerConfiguration"; -import { Track, Tracks } from "./TrackSelector"; -import { ConfigurationAspectDisplay } from "renderer/components/AddonSection/Configure/ConfigurationAspectDisplay"; +import React, { FC } from 'react'; +import { useHistory, useParams } from 'react-router-dom'; +import ReactMarkdown from 'react-markdown'; +import { Addon, AddonTrack, ConfigurationAspect } from 'renderer/utils/InstallerConfiguration'; +import { Track, Tracks } from './TrackSelector'; +import { ConfigurationAspectDisplay } from 'renderer/components/AddonSection/Configure/ConfigurationAspectDisplay'; import './index.css'; export interface ConfigureProps { - routeAspectKey: string, - selectedAddon: Addon, - selectedTrack: () => AddonTrack, - installedTrack: () => AddonTrack, - onTrackSelection: (track: AddonTrack) => void, + routeAspectKey: string; + selectedAddon: Addon; + selectedTrack: () => AddonTrack; + installedTrack: () => AddonTrack; + onTrackSelection: (track: AddonTrack) => void; } -export const Configure: FC = ({ routeAspectKey, selectedAddon, selectedTrack, installedTrack, onTrackSelection }) => { - const history = useHistory(); - const { aspectKey: currentAspectKey } = useParams<{ aspectKey: string; }>(); +export const Configure: FC = ({ + routeAspectKey, + selectedAddon, + selectedTrack, + installedTrack, + onTrackSelection, +}) => { + const history = useHistory(); + const { aspectKey: currentAspectKey } = useParams<{ aspectKey: string }>(); - let page; - if (routeAspectKey === 'release-track') { - page = ( - <> -

- Choose Your Version -

-
-
- - {selectedAddon.tracks - .filter((track) => !track.isExperimental) - .map((track) => ( - onTrackSelection(track)} - /> - ))} - - - Mainline Releases - -
-
- - {selectedAddon.tracks - .filter((track) => track.isExperimental) - .map((track) => ( - onTrackSelection(track)} - /> - ))} - + let page; + if (routeAspectKey === 'release-track') { + page = ( + <> +

Choose Your Version

+
+
+ + {selectedAddon.tracks + .filter((track) => !track.isExperimental) + .map((track) => ( + onTrackSelection(track)} + /> + ))} + + Mainline Releases +
+
+ + {selectedAddon.tracks + .filter((track) => track.isExperimental) + .map((track) => ( + onTrackSelection(track)} + /> + ))} + - {selectedAddon.tracks.filter((track) => track.isExperimental).length > 0 && ( - - Experimental versions - - )} -
-
- {selectedTrack() && selectedTrack().description && -
-

Description

-

- -

-
- } - - ); - } else { - const aspect = selectedAddon.configurationAspects?.find((it) => it.key === routeAspectKey); - - if (!aspect) { - console.error(`Tried to build page for unknown configuration aspect (addon=${selectedAddon.key}, aspectKey=${routeAspectKey})`); - history.push('/addon-section/:publisher/main/configure/release-track'); - return null; - } + {selectedAddon.tracks.filter((track) => track.isExperimental).length > 0 && ( + Experimental versions + )} +
+
+ {selectedTrack() && selectedTrack().description && ( +
+

Description

+

+ + {selectedTrack().description} + +

+
+ )} + + ); + } else { + const aspect = selectedAddon.configurationAspects?.find((it) => it.key === routeAspectKey); - page = ; + if (!aspect) { + console.error( + `Tried to build page for unknown configuration aspect (addon=${selectedAddon.key}, aspectKey=${routeAspectKey})`, + ); + history.push('/addon-section/:publisher/main/configure/release-track'); + return null; } - return ( -
-
- {page} -
+ page = ; + } - {selectedAddon.configurationAspects?.length > 0 && ( -
- + return ( +
+
{page}
- {selectedAddon.configurationAspects.map((aspect) => ( - - ))} -
- )} + {selectedAddon.configurationAspects?.length > 0 && ( +
+ + + {selectedAddon.configurationAspects.map((aspect) => ( + + ))}
- ); + )} +
+ ); }; -const ConfigurationAspectTab: FC<{ aspect: ConfigurationAspect, selected: boolean }> = ({ aspect, selected }) => { - const history = useHistory(); +const ConfigurationAspectTab: FC<{ aspect: ConfigurationAspect; selected: boolean }> = ({ aspect, selected }) => { + const history = useHistory(); - const color = (selected ? 'text-quasi-white' : 'text-gray-300') + ' hover:text-cyan'; + const color = (selected ? 'text-quasi-white' : 'text-gray-300') + ' hover:text-cyan'; - const handleClick = () => { - history.push(`/addon-section/:publisher/main/configure/${aspect.key}`); - }; + const handleClick = () => { + history.push(`/addon-section/:publisher/main/configure/${aspect.key}`); + }; - return ( -
- {aspect.tabSupertitle} - {aspect.tabTitle} -
- ); + return ( +
+ {aspect.tabSupertitle} + {aspect.tabTitle} +
+ ); }; diff --git a/src/renderer/components/AddonSection/Enums.ts b/src/renderer/components/AddonSection/Enums.ts index ac195510..e7af481c 100644 --- a/src/renderer/components/AddonSection/Enums.ts +++ b/src/renderer/components/AddonSection/Enums.ts @@ -1,60 +1,48 @@ export enum InstallStatus { - UpToDate, - NeedsUpdate, - NotInstalled, - GitInstall, - TrackSwitch, - InstallingDependency, - InstallingDependencyEnding, - DownloadPending, - DownloadPrep, - Downloading, - Decompressing, - DownloadEnding, - DownloadDone, - DownloadRetry, - DownloadError, - DownloadCanceled, - Unknown, - Hidden, + UpToDate, + NeedsUpdate, + NotInstalled, + GitInstall, + TrackSwitch, + InstallingDependency, + InstallingDependencyEnding, + DownloadPending, + DownloadPrep, + Downloading, + Decompressing, + DownloadEnding, + DownloadDone, + DownloadRetry, + DownloadError, + DownloadCanceled, + Unknown, + Hidden, } export const InstallStatusCategories = { - installing: [ - InstallStatus.DownloadPending, - InstallStatus.Downloading, - InstallStatus.DownloadPrep, - InstallStatus.InstallingDependency, - InstallStatus.InstallingDependencyEnding, - InstallStatus.Decompressing, - InstallStatus.DownloadRetry, - InstallStatus.DownloadEnding, - ], - installingNoProgress: [ - InstallStatus.DownloadPrep, - InstallStatus.DownloadPending, - InstallStatus.DownloadRetry, - InstallStatus.DownloadEnding, - ], - installingDependency: [ - InstallStatus.InstallingDependency, - InstallStatus.InstallingDependencyEnding, - ], - installed: [ - InstallStatus.UpToDate, - InstallStatus.NeedsUpdate, - InstallStatus.TrackSwitch, - InstallStatus.DownloadDone, - ], - installOrUpdatePending: [ - InstallStatus.NeedsUpdate, - InstallStatus.NotInstalled, - InstallStatus.TrackSwitch, - ], + installing: [ + InstallStatus.DownloadPending, + InstallStatus.Downloading, + InstallStatus.DownloadPrep, + InstallStatus.InstallingDependency, + InstallStatus.InstallingDependencyEnding, + InstallStatus.Decompressing, + InstallStatus.DownloadRetry, + InstallStatus.DownloadEnding, + ], + installingNoProgress: [ + InstallStatus.DownloadPrep, + InstallStatus.DownloadPending, + InstallStatus.DownloadRetry, + InstallStatus.DownloadEnding, + ], + installingDependency: [InstallStatus.InstallingDependency, InstallStatus.InstallingDependencyEnding], + installed: [InstallStatus.UpToDate, InstallStatus.NeedsUpdate, InstallStatus.TrackSwitch, InstallStatus.DownloadDone], + installOrUpdatePending: [InstallStatus.NeedsUpdate, InstallStatus.NotInstalled, InstallStatus.TrackSwitch], }; export enum ApplicationStatus { - Open, - Closed, - Checking, + Open, + Closed, + Checking, } diff --git a/src/renderer/components/AddonSection/MainActionButton.tsx b/src/renderer/components/AddonSection/MainActionButton.tsx index c3da705f..a4959535 100644 --- a/src/renderer/components/AddonSection/MainActionButton.tsx +++ b/src/renderer/components/AddonSection/MainActionButton.tsx @@ -1,84 +1,84 @@ -import React, { FC } from "react"; -import { ButtonType } from "renderer/components/Button"; -import { SidebarButton } from "renderer/components/AddonSection/index"; -import { InstallStatus } from "renderer/components/AddonSection/Enums"; -import { InstallState } from "renderer/redux/features/installStatus"; +import React, { FC } from 'react'; +import { ButtonType } from 'renderer/components/Button'; +import { SidebarButton } from 'renderer/components/AddonSection/index'; +import { InstallStatus } from 'renderer/components/AddonSection/Enums'; +import { InstallState } from 'renderer/redux/features/installStatus'; interface ActiveInstallButtonProps { - installState: InstallState, - onInstall: () => void, - onCancel: () => void, + installState: InstallState; + onInstall: () => void; + onCancel: () => void; } export const MainActionButton: FC = ({ - installState: { status: installStatus }, - onInstall, - onCancel, + installState: { status: installStatus }, + onInstall, + onCancel, }): JSX.Element => { - switch (installStatus) { - case InstallStatus.DownloadDone: - case InstallStatus.UpToDate: - return ( - - Installed - - ); - case InstallStatus.NeedsUpdate: - return ( - - Update - - ); - case InstallStatus.NotInstalled: - return ( - - Install - - ); - case InstallStatus.GitInstall: - return ( - - Installed (git) - - ); - case InstallStatus.TrackSwitch: - return ( - - Switch Version - - ); - case InstallStatus.InstallingDependency: - case InstallStatus.Downloading: - case InstallStatus.Decompressing: - return ( - - Cancel - - ); - case InstallStatus.DownloadPending: - case InstallStatus.DownloadPrep: - case InstallStatus.DownloadEnding: - case InstallStatus.InstallingDependencyEnding: - return ( - - Cancel - - ); - case InstallStatus.DownloadCanceled: - return ( - - Cancelled - - ); - case InstallStatus.DownloadRetry: - case InstallStatus.DownloadError: - case InstallStatus.Unknown: - return ( - - Error - - ); - default: - return <>; - } + switch (installStatus) { + case InstallStatus.DownloadDone: + case InstallStatus.UpToDate: + return ( + + Installed + + ); + case InstallStatus.NeedsUpdate: + return ( + + Update + + ); + case InstallStatus.NotInstalled: + return ( + + Install + + ); + case InstallStatus.GitInstall: + return ( + + Installed (git) + + ); + case InstallStatus.TrackSwitch: + return ( + + Switch Version + + ); + case InstallStatus.InstallingDependency: + case InstallStatus.Downloading: + case InstallStatus.Decompressing: + return ( + + Cancel + + ); + case InstallStatus.DownloadPending: + case InstallStatus.DownloadPrep: + case InstallStatus.DownloadEnding: + case InstallStatus.InstallingDependencyEnding: + return ( + + Cancel + + ); + case InstallStatus.DownloadCanceled: + return ( + + Cancelled + + ); + case InstallStatus.DownloadRetry: + case InstallStatus.DownloadError: + case InstallStatus.Unknown: + return ( + + Error + + ); + default: + return <>; + } }; diff --git a/src/renderer/components/AddonSection/MyInstall/index.tsx b/src/renderer/components/AddonSection/MyInstall/index.tsx index 03cf2414..64f634f3 100644 --- a/src/renderer/components/AddonSection/MyInstall/index.tsx +++ b/src/renderer/components/AddonSection/MyInstall/index.tsx @@ -1,106 +1,104 @@ -import React, { FC } from "react"; +import React, { FC } from 'react'; import { - Addon, - DirectoryDefinition, - ExternalLink, - NamedDirectoryDefinition, -} from "renderer/utils/InstallerConfiguration"; -import { BoxArrowRight, Folder } from "react-bootstrap-icons"; -import { shell } from "electron"; -import { Directories } from "renderer/utils/Directories"; -import { useAppSelector } from "renderer/redux/store"; -import { InstallStatusCategories } from "renderer/components/AddonSection/Enums"; + Addon, + DirectoryDefinition, + ExternalLink, + NamedDirectoryDefinition, +} from 'renderer/utils/InstallerConfiguration'; +import { BoxArrowRight, Folder } from 'react-bootstrap-icons'; +import { shell } from 'electron'; +import { Directories } from 'renderer/utils/Directories'; +import { useAppSelector } from 'renderer/redux/store'; +import { InstallStatusCategories } from 'renderer/components/AddonSection/Enums'; export interface MyInstallProps { - addon: Addon, + addon: Addon; } export const MyInstall: FC = ({ addon }) => { - const installStates = useAppSelector((state) => state.installStatus); + const installStates = useAppSelector((state) => state.installStatus); - const links: ExternalLink[] = [ - ...(addon.myInstallPage?.links ?? []), - ]; + const links: ExternalLink[] = [...(addon.myInstallPage?.links ?? [])]; - const directories: NamedDirectoryDefinition[] = [ - { - location: { - in: 'community', - path: addon.targetDirectory, - }, - title: 'Package Files', - }, - ...(addon.myInstallPage?.directories ?? []), - ]; + const directories: NamedDirectoryDefinition[] = [ + { + location: { + in: 'community', + path: addon.targetDirectory, + }, + title: 'Package Files', + }, + ...(addon.myInstallPage?.directories ?? []), + ]; - const handleClickLink = (link: ExternalLink) => { - const parsed = new URL(link.url); + const handleClickLink = (link: ExternalLink) => { + const parsed = new URL(link.url); - if (parsed.protocol.match(/https?/)) { - shell.openExternal(link.url).then(); - } - }; + if (parsed.protocol.match(/https?/)) { + shell.openExternal(link.url).then(); + } + }; - const handleClickDirectory = (def: DirectoryDefinition) => { - let fullPath; - switch (def.location.in) { - case 'community': - fullPath = Directories.inInstallLocation(def.location.path); - break; - case 'package': - fullPath = Directories.inInstallPackage(addon, def.location.path); - break; - case "packageCache": - fullPath = Directories.inPackageCache(addon, def.location.path); - break; - } + const handleClickDirectory = (def: DirectoryDefinition) => { + let fullPath; + switch (def.location.in) { + case 'community': + fullPath = Directories.inInstallLocation(def.location.path); + break; + case 'package': + fullPath = Directories.inInstallPackage(addon, def.location.path); + break; + case 'packageCache': + fullPath = Directories.inPackageCache(addon, def.location.path); + break; + } - shell.openPath(fullPath).then(); - }; + shell.openPath(fullPath).then(); + }; - const directoriesDisabled = !InstallStatusCategories.installed.includes(installStates[addon.key]?.status); + const directoriesDisabled = !InstallStatusCategories.installed.includes(installStates[addon.key]?.status); - return ( -
- {links.length > 0 && ( -
-

Links

+ return ( +
+ {links.length > 0 && ( +
+

Links

-
- {links.map((it) => ( - - ))} -
-
- )} + {it.title} + + ))} +
+
+ )} - {directories.length > 0 && ( -
-

Directories

+ {directories.length > 0 && ( +
+

Directories

-
- {directories.map((it) => ( - - ))} -
-
- )} + {it.title} + + ))} +
- ); + )} +
+ ); }; diff --git a/src/renderer/components/AddonSection/ReleaseNotes/index.tsx b/src/renderer/components/AddonSection/ReleaseNotes/index.tsx index 9dc08e90..a4095bd0 100644 --- a/src/renderer/components/AddonSection/ReleaseNotes/index.tsx +++ b/src/renderer/components/AddonSection/ReleaseNotes/index.tsx @@ -1,152 +1,150 @@ import React, { forwardRef, useEffect, useRef, useState } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; -import { store, useAppSelector } from "renderer/redux/store"; -import "./index.css"; -import { Addon } from "renderer/utils/InstallerConfiguration"; -import { useInView } from "react-intersection-observer"; -import { ReleaseData } from "renderer/redux/types"; -import { GitVersions } from "@flybywiresim/api-client"; -import { addReleases } from "renderer/redux/features/releaseNotes"; +import { store, useAppSelector } from 'renderer/redux/store'; +import './index.css'; +import { Addon } from 'renderer/utils/InstallerConfiguration'; +import { useInView } from 'react-intersection-observer'; +import { ReleaseData } from 'renderer/redux/types'; +import { GitVersions } from '@flybywiresim/api-client'; +import { addReleases } from 'renderer/redux/features/releaseNotes'; import { useSetting } from 'common/settings'; import dateFormat from 'dateformat'; -import { ArrowUp } from "react-bootstrap-icons"; +import { ArrowUp } from 'react-bootstrap-icons'; interface ReleaseNoteCardProps { - release: ReleaseData; - isLatest?: boolean; + release: ReleaseData; + isLatest?: boolean; } const ReleaseNoteCard = forwardRef(({ release, isLatest }, ref) => { - let [dateLayout] = useSetting('mainSettings.dateLayout'); - const [useLongDateFormat] = useSetting('mainSettings.useLongDateFormat'); - - if (useLongDateFormat) { - dateLayout = dateLayout. - replace('mm', 'mmmm'). - replace(/\//g, ' '); - } - - return ( -
-
-
-

{release.name}

- {isLatest && ( -
Latest
- )} -
-
- {dateFormat(new Date(release.publishedAt), dateLayout)} -
-
- + let [dateLayout] = useSetting('mainSettings.dateLayout'); + const [useLongDateFormat] = useSetting('mainSettings.useLongDateFormat'); + + if (useLongDateFormat) { + dateLayout = dateLayout.replace('mm', 'mmmm').replace(/\//g, ' '); + } + + return ( +
+
+
+

{release.name}

+ {isLatest &&
Latest
}
- ); +
{dateFormat(new Date(release.publishedAt), dateLayout)}
+
+ + {release.body ?? ''} + +
+ ); }); -export const ReleaseNotes = ({ addon }: {addon: Addon}): JSX.Element => { - const { ref, inView } = useInView({ - threshold: 0, - }); +ReleaseNoteCard.displayName = 'ReleaseNoteCard'; - const releaseNotes = useAppSelector(state => state.releaseNotes[addon.key]); - const [releaseComponent, setReleaseComponent] = useState(undefined); - const releaseNotesRef = useRef(null); - const [scrollButtonShown, setScrollButtonShown] = useState(false); +export const ReleaseNotes = ({ addon }: { addon: Addon }): JSX.Element => { + const { ref, inView } = useInView({ + threshold: 0, + }); - const handleScrollUp = () => { - if (releaseNotesRef) { - releaseNotesRef.current.scroll({ top: 0, behavior: "smooth" }); - } + const releaseNotes = useAppSelector((state) => state.releaseNotes[addon.key]); + const [releaseComponent, setReleaseComponent] = useState(undefined); + const releaseNotesRef = useRef(null); + const [scrollButtonShown, setScrollButtonShown] = useState(false); + + const handleScrollUp = () => { + if (releaseNotesRef) { + releaseNotesRef.current.scroll({ top: 0, behavior: 'smooth' }); + } + }; + + useEffect(() => { + setReleaseComponent( +
+ {releaseNotes.map((release, index) => ( + + ))} +
, + ); + }, [ref, releaseNotes]); + + useEffect(() => { + if (inView) { + if (addon.repoOwner && addon.repoName) { + GitVersions.getReleases(addon.repoOwner, addon.repoName, false, releaseNotes.length, 5).then((res) => { + const content = res.map((release) => ({ + name: release.name, + publishedAt: release.publishedAt.getTime(), + htmlUrl: release.htmlUrl, + body: release.body, + })); + + if (content.length) { + store.dispatch(addReleases({ key: addon.key, releases: content })); + } + }); + } else { + store.dispatch(addReleases({ key: addon.key, releases: [] })); + } + } + }, [addon.key, addon.repoName, addon.repoOwner, inView, releaseNotes.length]); + + useEffect(() => { + const releaseNotes = releaseNotesRef.current; + + const handleScroll = () => { + if (releaseNotes) { + setScrollButtonShown(!!releaseNotes.scrollTop); + } }; - useEffect(() => { - setReleaseComponent( -
- {releaseNotes.map((release, index) => - , - )} -
, - ); - }, [releaseNotes]); - - useEffect(() => { - if (inView) { - if (addon.repoOwner && addon.repoName) { - GitVersions.getReleases(addon.repoOwner, addon.repoName, false, releaseNotes.length, 5).then(res => { - const content = res.map(release => ({ - name: release.name, - publishedAt: release.publishedAt.getTime(), - htmlUrl: release.htmlUrl, - body: release.body, - })); - - if (content.length) { - store.dispatch(addReleases({ key: addon.key, releases: content })); - } - }); - } else { - store.dispatch(addReleases({ key: addon.key, releases: [] })); - } - } - }, [inView]); - - useEffect(() => { - const handleScroll = () => { - if (releaseNotesRef.current) { - setScrollButtonShown(!!releaseNotesRef.current.scrollTop); - } - }; - - if (releaseNotesRef.current) { - releaseNotesRef.current.addEventListener('scroll', handleScroll); - } - - return () => releaseNotesRef.current.removeEventListener('scroll', handleScroll); - }, [releaseNotesRef.current]); - - const DummyComponent = () => ( -
- {[...Array(10)].map(index => ( -
-
-

-

-

-
-
- ))} + if (releaseNotes) { + releaseNotes.addEventListener('scroll', handleScroll); + } + + return () => releaseNotes.removeEventListener('scroll', handleScroll); + }, []); + + const DummyComponent = () => ( +
+ {[...Array(10)].map((index) => ( +
+
+

+

+

+
- ); + ))} +
+ ); + + return ( +
+ {scrollButtonShown && releaseComponent && ( +
+
+ +
+
+ )} +
+
+

Release Notes

- return ( -
- {scrollButtonShown && releaseComponent && ( -
-
- -
-
- )} -
-
-

- Release Notes -

- -

Stable Version

-
- {releaseComponent ?? } -
+

Stable Version

- ); + {releaseComponent ?? } +
+
+ ); }; diff --git a/src/renderer/components/AddonSection/StateSection.tsx b/src/renderer/components/AddonSection/StateSection.tsx index bb99b59b..aa1270d2 100644 --- a/src/renderer/components/AddonSection/StateSection.tsx +++ b/src/renderer/components/AddonSection/StateSection.tsx @@ -1,332 +1,336 @@ -import React, { FC } from "react"; -import { InstallStatus, InstallStatusCategories } from "renderer/components/AddonSection/Enums"; -import { useAppSelector } from "renderer/redux/store"; -import { Addon, Publisher } from "renderer/utils/InstallerConfiguration"; -import { InstallingDependencyInstallState, InstallState } from "renderer/redux/features/installStatus"; -import { DownloadItem } from "renderer/redux/types"; -import { Activity, Download, Gear, Stopwatch, WifiOff, XOctagon } from "react-bootstrap-icons"; -import { BackgroundServices } from "renderer/utils/BackgroundServices"; -import { Button, ButtonType } from "renderer/components/Button"; -import { PromptModal, useModals } from "renderer/components/Modal"; -import { AutostartDialog } from "renderer/components/Modal/AutostartDialog"; -import { useAddonExternalApps } from "renderer/utils/ExternalAppsUI"; +import React, { FC } from 'react'; +import { InstallStatus, InstallStatusCategories } from 'renderer/components/AddonSection/Enums'; +import { useAppSelector } from 'renderer/redux/store'; +import { Addon, Publisher } from 'renderer/utils/InstallerConfiguration'; +import { InstallingDependencyInstallState, InstallState } from 'renderer/redux/features/installStatus'; +import { DownloadItem } from 'renderer/redux/types'; +import { Activity, Download, Gear, Stopwatch, WifiOff, XOctagon } from 'react-bootstrap-icons'; +import { BackgroundServices } from 'renderer/utils/BackgroundServices'; +import { Button, ButtonType } from 'renderer/components/Button'; +import { PromptModal, useModals } from 'renderer/components/Modal'; +import { AutostartDialog } from 'renderer/components/Modal/AutostartDialog'; +import { useAddonExternalApps } from 'renderer/utils/ExternalAppsUI'; export interface StateSectionProps { - publisher: Publisher, - addon: Addon, + publisher: Publisher; + addon: Addon; } export const StateSection: FC = ({ publisher, addon }) => { - const installStates = useAppSelector(state => state.installStatus); - const downloads = useAppSelector(state => state.downloads); + const installStates = useAppSelector((state) => state.installStatus); + const downloads = useAppSelector((state) => state.downloads); - const addonInstallState = installStates[addon.key]; - const addonDownload = downloads.find((it) => it.id === addon.key); + const addonInstallState = installStates[addon.key]; + const addonDownload = downloads.find((it) => it.id === addon.key); - if (!addonInstallState) { - return null; - } - - const status = addonInstallState.status; - - const isInstallingDependency = InstallStatusCategories.installingDependency.includes(status); - - let dependencyAddonDownload; - let dependencyAddonInstallState; - if (isInstallingDependency) { - const dependencyAddonKey = (addonInstallState as InstallingDependencyInstallState).dependencyAddonKey; - - dependencyAddonInstallState = installStates[dependencyAddonKey]; - dependencyAddonDownload = downloads.find((it) => it.id === dependencyAddonKey); - } - - return ( - <> - - - - ); + if (!addonInstallState) { + return null; + } + + const status = addonInstallState.status; + + const isInstallingDependency = InstallStatusCategories.installingDependency.includes(status); + + let dependencyAddonDownload; + let dependencyAddonInstallState; + if (isInstallingDependency) { + const dependencyAddonKey = (addonInstallState as InstallingDependencyInstallState).dependencyAddonKey; + + dependencyAddonInstallState = installStates[dependencyAddonKey]; + dependencyAddonDownload = downloads.find((it) => it.id === dependencyAddonKey); + } + + return ( + <> + + + + ); }; const StateContainer: FC = ({ children }) => ( -
- {children} -
+
+ {children} +
); -const SmallStateText: FC = ({ children }) => ( -
{children}
-); +const SmallStateText: FC = ({ children }) =>
{children}
; -const StateText: FC = ({ children }) => ( -
{children}
-); +const StateText: FC = ({ children }) =>
{children}
; interface ProgressBarProps { - className: string, - value: number, - animated?: boolean, + className: string; + value: number; + animated?: boolean; } const ProgressBar: FC = ({ className, value }) => ( -
-
- {/* FIXME animation */} -
+
+
+ {/* FIXME animation */} +
); interface BackgroundServiceBannerProps { - publisher: Publisher, - addon: Addon, - installState: InstallState, + publisher: Publisher; + addon: Addon; + installState: InstallState; } const BackgroundServiceBanner: FC = ({ publisher, addon, installState }) => { - const { showModal, showModalAsync } = useModals(); - const [runningExternalApps] = useAddonExternalApps(addon, publisher); + const { showModal, showModalAsync } = useModals(); + const [runningExternalApps] = useAddonExternalApps(addon, publisher); - if (addon.backgroundService && InstallStatusCategories.installed.includes(installState.status)) { - const app = BackgroundServices.getExternalAppFromBackgroundService(addon, publisher); + if (addon.backgroundService && InstallStatusCategories.installed.includes(installState.status)) { + const app = BackgroundServices.getExternalAppFromBackgroundService(addon, publisher); - const isRunning = !!runningExternalApps.find((it) => it.key === app.key); + const isRunning = !!runningExternalApps.find((it) => it.key === app.key); - const bgAccentColor = isRunning ? 'bg-utility-green' : 'bg-gray-500'; + const bgAccentColor = isRunning ? 'bg-utility-green' : 'bg-gray-500'; - const handleClickAutostart = () => showModal( - , - ); + const handleClickAutostart = () => + showModal(); - const handleStartStop = async () => { - if (isRunning) { - const md = `Are you sure you want to shut down **${addon.name}**?. All related functionality will no longer be available.`; - - const doStop = await showModalAsync( - , - ); - - if (doStop) { - await BackgroundServices.kill(addon, publisher); - } - } else { - await BackgroundServices.start(addon); - } - }; - - return ( -
- -
- { - isRunning ? ( - - ) : ( - - ) - } - -
- {addon.name} - {isRunning ? 'Running' : 'Not Running'} -
-
- -
- {(addon.backgroundService.enableAutostartConfiguration ?? true) && ( - - - Autostart... - - )} - - -
-
- - -
+ const handleStartStop = async () => { + if (isRunning) { + const md = `Are you sure you want to shut down **${addon.name}**?. All related functionality will no longer be available.`; + const doStop = await showModalAsync( + , ); - } - return null; + if (doStop) { + await BackgroundServices.kill(addon, publisher); + } + } else { + await BackgroundServices.start(addon); + } + }; + + return ( +
+ +
+ {isRunning ? ( + + ) : ( + + )} + +
+ {addon.name} + {isRunning ? 'Running' : 'Not Running'} +
+
+ +
+ {(addon.backgroundService.enableAutostartConfiguration ?? true) && ( + + + Autostart... + + )} + + +
+
+ + +
+ ); + } + + return null; }; interface DownloadProgressBannerProps { - addon: Addon, - installState: InstallState, - download: DownloadItem, - isDependency: boolean, + addon: Addon; + installState: InstallState; + download: DownloadItem; + isDependency: boolean; } const DownloadProgressBanner: FC = ({ addon, installState, download }) => { - if (!installState || !download) { - return null; - } + if (!installState || !download) { + return null; + } + + // TODO dependency state + + let stateIcon; + let stateText; + let progressBarBg; + let progressBarValue; + switch (installState.status) { + case InstallStatus.DownloadPrep: + stateIcon = ; + stateText = Preparing update; + progressBarBg = 'bg-cyan'; + progressBarValue = 0; + break; + case InstallStatus.Downloading: { + if (download.progress.interrupted) { + stateIcon = ; + stateText = {`Download interrupted`}; + progressBarBg = 'bg-utility-red'; + } else { + const part = + Number.isFinite(download.progress.splitPartIndex) && !Number.isFinite(download.progress.totalPercent) ? ( + + part {download.progress.splitPartIndex + 1}/{download.progress.splitPartCount} + + ) : null; + + stateIcon = ; + stateText = ( + + {`Downloading`} + {part} + + ); + progressBarBg = 'bg-cyan'; + } - // TODO dependency state - - let stateIcon; - let stateText; - let progressBarBg; - let progressBarValue; - switch (installState.status) { - case InstallStatus.DownloadPrep: - stateIcon = ; - stateText = Preparing update; - progressBarBg = 'bg-cyan'; - progressBarValue = 0; - break; - case InstallStatus.Downloading: { - if (download.progress.interrupted) { - stateIcon = ; - stateText = {`Download interrupted`}; - progressBarBg = 'bg-utility-red'; - } else { - const part = Number.isFinite(download.progress.splitPartIndex) && !Number.isFinite(download.progress.totalPercent) - ? part {download.progress.splitPartIndex + 1}/{download.progress.splitPartCount} - : null; - - stateIcon = ; - stateText = {`Downloading`}{part}; - progressBarBg = 'bg-cyan'; - } - - progressBarValue = Number.isFinite(download.progress.totalPercent) ? download.progress.totalPercent : download.progress.splitPartPercent; - - break; - } - case InstallStatus.Decompressing: - case InstallStatus.InstallingDependencyEnding: - stateIcon = ; - stateText = Decompressing; - progressBarBg = 'bg-cyan'; - progressBarValue = installState.percent; - break; - case InstallStatus.DownloadEnding: - stateIcon = ; - stateText = Finishing update; - progressBarBg = 'bg-cyan'; - progressBarValue = 100; - break; - case InstallStatus.DownloadDone: - stateText = Completed!; - progressBarBg = 'bg-cyan'; - progressBarValue = 100; - break; - case InstallStatus.DownloadRetry: - stateIcon = ; - stateText = Retrying {download?.module.toLowerCase()} module; - progressBarBg = 'bg-utility-amber'; - progressBarValue = 100; - break; - case InstallStatus.DownloadError: - stateIcon = ; - stateText = Failed to install; - progressBarBg = 'bg-utility-red'; - progressBarValue = 100; - break; - case InstallStatus.DownloadCanceled: - stateIcon = ; - stateText = Download canceled; - progressBarBg = 'bg-utility-amber'; - progressBarValue = 100; - break; - case InstallStatus.Unknown: - stateText = Unknown state; - progressBarBg = 'bg-utility-amber'; - progressBarValue = 100; - break; - case InstallStatus.NotInstalled: - case InstallStatus.GitInstall: - case InstallStatus.TrackSwitch: - default: - stateText = <>; - progressBarBg = 'bg-cyan'; - progressBarValue = 0; - break; + progressBarValue = Number.isFinite(download.progress.totalPercent) + ? download.progress.totalPercent + : download.progress.splitPartPercent; + + break; } + case InstallStatus.Decompressing: + case InstallStatus.InstallingDependencyEnding: + stateIcon = ; + stateText = Decompressing; + progressBarBg = 'bg-cyan'; + progressBarValue = installState.percent; + break; + case InstallStatus.DownloadEnding: + stateIcon = ; + stateText = Finishing update; + progressBarBg = 'bg-cyan'; + progressBarValue = 100; + break; + case InstallStatus.DownloadDone: + stateText = Completed!; + progressBarBg = 'bg-cyan'; + progressBarValue = 100; + break; + case InstallStatus.DownloadRetry: + stateIcon = ; + stateText = Retrying {download?.module.toLowerCase()} module; + progressBarBg = 'bg-utility-amber'; + progressBarValue = 100; + break; + case InstallStatus.DownloadError: + stateIcon = ; + stateText = Failed to install; + progressBarBg = 'bg-utility-red'; + progressBarValue = 100; + break; + case InstallStatus.DownloadCanceled: + stateIcon = ; + stateText = Download canceled; + progressBarBg = 'bg-utility-amber'; + progressBarValue = 100; + break; + case InstallStatus.Unknown: + stateText = Unknown state; + progressBarBg = 'bg-utility-amber'; + progressBarValue = 100; + break; + case InstallStatus.NotInstalled: + case InstallStatus.GitInstall: + case InstallStatus.TrackSwitch: + default: + stateText = <>; + progressBarBg = 'bg-cyan'; + progressBarValue = 0; + break; + } + + const showProgress = + InstallStatusCategories.installing.includes(installState.status) && + !InstallStatusCategories.installingNoProgress.includes(installState.status); + + // let smallStateText; + // if (installState.status === InstallStatus.Downloading && download?.progress.interrupted) { + // smallStateText = Waiting for network connection; + // } else if (isDependency) { + // smallStateText = Installing dependency; + // } else { + // smallStateText = Installing; + // } + + let moduleStateText; + if ( + download.module && + installState.status !== InstallStatus.DownloadCanceled && + installState.status !== InstallStatus.DownloadEnding + ) { + const moduleIndicator = + showProgress && download.moduleCount > 1 ? ( + + {download.moduleIndex + 1}/{download.moduleCount} + + ) : null; + + moduleStateText = ( + + {moduleIndicator} + + + {download.module === 'full' ? 'Full package' : download.module} + + + ); + } else { + moduleStateText = ( + + {addon.name} + + ); + } - const showProgress = InstallStatusCategories.installing.includes(installState.status) && !(InstallStatusCategories.installingNoProgress.includes(installState.status)); - - // let smallStateText; - // if (installState.status === InstallStatus.Downloading && download?.progress.interrupted) { - // smallStateText = Waiting for network connection; - // } else if (isDependency) { - // smallStateText = Installing dependency; - // } else { - // smallStateText = Installing; - // } - - let moduleStateText; - if (download.module && installState.status !== InstallStatus.DownloadCanceled && installState.status !== InstallStatus.DownloadEnding) { - const moduleIndicator = showProgress && download.moduleCount > 1 ? - {download.moduleIndex + 1}/{download.moduleCount} : null; - - moduleStateText = ( - - {moduleIndicator} - - - {download.module === 'full' ? 'Full package' : download.module} - - - ); - } else { - moduleStateText = ( - - - {addon.name} - + return ( +
+ + + {stateIcon} + +
+ +
+
+ {moduleStateText} + {stateText} +
+
+
+ + {showProgress && ( + + + {progressBarValue}% - ); - } + + )} +
- return ( -
- - - {stateIcon} - -
- -
-
- {moduleStateText} - {stateText} -
-
-
- - {showProgress && ( - - - {progressBarValue}% - - - )} -
- - -
- ); + +
+ ); }; diff --git a/src/renderer/components/AddonSection/index.tsx b/src/renderer/components/AddonSection/index.tsx index 150d101f..8bacabd9 100644 --- a/src/renderer/components/AddonSection/index.tsx +++ b/src/renderer/components/AddonSection/index.tsx @@ -1,549 +1,575 @@ -import React, { FC, useCallback, useEffect, useState } from "react"; -import { setupInstallPath } from "renderer/actions/install-path.utils"; -import { DownloadItem } from "renderer/redux/types"; -import { useSelector } from "react-redux"; -import { getCurrentInstall } from "@flybywiresim/fragmenter"; -import { InstallerStore, useAppDispatch, useAppSelector } from "../../redux/store"; -import { Addon, AddonCategoryDefinition, AddonTrack } from "renderer/utils/InstallerConfiguration"; -import { Directories } from "renderer/utils/Directories"; -import { NavLink, Redirect, Route, useHistory, useParams } from "react-router-dom"; -import { Gear, InfoCircle, JournalText, Sliders } from "react-bootstrap-icons"; -import settings, { useSetting } from "common/settings"; -import { ipcRenderer } from "electron"; -import { AddonBar, AddonBarItem } from "../App/AddonBar"; -import { NoAvailableAddonsSection } from "../NoAvailableAddonsSection"; -import { ReleaseNotes } from "./ReleaseNotes"; +import React, { FC, useCallback, useEffect, useState } from 'react'; +import { setupInstallPath } from 'renderer/actions/install-path.utils'; +import { DownloadItem } from 'renderer/redux/types'; +import { useSelector } from 'react-redux'; +import { getCurrentInstall } from '@flybywiresim/fragmenter'; +import { InstallerStore, useAppDispatch, useAppSelector } from '../../redux/store'; +import { Addon, AddonCategoryDefinition, AddonTrack } from 'renderer/utils/InstallerConfiguration'; +import { Directories } from 'renderer/utils/Directories'; +import { NavLink, Redirect, Route, useHistory, useParams } from 'react-router-dom'; +import { Gear, InfoCircle, JournalText, Sliders } from 'react-bootstrap-icons'; +import settings, { useSetting } from 'common/settings'; +import { ipcRenderer } from 'electron'; +import { AddonBar, AddonBarItem } from '../App/AddonBar'; +import { NoAvailableAddonsSection } from '../NoAvailableAddonsSection'; +import { ReleaseNotes } from './ReleaseNotes'; import { setInstalledTrack } from 'renderer/redux/features/installedTrack'; -import { InstallState, setInstallStatus } from "renderer/redux/features/installStatus"; -import { setSelectedTrack } from "renderer/redux/features/selectedTrack"; -import { PromptModal, useModals } from "renderer/components/Modal"; -import ReactMarkdown from "react-markdown"; -import { Button, ButtonType } from "renderer/components/Button"; -import { MainActionButton } from "renderer/components/AddonSection/MainActionButton"; -import { ApplicationStatus, InstallStatus, InstallStatusCategories } from "renderer/components/AddonSection/Enums"; -import { setApplicationStatus } from "renderer/redux/features/applicationStatus"; -import { LocalApiConfigEditUI } from "../LocalApiConfigEditUI"; -import { Configure } from "renderer/components/AddonSection/Configure"; -import { InstallManager } from "renderer/utils/InstallManager"; -import { StateSection } from "renderer/components/AddonSection/StateSection"; -import { ExternalApps } from "renderer/utils/ExternalApps"; -import { MyInstall } from "renderer/components/AddonSection/MyInstall"; +import { InstallState, setInstallStatus } from 'renderer/redux/features/installStatus'; +import { setSelectedTrack } from 'renderer/redux/features/selectedTrack'; +import { PromptModal, useModals } from 'renderer/components/Modal'; +import ReactMarkdown from 'react-markdown'; +import { Button, ButtonType } from 'renderer/components/Button'; +import { MainActionButton } from 'renderer/components/AddonSection/MainActionButton'; +import { ApplicationStatus, InstallStatus, InstallStatusCategories } from 'renderer/components/AddonSection/Enums'; +import { setApplicationStatus } from 'renderer/redux/features/applicationStatus'; +import { LocalApiConfigEditUI } from '../LocalApiConfigEditUI'; +import { Configure } from 'renderer/components/AddonSection/Configure'; +import { InstallManager } from 'renderer/utils/InstallManager'; +import { StateSection } from 'renderer/components/AddonSection/StateSection'; +import { ExternalApps } from 'renderer/utils/ExternalApps'; +import { MyInstall } from 'renderer/components/AddonSection/MyInstall'; const abortControllers = new Array(20); -abortControllers.fill(new AbortController); +abortControllers.fill(new AbortController()); interface InstallButtonProps { - type?: ButtonType, - disabled?: boolean, - className?: string; - onClick?: () => void; + type?: ButtonType; + disabled?: boolean; + className?: string; + onClick?: () => void; } export const SidebarButton: FC = ({ - type = ButtonType.Neutral, - disabled = false, - onClick, - children, + type = ButtonType.Neutral, + disabled = false, + onClick, + children, }) => ( - + ); interface SideBarLinkProps { - to: string; - disabled?: boolean; + to: string; + disabled?: boolean; } const SideBarLink: FC = ({ to, children, disabled = false }) => ( - - {children} - + + {children} + ); export interface AircraftSectionURLParams { - publisherName: string; + publisherName: string; } export const AddonSection = (): JSX.Element => { - const dispatch = useAppDispatch(); - const history = useHistory(); - - const { publisherName } = useParams(); - const publisherData = useAppSelector(state => state.configuration.publishers.find(pub => pub.name === publisherName) ? state.configuration.publishers.find(pub => pub.name === publisherName) : state.configuration.publishers[0]); + const dispatch = useAppDispatch(); + const history = useHistory(); + + const { publisherName } = useParams(); + const publisherData = useAppSelector((state) => + state.configuration.publishers.find((pub) => pub.name === publisherName) + ? state.configuration.publishers.find((pub) => pub.name === publisherName) + : state.configuration.publishers[0], + ); + + const [selectedAddon, setSelectedAddon] = useState(() => { + try { + return publisherData.addons[0]; + } catch (e) { + throw new Error('Invalid publisher key: ' + publisherName); + } + }); - const [selectedAddon, setSelectedAddon] = useState(() => { - try { - return publisherData.addons[0]; - } catch (e) { - throw new Error('Invalid publisher key: ' + publisherName); - } - }); + const [hiddenAddon, setHiddenAddon] = useState(undefined); - const [hiddenAddon, setHiddenAddon] = useState(undefined); + const installedTracks = useAppSelector((state) => state.installedTracks); + const selectedTracks = useAppSelector((state) => state.selectedTracks); + const installStates = useAppSelector((state) => state.installStatus); + const releaseNotes = useAppSelector((state) => state.releaseNotes[selectedAddon.key]); - const installedTracks = useAppSelector(state => state.installedTracks); - const selectedTracks = useAppSelector(state => state.selectedTracks); - const installStates = useAppSelector(state => state.installStatus); - const releaseNotes = useAppSelector(state => state.releaseNotes[selectedAddon.key]); + useEffect(() => { + const hiddenAddon = publisherData.addons.find((addon) => addon.key === selectedAddon.hidesAddon); - useEffect(() => { - const hiddenAddon = publisherData.addons.find((addon) => addon.key === selectedAddon.hidesAddon); + if (hiddenAddon) { + setHiddenAddon(hiddenAddon); + history.push(`/addon-section/${publisherName}/hidden-addon-cover`); + } else { + setHiddenAddon(undefined); + history.push(`/addon-section/${publisherName}/main/configure/release-track`); + } - if (hiddenAddon) { - setHiddenAddon(hiddenAddon); - history.push(`/addon-section/${publisherName}/hidden-addon-cover`); - } else { - setHiddenAddon(undefined); - history.push(`/addon-section/${publisherName}/main/configure/release-track`); - } + settings.set('cache.main.lastShownAddonKey', selectedAddon.key); + }, [history, publisherData.addons, publisherName, selectedAddon]); - settings.set('cache.main.lastShownAddonKey', selectedAddon.key); - }, [selectedAddon]); + useEffect(() => { + const firstAvailableAddon = publisherData.addons.find((addon) => addon.enabled); - useEffect(() => { - const firstAvailableAddon = publisherData.addons.find((addon) => addon.enabled); + if (!firstAvailableAddon) { + history.push(`/addon-section/${publisherName}/no-available-addons`); + return; + } - if (!firstAvailableAddon) { - history.push(`/addon-section/${publisherName}/no-available-addons`); - return; - } + const lastSeenAddonKey = settings.get('cache.main.lastShownAddonKey'); + const addonToSelect = + publisherData.addons.find((addon) => addon.key === lastSeenAddonKey) || + publisherData.addons.find((addon) => addon.key === firstAvailableAddon.key); - const lastSeenAddonKey = settings.get('cache.main.lastShownAddonKey'); - const addonToSelect = publisherData.addons.find(addon => addon.key === lastSeenAddonKey) || publisherData.addons.find(addon => addon.key === firstAvailableAddon.key); - - setSelectedAddon(addonToSelect); - }, [publisherName]); - - const findInstalledTrack = (): AddonTrack => { - if (!Directories.isFragmenterInstall(selectedAddon)) { - console.log("Not installed"); - if (selectedTrack()) { - selectAndSetTrack(selectedTrack().key); - return selectedTrack(); - } else { - setCurrentlySelectedTrack(selectedAddon.tracks[0]); - return selectedAddon.tracks[0]; - } - } + setSelectedAddon(addonToSelect); + }, [history, publisherData.addons, publisherName]); - try { - const manifest = getCurrentInstall( - Directories.inInstallLocation(selectedAddon.targetDirectory), - ); - console.log("Currently installed", manifest); - - let track = selectedAddon.tracks.find(track => track.url.includes(manifest.source)); - if (!track) { - track = selectedAddon.tracks.find(track => track.alternativeUrls?.includes(manifest.source)); - } - - console.log("Currently installed", track); - setCurrentlyInstalledTrack(track); - if (selectedTrack()) { - selectAndSetTrack(selectedTrack().key); - return selectedTrack(); - } else { - setCurrentlySelectedTrack(track); - return track; - } - } catch (e) { - console.error(e); - console.log("Not installed"); - if (selectedTrack()) { - selectAndSetTrack(selectedTrack().key); - return selectedTrack(); - } else { - setCurrentlySelectedTrack(selectedAddon.tracks[0]); - return selectedAddon.tracks[0]; - } - } - }; - - const installedTrack = (): AddonTrack => { - try { - return installedTracks[selectedAddon.key] as AddonTrack; - } catch (e) { - setCurrentlyInstalledTrack(null); - return null; - } - }; - - const setCurrentlyInstalledTrack = (newInstalledTrack: AddonTrack) => { - dispatch(setInstalledTrack({ addonKey: selectedAddon.key, installedTrack: newInstalledTrack })); - }; - - const selectedTrack = (): AddonTrack => { - try { - return selectedTracks[selectedAddon.key] as AddonTrack; - } catch (e) { - setCurrentlySelectedTrack(null); - return null; - } - }; - - const setCurrentlySelectedTrack = (newSelectedTrack: AddonTrack) => { - dispatch(setSelectedTrack({ addonKey: selectedAddon.key, track: newSelectedTrack })); - }; - - const getCurrentInstallStatus = (): InstallState => { - try { - return installStates[selectedAddon.key]; - } catch (e) { - setCurrentInstallStatus({ status: InstallStatus.Unknown }); - return { status: InstallStatus.Unknown }; - } - }; - - const setCurrentInstallStatus = (new_state: InstallState) => { - dispatch(setInstallStatus({ addonKey: selectedAddon.key, installState: new_state })); - }; - - const download: DownloadItem = useSelector((state: InstallerStore) => - state.downloads.find(download => download.id === selectedAddon.key), - ); - - const isDownloading = download?.progress.totalPercent >= 0; - const status = getCurrentInstallStatus()?.status; - const isInstalling = InstallStatusCategories.installing.includes(status); - const isFinishingDependencyInstall = status === InstallStatus.InstallingDependencyEnding; - - useEffect(() => { - const checkApplicationInterval = setInterval(async () => { - // Map app references to definition objects - const disallowedRunningExternalApps = ExternalApps.forAddon(selectedAddon, publisherData); - - for (const app of disallowedRunningExternalApps ?? []) { - // Determine what state the app is in - let state = false; - switch (app.detectionType) { - case 'ws': - state = await ExternalApps.determineStateWithWS(app); - break; - case 'http': - state = await ExternalApps.determineStateWithHttp(app); - break; - case 'tcp': - state = await ExternalApps.determineStateWithTcp(app); - break; - } - - // Dispatch the app's state - dispatch(setApplicationStatus({ - applicationName: app.key, - applicationStatus: state ? ApplicationStatus.Open : ApplicationStatus.Closed, - })); - } - }, 500); - - return () => clearInterval(checkApplicationInterval); - }, [selectedAddon]); - - useEffect(() => { - findInstalledTrack(); - if (!isInstalling) { - InstallManager.determineAddonInstallState(selectedAddon).then(setCurrentInstallStatus); - } - }, [selectedAddon, selectedTrack(), installedTrack()]); + const installedTrack = (): AddonTrack => { + try { + return installedTracks[selectedAddon.key] as AddonTrack; + } catch (e) { + setCurrentlyInstalledTrack(null); + return null; + } + }; + + const setCurrentlyInstalledTrack = useCallback( + (newInstalledTrack: AddonTrack) => { + dispatch(setInstalledTrack({ addonKey: selectedAddon.key, installedTrack: newInstalledTrack })); + }, + [dispatch, selectedAddon.key], + ); + + const setCurrentlySelectedTrack = useCallback( + (newSelectedTrack: AddonTrack) => { + dispatch(setSelectedTrack({ addonKey: selectedAddon.key, track: newSelectedTrack })); + }, + [dispatch, selectedAddon.key], + ); + + const selectedTrack = useCallback((): AddonTrack => { + try { + return selectedTracks[selectedAddon.key] as AddonTrack; + } catch (e) { + setCurrentlySelectedTrack(null); + return null; + } + }, [selectedAddon.key, selectedTracks, setCurrentlySelectedTrack]); + + const selectAndSetTrack = useCallback( + (key: string) => { + const newTrack = selectedAddon.tracks.find((track) => track.key === key); + setCurrentlySelectedTrack(newTrack); + }, + [selectedAddon.tracks, setCurrentlySelectedTrack], + ); + + const getCurrentInstallStatus = (): InstallState => { + try { + return installStates[selectedAddon.key]; + } catch (e) { + setCurrentInstallStatus({ status: InstallStatus.Unknown }); + return { status: InstallStatus.Unknown }; + } + }; + + const setCurrentInstallStatus = useCallback( + (new_state: InstallState) => { + dispatch(setInstallStatus({ addonKey: selectedAddon.key, installState: new_state })); + }, + [dispatch, selectedAddon.key], + ); + + const findInstalledTrack = useCallback((): AddonTrack => { + if (!Directories.isFragmenterInstall(selectedAddon)) { + console.log('Not installed'); + if (selectedTrack()) { + selectAndSetTrack(selectedTrack().key); + return selectedTrack(); + } else { + setCurrentlySelectedTrack(selectedAddon.tracks[0]); + return selectedAddon.tracks[0]; + } + } - useEffect(() => { - if (download && isDownloading) { - ipcRenderer.send("set-window-progress-bar", download.progress.totalPercent / 100); - } else { - ipcRenderer.send("set-window-progress-bar", -1); + try { + const manifest = getCurrentInstall(Directories.inInstallLocation(selectedAddon.targetDirectory)); + console.log('Currently installed', manifest); + + let track = selectedAddon.tracks.find((track) => track.url.includes(manifest.source)); + if (!track) { + track = selectedAddon.tracks.find((track) => track.alternativeUrls?.includes(manifest.source)); + } + + console.log('Currently installed', track); + setCurrentlyInstalledTrack(track); + if (selectedTrack()) { + selectAndSetTrack(selectedTrack().key); + return selectedTrack(); + } else { + setCurrentlySelectedTrack(track); + return track; + } + } catch (e) { + console.error(e); + console.log('Not installed'); + if (selectedTrack()) { + selectAndSetTrack(selectedTrack().key); + return selectedTrack(); + } else { + setCurrentlySelectedTrack(selectedAddon.tracks[0]); + return selectedAddon.tracks[0]; + } + } + }, [selectAndSetTrack, selectedAddon, selectedTrack, setCurrentlyInstalledTrack, setCurrentlySelectedTrack]); + + const download: DownloadItem = useSelector((state: InstallerStore) => + state.downloads.find((download) => download.id === selectedAddon.key), + ); + + const isDownloading = download?.progress.totalPercent >= 0; + const status = getCurrentInstallStatus()?.status; + const isInstalling = InstallStatusCategories.installing.includes(status); + const isFinishingDependencyInstall = status === InstallStatus.InstallingDependencyEnding; + + useEffect(() => { + const checkApplicationInterval = setInterval(async () => { + // Map app references to definition objects + const disallowedRunningExternalApps = ExternalApps.forAddon(selectedAddon, publisherData); + + for (const app of disallowedRunningExternalApps ?? []) { + // Determine what state the app is in + let state = false; + switch (app.detectionType) { + case 'ws': + state = await ExternalApps.determineStateWithWS(app); + break; + case 'http': + state = await ExternalApps.determineStateWithHttp(app); + break; + case 'tcp': + state = await ExternalApps.determineStateWithTcp(app); + break; } - }, [download]); - const [addonDiscovered] = useSetting( - "cache.main.discoveredAddons." + hiddenAddon?.key, - ); + // Dispatch the app's state + dispatch( + setApplicationStatus({ + applicationName: app.key, + applicationStatus: state ? ApplicationStatus.Open : ApplicationStatus.Closed, + }), + ); + } + }, 500); + + return () => clearInterval(checkApplicationInterval); + }, [dispatch, publisherData, selectedAddon]); + + useEffect(() => { + findInstalledTrack(); + if (!isInstalling) { + InstallManager.determineAddonInstallState(selectedAddon).then(setCurrentInstallStatus); + } + }, [findInstalledTrack, isInstalling, selectedAddon, setCurrentInstallStatus]); - useEffect(() => { - if (addonDiscovered) { - setSelectedAddon(hiddenAddon); - } - }, [addonDiscovered]); - - const { showModal, showModalAsync } = useModals(); - - const selectAndSetTrack = (key: string) => { - const newTrack = selectedAddon.tracks.find((track) => track.key === key); - setCurrentlySelectedTrack(newTrack); - }; - - const handleTrackSelection = (track: AddonTrack) => { - if (!isInstalling) { - if (track.isExperimental) { - showModal( - { - selectAndSetTrack(track.key); - }} - dontShowAgainSettingName='mainSettings.disableExperimentalWarning' - />); - } else { - selectAndSetTrack(track.key); - } - } - }; + useEffect(() => { + if (download && isDownloading) { + ipcRenderer.send('set-window-progress-bar', download.progress.totalPercent / 100); + } else { + ipcRenderer.send('set-window-progress-bar', -1); + } + }, [download, isDownloading]); - const handleInstall = async () => { - if (settings.has("mainSettings.installPath")) { - await InstallManager.installAddon(selectedAddon, publisherData, showModalAsync); - } else { - await setupInstallPath(); - } - }; + const [addonDiscovered] = useSetting('cache.main.discoveredAddons.' + hiddenAddon?.key); - const handleCancel = useCallback(() => { - if (isInstalling && !isFinishingDependencyInstall) { - InstallManager.cancelDownload(selectedAddon); - } - }, [selectedAddon, isInstalling]); - - const UninstallButton = (): JSX.Element => { - switch (status) { - case InstallStatus.UpToDate: - case InstallStatus.NeedsUpdate: - case InstallStatus.TrackSwitch: - case InstallStatus.DownloadDone: - case InstallStatus.GitInstall: { - return ( - InstallManager.uninstallAddon(selectedAddon, publisherData, showModalAsync)} - > - Uninstall - - ); - } - default: return <>; - } - }; + useEffect(() => { + if (addonDiscovered) { + setSelectedAddon(hiddenAddon); + } + }, [addonDiscovered, hiddenAddon]); + + const { showModal, showModalAsync } = useModals(); + + const handleTrackSelection = (track: AddonTrack) => { + if (!isInstalling) { + if (track.isExperimental) { + showModal( + { + selectAndSetTrack(track.key); + }} + dontShowAgainSettingName="mainSettings.disableExperimentalWarning" + />, + ); + } else { + selectAndSetTrack(track.key); + } + } + }; - if (!publisherData) { - return null; + const handleInstall = async () => { + if (settings.has('mainSettings.installPath')) { + await InstallManager.installAddon(selectedAddon, publisherData, showModalAsync); + } else { + await setupInstallPath(); } + }; - if (publisherData.addons.length === 0) { - return ; + const handleCancel = useCallback(() => { + if (isInstalling && !isFinishingDependencyInstall) { + InstallManager.cancelDownload(selectedAddon); + } + }, [isInstalling, isFinishingDependencyInstall, selectedAddon]); + + const UninstallButton = (): JSX.Element => { + switch (status) { + case InstallStatus.UpToDate: + case InstallStatus.NeedsUpdate: + case InstallStatus.TrackSwitch: + case InstallStatus.DownloadDone: + case InstallStatus.GitInstall: { + return ( + InstallManager.uninstallAddon(selectedAddon, publisherData, showModalAsync)} + > + Uninstall + + ); + } + default: + return <>; } + }; + + if (!publisherData) { + return null; + } + + if (publisherData.addons.length === 0) { + return ; + } + + return ( +
+
+
+ +
+ {publisherData.addons + .filter((it) => !it.category) + .map((addon) => ( + { + history.push(`/addon-section/${publisherData.name}/`); + + setSelectedAddon(addon); + }} + /> + ))} +
- return ( -
-
-
- -
- {publisherData.addons.filter((it) => !it.category).map((addon) => ( - { - history.push(`/addon-section/${publisherData.name}/`); - - setSelectedAddon(addon); - }} - /> - ))} -
- -
- {publisherData.defs?.filter((it) => it.kind === 'addonCategory').map((category: AddonCategoryDefinition) => { - const categoryAddons = publisherData.addons.filter((it) => it.category?.substring(1) === category.key); - - if (categoryAddons.length === 0) { - return null; - } - - let classes = ''; - if (category.styles?.includes('align-bottom')) { - classes += 'mt-auto'; - } - - return ( -
-

{category.title}

- -
- {publisherData.addons.filter((it) => it.category?.substring(1) === category.key).map((addon) => ( - { - history.push(`/addon-section/${publisherData.name}/`); - - setSelectedAddon(addon); - }} - /> - ))} -
-
- ); - })} -
-
-
+
+ {publisherData.defs + ?.filter((it) => it.kind === 'addonCategory') + .map((category: AddonCategoryDefinition) => { + const categoryAddons = publisherData.addons.filter( + (it) => it.category?.substring(1) === category.key, + ); + + if (categoryAddons.length === 0) { + return null; + } + + let classes = ''; + if (category.styles?.includes('align-bottom')) { + classes += 'mt-auto'; + } + + return ( +
+

{category.title}

+ +
+ {publisherData.addons + .filter((it) => it.category?.substring(1) === category.key) + .map((addon) => ( + { + history.push(`/addon-section/${publisherData.name}/`); + + setSelectedAddon(addon); + }} + /> + ))} +
+
+ ); + })}
-
-
-
- - - - - - {publisherData.addons.every(addon => !addon.enabled) ? - : - - } - - - - - - - -
-
-
- -
-
-
- - - - - ( - - )} /> - - - {releaseNotes && releaseNotes.length > 0 ? ( - - ) : - - } - - - - - - - - - - -
-
- - - Configure - - {releaseNotes && releaseNotes.length > 0 && ( - - - Release Notes - - )} - {selectedAddon.key === 'simbridge' && ( // TODO find a better way to do this... - - - Settings - - )} - - - About - -
- -
- - {installStates[selectedAddon.key] && ( - - )} -
-
-
-
-
+ +
+
+
+
+
+ + + + + + {publisherData.addons.every((addon) => !addon.enabled) ? ( + + ) : ( + + )} + + + + + + + +
+
+
+ +
+
+
+ + + + + ( + + )} + /> + + + {releaseNotes && releaseNotes.length > 0 ? ( + + ) : ( + + )} + + + + + + + + + + +
+
+ + + Configure + + {releaseNotes && releaseNotes.length > 0 && ( + + + Release Notes + + )} + {selectedAddon.key === 'simbridge' && ( // TODO find a better way to do this... + + + Settings + + )} + + + About + +
+ +
+ + {installStates[selectedAddon.key] && ( + + )}
+
-
+
+ +
- ); +
+
+ ); }; const About: FC<{ addon: Addon }> = ({ addon }) => ( -
-
-

About

+
+
+

About

-

{addon.aircraftName}

+

{addon.aircraftName}

+
+ + {addon.description} + + + {addon.techSpecs && addon.techSpecs.length > 0 && ( + <> +

Tech Specs

+ +
+ {addon.techSpecs.map((spec) => ( + + {spec.name} + {spec.value} + + ))}
- - - {addon.techSpecs && addon.techSpecs.length > 0 && ( - <> -

Tech Specs

- -
- {addon.techSpecs.map((spec) => ( - - {spec.name} - {spec.value} - - ))} -
- - )} + + )} - -
+ +
); diff --git a/src/renderer/components/App/AddonBar.tsx b/src/renderer/components/App/AddonBar.tsx index 76db2a6f..562be227 100644 --- a/src/renderer/components/App/AddonBar.tsx +++ b/src/renderer/components/App/AddonBar.tsx @@ -1,189 +1,186 @@ -import React, { FC, memo } from "react"; -import { Addon, Publisher, PublisherButton } from "renderer/utils/InstallerConfiguration"; +import React, { FC, memo } from 'react'; +import { Addon, Publisher, PublisherButton } from 'renderer/utils/InstallerConfiguration'; import { shell } from 'electron'; import * as BootstrapIcons from 'react-bootstrap-icons'; -import { ArrowRepeat, Check2, CloudArrowDownFill, Icon } from "react-bootstrap-icons"; -import { useHistory, useParams } from "react-router-dom"; -import { useAppSelector } from "renderer/redux/store"; -import { AircraftSectionURLParams } from "../AddonSection"; -import { useIsDarkTheme } from "common/settings"; -import { Button } from "renderer/components/Button"; -import { ChevronRight } from "tabler-icons-react"; -import { InstallStatus } from "renderer/components/AddonSection/Enums"; +import { ArrowRepeat, Check2, CloudArrowDownFill, Icon } from 'react-bootstrap-icons'; +import { useHistory, useParams } from 'react-router-dom'; +import { useAppSelector } from 'renderer/redux/store'; +import { AircraftSectionURLParams } from '../AddonSection'; +import { useIsDarkTheme } from 'common/settings'; +import { Button } from 'renderer/components/Button'; +import { ChevronRight } from 'tabler-icons-react'; +import { InstallStatus } from 'renderer/components/AddonSection/Enums'; export interface AddonBarProps { - publisher: Publisher, + publisher: Publisher; } export enum UITheme { - Light = 'light', - Dark = 'dark', + Light = 'light', + Dark = 'dark', } export const AddonBar: FC = ({ children }) => { - const darkTheme = useIsDarkTheme(); - - const { publisherName } = useParams(); - const publisherData = useAppSelector(state => state.configuration.publishers.find(pub => pub.name === publisherName) ? state.configuration.publishers.find(pub => pub.name === publisherName) : state.configuration.publishers[0]); - - const PublisherButtons = (buttons: PublisherButton[]) => { - const groups: PublisherButton[][] = []; - - let currentGroup: PublisherButton[] = []; - for (const button of buttons) { - if (button.inline || currentGroup.length === 0) { - currentGroup.push(button); - } else { - groups.push(currentGroup); - currentGroup = [button]; - } - } - - if (!groups.includes(currentGroup)) { - groups.push(currentGroup); - } + const darkTheme = useIsDarkTheme(); + + const { publisherName } = useParams(); + const publisherData = useAppSelector((state) => + state.configuration.publishers.find((pub) => pub.name === publisherName) + ? state.configuration.publishers.find((pub) => pub.name === publisherName) + : state.configuration.publishers[0], + ); + + const PublisherButtons = (buttons: PublisherButton[]) => { + const groups: PublisherButton[][] = []; + + let currentGroup: PublisherButton[] = []; + for (const button of buttons) { + if (button.inline || currentGroup.length === 0) { + currentGroup.push(button); + } else { + groups.push(currentGroup); + currentGroup = [button]; + } + } - return ( - <> - {groups.map((group) => ( -
- {group.map((button, index) => ( - - ))} -
- ))} - - ); - }; - - const textClass = darkTheme ? 'text-quasi-white' : 'text-navy'; + if (!groups.includes(currentGroup)) { + groups.push(currentGroup); + } return ( -
-
-

{publisherData.name}

-
- -
- {children} -
- -
- {publisherData.buttons && ( - PublisherButtons(publisherData.buttons) - )} -
-
+ <> + {groups.map((group, index) => ( +
+ {group.map((button, index) => ( + + ))} +
+ ))} + ); + }; + + const textClass = darkTheme ? 'text-quasi-white' : 'text-navy'; + + return ( +
+
+

{publisherData.name}

+
+ +
{children}
+ +
+ {publisherData.buttons && PublisherButtons(publisherData.buttons)} +
+
+ ); }; export interface AddonBarItemProps { - addon: Addon; - enabled?: boolean; - selected?: boolean; - className?: string; - onClick?: () => void; + addon: Addon; + enabled?: boolean; + selected?: boolean; + className?: string; + onClick?: () => void; } export const AddonBarItem: FC = ({ addon, enabled, selected, className, onClick }) => { - const installState = useAppSelector(state => state.installStatus[addon.key]); - - const background = selected ? `bg-dodger-light text-navy-dark` : `bg-transparent text-quasi-white`; - const border = `${selected ? 'border-dodger-light' : 'border-navy-light'} ${enabled ? 'hover:border-dodger-light' : ''}`; - - return ( -
- {addon.aircraftName} -
- - {installState && ( - - )} -
-
- ); + const installState = useAppSelector((state) => state.installStatus[addon.key]); + + const background = selected ? `bg-dodger-light text-navy-dark` : `bg-transparent text-quasi-white`; + const border = `${selected ? 'border-dodger-light' : 'border-navy-light'} ${enabled ? 'hover:border-dodger-light' : ''}`; + + return ( +
+ {addon.aircraftName} +
+ + {installState && } +
+
+ ); }; interface AddonBarItemStatusProps { - status: InstallStatus; + status: InstallStatus; } -const AddonBarItemStatus: FC = memo(({ status }) => { - switch (status) { - case InstallStatus.UpToDate: - case InstallStatus.GitInstall: - return ; - case InstallStatus.InstallingDependency: - case InstallStatus.InstallingDependencyEnding: - case InstallStatus.DownloadPrep: - case InstallStatus.Decompressing: - case InstallStatus.Downloading: - case InstallStatus.DownloadEnding: - return ; - case InstallStatus.NeedsUpdate: - case InstallStatus.TrackSwitch: - return ; - default: return <>; - } +const AddonBarItemStatus: FC = memo(({ status }: AddonBarItemStatusProps) => { + switch (status) { + case InstallStatus.UpToDate: + case InstallStatus.GitInstall: + return ; + case InstallStatus.InstallingDependency: + case InstallStatus.InstallingDependencyEnding: + case InstallStatus.DownloadPrep: + case InstallStatus.Decompressing: + case InstallStatus.Downloading: + case InstallStatus.DownloadEnding: + return ; + case InstallStatus.NeedsUpdate: + case InstallStatus.TrackSwitch: + return ; + default: + return <>; + } }); +AddonBarItemStatus.displayName = 'AddonBarItemStatus'; + interface AddonBarPublisherButtonProps { - button: PublisherButton, + button: PublisherButton; } const AddonBarPublisherButton: FC = ({ button }) => { - const history = useHistory(); - - const handleClick = async () => { - switch (button.action) { - case 'openBrowser': - await shell.openExternal(button.url); - break; - case 'internal': - switch (button.call) { - case 'fbw-local-api-config': - history.push(`/addon-section/FlyByWire Simulations/configuration/fbw-local-api-config`); - } - break; + const history = useHistory(); + + const handleClick = async () => { + switch (button.action) { + case 'openBrowser': + await shell.openExternal(button.url); + break; + case 'internal': + switch (button.call) { + case 'fbw-local-api-config': + history.push(`/addon-section/FlyByWire Simulations/configuration/fbw-local-api-config`); } - }; - - const ButtonIcon = (BootstrapIcons as Record)[button.icon] ?? 'span'; - - if (!button.style || button.style === 'normal') { - return ( - - ); - } else { - - return ( - - ); + break; } + }; + const ButtonIcon = (BootstrapIcons as Record)[button.icon] ?? 'span'; + + if (!button.style || button.style === 'normal') { + return ( + + ); + } else { + return ( + + ); + } }; diff --git a/src/renderer/components/App/NavBar.tsx b/src/renderer/components/App/NavBar.tsx index 7bcb5919..51126a82 100644 --- a/src/renderer/components/App/NavBar.tsx +++ b/src/renderer/components/App/NavBar.tsx @@ -1,82 +1,79 @@ -import React from "react"; -import { FC } from "react"; -import { NavLink } from "react-router-dom"; -import { useIsDarkTheme } from "common/settings"; -import { Publisher } from "renderer/utils/InstallerConfiguration"; -import { useAppSelector } from "renderer/redux/store"; -import { Gear, Wrench } from "react-bootstrap-icons"; -import { InstallStatus } from "renderer/components/AddonSection/Enums"; +import React from 'react'; +import { FC } from 'react'; +import { NavLink } from 'react-router-dom'; +import { useIsDarkTheme } from 'common/settings'; +import { Publisher } from 'renderer/utils/InstallerConfiguration'; +import { useAppSelector } from 'renderer/redux/store'; +import { Gear, Wrench } from 'react-bootstrap-icons'; +import { InstallStatus } from 'renderer/components/AddonSection/Enums'; export const NavBar: FC = ({ children }) => { - const darkTheme = useIsDarkTheme(); + const darkTheme = useIsDarkTheme(); - const bg = darkTheme ? 'bg-navy-dark' : 'bg-navy'; + const bg = darkTheme ? 'bg-navy-dark' : 'bg-navy'; - return ( -
-
- {children} -
+ return ( +
+
{children}
-
- {process.env.NODE_ENV === 'development' && ( - - - - )} - - - -
-
- ); +
+ {process.env.NODE_ENV === 'development' && ( + + + + )} + + + +
+
+ ); }; -const BASE_STYLE = "w-20 h-20 flex flex-col justify-center items-center rounded-md bg-transparent hover:bg-navy-light transition duration-200 border-2 border-navy-light"; +const BASE_STYLE = + 'w-20 h-20 flex flex-col justify-center items-center rounded-md bg-transparent hover:bg-navy-light transition duration-200 border-2 border-navy-light'; export interface NavBarItemProps { - to: string; - showNotification?: boolean; - notificationColor?: string; - className?: string; + to: string; + showNotification?: boolean; + notificationColor?: string; + className?: string; } -export const NavbarItem: FC = ({ to = '/', showNotification = false, notificationColor = 'orange', className, children }) => ( - - {children} +export const NavbarItem: FC = ({ + to = '/', + showNotification = false, + notificationColor = 'orange', + className, + children, +}) => ( + + {children} - - - - - - + + + + + + ); export interface NavBarPublisherProps extends NavBarItemProps { - publisher: Publisher; + publisher: Publisher; } export const NavBarPublisher: FC = ({ to, publisher }) => { - const hasAvailableUpdates = useAppSelector((state) => { - return publisher.addons.some((addon) => { - const status = state.installStatus[addon.key]?.status; + const hasAvailableUpdates = useAppSelector((state) => { + return publisher.addons.some((addon) => { + const status = state.installStatus[addon.key]?.status; - return status === InstallStatus.NeedsUpdate || status === InstallStatus.TrackSwitch; - }); + return status === InstallStatus.NeedsUpdate || status === InstallStatus.TrackSwitch; }); + }); - return ( - - {`${publisher.name} - - ); + return ( + + {`${publisher.name} + + ); }; diff --git a/src/renderer/components/App/SideBar.tsx b/src/renderer/components/App/SideBar.tsx index 99abdbd9..c8b89e68 100644 --- a/src/renderer/components/App/SideBar.tsx +++ b/src/renderer/components/App/SideBar.tsx @@ -1,117 +1,129 @@ -import React, { useEffect, useState } from "react"; -import { Check, ChevronDown, Download, Refresh } from "tabler-icons-react"; -import { useSelector } from "react-redux"; -import { InstallerStore } from "renderer/redux/store"; -import { Addon } from "renderer/utils/InstallerConfiguration"; -import { InstallStatus } from "renderer/components/AddonSection/Enums"; +import React, { useEffect, useState } from 'react'; +import { Check, ChevronDown, Download, Refresh } from 'tabler-icons-react'; +import { useSelector } from 'react-redux'; +import { InstallerStore } from 'renderer/redux/store'; +import { Addon } from 'renderer/utils/InstallerConfiguration'; +import { InstallStatus } from 'renderer/components/AddonSection/Enums'; -export type SidebarItemProps = { enabled?: boolean, selected: boolean, onClick: () => void, className?: string } +export type SidebarItemProps = { enabled?: boolean; selected: boolean; onClick: () => void; className?: string }; export const SidebarItem: React.FC = ({ enabled = true, selected, onClick, children, className }) => { - return ( -
- {children} -
- ); + return ( +
+ {children} +
+ ); }; -export type SidebarPublisherProps = { name: string, logo: string } +export type SidebarPublisherProps = { name: string; logo: string }; export const SidebarPublisher: React.FC = ({ name, logo, children }) => { - const [expanded, setExpanded] = useState(true); + const [expanded, setExpanded] = useState(true); - return ( - <> - setExpanded(old => !old)} className="flex flex-row items-center transform transition-colors duration-300 hover:bg-navy-lightest text-lg text-white pl-3 py-3.5 cursor-pointer"> - - - {name} - - {expanded && children} - - ); + return ( + <> + setExpanded((old) => !old)} + className="flex cursor-pointer flex-row items-center py-3.5 pl-3 text-lg text-white transition-colors duration-300 hover:bg-navy-lightest" + > + + + {name} + + {expanded && children} + + ); }; -type SidebarAddonProps = { addon: Addon, isSelected: boolean, handleSelected: (key: string) => void } +type SidebarAddonProps = { addon: Addon; isSelected: boolean; handleSelected: (key: string) => void }; export const SidebarAddon: React.FC = ({ addon, isSelected, handleSelected }) => { - const [downloadState, setStatusText] = useState(''); - const [icon, setIcon] = useState<'notAvailable' | 'install' | 'installing' | 'installed' | 'update'>('install'); - const addonDownloadState = useSelector((state) => { - try { - return state.installStatus[addon.key] as InstallStatus; - } catch (e) { - return InstallStatus.Unknown; - } - }); + const [downloadState, setStatusText] = useState(''); + const [icon, setIcon] = useState<'notAvailable' | 'install' | 'installing' | 'installed' | 'update'>('install'); + const addonDownloadState = useSelector((state) => { + try { + return state.installStatus[addon.key] as InstallStatus; + } catch (e) { + return InstallStatus.Unknown; + } + }); - useEffect(() => { - if (addon.enabled) { - switch (addonDownloadState) { - case InstallStatus.Hidden: - setStatusText('Not Available'); - setIcon('notAvailable'); - break; - case InstallStatus.NotInstalled: - case InstallStatus.Unknown: - setStatusText('Not Installed'); - setIcon('install'); - break; - case InstallStatus.NeedsUpdate: - setStatusText('Update Available'); - setIcon('update'); - break; - case InstallStatus.DownloadPrep: - case InstallStatus.Downloading: - case InstallStatus.Decompressing: - case InstallStatus.DownloadRetry: - case InstallStatus.DownloadCanceled: - case InstallStatus.DownloadError: - case InstallStatus.DownloadEnding: - setStatusText('Installing...'); - setIcon('installing'); - break; - default: - setStatusText('Installed'); - setIcon('installed'); - break; - } - } else { - setStatusText('Not Available'); - setIcon('notAvailable'); - } - }, [addonDownloadState]); + useEffect(() => { + if (addon.enabled) { + switch (addonDownloadState) { + case InstallStatus.Hidden: + setStatusText('Not Available'); + setIcon('notAvailable'); + break; + case InstallStatus.NotInstalled: + case InstallStatus.Unknown: + setStatusText('Not Installed'); + setIcon('install'); + break; + case InstallStatus.NeedsUpdate: + setStatusText('Update Available'); + setIcon('update'); + break; + case InstallStatus.DownloadPrep: + case InstallStatus.Downloading: + case InstallStatus.Decompressing: + case InstallStatus.DownloadRetry: + case InstallStatus.DownloadCanceled: + case InstallStatus.DownloadError: + case InstallStatus.DownloadEnding: + setStatusText('Installing...'); + setIcon('installing'); + break; + default: + setStatusText('Installed'); + setIcon('installed'); + break; + } + } else { + setStatusText('Not Available'); + setIcon('notAvailable'); + } + }, [addon.enabled, addonDownloadState]); - const Icon = () => { - switch (icon) { - case 'notAvailable': - return ; - case 'install': - return ; - case 'installing': - return ; - case 'installed': - return ; - case 'update': - return ; - } - }; + const Icon = () => { + switch (icon) { + case 'notAvailable': + return ; + case 'install': + return ; + case 'installing': + return ; + case 'installed': + return ; + case 'update': + return ; + } + }; - return ( - { - if (addon.enabled) { - handleSelected(addon.key); - } - }}> -
- {addon.name} - {downloadState} -
+ return ( + { + if (addon.enabled) { + handleSelected(addon.key); + } + }} + > +
+ + {addon.name} + + {downloadState} +
- -
- ); + +
+ ); }; diff --git a/src/renderer/components/App/index.tsx b/src/renderer/components/App/index.tsx index b30a9ba0..2e988a36 100644 --- a/src/renderer/components/App/index.tsx +++ b/src/renderer/components/App/index.tsx @@ -1,27 +1,27 @@ import { hot } from 'react-hot-loader'; import React, { useEffect, useState } from 'react'; import SimpleBar from 'simplebar-react'; -import { Logo } from "renderer/components/Logo"; +import { Logo } from 'renderer/components/Logo'; import { SettingsSection } from 'renderer/components/SettingsSection'; import { DebugSection } from 'renderer/components/DebugSection'; -import { GitVersions } from "@flybywiresim/api-client"; +import { GitVersions } from '@flybywiresim/api-client'; import { DataCache } from '../../utils/DataCache'; -import InstallerUpdate from "renderer/components/InstallerUpdate"; -import { WindowButtons } from "renderer/components/WindowActionButtons"; -import { Addon, AddonVersion } from "renderer/utils/InstallerConfiguration"; -import { AddonData } from "renderer/utils/AddonData"; +import { InstallerUpdate } from 'renderer/components/InstallerUpdate'; +import { WindowButtons } from 'renderer/components/WindowActionButtons'; +import { Addon, AddonVersion } from 'renderer/utils/InstallerConfiguration'; +import { AddonData } from 'renderer/utils/AddonData'; import { ErrorModal } from '../ErrorModal'; -import { NavBar, NavBarPublisher } from "renderer/components/App/NavBar"; +import { NavBar, NavBarPublisher } from 'renderer/components/App/NavBar'; import { Redirect, Route, Switch, useHistory, useLocation } from 'react-router-dom'; import { store, useAppSelector } from 'renderer/redux/store'; import { setAddonAndTrackLatestReleaseInfo } from 'renderer/redux/features/latestVersionNames'; import settings from 'common/settings'; -import "./index.css"; +import './index.css'; import { ipcRenderer } from 'electron'; import channels from 'common/channels'; import { ModalContainer } from '../Modal'; -import { PublisherSection } from "renderer/components/PublisherSection"; -import * as packageInfo from "../../../../package.json"; +import { PublisherSection } from 'renderer/components/PublisherSection'; +import * as packageInfo from '../../../../package.json'; const releaseCache = new DataCache('releases', 1000 * 3600 * 24); @@ -31,169 +31,176 @@ const releaseCache = new DataCache('releases', 1000 * 3600 * 24) * @param addon */ export const getAddonReleases = async (addon: Addon): Promise => { - const releases = (await releaseCache.fetchOrCompute(async (): Promise => { - return (await GitVersions.getReleases(addon.repoOwner, addon.repoName)) - .filter(r => /v\d/.test(r.name)) - .map(r => ({ title: r.name, date: r.publishedAt, type: 'minor' })); - })).map(r => ({ ...r, date: new Date(r.date) })); // Local Data cache returns a string instead of Date - - releases - .forEach((version, index) => { - const currentVersionTitle = version.title; - const otherVersionTitle = index === releases.length - 1 - ? releases[index - 1].title - : releases[index + 1].title; - - if (currentVersionTitle[1] !== otherVersionTitle[1]) { - releases[index].type = 'major'; - } else if (currentVersionTitle[3] !== otherVersionTitle[3]) { - releases[index].type = 'minor'; - } else if (currentVersionTitle[5] !== otherVersionTitle[5] && index === releases.length - 1) { - releases[index].type = "minor"; - } else if (currentVersionTitle[5] !== otherVersionTitle[5]) { - releases[index].type = 'patch'; - } - }); - - return releases; + const releases = ( + await releaseCache.fetchOrCompute(async (): Promise => { + return (await GitVersions.getReleases(addon.repoOwner, addon.repoName)) + .filter((r) => /v\d/.test(r.name)) + .map((r) => ({ title: r.name, date: r.publishedAt, type: 'minor' })); + }) + ).map((r) => ({ ...r, date: new Date(r.date) })); // Local Data cache returns a string instead of Date + + releases.forEach((version, index) => { + const currentVersionTitle = version.title; + const otherVersionTitle = index === releases.length - 1 ? releases[index - 1].title : releases[index + 1].title; + + if (currentVersionTitle[1] !== otherVersionTitle[1]) { + releases[index].type = 'major'; + } else if (currentVersionTitle[3] !== otherVersionTitle[3]) { + releases[index].type = 'minor'; + } else if (currentVersionTitle[5] !== otherVersionTitle[5] && index === releases.length - 1) { + releases[index].type = 'minor'; + } else if (currentVersionTitle[5] !== otherVersionTitle[5]) { + releases[index].type = 'patch'; + } + }); + + return releases; }; export const fetchLatestVersionNames = async (addon: Addon): Promise => { - const dispatch = store.dispatch; - - for (const track of addon.tracks) { - const trackLatestVersionName = await AddonData.latestVersionForTrack(addon, track); - dispatch(setAddonAndTrackLatestReleaseInfo({ - addonKey: addon.key, - trackKey: track.key, - info: trackLatestVersionName, - })); - } + const dispatch = store.dispatch; + + for (const track of addon.tracks) { + const trackLatestVersionName = await AddonData.latestVersionForTrack(addon, track); + dispatch( + setAddonAndTrackLatestReleaseInfo({ + addonKey: addon.key, + trackKey: track.key, + info: trackLatestVersionName, + }), + ); + } }; const App = () => { - const history = useHistory(); - const location = useLocation(); + const history = useHistory(); + const location = useLocation(); - const configuration = useAppSelector(state => state.configuration); + const configuration = useAppSelector((state) => state.configuration); - const [addons] = useState( - configuration.publishers.reduce((arr, curr) => { - arr.push(...curr.addons); - return arr; - }, []), - ); + const [addons] = useState( + configuration.publishers.reduce((arr, curr) => { + arr.push(...curr.addons); + return arr; + }, []), + ); + + useEffect(() => { + addons.forEach(AddonData.configureInitialAddonState); + addons.forEach(fetchLatestVersionNames); + + if (settings.get('cache.main.lastShownSection')) { + history.push(settings.get('cache.main.lastShownSection')); + } - useEffect(() => { - addons.forEach(AddonData.configureInitialAddonState); + // Let's listen for a route change and set the last shown section to the incoming route pathname + history.listen((location) => { + settings.set('cache.main.lastShownSection', location.pathname); + }); + }, [addons, history]); + + useEffect(() => { + const updateCheck = setInterval( + () => { + ipcRenderer.send(channels.checkForInstallerUpdate); + addons.forEach(AddonData.checkForUpdates); addons.forEach(fetchLatestVersionNames); + }, + 5 * 60 * 1000, + ); - if (settings.get('cache.main.lastShownSection')) { - history.push(settings.get('cache.main.lastShownSection')); - } - - // Let's listen for a route change and set the last shown section to the incoming route pathname - history.listen((location) => { - settings.set("cache.main.lastShownSection", location.pathname); - }); - }, []); - - useEffect(() => { - const updateCheck = setInterval(() => { - ipcRenderer.send(channels.checkForInstallerUpdate); - addons.forEach(AddonData.checkForUpdates); - addons.forEach(fetchLatestVersionNames); - }, 5 * 60 * 1000); - - return () => clearInterval(updateCheck); - }, []); - - const configUrl = settings.get('mainSettings.configDownloadUrl') as string; - - const isDevelopmentConfigURL = () => { - const productionURL = packageInfo.configUrls.production; - // Protection against accidental screenshots of confidential config urls - // Limited to flybywire config url to prevent 3rd party urls to be hidden - let showDevURL = "n/a"; - if (!configUrl.includes(packageInfo.configUrls.confidentialBaseUrl)) { - showDevURL = configUrl; - } - return (configUrl !== productionURL) && ( -
-
Developer Configuration Used: 
-
{showDevURL}
-
- ); - }; + return () => clearInterval(updateCheck); + }, [addons]); + const configUrl = settings.get('mainSettings.configDownloadUrl') as string; + + const isDevelopmentConfigURL = () => { + const productionURL = packageInfo.configUrls.production; + // Protection against accidental screenshots of confidential config urls + // Limited to flybywire config url to prevent 3rd party urls to be hidden + let showDevURL = 'n/a'; + if (!configUrl.includes(packageInfo.configUrls.confidentialBaseUrl)) { + showDevURL = configUrl; + } return ( - <> - - - - - -
-
-
-
- - - {(process.env.NODE_ENV === 'development') && ( -
-
{packageInfo.version}
-
|
-
Development mode
-
|
-
{location.pathname}
-
- )} - {isDevelopmentConfigURL()} -
- -
- - -
-
- -
-
- - {configuration.publishers.map((publisher) => ( - - ))} - -
- -
- - - - - - - - - - - - - - - - - -
-
-
-
-
- + configUrl !== productionURL && ( +
+
Developer Configuration Used: 
+
{showDevURL}
+
+ ) ); + }; + + return ( + <> + + + + + +
+
+
+
+ + + {process.env.NODE_ENV === 'development' && ( +
+
{packageInfo.version}
+
|
+
Development mode
+
|
+
{location.pathname}
+
+ )} + {isDevelopmentConfigURL()} +
+ +
+ + +
+
+ +
+
+ + {configuration.publishers.map((publisher) => ( + + ))} + +
+ +
+ + + + + + + + + + + + + + + + + +
+
+
+
+
+ + ); }; export default hot(module)(App); diff --git a/src/renderer/components/Button.tsx b/src/renderer/components/Button.tsx index 5833143b..e61defc3 100644 --- a/src/renderer/components/Button.tsx +++ b/src/renderer/components/Button.tsx @@ -1,41 +1,49 @@ -import React, { FC } from "react"; +import React, { FC } from 'react'; export enum ButtonType { - Neutral, - Emphasis, - Positive, - Caution, - Danger, + Neutral, + Emphasis, + Positive, + Caution, + Danger, } export interface ButtonProps { - type?: ButtonType, - disabled?: boolean, - className?: string, - onClick?: () => void, + type?: ButtonType; + disabled?: boolean; + className?: string; + onClick?: () => void; } -export const Button: FC = ({ type = ButtonType.Neutral, disabled = false, className = '', onClick = () => {}, children }) => { - let buttonClass; - switch (type) { - case ButtonType.Neutral: - buttonClass = 'button-neutral'; - break; - case ButtonType.Emphasis: - buttonClass = 'button-emphasis'; - break; - case ButtonType.Positive: - buttonClass = 'button-positive'; - break; - case ButtonType.Caution: - buttonClass = 'button-caution'; - break; - case ButtonType.Danger: - buttonClass = 'button-danger'; - break; - } +export const Button: FC = ({ + type = ButtonType.Neutral, + disabled = false, + className = '', + onClick = () => {}, + children, +}) => { + let buttonClass; + switch (type) { + case ButtonType.Neutral: + buttonClass = 'button-neutral'; + break; + case ButtonType.Emphasis: + buttonClass = 'button-emphasis'; + break; + case ButtonType.Positive: + buttonClass = 'button-positive'; + break; + case ButtonType.Caution: + buttonClass = 'button-caution'; + break; + case ButtonType.Danger: + buttonClass = 'button-danger'; + break; + } - return ( - - ); + return ( + + ); }; diff --git a/src/renderer/components/DebugSection/index.tsx b/src/renderer/components/DebugSection/index.tsx index 405bec32..2b8ce3b4 100644 --- a/src/renderer/components/DebugSection/index.tsx +++ b/src/renderer/components/DebugSection/index.tsx @@ -1,38 +1,48 @@ import React, { useState } from 'react'; -import path from "path"; -import { ipcRenderer } from "electron"; -import channels from "common/channels"; +import path from 'path'; +import { ipcRenderer } from 'electron'; +import channels from 'common/channels'; export const DebugSection = (): JSX.Element => { - const [ipcMessage, setIpcMessage] = useState(channels.window.minimize); + const [ipcMessage, setIpcMessage] = useState(channels.window.minimize); - const sendNotification = () => { - Notification.requestPermission().then(() => { - console.log('Showing test notification'); - console.log(path.join(process.resourcesPath, 'extraResources', 'icon.ico')); - new Notification('This is a test!', { - 'icon': path.join(process.resourcesPath, 'extraResources', 'icon.ico'), - 'body': "We did something that you should know about.", - }); - }).catch(e => console.log(e)); - }; + const sendNotification = () => { + Notification.requestPermission() + .then(() => { + console.log('Showing test notification'); + console.log(path.join(process.resourcesPath, 'extraResources', 'icon.ico')); + new Notification('This is a test!', { + icon: path.join(process.resourcesPath, 'extraResources', 'icon.ico'), + body: 'We did something that you should know about.', + }); + }) + .catch((e) => console.log(e)); + }; - const sendIpcMessage = () => { - ipcRenderer.send(ipcMessage); - }; + const sendIpcMessage = () => { + ipcRenderer.send(ipcMessage); + }; - return ( -
-

Debug options

+ return ( +
+

Debug options

-

Notifications

- +

Notifications

+ -

Send IPC message

-
- setIpcMessage(event.target.value)} className="p-1 outline-none" /> - -
-
- ); +

Send IPC message

+
+ setIpcMessage(event.target.value)} + className="p-1 outline-none" + /> + +
+
+ ); }; diff --git a/src/renderer/components/ErrorModal/index.tsx b/src/renderer/components/ErrorModal/index.tsx index 4becea86..d9992a1c 100644 --- a/src/renderer/components/ErrorModal/index.tsx +++ b/src/renderer/components/ErrorModal/index.tsx @@ -1,65 +1,74 @@ import React, { useState } from 'react'; import { setupInstallPath } from 'renderer/actions/install-path.utils'; -import settings from "common/settings"; -import { Directories } from "renderer/utils/Directories"; -import * as fs from "fs"; -import { Button, ButtonType } from "renderer/components/Button"; +import settings from 'common/settings'; +import { Directories } from 'renderer/utils/Directories'; +import * as fs from 'fs'; +import { Button, ButtonType } from 'renderer/components/Button'; export const ErrorModal = (): JSX.Element => { - const [communityError, setCommunityError] = useState(!fs.existsSync(Directories.installLocation()) || Directories.installLocation() === 'C:\\'); - const [linuxError, setLinuxError] = useState(Directories.installLocation() === 'linux'); + const [communityError, setCommunityError] = useState( + !fs.existsSync(Directories.installLocation()) || Directories.installLocation() === 'C:\\', + ); + const [linuxError, setLinuxError] = useState(Directories.installLocation() === 'linux'); - const handleClose = () => { - setCommunityError(false); - setLinuxError(false); - }; + const handleClose = () => { + setCommunityError(false); + setLinuxError(false); + }; - const handleSelectPath = async () => { - const path = await setupInstallPath(); - if (path) { - settings.set('mainSettings.liveriesPath', path); - settings.set('mainSettings.separateLiveriesPath', false); - handleClose(); - } - }; - - const content = (): JSX.Element => { - // Linux's error goes first because it may interfere with the other dir checkers - if (linuxError) { - return ( - <> - Seems like you're using Linux - We're unable to autodetect your install currently. Please set the correct location before we can continue. + const handleSelectPath = async () => { + const path = await setupInstallPath(); + if (path) { + settings.set('mainSettings.liveriesPath', path); + settings.set('mainSettings.separateLiveriesPath', false); + handleClose(); + } + }; - - - ); - } - if (communityError && (Directories.installLocation() !== 'linux')) { - return ( - <> - Your Community folder is set to -
{Directories.installLocation()}
- but we couldn't find it there. Please set the correct location before we can continue. + const content = (): JSX.Element => { + // Linux's error goes first because it may interfere with the other dir checkers + if (linuxError) { + return ( + <> + Seems like you're using Linux + + We're unable to autodetect your install currently. Please set the correct location before we can + continue. + - - - ); - } - return <>; - }; + + + ); + } + if (communityError && Directories.installLocation() !== 'linux') { + return ( + <> + Your Community folder is set to +
+            {Directories.installLocation()}
+          
+ + but we couldn't find it there. Please set the correct location before we can continue. + - if (communityError || linuxError) { - return ( -
- Something went wrong. - {content()} -
- ); + + + ); } - return (<>); + return <>; + }; + + if (communityError || linuxError) { + return ( +
+ Something went wrong. + {content()} +
+ ); + } + return <>; }; diff --git a/src/renderer/components/InstallerUpdate/index.tsx b/src/renderer/components/InstallerUpdate/index.tsx index 02b13524..0886e659 100644 --- a/src/renderer/components/InstallerUpdate/index.tsx +++ b/src/renderer/components/InstallerUpdate/index.tsx @@ -1,78 +1,84 @@ -import React, { useEffect, useState } from "react"; -import { ipcRenderer } from "electron"; +import React, { useEffect, useState } from 'react'; +import { ipcRenderer } from 'electron'; import * as path from 'path'; -import channels from "common/channels"; +import channels from 'common/channels'; -type IpcCallback = Parameters<(typeof ipcRenderer)['on']>[1] +type IpcCallback = Parameters<(typeof ipcRenderer)['on']>[1]; enum UpdateState { - Standby, - DownloadingUpdate, - RestartToUpdate, + Standby, + DownloadingUpdate, + RestartToUpdate, } -const index = (): JSX.Element => { - const [updateState, setUpdateState] = useState(UpdateState.Standby); +export const InstallerUpdate = (): JSX.Element => { + const [updateState, setUpdateState] = useState(UpdateState.Standby); - const updateNeeded = updateState !== UpdateState.Standby; + const updateNeeded = updateState !== UpdateState.Standby; - let buttonText; - switch (updateState) { - case UpdateState.Standby: buttonText = ''; break; - case UpdateState.DownloadingUpdate: buttonText = 'Downloading update'; break; - case UpdateState.RestartToUpdate: buttonText = 'Restart to update'; break; - } + let buttonText; + switch (updateState) { + case UpdateState.Standby: + buttonText = ''; + break; + case UpdateState.DownloadingUpdate: + buttonText = 'Downloading update'; + break; + case UpdateState.RestartToUpdate: + buttonText = 'Restart to update'; + break; + } - useEffect(() => { - const updateErrorHandler: IpcCallback = (_, args) => { - console.error('Update error', args); - }; + useEffect(() => { + const updateErrorHandler: IpcCallback = (_, args) => { + console.error('Update error', args); + }; - const updateAvailableHandler: IpcCallback = () => { - console.log('Update available'); + const updateAvailableHandler: IpcCallback = () => { + console.log('Update available'); - setUpdateState(UpdateState.DownloadingUpdate); - }; + setUpdateState(UpdateState.DownloadingUpdate); + }; - const updateDownloadedHandler: IpcCallback = (_, args) => { - console.log('Update downloaded', args); + const updateDownloadedHandler: IpcCallback = (_, args) => { + console.log('Update downloaded', args); - setUpdateState(UpdateState.RestartToUpdate); + setUpdateState(UpdateState.RestartToUpdate); - Notification.requestPermission().then(() => { - console.log('Showing Update notification'); - new Notification('Restart to update!', { - 'icon': path.join(process.resourcesPath, 'extraResources', 'icon.ico'), - 'body': "An update to the installer has been downloaded", - }); - }).catch(e => console.log(e)); - }; + Notification.requestPermission() + .then(() => { + console.log('Showing Update notification'); + new Notification('Restart to update!', { + icon: path.join(process.resourcesPath, 'extraResources', 'icon.ico'), + body: 'An update to the installer has been downloaded', + }); + }) + .catch((e) => console.log(e)); + }; - ipcRenderer.on(channels.update.error, updateErrorHandler); - ipcRenderer.on(channels.update.available, updateAvailableHandler); - ipcRenderer.on(channels.update.downloaded, updateDownloadedHandler); + ipcRenderer.on(channels.update.error, updateErrorHandler); + ipcRenderer.on(channels.update.available, updateAvailableHandler); + ipcRenderer.on(channels.update.downloaded, updateDownloadedHandler); - return () => { - ipcRenderer.off(channels.update.error, updateErrorHandler); - ipcRenderer.off(channels.update.available, updateAvailableHandler); - ipcRenderer.off(channels.update.downloaded, updateDownloadedHandler); - }; - }, []); + return () => { + ipcRenderer.off(channels.update.error, updateErrorHandler); + ipcRenderer.off(channels.update.available, updateAvailableHandler); + ipcRenderer.off(channels.update.downloaded, updateDownloadedHandler); + }; + }, []); - return ( -
{ - if (updateState === UpdateState.RestartToUpdate) { - ipcRenderer.send('restartAndUpdate'); - } - }} - > -
{buttonText}
-
- ); + return ( +
{ + if (updateState === UpdateState.RestartToUpdate) { + ipcRenderer.send('restartAndUpdate'); + } + }} + > +
{buttonText}
+
+ ); }; - -export default index; diff --git a/src/renderer/components/InstallerUpdate/styles.tsx b/src/renderer/components/InstallerUpdate/styles.tsx index 8c256742..145f2e0a 100644 --- a/src/renderer/components/InstallerUpdate/styles.tsx +++ b/src/renderer/components/InstallerUpdate/styles.tsx @@ -1,18 +1,18 @@ -import styled from "styled-components"; -import { colors } from "renderer/style/theme"; +import styled from 'styled-components'; +import { colors } from 'renderer/style/theme'; export const Container = styled.div` - padding-left: 1rem; - padding-right: 1rem; - margin-right: 2rem; - background: ${colors.positive}; - height: 50%; - border-bottom-right-radius: 5px; - border-bottom-left-radius: 5px; + padding-left: 1rem; + padding-right: 1rem; + margin-right: 2rem; + background: ${colors.positive}; + height: 50%; + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; `; export const UpdateText = styled.h6` - margin-bottom: 0; - line-height: 1.6rem; - color: #fff !important; + margin-bottom: 0; + line-height: 1.6rem; + color: #fff !important; `; diff --git a/src/renderer/components/LocalApiConfigEditUI/index.tsx b/src/renderer/components/LocalApiConfigEditUI/index.tsx index 65a7f439..5265eade 100644 --- a/src/renderer/components/LocalApiConfigEditUI/index.tsx +++ b/src/renderer/components/LocalApiConfigEditUI/index.tsx @@ -10,239 +10,278 @@ import { Toggle } from '../Toggle'; const SIMBRIDGE_DIRECTORY = 'flybywire-externaltools-simbridge'; interface LocalApiConfiguration { - server: { - port: number; - }; - printer: { - enabled: boolean; - printerName: string; - fontSize: number; - paperSize: string; - margin: number; - }; + server: { + port: number; + }; + printer: { + enabled: boolean; + printerName: string; + fontSize: number; + paperSize: string; + margin: number; + }; } const localApiDefaultConfiguration: LocalApiConfiguration = { - server: { - port: 8380, - }, - printer: { - enabled: false, - printerName: null, - fontSize: 19, - paperSize: "A4", - margin: 30, - }, + server: { + port: 8380, + }, + printer: { + enabled: false, + printerName: null, + fontSize: 19, + paperSize: 'A4', + margin: 30, + }, }; class LocalApiConfigurationHandler { - private static get simbridgeDirectory(): string { - return path.join(Directories.inInstallLocation(SIMBRIDGE_DIRECTORY)); - } + private static get simbridgeDirectory(): string { + return path.join(Directories.inInstallLocation(SIMBRIDGE_DIRECTORY)); + } - private static get simbridgeConfigPath(): string { - return path.join(this.simbridgeDirectory, 'resources', 'properties.json'); - } + private static get simbridgeConfigPath(): string { + return path.join(this.simbridgeDirectory, 'resources', 'properties.json'); + } - static getConfiguration(): LocalApiConfiguration { - if (fs.existsSync(this.simbridgeConfigPath)) { - console.log(`Loading configuration from ${this.simbridgeConfigPath}`); + static getConfiguration(): LocalApiConfiguration { + if (fs.existsSync(this.simbridgeConfigPath)) { + console.log(`Loading configuration from ${this.simbridgeConfigPath}`); - return JSON.parse(fs.readFileSync(this.simbridgeConfigPath, "utf8")); - } else { - console.log(`No configuration found at ${this.simbridgeConfigPath}`); + return JSON.parse(fs.readFileSync(this.simbridgeConfigPath, 'utf8')); + } else { + console.log(`No configuration found at ${this.simbridgeConfigPath}`); - if (fs.existsSync(path.join(this.simbridgeDirectory, 'resources'))) { - console.log(`Creating configuration at ${this.simbridgeConfigPath}`); + if (fs.existsSync(path.join(this.simbridgeDirectory, 'resources'))) { + console.log(`Creating configuration at ${this.simbridgeConfigPath}`); - fs.writeFileSync(path.join(this.simbridgeConfigPath), JSON.stringify(localApiDefaultConfiguration)); + fs.writeFileSync(path.join(this.simbridgeConfigPath), JSON.stringify(localApiDefaultConfiguration)); - return localApiDefaultConfiguration; - } else { - throw new Error(`No configuration found and no directory to create it in`); - } - } + return localApiDefaultConfiguration; + } else { + throw new Error(`No configuration found and no directory to create it in`); + } } + } - static saveConfiguration(propertyConfiguration: LocalApiConfiguration) { - if (fs.existsSync(this.simbridgeConfigPath)) { - fs.writeFileSync(this.simbridgeConfigPath, JSON.stringify(propertyConfiguration)); - } + static saveConfiguration(propertyConfiguration: LocalApiConfiguration) { + if (fs.existsSync(this.simbridgeConfigPath)) { + fs.writeFileSync(this.simbridgeConfigPath, JSON.stringify(propertyConfiguration)); } + } } export const LocalApiConfigEditUI: FC = () => { - const [config, setConfig] = useState(null as LocalApiConfiguration); - const [printers, setPrinters] = useState([]); - - useEffect(() => { - print.getPrinters().then(p => setPrinters(p)); - - try { - const loaded = LocalApiConfigurationHandler.getConfiguration(); - setConfig(loaded); - } catch (_) {/**/ } - }, []); - - const { showModal } = useModals(); - - const handleReset = () => { - showModal( - { - LocalApiConfigurationHandler.saveConfiguration(localApiDefaultConfiguration); - setConfig(localApiDefaultConfiguration); - }} - />, - ); - }; - - const handleConfigSave = () => { - LocalApiConfigurationHandler.saveConfiguration(config); - setConfig(LocalApiConfigurationHandler.getConfiguration()); - }; - - const handleDiscard = () => { - setConfig(LocalApiConfigurationHandler.getConfiguration()); - }; - - if (config === null) { - return ( -
-

- Could not load configuration file. This likely means that you do not have SimBridge currently installed. -

-
- ); + const [config, setConfig] = useState(null as LocalApiConfiguration); + const [printers, setPrinters] = useState([]); + + useEffect(() => { + print.getPrinters().then((p) => setPrinters(p)); + + try { + const loaded = LocalApiConfigurationHandler.getConfiguration(); + setConfig(loaded); + } catch (_) { + /**/ } + }, []); + + const { showModal } = useModals(); + + const handleReset = () => { + showModal( + { + LocalApiConfigurationHandler.saveConfiguration(localApiDefaultConfiguration); + setConfig(localApiDefaultConfiguration); + }} + />, + ); + }; - const changesBeenMade = JSON.stringify(config) - !== JSON.stringify(LocalApiConfigurationHandler.getConfiguration()); + const handleConfigSave = () => { + LocalApiConfigurationHandler.saveConfiguration(config); + setConfig(LocalApiConfigurationHandler.getConfiguration()); + }; - const isDefaultConfig = JSON.stringify(config) === JSON.stringify(localApiDefaultConfiguration); + const handleDiscard = () => { + setConfig(LocalApiConfigurationHandler.getConfiguration()); + }; + if (config === null) { return ( -
-
-

SimBridge Settings

- -
- {changesBeenMade && ( - - )} - - {!isDefaultConfig && ( - - )} - - {changesBeenMade && ( - - )} -
-
- -
-
-

Server

- -
- - setConfig(old => ({ - ...old, - server: { - ...old.server, - port: parseFloat(event.target.value), - }, - }))} - /> - -
-
-
-

Printer

- -
- - setConfig(old => ({ - ...old, - printer: { - ...old.printer, - enabled: value, - }, - }))} /> - - - - - - - - setConfig(old => ({ - ...old, - printer: { - ...old.printer, - fontSize: parseInt(event.target.value), - }, - }))} - /> - - - - setConfig(old => ({ - ...old, - printer: { - ...old.printer, - paperSize: event.target.value, - }, - }))} - /> - - - - setConfig(old => ({ - ...old, - printer: { - ...old.printer, - margin: parseFloat(event.target.value), - }, - }))} - /> - -
-
-
-
+
+

+ Could not load configuration file. This likely means that you do not have SimBridge currently installed. +

+
); + } + + const changesBeenMade = JSON.stringify(config) !== JSON.stringify(LocalApiConfigurationHandler.getConfiguration()); + + const isDefaultConfig = JSON.stringify(config) === JSON.stringify(localApiDefaultConfiguration); + + return ( +
+
+

SimBridge Settings

+ +
+ {changesBeenMade && ( + + )} + + {!isDefaultConfig && ( + + )} + + {changesBeenMade && ( + + )} +
+
+ +
+
+

Server

+ +
+ + + setConfig((old) => ({ + ...old, + server: { + ...old.server, + port: parseFloat(event.target.value), + }, + })) + } + /> + +
+
+
+

Printer

+ +
+ + + setConfig((old) => ({ + ...old, + printer: { + ...old.printer, + enabled: value, + }, + })) + } + /> + + + + + + + + + setConfig((old) => ({ + ...old, + printer: { + ...old.printer, + fontSize: parseInt(event.target.value), + }, + })) + } + /> + + + + + setConfig((old) => ({ + ...old, + printer: { + ...old.printer, + paperSize: event.target.value, + }, + })) + } + /> + + + + + setConfig((old) => ({ + ...old, + printer: { + ...old.printer, + margin: parseFloat(event.target.value), + }, + })) + } + /> + +
+
+
+
+ ); }; interface SimBridgeSettingItemProps { - name: string, + name: string; } const SimBridgeSettingItem: React.FC = ({ name, children }) => { - return ( -
-

{name}

+ return ( +
+

{name}

- {children} -
- ); + {children} +
+ ); }; diff --git a/src/renderer/components/Logo/index.tsx b/src/renderer/components/Logo/index.tsx index 490e69c6..0572d67a 100644 --- a/src/renderer/components/Logo/index.tsx +++ b/src/renderer/components/Logo/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import headerLogo from 'renderer/assets/header-logo.svg'; export const Logo = (): JSX.Element => ( -
- -
+
+ +
); diff --git a/src/renderer/components/Modal/AutostartDialog.tsx b/src/renderer/components/Modal/AutostartDialog.tsx index ca460b5c..38bb2220 100644 --- a/src/renderer/components/Modal/AutostartDialog.tsx +++ b/src/renderer/components/Modal/AutostartDialog.tsx @@ -1,101 +1,128 @@ -import React, { FC, useEffect, useState } from "react"; -import { Addon, ExternalApplicationDefinition, Publisher } from "renderer/utils/InstallerConfiguration"; -import { AlertModal } from "renderer/components/Modal/index"; -import { BackgroundServices } from "renderer/utils/BackgroundServices"; -import ReactMarkdown from "react-markdown"; -import remarkGfm from "remark-gfm"; -import { Toggle } from "renderer/components/Toggle"; +import React, { FC, useEffect, useState } from 'react'; +import { Addon, ExternalApplicationDefinition, Publisher } from 'renderer/utils/InstallerConfiguration'; +import { AlertModal } from 'renderer/components/Modal/index'; +import { BackgroundServices } from 'renderer/utils/BackgroundServices'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { Toggle } from 'renderer/components/Toggle'; export interface AutostartDialogProps { - app: ExternalApplicationDefinition, - addon: Addon, - publisher: Publisher, - isPrompted: boolean, - onAcknowledge?: () => void, + app: ExternalApplicationDefinition; + addon: Addon; + publisher: Publisher; + isPrompted: boolean; + onAcknowledge?: () => void; } -export const AutostartDialog: FC = ({ app, addon, publisher, isPrompted, onAcknowledge = () => {} }) => { - const [enabled, setEnabled] = useState(false); - - useEffect(() => { - BackgroundServices.isAutoStartEnabled(addon).then((state) => { - setEnabled(state); - }); - - const interval = setInterval(async () => { - setEnabled(await BackgroundServices.isAutoStartEnabled(addon)); - }, 500); - - return () => clearInterval(interval); - }, []); - - const handleToggle = async () => { - const isEnabled = await BackgroundServices.isAutoStartEnabled(addon); - - await BackgroundServices.setAutoStartEnabled(addon, publisher, !isEnabled); - }; - - return ( - - - - Autostart {enabled ? 'Enabled' : 'Disabled'} - - - )} - onAcknowledge={onAcknowledge} - acknowledgeText="Close" - /> - ); +export const AutostartDialog: FC = ({ + app, + addon, + publisher, + isPrompted, + onAcknowledge = () => {}, +}) => { + const [enabled, setEnabled] = useState(false); + + useEffect(() => { + BackgroundServices.isAutoStartEnabled(addon).then((state) => { + setEnabled(state); + }); + + const interval = setInterval(async () => { + setEnabled(await BackgroundServices.isAutoStartEnabled(addon)); + }, 500); + + return () => clearInterval(interval); + }, [addon]); + + const handleToggle = async () => { + const isEnabled = await BackgroundServices.isAutoStartEnabled(addon); + + await BackgroundServices.setAutoStartEnabled(addon, publisher, !isEnabled); + }; + + return ( + + + {`You can choose to automatically start **${app.prettyName}** when you log into your Windows session.`} + + + Autostart {enabled ? 'Enabled' : 'Disabled'} + + + } + onAcknowledge={onAcknowledge} + acknowledgeText="Close" + /> + ); }; interface YesNoOptionToggleProps { - enabled: boolean, - onToggle: () => void, - enabledBgColor?: string, - disabledBgColor?: string, + enabled: boolean; + onToggle: () => void; + enabledBgColor?: string; + disabledBgColor?: string; } -export const YesNoOptionToggle: FC = ({ enabled, onToggle, enabledBgColor = 'bg-utility-green', disabledBgColor = 'bg-navy-light', children }) => { - const handleClick = onToggle; - - const bgColor = enabled ? enabledBgColor : disabledBgColor; - const titleColor = enabled ? 'text-navy' : 'text-quasi-white'; - - return ( -
- - - - {children} - -
- ); +export const YesNoOptionToggle: FC = ({ + enabled, + onToggle, + enabledBgColor = 'bg-utility-green', + disabledBgColor = 'bg-navy-light', + children, +}) => { + const handleClick = onToggle; + + const bgColor = enabled ? enabledBgColor : disabledBgColor; + const titleColor = enabled ? 'text-navy' : 'text-quasi-white'; + + return ( +
+ + + + {children} + +
+ ); }; -export const CompactYesNoOptionToggle: FC = ({ enabled, onToggle, enabledBgColor = 'bg-utility-green', children }) => { - const handleClick = onToggle; - - const borderColor = enabled ? 'border-cyan' : 'border-navy-light'; - const titleColor = enabled ? 'text-cyan' : 'text-quasi-white'; - - return ( -
- - - - {children} - -
- ); +export const CompactYesNoOptionToggle: FC = ({ + enabled, + onToggle, + enabledBgColor = 'bg-utility-green', + children, +}) => { + const handleClick = onToggle; + + const borderColor = enabled ? 'border-cyan' : 'border-navy-light'; + const titleColor = enabled ? 'text-cyan' : 'text-quasi-white'; + + return ( +
+ + + + {children} + +
+ ); }; diff --git a/src/renderer/components/Modal/CannotInstallDialog.tsx b/src/renderer/components/Modal/CannotInstallDialog.tsx index e4f8bffa..355b22a1 100644 --- a/src/renderer/components/Modal/CannotInstallDialog.tsx +++ b/src/renderer/components/Modal/CannotInstallDialog.tsx @@ -1,99 +1,106 @@ -import React, { FC, useMemo } from "react"; -import { Addon, ExternalApplicationDefinition, Publisher } from "renderer/utils/InstallerConfiguration"; -import { PromptModal } from "renderer/components/Modal/index"; -import { Window } from "react-bootstrap-icons"; -import { useAddonExternalApps } from "renderer/utils/ExternalAppsUI"; -import { BackgroundServices } from "renderer/utils/BackgroundServices"; -import { Resolver } from "renderer/utils/Resolver"; -import { Button } from "renderer/components/Button"; +import React, { FC, useMemo } from 'react'; +import { Addon, ExternalApplicationDefinition, Publisher } from 'renderer/utils/InstallerConfiguration'; +import { PromptModal } from 'renderer/components/Modal/index'; +import { Window } from 'react-bootstrap-icons'; +import { useAddonExternalApps } from 'renderer/utils/ExternalAppsUI'; +import { BackgroundServices } from 'renderer/utils/BackgroundServices'; +import { Resolver } from 'renderer/utils/Resolver'; +import { Button } from 'renderer/components/Button'; export interface CannotInstallDialogProps { - addon: Addon, - publisher: Publisher, - onCancel?: () => void, - onConfirm?: () => void, + addon: Addon; + publisher: Publisher; + onCancel?: () => void; + onConfirm?: () => void; } export const CannotInstallDialog: FC = ({ addon, publisher, onCancel, onConfirm }) => { - const [runningExternalApps, disallowedRunningExternalApps] = useAddonExternalApps(addon, publisher); - - return ( - -

- You cannot install or update - {' '} - {addon.name} - {' '} - while the following external apps are running: -

- -
- {disallowedRunningExternalApps.map((app) => ( - - ))} -
- -

Close all apps above to continue.

- -
- - )} - onCancel={onCancel} - confirmText="Continue" - confirmEnabled={runningExternalApps.length === 0} - onConfirm={onConfirm} - /> - ); + const [runningExternalApps, disallowedRunningExternalApps] = useAddonExternalApps(addon, publisher); + + return ( + +

+ You cannot install or update {addon.name} while the following external apps are running: +

+ +
+ {disallowedRunningExternalApps.map((app) => ( + + ))} +
+ +

Close all apps above to continue.

+ +
+ + } + onCancel={onCancel} + confirmText="Continue" + confirmEnabled={runningExternalApps.length === 0} + onConfirm={onConfirm} + /> + ); }; interface RunningExternalAppEntryProps { - addon: Addon, - publisher: Publisher, - app: ExternalApplicationDefinition, - runningExternalApps: ExternalApplicationDefinition[], + addon: Addon; + publisher: Publisher; + app: ExternalApplicationDefinition; + runningExternalApps: ExternalApplicationDefinition[]; } const RunningExternalAppEntry: FC = ({ addon, publisher, app, runningExternalApps }) => { - const appIsRunning = runningExternalApps.includes(app); - - const canAppBeStopped = useMemo(() => { - if (addon.backgroundService) { - const def = Resolver.findDefinition(addon.backgroundService.runCheckExternalAppRef, publisher); - - if (def.kind !== 'externalApp') { - return; - } - - return def.key === app.key; - } - }, [addon]); - - const handleStop = () => BackgroundServices.kill(addon, publisher); - - const borderColor = appIsRunning ? 'border-utility-amber' : 'border-utility-green'; - const textColor = appIsRunning ? 'text-utility-amber' : 'text-utility-green'; - - return ( -
- - -
- - {/*{publisher.name} /!* TODO when we support external apps in the global scope or other publishers, change this *!/*/} - {app.prettyName} - - - - {canAppBeStopped ? ( - - ) : ( - {appIsRunning ? 'Running' : 'Closed'} - )} - -
-
- ); + const appIsRunning = runningExternalApps.includes(app); + + const canAppBeStopped = useMemo(() => { + if (addon.backgroundService) { + const def = Resolver.findDefinition(addon.backgroundService.runCheckExternalAppRef, publisher); + + if (def.kind !== 'externalApp') { + return; + } + + return def.key === app.key; + } + }, [addon.backgroundService, app.key, publisher]); + + const handleStop = () => BackgroundServices.kill(addon, publisher); + + const borderColor = appIsRunning ? 'border-utility-amber' : 'border-utility-green'; + const textColor = appIsRunning ? 'text-utility-amber' : 'text-utility-green'; + + return ( +
+ + +
+ + {/*{publisher.name} /!* TODO when we support external apps in the global scope or other publishers, change this *!/*/} + {app.prettyName} + + + + {canAppBeStopped ? ( + + ) : ( + {appIsRunning ? 'Running' : 'Closed'} + )} + +
+
+ ); }; diff --git a/src/renderer/components/Modal/DependencyDialog.tsx b/src/renderer/components/Modal/DependencyDialog.tsx index 3246fe50..ece10544 100644 --- a/src/renderer/components/Modal/DependencyDialog.tsx +++ b/src/renderer/components/Modal/DependencyDialog.tsx @@ -1,38 +1,35 @@ -import React, { FC } from "react"; -import { Box } from "react-bootstrap-icons"; -import { Addon, AddonDependency, Publisher } from "renderer/utils/InstallerConfiguration"; +import React, { FC } from 'react'; +import { Box } from 'react-bootstrap-icons'; +import { Addon, AddonDependency, Publisher } from 'renderer/utils/InstallerConfiguration'; export interface DependencyDialogBodyProps { - addon: Addon, - dependency: AddonDependency, - dependencyAddon: Addon, - dependencyPublisher: Publisher, + addon: Addon; + dependency: AddonDependency; + dependencyAddon: Addon; + dependencyPublisher: Publisher; } -export const DependencyDialogBody: FC = ({ addon, dependency, dependencyAddon, dependencyPublisher }) => ( - <> -

- {dependencyAddon.name} - {' '} - by - {' '} - {dependencyPublisher.name} - {' '} - needs to be installed to use the full functionality of - {' '} - {addon.name} - . -

+export const DependencyDialogBody: FC = ({ + addon, + dependency, + dependencyAddon, + dependencyPublisher, +}) => ( + <> +

+ {dependencyAddon.name} by {dependencyPublisher.name} needs to be installed to use the full + functionality of {addon.name}. +

-
- +
+ -
- {dependencyPublisher.name} - {dependencyAddon.name} -
-
+
+ {dependencyPublisher.name} + {dependencyAddon.name} +
+
-

{dependency.modalText}

- +

{dependency.modalText}

+ ); diff --git a/src/renderer/components/Modal/ErrorDialog.tsx b/src/renderer/components/Modal/ErrorDialog.tsx index dd80944f..e3e5126f 100644 --- a/src/renderer/components/Modal/ErrorDialog.tsx +++ b/src/renderer/components/Modal/ErrorDialog.tsx @@ -1,155 +1,163 @@ import React, { FC, useState } from 'react'; -import { AlertModal } from "renderer/components/Modal/index"; -import { Clipboard, ClipboardCheck, Ethernet, ExclamationTriangle, Hdd, ShieldExclamation, ShieldLock } from "react-bootstrap-icons"; -import { clipboard, shell } from "electron"; -import { FragmenterError, FragmenterErrorCode } from "@flybywiresim/fragmenter"; -import { SentrySessionCard } from "renderer/components/SentrySessionCard"; +import { AlertModal } from 'renderer/components/Modal/index'; +import { + Clipboard, + ClipboardCheck, + Ethernet, + ExclamationTriangle, + Hdd, + ShieldExclamation, + ShieldLock, +} from 'react-bootstrap-icons'; +import { clipboard, shell } from 'electron'; +import { FragmenterError, FragmenterErrorCode } from '@flybywiresim/fragmenter'; +import { SentrySessionCard } from 'renderer/components/SentrySessionCard'; const DISCORD_SUPPORT_URL = 'https://discord.com/channels/738864299392630914/1065394439608078336'; export interface ErrorDialogProps { - error: Error; - onAcknowledge?: () => void; + error: Error; + onAcknowledge?: () => void; } export const ErrorDialog: FC = ({ error, onAcknowledge }) => { - const handleOpenDiscordSupport = async () => { - await shell.openExternal(DISCORD_SUPPORT_URL); - }; + const handleOpenDiscordSupport = async () => { + await shell.openExternal(DISCORD_SUPPORT_URL); + }; - let fragmenterError; - try { - fragmenterError = FragmenterError.parseFromMessage(error.message); - } catch (e) { - // noop - } + let fragmenterError; + try { + fragmenterError = FragmenterError.parseFromMessage(error.message); + } catch (e) { + // noop + } - let errorVisualisation = null; - if (fragmenterError) { - switch (fragmenterError.code) { - case FragmenterErrorCode.PermissionsError: - errorVisualisation = ( - }> - Windows permissions error - Make sure the install folder has appropriate permissions. - - ); - break; - case FragmenterErrorCode.NoSpaceOnDevice: - errorVisualisation = ( - }> - No space left on device - Try to free up space in order to install this addon. - - ); - break; - case FragmenterErrorCode.NetworkError: - errorVisualisation = ( - }> - Network error - Try again or use a VPN when connection problems persist. - - ); - break; - case FragmenterErrorCode.ResourcesBusy: // fallthrough - case FragmenterErrorCode.MaxModuleRetries: // fallthrough - case FragmenterErrorCode.FileNotFound: // fallthrough - case FragmenterErrorCode.DirectoryNotEmpty: // fallthrough - case FragmenterErrorCode.NotADirectory: // fallthrough - case FragmenterErrorCode.ModuleJsonInvalid: // fallthrough - case FragmenterErrorCode.ModuleCrcMismatch: // fallthrough - case FragmenterErrorCode.UserAborted: // fallthrough - case FragmenterErrorCode.CorruptedZipFile: - case FragmenterErrorCode.Null: // fallthrough - case FragmenterErrorCode.Unknown: // Fallthrough - default: - errorVisualisation = ( - }> - An error has occurred! - Please contact FlyByWire support on Discord. See below. - - ); - break; - } + let errorVisualisation = null; + if (fragmenterError) { + switch (fragmenterError.code) { + case FragmenterErrorCode.PermissionsError: + errorVisualisation = ( + }> + Windows permissions error + Make sure the install folder has appropriate permissions. + + ); + break; + case FragmenterErrorCode.NoSpaceOnDevice: + errorVisualisation = ( + }> + No space left on device + Try to free up space in order to install this addon. + + ); + break; + case FragmenterErrorCode.NetworkError: + errorVisualisation = ( + }> + Network error + Try again or use a VPN when connection problems persist. + + ); + break; + case FragmenterErrorCode.ResourcesBusy: // fallthrough + case FragmenterErrorCode.MaxModuleRetries: // fallthrough + case FragmenterErrorCode.FileNotFound: // fallthrough + case FragmenterErrorCode.DirectoryNotEmpty: // fallthrough + case FragmenterErrorCode.NotADirectory: // fallthrough + case FragmenterErrorCode.ModuleJsonInvalid: // fallthrough + case FragmenterErrorCode.ModuleCrcMismatch: // fallthrough + case FragmenterErrorCode.UserAborted: // fallthrough + case FragmenterErrorCode.CorruptedZipFile: + case FragmenterErrorCode.Null: // fallthrough + case FragmenterErrorCode.Unknown: // Fallthrough + default: + errorVisualisation = ( + }> + An error has occurred! + Please contact FlyByWire support on Discord. See below. + + ); + break; } + } - // Error stack to clipboard handling - const [showCopied, setShowCopied] = useState(false); - const handleCopy = () => { - clipboard.writeText(error.stack, 'clipboard'); - setShowCopied(true); - setTimeout(() => { - setShowCopied(false); - }, 1_500); - }; + // Error stack to clipboard handling + const [showCopied, setShowCopied] = useState(false); + const handleCopy = () => { + clipboard.writeText(error.stack, 'clipboard'); + setShowCopied(true); + setTimeout(() => { + setShowCopied(false); + }, 1_500); + }; - return ( - - -

Error while installing

-
- )} - bodyText={( -
-
- {errorVisualisation} -
{error.stack}
-
+ return ( + + +

Error while installing

+
+ } + bodyText={ +
+
+ {errorVisualisation} +
{error.stack}
+
-
-

Obtain support on Discord and provide the error message and on request - the sentry code:

-
- {showCopied ? ( - <> - Copied! -
- - - -
- - ) : ( - <> - Copy error message to clipboard -
- - - -
- - )} -
- -
-
- )} - acknowledgeText="Dismiss" - onAcknowledge={ - onAcknowledge - } - /> - ) - ; +
+

+ Obtain support on Discord and provide the error message and on + request the sentry code: +

+
+ {showCopied ? ( + <> + Copied! +
+ + + +
+ + ) : ( + <> + Copy error message to clipboard +
+ + + +
+ + )} +
+ +
+
+ } + acknowledgeText="Dismiss" + onAcknowledge={onAcknowledge} + /> + ); }; interface ErrorVisualisationBoxProps { - icon: JSX.Element, + icon: JSX.Element; } const ErrorVisualisationBox: FC = ({ icon, children }) => ( -
- {icon} +
+ {icon} -
- {children} -
-
+
{children}
+
); diff --git a/src/renderer/components/Modal/IncompatibleAddonDialog.tsx b/src/renderer/components/Modal/IncompatibleAddonDialog.tsx index e4b62024..55a63198 100644 --- a/src/renderer/components/Modal/IncompatibleAddonDialog.tsx +++ b/src/renderer/components/Modal/IncompatibleAddonDialog.tsx @@ -1,33 +1,28 @@ -import React, { FC } from "react"; -import { Addon, AddonIncompatibleAddon } from "renderer/utils/InstallerConfiguration"; +import React, { FC } from 'react'; +import { Addon, AddonIncompatibleAddon } from 'renderer/utils/InstallerConfiguration'; export interface IncompatibleAddonDialogBodyProps { - addon: Addon, - incompatibleAddons: AddonIncompatibleAddon[], + addon: Addon; + incompatibleAddons: AddonIncompatibleAddon[]; } export const IncompatibleAddonDialogBody: FC = ({ addon, incompatibleAddons }) => ( - <> -

- The following addons are incompatible with "{addon.name}": -

-

-

    - {incompatibleAddons.map((a) => ( -
    -
    - {a.title} - {' '} - {a.folder} - {' '} - {a.description} -
    -
    - ))} -
-

-

- Continue installing "{addon.name}"? -

- + <> +

The following addons are incompatible with "{addon.name}":

+

+

    + {incompatibleAddons.map((a) => ( +
    +
    + {a.title} {a.folder}{' '} + {a.description} +
    +
    + ))} +
+

+

+ Continue installing "{addon.name}"? +

+ ); diff --git a/src/renderer/components/Modal/InstallSizeDialog.tsx b/src/renderer/components/Modal/InstallSizeDialog.tsx index e9bde022..8c6684e9 100644 --- a/src/renderer/components/Modal/InstallSizeDialog.tsx +++ b/src/renderer/components/Modal/InstallSizeDialog.tsx @@ -1,120 +1,130 @@ -import React, { FC } from "react"; -import { UpdateInfo } from "@flybywiresim/fragmenter"; -import { PromptModal } from "renderer/components/Modal/index"; -import { Download, Hdd, HddFill } from "react-bootstrap-icons"; -import { ButtonType } from "renderer/components/Button"; -import { FreeDiskSpaceInfo } from "renderer/utils/FreeDiskSpace"; +import React, { FC } from 'react'; +import { UpdateInfo } from '@flybywiresim/fragmenter'; +import { PromptModal } from 'renderer/components/Modal/index'; +import { Download, Hdd, HddFill } from 'react-bootstrap-icons'; +import { ButtonType } from 'renderer/components/Button'; +import { FreeDiskSpaceInfo } from 'renderer/utils/FreeDiskSpace'; const GIB = 1_074_000_000; const MIB = 1_049_000; function formatSize(size: number): string { - const numGigabytes = size / GIB; + const numGigabytes = size / GIB; - if (numGigabytes > 1) { - return `${numGigabytes.toFixed(1)} GiB`; - } else { - const numMegabytes = size / MIB; + if (numGigabytes > 1) { + return `${numGigabytes.toFixed(1)} GiB`; + } else { + const numMegabytes = size / MIB; - return `${numMegabytes.toFixed(1)} MiB`; - } + return `${numMegabytes.toFixed(1)} MiB`; + } } export interface InstallSizeDialogProps { - updateInfo: UpdateInfo, - freeDeskSpaceInfo: FreeDiskSpaceInfo, - onConfirm?: () => void, - onCancel?: () => void, - dontShowAgainSettingName: string, + updateInfo: UpdateInfo; + freeDeskSpaceInfo: FreeDiskSpaceInfo; + onConfirm?: () => void; + onCancel?: () => void; + dontShowAgainSettingName: string; } -export const InstallSizeDialog: FC = ({ updateInfo, freeDeskSpaceInfo, onConfirm, onCancel, dontShowAgainSettingName }) => { - const requiredDiskSpace = (updateInfo.requiredDiskSpace + updateInfo.downloadSize) + (10 * MIB); - - const showTemporaryAsSeparate = freeDeskSpaceInfo.freeSpaceInDest !== freeDeskSpaceInfo.freeSpaceInTemp; - - const sufficientSpaceInDest = freeDeskSpaceInfo.freeSpaceInDest > requiredDiskSpace; - const sufficientSpaceInTemp = freeDeskSpaceInfo.freeSpaceInTemp > requiredDiskSpace; - - const canInstall = sufficientSpaceInDest && sufficientSpaceInTemp; - - const availableDiskSpaceColorDest = sufficientSpaceInDest ? 'text-utility-green' : 'text-utility-red'; - const availableDiskSpaceColorTemp = sufficientSpaceInTemp ? 'text-utility-green' : 'text-utility-red'; - - return ( - -
- - - - - Download size - - - {formatSize(updateInfo.downloadSize)} - -
- -
- -
- - - - - Required disk space - - - {formatSize(requiredDiskSpace)} - -
- -
- - - - - Available disk space (destination) - - - {formatSize(freeDeskSpaceInfo.freeSpaceInDest)} - - - {showTemporaryAsSeparate && ( - - - - - Available disk space (temporary) - - - {formatSize(freeDeskSpaceInfo.freeSpaceInTemp)} - - )} -
- - {!canInstall && ( -
- - -
- Not enough available disk space - - Try to free up space in order to install this addon. -
-
- )} -
+export const InstallSizeDialog: FC = ({ + updateInfo, + freeDeskSpaceInfo, + onConfirm, + onCancel, + dontShowAgainSettingName, +}) => { + const requiredDiskSpace = updateInfo.requiredDiskSpace + updateInfo.downloadSize + 10 * MIB; + + const showTemporaryAsSeparate = freeDeskSpaceInfo.freeSpaceInDest !== freeDeskSpaceInfo.freeSpaceInTemp; + + const sufficientSpaceInDest = freeDeskSpaceInfo.freeSpaceInDest > requiredDiskSpace; + const sufficientSpaceInTemp = freeDeskSpaceInfo.freeSpaceInTemp > requiredDiskSpace; + + const canInstall = sufficientSpaceInDest && sufficientSpaceInTemp; + + const availableDiskSpaceColorDest = sufficientSpaceInDest ? 'text-utility-green' : 'text-utility-red'; + const availableDiskSpaceColorTemp = sufficientSpaceInTemp ? 'text-utility-green' : 'text-utility-red'; + + return ( + +
+ + + + + Download size + + + {formatSize(updateInfo.downloadSize)} + +
+ +
+ +
+ + + + + Required disk space + + + {formatSize(requiredDiskSpace)} + +
+ +
+ + + + + Available disk space (destination) + + + + {formatSize(freeDeskSpaceInfo.freeSpaceInDest)} + + + + {showTemporaryAsSeparate && ( + + + + + Available disk space (temporary) + + + + {formatSize(freeDeskSpaceInfo.freeSpaceInTemp)} + + )} - confirmText="Install" - confirmColor={ButtonType.Positive} - onConfirm={onConfirm} - confirmEnabled={canInstall} - onCancel={onCancel} - dontShowAgainSettingName={sufficientSpaceInDest ? dontShowAgainSettingName : undefined} - /> - ); +
+ + {!canInstall && ( +
+ + +
+ Not enough available disk space + + Try to free up space in order to install this addon. +
+
+ )} +
+ } + confirmText="Install" + confirmColor={ButtonType.Positive} + onConfirm={onConfirm} + confirmEnabled={canInstall} + onCancel={onCancel} + dontShowAgainSettingName={sufficientSpaceInDest ? dontShowAgainSettingName : undefined} + /> + ); }; diff --git a/src/renderer/components/Modal/index.tsx b/src/renderer/components/Modal/index.tsx index 5324dbec..3a54b114 100644 --- a/src/renderer/components/Modal/index.tsx +++ b/src/renderer/components/Modal/index.tsx @@ -3,18 +3,17 @@ import React, { createContext, FC, useContext, useState } from 'react'; import { Dot, X } from 'react-bootstrap-icons'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; -import "./index.css"; -// @ts-ignore +import './index.css'; import changelog from './../../../../.github/CHANGELOG.yaml'; import * as packageInfo from '../../../../package.json'; -import { Button, ButtonType } from "renderer/components/Button"; +import { Button, ButtonType } from 'renderer/components/Button'; import { CompactYesNoOptionToggle } from './AutostartDialog'; -interface ModalContextInterface{ - showModal: (modal: JSX.Element) => void; - showModalAsync: (modal: JSX.Element) => Promise; - modal?: JSX.Element; - popModal: () => void; +interface ModalContextInterface { + showModal: (modal: JSX.Element) => void; + showModalAsync: (modal: JSX.Element) => Promise; + modal?: JSX.Element; + popModal: () => void; } const ModalContext = createContext(undefined); @@ -22,278 +21,283 @@ const ModalContext = createContext(undefined); export const useModals = (): ModalContextInterface => useContext(ModalContext); export const ModalProvider: FC = ({ children }) => { - const [modal, setModal] = useState(undefined); - - const popModal = () => { - setModal(undefined); - }; - - const showModal = (modal: JSX.Element) => { - setModal(modal); - }; - - const showModalAsync = (modal: JSX.Element): Promise => { - return new Promise((resolve) => { - setModal(React.cloneElement(modal, { - onConfirm: () => { - resolve(true); - modal.props.onConfirm?.(); - }, - onCancel: () => { - resolve(false); - modal.props.onCancel?.(); - }, - onAcknowledge: () => { - resolve(true); - modal.props.onAcknowledge?.(); - }, - })); - }); - }; - - return ( - - {children} - - ); + const [modal, setModal] = useState(undefined); + + const popModal = () => { + setModal(undefined); + }; + + const showModal = (modal: JSX.Element) => { + setModal(modal); + }; + + const showModalAsync = (modal: JSX.Element): Promise => { + return new Promise((resolve) => { + setModal( + React.cloneElement(modal, { + onConfirm: () => { + resolve(true); + modal.props.onConfirm?.(); + }, + onCancel: () => { + resolve(false); + modal.props.onCancel?.(); + }, + onAcknowledge: () => { + resolve(true); + modal.props.onAcknowledge?.(); + }, + }), + ); + }); + }; + + return ( + {children} + ); }; interface BaseModalProps { - title: React.ReactElement | string; - bodyText: React.ReactElement | string; - dontShowAgainSettingName?: string; - closeIfDontShowAgain?: boolean, + title: React.ReactElement | string; + bodyText: React.ReactElement | string; + dontShowAgainSettingName?: string; + closeIfDontShowAgain?: boolean; } interface PromptModalProps extends BaseModalProps { - onConfirm?: () => void; - onCancel?: () => void; - cancelText?: string; - confirmColor?: ButtonType; - confirmText?: string; - confirmEnabled?: boolean; + onConfirm?: () => void; + onCancel?: () => void; + cancelText?: string; + confirmColor?: ButtonType; + confirmText?: string; + confirmEnabled?: boolean; } export const PromptModal: FC = ({ - onConfirm, - onCancel, - cancelText, - confirmColor, - confirmText, - confirmEnabled = true, - title, - bodyText, - dontShowAgainSettingName, - closeIfDontShowAgain = true, + onConfirm, + onCancel, + cancelText, + confirmColor, + confirmText, + confirmEnabled = true, + title, + bodyText, + dontShowAgainSettingName, + closeIfDontShowAgain = true, }) => { - const [dontShowAgain, setDontShowAgain] = useSetting(dontShowAgainSettingName ?? '', false); - const [checkMark, setCheckMark] = useState(dontShowAgain); - - const { popModal } = useModals(); - - const handleConfirm = () => { - if (dontShowAgainSettingName) { - setDontShowAgain(checkMark); - } - onConfirm?.(); - popModal(); - }; - - const handleCancel = () => { - onCancel?.(); - popModal(); - }; - - if (dontShowAgain && closeIfDontShowAgain) { - handleConfirm(); - } + const [dontShowAgain, setDontShowAgain] = useSetting(dontShowAgainSettingName ?? '', false); + const [checkMark, setCheckMark] = useState(dontShowAgain); - return ( -
- {typeof title === 'string' ? ( -

{title}

- ) : ( - title - )} - {typeof bodyText === 'string' ? ( - - ) : ( - bodyText - )} - - {dontShowAgainSettingName && ( - setCheckMark((old) => !old)} /> - )} - -
- - -
-
- ); + const { popModal } = useModals(); + + const handleConfirm = () => { + if (dontShowAgainSettingName) { + setDontShowAgain(checkMark); + } + onConfirm?.(); + popModal(); + }; + + const handleCancel = () => { + onCancel?.(); + popModal(); + }; + + if (dontShowAgain && closeIfDontShowAgain) { + handleConfirm(); + } + + return ( +
+ {typeof title === 'string' ?

{title}

: title} + {typeof bodyText === 'string' ? ( + + {bodyText} + + ) : ( + bodyText + )} + + {dontShowAgainSettingName && ( + setCheckMark((old) => !old)} /> + )} + +
+ + +
+
+ ); }; interface AlertModalProps extends BaseModalProps { - onAcknowledge?: () => void; - acknowledgeText?: string; - acknowledgeColor?: ButtonType, + onAcknowledge?: () => void; + acknowledgeText?: string; + acknowledgeColor?: ButtonType; } export const AlertModal: FC = ({ - title, - bodyText, - onAcknowledge, - acknowledgeText, - acknowledgeColor = ButtonType.Neutral, - dontShowAgainSettingName, - closeIfDontShowAgain = true, + title, + bodyText, + onAcknowledge, + acknowledgeText, + acknowledgeColor = ButtonType.Neutral, + dontShowAgainSettingName, + closeIfDontShowAgain = true, }) => { - const [dontShowAgain, setDontShowAgain] = useSetting(dontShowAgainSettingName ?? '', false); - const [checkMark, setCheckMark] = useState(dontShowAgain); - - const { popModal } = useModals(); + const [dontShowAgain, setDontShowAgain] = useSetting(dontShowAgainSettingName ?? '', false); + const [checkMark, setCheckMark] = useState(dontShowAgain); - const handleAcknowledge = () => { - if (dontShowAgainSettingName) { - setDontShowAgain(checkMark); - } - onAcknowledge?.(); - popModal(); - }; + const { popModal } = useModals(); - if (dontShowAgain && closeIfDontShowAgain) { - handleAcknowledge(); + const handleAcknowledge = () => { + if (dontShowAgainSettingName) { + setDontShowAgain(checkMark); } - - return ( -
- {typeof title === 'string' ? ( -

{title}

- ) : ( - title - )} - {typeof bodyText === 'string' ? ( - - ) : ( - bodyText - )} - - {dontShowAgainSettingName && setCheckMark((old) => !old)} />} - -
- -
-
- ); + onAcknowledge?.(); + popModal(); + }; + + if (dontShowAgain && closeIfDontShowAgain) { + handleAcknowledge(); + } + + return ( +
+ {typeof title === 'string' ?

{title}

: title} + {typeof bodyText === 'string' ? ( + + {bodyText} + + ) : ( + bodyText + )} + + {dontShowAgainSettingName && ( + setCheckMark((old) => !old)} /> + )} + +
+ +
+
+ ); }; export const ChangelogModal: React.FC = () => { - interface ChangelogType { - releases: Release[]; - } - interface Release { - name: string; - changes: Change[]; - } - interface Change { - title: string; - categories: string[]; - authors: string[]; - } - const { popModal } = useModals(); - - const handleClose = () => { - popModal(); - }; - - return ( -
-
-

{'Changelog'}

-
- + interface ChangelogType { + releases: Release[]; + } + interface Release { + name: string; + changes: Change[]; + } + interface Change { + title: string; + categories: string[]; + authors: string[]; + } + const { popModal } = useModals(); + + const handleClose = () => { + popModal(); + }; + + return ( +
+
+

{'Changelog'}

+
+ +
+
+
+ {(changelog as ChangelogType).releases.map((release) => ( +
+
{release.name}
+ {release.changes.map((change, index) => ( +
+
+
-
-
- {(changelog as ChangelogType).releases.map((release) =>
-
{release.name}
- {release.changes.map((change) =>
-
- -
-
-
- {change.title} - {change.categories ? change.categories.map((category) => -
- {category} -
) : <>} -
-
- {change.authors ? change.authors.map((author, index) => -
- {index == 0 ? 'by ' + author : ', ' + author} -
) : <>} -
+
+
+ {change.title} + {change.categories ? ( + change.categories.map((category) => ( +
+ {category}
-
)} -
)} -
-
- ); + )) + ) : ( + <> + )} +
+
+ {change.authors ? ( + change.authors.map((author, index) => ( +
{index == 0 ? 'by ' + author : ', ' + author}
+ )) + ) : ( + <> + )} +
+
+
+ ))} +
+ ))} +
+
+ ); }; interface DoNotAskAgainProps { - checked: boolean, - toggleChecked: () => void, + checked: boolean; + toggleChecked: () => void; } const DoNotAskAgain: FC = ({ checked, toggleChecked }) => ( -
- - Don't show this again - -
+
+ + Don't show this again + +
); export const ModalContainer: FC = () => { - const onVersionChanged = () => { - const { showModal } = useModals(); - - if (packageInfo.version !== settings.get('metaInfo.lastVersion')) { - settings.set('metaInfo.lastVersion', packageInfo.version); - showModal(); - } - }; - - onVersionChanged(); - const { modal } = useModals(); - - return ( -
-
-
- {modal} -
-
- ); + const { modal, showModal } = useModals(); + + const onVersionChanged = () => { + if (packageInfo.version !== settings.get('metaInfo.lastVersion')) { + settings.set('metaInfo.lastVersion', packageInfo.version); + showModal(); + } + }; + + // FIXME Why is this being called every render? + onVersionChanged(); + + return ( +
+
+
{modal}
+
+ ); }; diff --git a/src/renderer/components/NoAvailableAddonsSection/index.tsx b/src/renderer/components/NoAvailableAddonsSection/index.tsx index 8b86d115..9796b855 100644 --- a/src/renderer/components/NoAvailableAddonsSection/index.tsx +++ b/src/renderer/components/NoAvailableAddonsSection/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; export const NoAvailableAddonsSection = (): JSX.Element => ( -
- There are currently no available addons provided by this publisher. -
+
+ There are currently no available addons provided by this publisher. +
); diff --git a/src/renderer/components/PublisherSection/index.tsx b/src/renderer/components/PublisherSection/index.tsx index 9509c383..ea9e2053 100644 --- a/src/renderer/components/PublisherSection/index.tsx +++ b/src/renderer/components/PublisherSection/index.tsx @@ -1,16 +1,16 @@ -import React, { FC } from "react"; -import { useParams } from "react-router-dom"; -import { NoAvailableAddonsSection } from "renderer/components/NoAvailableAddonsSection"; -import { AddonSection } from "renderer/components/AddonSection"; -import { useAppSelector } from "renderer/redux/store"; +import React, { FC } from 'react'; +import { useParams } from 'react-router-dom'; +import { NoAvailableAddonsSection } from 'renderer/components/NoAvailableAddonsSection'; +import { AddonSection } from 'renderer/components/AddonSection'; +import { useAppSelector } from 'renderer/redux/store'; export const PublisherSection: FC = () => { - const { publisherName } = useParams<{ publisherName: string }>(); - const publisher = useAppSelector((state) => state.configuration.publishers.find((it) => it.name === publisherName)); + const { publisherName } = useParams<{ publisherName: string }>(); + const publisher = useAppSelector((state) => state.configuration.publishers.find((it) => it.name === publisherName)); - if (!publisher?.addons.length) { - return ; - } + if (!publisher?.addons.length) { + return ; + } - return ; + return ; }; diff --git a/src/renderer/components/SentrySessionCard.tsx b/src/renderer/components/SentrySessionCard.tsx index eaae3854..4a4ebc20 100644 --- a/src/renderer/components/SentrySessionCard.tsx +++ b/src/renderer/components/SentrySessionCard.tsx @@ -1,47 +1,52 @@ -import React, { FC, useState } from "react"; -import { useAppSelector } from "renderer/redux/store"; -import { Clipboard, ClipboardCheck } from "react-bootstrap-icons"; -import { clipboard } from "electron"; +import React, { FC, useState } from 'react'; +import { useAppSelector } from 'renderer/redux/store'; +import { Clipboard, ClipboardCheck } from 'react-bootstrap-icons'; +import { clipboard } from 'electron'; export const SentrySessionCard: FC = () => { - const sessionID = useAppSelector((state) => state.sentrySessionID.sessionID); + const sessionID = useAppSelector((state) => state.sentrySessionID.sessionID); - const [showCopied, setShowCopied] = useState(false); + const [showCopied, setShowCopied] = useState(false); - const handleCopy = () => { - clipboard.writeText(sessionID, 'clipboard'); + const handleCopy = () => { + clipboard.writeText(sessionID, 'clipboard'); - setShowCopied(true); + setShowCopied(true); - setTimeout(() => { - setShowCopied(false); - }, 1_500); - }; + setTimeout(() => { + setShowCopied(false); + }, 1_500); + }; - return ( -
- {showCopied ? ( - <> - Copied! -
- - - -
- - ) : ( - <> - Copy Sentry Code to clipboard: {sessionID} -
- - - -
- - )} -
- ); + return ( +
+ {showCopied ? ( + <> + Copied! +
+ + + +
+ + ) : ( + <> + Copy Sentry Code to clipboard: {sessionID} +
+ + + +
+ + )} +
+ ); }; diff --git a/src/renderer/components/SettingsSection/About.tsx b/src/renderer/components/SettingsSection/About.tsx index 6cd47305..69805953 100644 --- a/src/renderer/components/SettingsSection/About.tsx +++ b/src/renderer/components/SettingsSection/About.tsx @@ -1,73 +1,81 @@ -import React, { FC, useEffect, useState } from "react"; +import React, { FC, useEffect, useState } from 'react'; import FbwTail from 'renderer/assets/FBW-Tail.svg'; -import * as packageInfo from "../../../../package.json"; -import path from "path"; -import fs from "fs"; -import { shell } from "electron"; -import { ChangelogModal, useModals } from "../Modal"; -import { SentrySessionCard } from "renderer/components/SentrySessionCard"; +import * as packageInfo from '../../../../package.json'; +import path from 'path'; +import fs from 'fs'; +import { shell } from 'electron'; +import { ChangelogModal, useModals } from '../Modal'; +import { SentrySessionCard } from 'renderer/components/SentrySessionCard'; export const AboutSettings: FC = () => { - const [logoRotation, setLogoRotation] = useState(0); + const [logoRotation, setLogoRotation] = useState(0); - useEffect(() => { - if (logoRotation / 360 > 5) { - shell.openExternal('https://www.youtube.com/watch?v=dQw4w9WgXcQ?autoplay=1'); + useEffect(() => { + if (logoRotation / 360 > 5) { + shell.openExternal('https://www.youtube.com/watch?v=dQw4w9WgXcQ?autoplay=1'); - setLogoRotation(0); - } - }); + setLogoRotation(0); + } + }, [logoRotation]); - const handleOpenThirdPartyLicenses = () => { - const licensesPath = path.join(process.resourcesPath, 'extraResources', 'licenses.md'); + const handleOpenThirdPartyLicenses = () => { + const licensesPath = path.join(process.resourcesPath, 'extraResources', 'licenses.md'); - if (!fs.existsSync(licensesPath)) { - alert('The requested file does not exist.'); - return; - } + if (!fs.existsSync(licensesPath)) { + alert('The requested file does not exist.'); + return; + } - shell.openExternal(licensesPath) - .catch(console.error); - }; + shell.openExternal(licensesPath).catch(console.error); + }; - const handleLogoClick = () => { - setLogoRotation((old) => old + 360); - }; + const handleLogoClick = () => { + setLogoRotation((old) => old + 360); + }; - const { showModal } = useModals(); + const { showModal } = useModals(); - return ( -
-
- + return ( +
+
+ - -
- -
- Copyright (c) 2020-2022 FlyByWire Simulations and its contributors - Licensed under the GNU General Public License Version 3 + +
- - All publisher associated logos are the intellectual property of their respective owners. - Media content included is licensed under the terms set by the publisher. - +
+ Copyright (c) 2020-2022 FlyByWire Simulations and its contributors + Licensed under the GNU General Public License Version 3 -
- -
+ + All publisher associated logos are the intellectual property of their respective owners. Media content + included is licensed under the terms set by the publisher. + - - Third party licenses -
+
+
- ); + + + Third party licenses + +
+
+ ); }; diff --git a/src/renderer/components/SettingsSection/Customization.tsx b/src/renderer/components/SettingsSection/Customization.tsx index a77a49c8..ecda7565 100644 --- a/src/renderer/components/SettingsSection/Customization.tsx +++ b/src/renderer/components/SettingsSection/Customization.tsx @@ -1,69 +1,61 @@ import React, { FC } from 'react'; -import settings, { useSetting } from "common/settings"; +import settings, { useSetting } from 'common/settings'; import { Toggle } from '../Toggle'; -const SettingsItem: FC<{name: string}> = ({ name, children }) => ( -
- {/* TODO: Remove this styling later */} -

{name}

- {children} -
+const SettingsItem: FC<{ name: string }> = ({ name, children }) => ( +
+ {/* TODO: Remove this styling later */} +

{name}

+ {children} +
); interface SettingItemProps { - value: T; - setValue: (value: T) => void; + value: T; + setValue: (value: T) => void; } const DarkThemeItem = ({ value, setValue }: SettingItemProps) => { - const handleClick = () => { - const newState = !value; - setValue(newState); - settings.set('mainSettings.useDarkTheme', newState); - }; - - return ( - - - - ); + const handleClick = () => { + const newState = !value; + setValue(newState); + settings.set('mainSettings.useDarkTheme', newState); + }; + + return ( + + + + ); }; const SeasonalEffectsItem = ({ value, setValue }: SettingItemProps) => { - const handleClick = () => { - const newState = !value; - setValue(newState); - settings.set('mainSettings.allowSeasonalEffects', newState); - }; - - return ( - - - - ); + const handleClick = () => { + const newState = !value; + setValue(newState); + settings.set('mainSettings.allowSeasonalEffects', newState); + }; + + return ( + + + + ); }; -const index = (): JSX.Element => { - const [useDarkTheme, setUseDarkTheme] = useSetting('mainSettings.useDarkTheme'); - const [seasonalEffects, setSeasonalEffects] = useSetting('mainSettings.allowSeasonalEffects'); - - return ( -
-
-

Customization Settings

-
- - -
-
+export const CustomizationSettings = (): JSX.Element => { + const [useDarkTheme, setUseDarkTheme] = useSetting('mainSettings.useDarkTheme'); + const [seasonalEffects, setSeasonalEffects] = useSetting('mainSettings.allowSeasonalEffects'); + + return ( +
+
+

Customization Settings

+
+ +
- ); +
+
+ ); }; - -export default index; diff --git a/src/renderer/components/SettingsSection/Developer.tsx b/src/renderer/components/SettingsSection/Developer.tsx index df835024..d17fc7a4 100644 --- a/src/renderer/components/SettingsSection/Developer.tsx +++ b/src/renderer/components/SettingsSection/Developer.tsx @@ -1,53 +1,50 @@ -import React, { FC, useEffect, useState } from 'react'; -import { useSetting } from "common/settings"; +import React, { FC, useCallback, useEffect, useState } from 'react'; +import { useSetting } from 'common/settings'; const SettingsItem: FC<{ name: string }> = ({ name, children }) => ( -
-

{name}

- {children} -
+
+

{name}

+ {children} +
); -const index = (): JSX.Element => { - const [configDownloadUrl, setConfigDownloadUrl] = useSetting('mainSettings.configDownloadUrl'); - const [configDownloadUrlValid, setConfigDownloadUrlValid] = useState(false); +export const DeveloperSettings = (): JSX.Element => { + const [configDownloadUrl, setConfigDownloadUrl] = useSetting('mainSettings.configDownloadUrl'); + const [configDownloadUrlValid, setConfigDownloadUrlValid] = useState(false); - const validateUrl = () => { - try { - fetch(configDownloadUrl) - .then((response) => { - setConfigDownloadUrlValid(response.status === 200); - }); - } catch (e) { - setConfigDownloadUrlValid(false); - } - }; + const validateUrl = useCallback(() => { + try { + fetch(configDownloadUrl).then((response) => { + setConfigDownloadUrlValid(response.status === 200); + }); + } catch (e) { + setConfigDownloadUrlValid(false); + } + }, [configDownloadUrl]); - useEffect(() => { - validateUrl(); - }, []); + useEffect(() => { + validateUrl(); + }, [validateUrl]); - return ( -
-
-

General Settings

-
- -
- setConfigDownloadUrl(event.target.value)} - onBlur={() => validateUrl()} - size={50} - /> -
-
-
+ return ( +
+
+

General Settings

+
+ +
+ setConfigDownloadUrl(event.target.value)} + onBlur={() => validateUrl()} + size={50} + />
+
- ); +
+
+ ); }; - -export default index; diff --git a/src/renderer/components/SettingsSection/Download.tsx b/src/renderer/components/SettingsSection/Download.tsx index 6996fb39..c867067b 100644 --- a/src/renderer/components/SettingsSection/Download.tsx +++ b/src/renderer/components/SettingsSection/Download.tsx @@ -1,134 +1,135 @@ import React, { FC } from 'react'; import { setupMsfsCommunityPath, setupInstallPath, setupTempLocation } from 'renderer/actions/install-path.utils'; -import settings, { useSetting } from "common/settings"; +import settings, { useSetting } from 'common/settings'; import { Toggle } from '../Toggle'; -const SettingsItem: FC<{name: string}> = ({ name, children }) => ( -
- {/* TODO: Remove this styling later */} -

{name}

- {children} -
+const SettingsItem: FC<{ name: string }> = ({ name, children }) => ( +
+ {/* TODO: Remove this styling later */} +

{name}

+ {children} +
); interface SettingItemProps { - value: T; - setValue: (value: T) => void; + value: T; + setValue: (value: T) => void; } interface PathSettingItemProps extends SettingItemProps { - name: string, - callback: () => Promise; + name: string; + callback: () => Promise; } const PathSettingItem: React.FC = ({ value, setValue, name, callback }) => { - const handleClick = async () => { - const path = await callback(); - - if (path) { - setValue(path); - } - }; - - return ( - -
{value}
-
- ); + const handleClick = async () => { + const path = await callback(); + + if (path) { + setValue(path); + } + }; + + return ( + +
+ {value} +
+
+ ); }; -const MsfsCommunityPathSettingItem = ({ value, setValue }: SettingItemProps): JSX.Element => - ( - - ); +const MsfsCommunityPathSettingItem = ({ value, setValue }: SettingItemProps): JSX.Element => ( + +); -const InstallPathSettingItem = ({ value, setValue }: SettingItemProps): JSX.Element => - ( - - ); +const InstallPathSettingItem = ({ value, setValue }: SettingItemProps): JSX.Element => ( + +); -const TempLocationSettingItem = ({ value, setValue }: SettingItemProps): JSX.Element => - ( - - ); +const TempLocationSettingItem = ({ value, setValue }: SettingItemProps): JSX.Element => ( + +); const SeparateTempLocationSettingItem = ({ value, setValue }: SettingItemProps) => { - const handleClick = () => { - const newState = !value; - setValue(newState); - settings.set('mainSettings.separateTempLocation', newState); - settings.set('mainSettings.tempLocation', settings.get('mainSettings.installPath')); - }; - - return ( - - - - ); + const handleClick = () => { + const newState = !value; + setValue(newState); + settings.set('mainSettings.separateTempLocation', newState); + settings.set('mainSettings.tempLocation', settings.get('mainSettings.installPath')); + }; + + return ( + + + + ); }; const DisableWarningSettingItem = ({ value, setValue }: SettingItemProps) => { - const handleClick = () => { - const newState = !value; - setValue(newState); - settings.set('mainSettings.disableExperimentalWarning', newState); - }; - - return ( - - - - ); + const handleClick = () => { + const newState = !value; + setValue(newState); + settings.set('mainSettings.disableExperimentalWarning', newState); + }; + + return ( + + + + ); }; const UseCdnSettingItem = ({ value, setValue }: SettingItemProps) => { - const handleClick = () => { - const newState = !value; - setValue(newState); - settings.set('mainSettings.useCdnCache', newState); - }; - - return ( - - - - ); + const handleClick = () => { + const newState = !value; + setValue(newState); + settings.set('mainSettings.useCdnCache', newState); + }; + + return ( + + + + ); }; -const index = (): JSX.Element => { - const [communityPath, setCommunityPath] = useSetting('mainSettings.msfsCommunityPath'); - const [installPath, setInstallPath] = useSetting('mainSettings.installPath'); - const [tempLocation, setTempLocation] = useSetting('mainSettings.tempLocation'); - const [separateTempLocation, setSeparateTempLocation] = useSetting('mainSettings.separateTempLocation'); - const [disableVersionWarning, setDisableVersionWarning] = useSetting('mainSettings.disableExperimentalWarning'); - const [useCdnCache, setUseCdnCache] = useSetting('mainSettings.useCdnCache'); - - return ( -
-
-

Download Settings

-
- - - - {separateTempLocation && - - } - - -
-
+export const DownloadSettings = (): JSX.Element => { + const [communityPath, setCommunityPath] = useSetting('mainSettings.msfsCommunityPath'); + const [installPath, setInstallPath] = useSetting('mainSettings.installPath'); + const [tempLocation, setTempLocation] = useSetting('mainSettings.tempLocation'); + const [separateTempLocation, setSeparateTempLocation] = useSetting('mainSettings.separateTempLocation'); + const [disableVersionWarning, setDisableVersionWarning] = useSetting( + 'mainSettings.disableExperimentalWarning', + ); + const [useCdnCache, setUseCdnCache] = useSetting('mainSettings.useCdnCache'); + + return ( +
+
+

Download Settings

+
+ + + + {separateTempLocation && } + +
- ); +
+
+ ); }; - -export default index; diff --git a/src/renderer/components/SettingsSection/General.tsx b/src/renderer/components/SettingsSection/General.tsx index 89e1a496..3c8ae306 100644 --- a/src/renderer/components/SettingsSection/General.tsx +++ b/src/renderer/components/SettingsSection/General.tsx @@ -1,94 +1,86 @@ import React, { FC } from 'react'; -import settings, { useSetting } from "common/settings"; +import settings, { useSetting } from 'common/settings'; import { ipcRenderer } from 'electron'; import { Toggle } from '../Toggle'; -const SettingsItem: FC<{name: string}> = ({ name, children }) => ( -
- {/* TODO: Remove this styling later */} -

{name}

- {children} -
+const SettingsItem: FC<{ name: string }> = ({ name, children }) => ( +
+ {/* TODO: Remove this styling later */} +

{name}

+ {children} +
); interface SettingItemProps { - value: T; - setValue: (value: T) => void; + value: T; + setValue: (value: T) => void; } const AutoStartSettingItem = ({ value, setValue }: SettingItemProps) => { - const handleClick = () => { - const newState = !value; - setValue(newState); - settings.set('mainSettings.autoStartApp', newState); - ipcRenderer.send('request-startup-at-login-changed', newState); - }; + const handleClick = () => { + const newState = !value; + setValue(newState); + settings.set('mainSettings.autoStartApp', newState); + ipcRenderer.send('request-startup-at-login-changed', newState); + }; - return ( - - - - ); + return ( + + + + ); }; const DateLayoutItem = ({ value, setValue }: SettingItemProps) => { - const handleSelect = (value: string) => { - settings.set('mainSettings.dateLayout', value); - setValue(value); - }; + const handleSelect = (value: string) => { + settings.set('mainSettings.dateLayout', value); + setValue(value); + }; - return ( - - - - ); + return ( + + + + ); }; const LongDateFormatItem = ({ value, setValue }: SettingItemProps) => { - const handleClick = (value: boolean) => { - settings.set('mainSettings.useLongDateFormat', value); - setValue(value); - }; + const handleClick = (value: boolean) => { + settings.set('mainSettings.useLongDateFormat', value); + setValue(value); + }; - return ( - - - - ); + return ( + + + + ); }; -const index = (): JSX.Element => { - const [autoStart, setAutoStart] = useSetting('mainSettings.autoStartApp'); - const [dateLayout, setDateLayout] = useSetting('mainSettings.dateLayout'); - const [useLongDate, setUseLongDate] = useSetting('mainSettings.useLongDateFormat'); +export const GeneralSettings = (): JSX.Element => { + const [autoStart, setAutoStart] = useSetting('mainSettings.autoStartApp'); + const [dateLayout, setDateLayout] = useSetting('mainSettings.dateLayout'); + const [useLongDate, setUseLongDate] = useSetting('mainSettings.useLongDateFormat'); - return ( -
-
-

General Settings

-
- - - -
-
+ return ( +
+
+

General Settings

+
+ + +
- ); +
+
+ ); }; - -export default index; diff --git a/src/renderer/components/SettingsSection/index.tsx b/src/renderer/components/SettingsSection/index.tsx index 31359ea7..bf146ef5 100644 --- a/src/renderer/components/SettingsSection/index.tsx +++ b/src/renderer/components/SettingsSection/index.tsx @@ -1,135 +1,122 @@ import React, { FC, useState } from 'react'; -import GeneralSettings from 'renderer/components/SettingsSection/General'; -import { Redirect, Route } from "react-router-dom"; -import { AboutSettings } from "renderer/components/SettingsSection/About"; -import { SideBar, SideBarLink, SideBarTitle } from "renderer/components/SideBar"; -import CustomizationSettings from './Customization'; -import DownloadSettings from './Download'; -import DeveloperSettings from './Developer'; +import { GeneralSettings } from 'renderer/components/SettingsSection/General'; +import { Redirect, Route } from 'react-router-dom'; +import { AboutSettings } from 'renderer/components/SettingsSection/About'; +import { SideBar, SideBarLink, SideBarTitle } from 'renderer/components/SideBar'; +import { CustomizationSettings } from './Customization'; +import { DownloadSettings } from './Download'; +import { DeveloperSettings } from './Developer'; import settings from 'common/settings'; import * as packageInfo from '../../../../package.json'; import { Button, ButtonType } from '../Button'; -import { PromptModal, useModals } from "renderer/components/Modal"; +import { PromptModal, useModals } from 'renderer/components/Modal'; interface InstallButtonProps { - type?: ButtonType, - disabled?: boolean, - className?: string; - onClick?: () => void; + type?: ButtonType; + disabled?: boolean; + className?: string; + onClick?: () => void; } -export const ResetButton: FC = ({ - type = ButtonType.Neutral, - onClick, - children, -}) => ( - +export const ResetButton: FC = ({ type = ButtonType.Neutral, onClick, children }) => ( + ); export const SettingsSection = (): JSX.Element => { - const { showModal } = useModals(); - - const [showDevSettings, setShowDevSettings] = useState(false); - - const handleReset = async () => { - showModal( - { - settings.reset('mainSettings' as never); - - // Workaround to flush the defaults - settings.set('metaInfo.lastVersion', packageInfo.version); - }}/>, - ); - }; - - return ( -
-
- -
{ - if (event.ctrlKey && event.altKey) { - setShowDevSettings((old) => !old); - } - }}> - Settings -
- - - - General - - - - - - Download - - - - {/**/} - {/* */} - {/* Customization*/} - {/* */} - {/**/} - - {showDevSettings && ( - - - Developer - - - )} - - - - About - - -
- - Reset Settings - -
-
- -
- - - - - - - - - - - - - - - - - {showDevSettings && ( - - - - )} - - - - -
-
-
+ const { showModal } = useModals(); + + const [showDevSettings, setShowDevSettings] = useState(false); + + const handleReset = async () => { + showModal( + { + settings.reset('mainSettings' as never); + + // Workaround to flush the defaults + settings.set('metaInfo.lastVersion', packageInfo.version); + }} + />, ); + }; + + return ( +
+
+ +
{ + if (event.ctrlKey && event.altKey) { + setShowDevSettings((old) => !old); + } + }} + > + Settings +
+ + + General + + + + Download + + + {/**/} + {/* */} + {/* Customization*/} + {/* */} + {/**/} + + {showDevSettings && ( + + Developer + + )} + + + About + +
+ + Reset Settings + +
+
+ +
+ + + + + + + + + + + + + + + + + {showDevSettings && ( + + + + )} + + + + +
+
+
+ ); }; diff --git a/src/renderer/components/SideBar/index.tsx b/src/renderer/components/SideBar/index.tsx index 49a89c64..e42456dd 100644 --- a/src/renderer/components/SideBar/index.tsx +++ b/src/renderer/components/SideBar/index.tsx @@ -1,73 +1,84 @@ -import React, { FC } from "react"; -import { useIsDarkTheme } from "common/settings"; -import { NavLink, useRouteMatch } from "react-router-dom"; +import React, { FC } from 'react'; +import { useIsDarkTheme } from 'common/settings'; +import { NavLink, useRouteMatch } from 'react-router-dom'; export interface SideBarProps { - className?: string; + className?: string; } export const SideBar: FC = ({ className, children }) => { - const darkTheme = useIsDarkTheme(); - - const textClass = darkTheme ? 'text-quasi-white' : 'text-navy'; - - return ( -
- {children} -
- ); + const darkTheme = useIsDarkTheme(); + + const textClass = darkTheme ? 'text-quasi-white' : 'text-navy'; + + return ( +
+ {children} +
+ ); }; export interface SideBarItemProps { - enabled?: boolean; - selected?: boolean; - onClick?: () => void; - className?: string; + enabled?: boolean; + selected?: boolean; + onClick?: () => void; + className?: string; } -export const SideBarItem: FC = ({ enabled = true, selected = false, onClick = () => {}, className, children }) => { - const darkTheme = useIsDarkTheme(); - - const defaultBorderStyle = darkTheme ? 'border-navy-dark' : 'border-quasi-white'; - - const enabledUnselectedStyle = darkTheme ? 'bg-navy-dark border-navy-light text-quasi-white' : 'bg-grey-medium text-navy'; - - const dependantStyles = selected ? ` bg-dodger-light text-navy-dark` : `${enabledUnselectedStyle} ${enabled && 'hover:border-dodger-light'}`; - - return ( -
- {children} -
- ); +export const SideBarItem: FC = ({ + enabled = true, + selected = false, + onClick = () => {}, + className, + children, +}) => { + const darkTheme = useIsDarkTheme(); + + const defaultBorderStyle = darkTheme ? 'border-navy-dark' : 'border-quasi-white'; + + const enabledUnselectedStyle = darkTheme + ? 'bg-navy-dark border-navy-light text-quasi-white' + : 'bg-grey-medium text-navy'; + + const dependantStyles = selected + ? ` bg-dodger-light text-navy-dark` + : `${enabledUnselectedStyle} ${enabled && 'hover:border-dodger-light'}`; + + return ( +
+ {children} +
+ ); }; export const SideBarTitle: FC = ({ children }) => { - const darkTheme = useIsDarkTheme(); + const darkTheme = useIsDarkTheme(); - const textClass = darkTheme ? 'text-quasi-white' : 'text-navy'; + const textClass = darkTheme ? 'text-quasi-white' : 'text-navy'; - return ( -
-

{children}

-
- ); + return ( +
+

{children}

+
+ ); }; export interface SideBarLinkProps { - to: string; + to: string; } export const SideBarLink: FC = ({ to, children }) => { - const match = useRouteMatch(to); - - return ( - - - {children} - - - ); + const match = useRouteMatch(to); + + return ( + + {children} + + ); }; diff --git a/src/renderer/components/Toggle.tsx b/src/renderer/components/Toggle.tsx index aed0e2f8..35e02277 100644 --- a/src/renderer/components/Toggle.tsx +++ b/src/renderer/components/Toggle.tsx @@ -1,15 +1,28 @@ import React, { FC } from 'react'; interface ToggleProps { - value: boolean; - onToggle: (value: boolean) => void; - scale?: number; - bgColor?: string, - onColor?: string; + value: boolean; + onToggle: (value: boolean) => void; + scale?: number; + bgColor?: string; + onColor?: string; } -export const Toggle: FC = ({ value, onToggle, scale = 1, bgColor = 'bg-navy-light', onColor = 'bg-cyan-medium' }) => ( -
onToggle(!value)} style={{ transform: `scale(${scale})` }}> -
-
+export const Toggle: FC = ({ + value, + onToggle, + scale = 1, + bgColor = 'bg-navy-light', + onColor = 'bg-cyan-medium', +}) => ( +
onToggle(!value)} + style={{ transform: `scale(${scale})` }} + > +
+
); diff --git a/src/renderer/components/WindowActionButtons/index.tsx b/src/renderer/components/WindowActionButtons/index.tsx index 9b86ae12..3e5e2862 100644 --- a/src/renderer/components/WindowActionButtons/index.tsx +++ b/src/renderer/components/WindowActionButtons/index.tsx @@ -4,59 +4,67 @@ import { ipcRenderer } from 'electron'; import { WindowsControl } from 'react-windows-controls'; import channels from 'common/channels'; import { Directories } from 'renderer/utils/Directories'; -import { store } from "renderer/redux/store"; -import { InstallStatusCategories } from "renderer/components/AddonSection/Enums"; -import { AlertModal, useModals } from "renderer/components/Modal"; +import { store } from 'renderer/redux/store'; +import { InstallStatusCategories } from 'renderer/components/AddonSection/Enums'; +import { AlertModal, useModals } from 'renderer/components/Modal'; import { ExclamationCircle } from 'react-bootstrap-icons'; -export type ButtonProps = { id?: string, className?: string, onClick?: () => void, isClose?: boolean } +export type ButtonProps = { id?: string; className?: string; onClick?: () => void; isClose?: boolean }; export const Button: React.FC = ({ id, className, onClick, isClose, children }) => { - return ( - - ); + return ( + + ); }; export const WindowButtons: React.FC = () => { - const { showModal } = useModals(); - - const openGithub = () => shell.openExternal("https://github.com/flybywiresim/a32nx/issues/new/choose"); - - const handleMinimize = () => { - ipcRenderer.send(channels.window.minimize); - }; - - const handleMaximize = () => { - ipcRenderer.send(channels.window.maximize); - }; - - const handleClose = () => { - const installStatuses = store.getState().installStatus; - - const anyInstalling = Object.values(installStatuses).some((it) => InstallStatusCategories.installing.includes(it.status)); - - if (anyInstalling) { - showModal( - , - ); - } else { - Directories.removeAllTemp(); - ipcRenderer.send(channels.window.close); - } - }; - - return ( -
- - - - -
+ const { showModal } = useModals(); + + const openGithub = () => shell.openExternal('https://github.com/flybywiresim/a32nx/issues/new/choose'); + + const handleMinimize = () => { + ipcRenderer.send(channels.window.minimize); + }; + + const handleMaximize = () => { + ipcRenderer.send(channels.window.maximize); + }; + + const handleClose = () => { + const installStatuses = store.getState().installStatus; + + const anyInstalling = Object.values(installStatuses).some((it) => + InstallStatusCategories.installing.includes(it.status), ); + + if (anyInstalling) { + showModal( + , + ); + } else { + Directories.removeAllTemp(); + ipcRenderer.send(channels.window.close); + } + }; + + return ( +
+ + + + +
+ ); }; diff --git a/src/renderer/data.ts b/src/renderer/data.ts index a2101b8a..48daca1d 100644 --- a/src/renderer/data.ts +++ b/src/renderer/data.ts @@ -1,572 +1,613 @@ -import { Configuration } from "./utils/InstallerConfiguration"; +import { Configuration } from './utils/InstallerConfiguration'; export const defaultConfiguration: Configuration = { - version: 1, - publishers: [ + version: 1, + publishers: [ + { + name: 'FlyByWire Simulations', + key: 'flybywiresim', + logoUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/publisher-icons/flybywiresim/0.svg', + defs: [ { - name: 'FlyByWire Simulations', - key: 'flybywiresim', - logoUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/publisher-icons/flybywiresim/0.svg', - defs: [ - { - kind: 'addonCategory', - key: 'aircraft', - title: 'Aircraft', - }, - { - kind: 'addonCategory', - key: 'scenery', - title: 'Scenery', - }, - { - kind: 'addonCategory', - key: 'simbridge', - styles: ['align-bottom'], - }, - { - kind: 'externalApp', - key: 'mcdu-server', - prettyName: 'MCDU Server', - detectionType: 'ws', - url: 'ws://localhost:8380', - }, - { - kind: 'externalApp', - key: 'simbridge-app', - prettyName: 'SimBridge', - detectionType: 'http', - url: 'http://localhost:8380/health', - killUrl: 'http://localhost:8380/health/kill', - killMethod: 'GET', - }, - { - kind: 'externalApp', - key: 'msfs', - prettyName: 'MSFS', - detectionType: 'tcp', - port: 500, - }, - ], - addons: [ - { - key: 'A32NX', - name: 'A32NX', - repoOwner: 'flybywiresim', - repoName: 'a32nx', - category: '@aircraft', - aircraftName: 'A320-251N', - titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-a32nx/dark.svg', - titleImageUrlSelected: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-a32nx/light.svg', - enabled: true, - // TODO: Change this - backgroundImageUrls: ['https://flybywiresim.b-cdn.net/installer/media-assets/addon-headers/fbw-a32nx/1.png'], - shortDescription: 'Airbus A320neo Series', - description: 'The A320neo (new engine option) is one of many upgrades introduced by Airbus to help maintain ' + - 'its A320 product line’s position as the world’s most advanced and fuel-efficient single-aisle ' + - 'aircraft family. The baseline A320neo jetliner has a choice of two new-generation engines ' + - '(the PurePower PW1100G-JM from Pratt and Whitney and the LEAP-1A from CFM International) ' + - 'and features large, fuel-saving wingtip devices known as Sharklets.', - techSpecs: [ - { - name: 'Engines', - value: 'CFM LEAP 1A-26', - }, - { - name: 'APU', - value: 'APS3200', - }, - ], - targetDirectory: 'flybywire-aircraft-a320-neo', - alternativeNames: [ - 'A32NX', - 'a32nx', - ], - tracks: [ - { - name: 'Stable', - key: 'a32nx-stable', - url: 'https://flybywirecdn.com/addons/a32nx/stable', - alternativeUrls: [ - 'external/a32nx/stable', - // move bunnycdn users to cloudflare - 'https://cdn.flybywiresim.com/addons/a32nx/stable', - ], - description: 'Stable is our variant that has the least bugs and best performance. ' + - 'This version will not always be up to date but we guarantee its compatibility ' + - 'with each major patch from MSFS.', - isExperimental: false, - releaseModel: { - type: 'githubRelease', - }, - }, - { - name: 'Development', - key: 'a32nx-dev', - url: 'https://flybywirecdn.com/addons/a32nx/master', - alternativeUrls: [ - 'external/a32nx/master', - // move old experimental users over to dev - 'https://cdn.flybywiresim.com/addons/a32nx/cfbw-cap', - 'https://cdn.flybywiresim.com/addons/a32nx/cfbw', - // move bunnycdn users to cloudflare - 'https://cdn.flybywiresim.com/addons/a32nx/master', - ], - description: 'Development will have the latest features that will end up in the next stable. ' + - 'Bugs are to be expected. It updates whenever something is added to the \'master\' ' + - 'branch on Github. Please visit our discord for support.', - isExperimental: false, - releaseModel: { - type: 'githubBranch', - branch: 'master', - }, - }, - { - name: 'Experimental', - key: 'experimental', - url: 'https://flybywirecdn.com/addons/a32nx/experimental', - alternativeUrls: [ - 'external/a32nx/experimental', - 'https://cdn.flybywiresim.com/addons/a32nx/experimental', - 'https://github.com/flybywiresim/a32nx/releases/download/assets/experimental/', - ], - description: 'This version is similar to the Development version, ' + - 'but contains features that we wish to test publicly as we perfect them. ' + - 'The build is in practice stable most of the time, but you may encounter flight-breaking bugs, ' + - 'performance loss, crashes or other issues as the features present in this version are not completely finished. ' + - 'Not advised for flying on online networks.', - isExperimental: true, - warningContent: 'The experimental version contains custom systems that more closely matches ' + - 'real-life behaviour of an A320neo. Those are in development and bugs are to be expected.\n\n' + - 'To understand what you are getting into and the potential issues you might experience, ' + - 'please read [this guide](https://docs.flybywiresim.com/fbw-a32nx/support/exp/).\n\n' + - '**Please be aware that no support will be offered via Discord support channels.**', - releaseModel: { - type: 'githubBranch', - branch: 'experimental', - }, - }, - ], - dependencies: [ - { - addon: '@flybywiresim/simbridge', - optional: true, - modalText: 'SimBridge allows the A32NX to expose remote tools like the Web MCDU, as well as use the external terrain database.', - }, - ], - incompatibleAddons: [ - // title: the exact title as it appears in the manifest.json - // creator: the exact creator as it appears in the manifest.json - // packageVersion syntax follows: https://www.npmjs.com/package/semver - // description: a short description of why the addon is incompatible - { - title: 'Horizon Simulations A319ceo', - packageVersion: '<0.6.1', - description: "It is recommended to upgrade to the latest version (0.6.1 or later) or to remove this add-on before installing and using the A32NX. The older versions of this add-on are known to override A32NX components and to break the A32NX.", - }, - { - title: 'Horizon Simulations A321neo', - // packageVersion: '<0.4.0', see https://discord.com/channels/738864299392630914/785976111875751956/1055617417189011546 - description: "It is recommended to remove this add-on before installing and using the A32NX. This add-on is known to override A32NX components and cause unexpected behavior and issues when flying the A32NX.", - }, - { - title: 'LVFR A321neo FBW A32NX Compatibility Mod', - description: "It is recommended to remove this add-on before installing and using the A32NX. This add-on is known to override A32NX components and to break the A32NX.", - }, - { - title: 'LVFR A321neo Extreme', - description: "It is recommended to remove this add-on before installing and using the A32NX. This add-on is known to override A32NX components and cause unexpected behavior and issues when flying the A32NX.", - }, - { - title: 'lvfr-airbus-a319-fbw-standalone', - packageVersion: '<0.6.1', - description: "It is recommended to upgrade to the latest version (0.6.1 or later) or to remove this add-on before installing and using the A32NX. The older versions of this add-on are known to override A32NX components and to break the A32NX.", - }, - { - title: '[MOD] Mugz FBW A32NX Dev', - description: "It is required to remove this add-on before installing and using the A32NX. This add-on overrides A32NX components and renders the A32NX unusable.", - }, - { - title: '[MOD] Mugz FBW A32NX Stable', - description: "It is required to remove this add-on before installing and using the A32NX. This add-on overrides A32NX components and renders the A32NX unusable.", - }, - { - title: 'Toolbar Pushback', - creator: "AmbitiousPilots", - description: "This add-on sometimes causes performance issues and also sometimes prevents the A32NX from taxiing. Consider removing it if you experience these issues.", - }, - { - title: 'Asobo_A320_A (A32NX Converted)', - creator: "UnitDeath", - description: "It is required to remove this livery before installing and using the A32NX as it breaks the A32NX", - }, - ], - myInstallPage: { - links: [ - { - url: 'https://docs.flybywiresim.com/fbw-a32nx/', - title: 'Documentation', - }, - ], - directories: [ - { - location: { - in: 'packageCache', - path: 'work', - }, - title: 'Work Folder', - }, - ], - }, - disallowedRunningExternalApps: ['@/msfs', '@/mcdu-server'], - }, - { - name: 'A380X', - key: 'A380X', - repoOwner: 'flybywiresim', - repoName: 'a380x', - category: '@aircraft', - aircraftName: 'A380-841', - titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-a380x/dark.svg', - titleImageUrlSelected: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-a380x/light.svg', - enabled: false, - backgroundImageUrls: ['https://nyc3.digitaloceanspaces.com/fselite/2020/11/123263426_126778999193686_7966913238295950901_o.png'], - shortDescription: 'Airbus A380-800', - description: '', - targetDirectory: 'A380', - tracks: [], - disallowedRunningExternalApps: ['@/msfs'], - }, - { - name: 'KFBW', - key: 'KFBW', - category: '@scenery', - aircraftName: 'FBW Headquarters', - enabled: true, - overrideAddonWhileHidden: 'A380X', - backgroundImageUrls: ['https://flybywiresim.b-cdn.net/installer/media-assets/addon-headers/fbw-kfbw/0.png'], - titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-kfbw/dark.svg', - titleImageUrlSelected: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-kfbw/light.svg', - shortDescription: 'FlyByWire Headquarters', - description: 'Welcome to KFBW! \n\n' + - 'This is a showcase of the A380 project. Spawn at KFBW or fly there! The nearest airport is KTNP (Twenty-Nine Palms, California, USA). ' + - 'There is an ILS without waypoints to Runway 10. Freq: 108.9 CRS: 100 \n' + - 'The airport is designed to be used by our developers for flight testing of the A380 and also designed to match the real-world A380 testing airport in Hamburg, Germany (EDHI). \n' + - 'The location allows for quick test flights to LAX, which is also serviced by the A380. \n' + - 'Use the developer or drone camera to explore! \n\n' + - 'Happy holidays and enjoy! -FBW Team', - targetDirectory: 'flybywire-airport-kfbw-flybywire-field', - tracks: [ - { - name: 'Release', - key: 'kfbw-release', - url: 'https://cdn.flybywiresim.com/addons/kfbw/release/', - isExperimental: false, - releaseModel: { - type: 'CDN', - }, - description: 'FlyByWire Headquarters is transformed into a winter wonderland - complete with a plethora of festive decorations in addition to the standard progress showcase.', - }, - ], - }, - { - name: 'SimBridge', - key: 'simbridge', - category: '@simbridge', - repoOwner: 'flybywiresim', - repoName: 'simbridge', - aircraftName: 'FBW SimBridge', - titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-simbridge/dark.svg', - titleImageUrlSelected: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-simbridge/light.svg', - enabled: true, - backgroundImageUrls: ['https://flybywiresim.b-cdn.net/installer/media-assets/addon-headers/fbw-simbridge/0.png'], - backgroundImageShadow: false, - shortDescription: 'Airbus A380-800', - description: 'SimBridge is an external application which allows FBW aircraft to communicate with components located outside the simulator. SimBridge will be used for a number of features requiring external data (such as TAWS terrain display), as well as for functionality providing remote access to aircraft systems or data.', - targetDirectory: 'flybywire-externaltools-simbridge', - tracks: [ - { - name: 'Release', - key: 'release', - releaseModel: { - type: 'githubRelease', - }, - url: 'https://cdn.flybywiresim.com/addons/simbridge/release/', - isExperimental: false, - description: 'SimBridge is an external app that enables FlyByWire Simulations aircraft to communicate outside your simulator. From remote displays to external terrain display rendering, it is used for a variety of optional features.', - }, - ], - disallowedRunningExternalApps: ['@/simbridge-app'], - backgroundService: { - executableFileBasename: 'fbw-simbridge', - runCheckExternalAppRef: '@/simbridge-app', - commandLineArgs: ['--hide'], - }, - myInstallPage: { - links: [ - { - url: 'https://docs.flybywiresim.com/simbridge/', - title: 'Documentation', - }, - ], - directories: [ - { - location: { - in: 'package', - path: 'resources', - }, - title: 'Resources', - }, - ], - }, - }, - ], - buttons: [ - { - text: 'Documentation', - action: 'openBrowser', - url: 'https://docs.flybywiresim.com/', - }, - { - text: 'Website', - action: 'openBrowser', - url: 'https://flybywiresim.com/', - }, - { - text: 'Discord', - action: 'openBrowser', - url: 'https://discord.gg/flybywire', - }, - { - text: 'Twitter', - action: 'openBrowser', - url: 'https://twitter.com/FlyByWireSim', - inline: true, - }, - ], + kind: 'addonCategory', + key: 'aircraft', + title: 'Aircraft', }, { - name: 'Salty Simulations', - key: 'salty', - logoUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/publisher-icons/salty/0.svg', - defs: [ - { - kind: 'addonCategory', - key: 'aircraft', - title: 'Aircraft', - }, - ], - addons: [ - { - key: '74S', - name: '74S', - repoOwner: 'saltysimulations', - repoName: 'salty-747', - category: '@aircraft', - aircraftName: 'B747-8I', - titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/salty-74S/dark.svg', - titleImageUrlSelected: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/salty-74S/light.svg', - enabled: true, - backgroundImageUrls: ['https://raw.githubusercontent.com/saltysimulations/branding/main/png/salty_banner.png'], - shortDescription: 'Boeing 747-8I', - description: - 'The Boeing 747-8 is the largest variant of the 747. ' + - 'It features a thicker and wider wing, allowing it to hold more fuel, as well as raked wingtips. ' + - 'The aircraft, powered by the more efficient General Electric GEnx engines, ' + - 'can carry 467 passengers in a typical three-class configuration, and has a range of 7,730 nautical miles.', - techSpecs: [ - { - name: 'Engines', - value: 'GEnx-2B', - }, - ], - targetDirectory: 'salty-747', - tracks: [ - { - name: 'Stable', - key: '74S-stable', - url: 'https://github.com/saltysimulations/salty-747/releases/download/vinstaller-stable/', - description: 'Stable is our variant that has the least bugs and best performance. ' + - 'This version will not always be up to date but we guarantee its compatibility ' + - 'with each major patch from MSFS.', - isExperimental: false, - releaseModel: { - type: 'githubRelease', - }, - }, - { - name: 'Development', - key: '74S-dev', - url: 'https://github.com/saltysimulations/salty-747/releases/download/vinstaller/', - description: - 'The development version has all the latest features that will end up in the next stable. ' + - 'You may encounter bugs more frequently.', - isExperimental: false, - releaseModel: { - type: 'githubBranch', - branch: 'master', - }, - }, - ], - }, - ], - buttons: [ - { - text: 'Discord', - action: 'openBrowser', - url: 'https://discord.gg/S4PJDwk', - }, - { - text: 'Twitter', - action: 'openBrowser', - url: 'https://twitter.com/Salty_Sim', - inline: true, - }, - ], + kind: 'addonCategory', + key: 'scenery', + title: 'Scenery', }, { - name: 'FSLTL', - key: 'fsltl', - logoUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/publisher-icons/fsltl/fsltl-test-2.png', - logoSize: 36, - defs: [ - { - kind: 'externalApp', - key: 'traffic-injector-app', - prettyName: 'FSLTL Traffic Injector', - detectionType: 'http', - url: 'http://localhost:42888', - killUrl: 'http://localhost:42888/kill', - killMethod: 'POST', - }, - { - kind: 'externalApp', - key: 'msfs', - prettyName: 'MSFS', - detectionType: 'tcp', - port: 500, - }, - ], - addons: [ - { - key: 'traffic-base-models', - name: 'FSLTL Traffic', - aircraftName: 'FSLTL Traffic', - titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fsltl/base-models/dark.svg', - titleImageUrlSelected: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fsltl/base-models/light.svg', - enabled: true, - backgroundImageUrls: ['https://flybywiresim.b-cdn.net/installer/media-assets/addon-headers/fsltl/traffic/0.png'], - shortDescription: 'FSLTL Traffic Base Models', - description: 'FSLTL is a free standalone real-time online traffic overhaul and VATSIM model-matching solution for MSFS. Utilising native glTF models and MSFS independent online IFR/VFR traffic injection system with stock ATC interaction based on Flightradar24.\n', - targetDirectory: 'fsltl-traffic-base', - alternativeNames: [], - tracks: [ - { - name: 'Release', - key: 'release', - url: 'https://github.com/FSLiveTrafficLiveries/base/releases/latest/download/', - isExperimental: false, - releaseModel: { - type: 'CDN', - }, - description: 'FSLTL is a free standalone real-time online traffic overhaul and VATSIM model-matching solution for MSFS. Utilising native glTF models and MSFS independent online IFR/VFR traffic injection system with stock ATC interaction based on Flightradar24.\n', - }, - ], - disallowedRunningExternalApps: ['@/msfs'], - }, - { - key: 'traffic-injector', - name: 'FSLTL Injector', - aircraftName: 'FSLTL Traffic', - titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fsltl/injector/dark.svg', - titleImageUrlSelected: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fsltl/injector/light.svg', - enabled: true, - backgroundImageUrls: ['https://flybywiresim.b-cdn.net/installer/media-assets/addon-headers/fsltl/traffic/0.png'], - shortDescription: 'FSLTL Traffic Injector Software', - description: 'FSLTL Live Traffic Injector - giving you a more immersive experience at airports globally!\n\n* Live IFR and VFR traffic based on Flightradar24\n* Parked aircraft based on historic real data for immersive full airports\n* Ability to have any combination of IFR, VFR and parked aircraft', - targetDirectory: 'fsltl-traffic-injector', - tracks: [ - { - name: 'Release', - key: 'release', - url: 'https://packages.fslivetrafficliveries.com/injector/', - isExperimental: false, - releaseModel: { - type: 'CDN', - }, - description: 'FSLTL Live Traffic Injector - giving you a more immersive experience at airports globally!\n\n* Live IFR and VFR traffic based on Flightradar24\n* Parked aircraft based on historic real data for immersive full airports\n* Ability to have any combination of IFR, VFR and parked aircraft', - }, - ], - backgroundService: { - executableFileBasename: 'fsltl-trafficinjector', - runCheckExternalAppRef: '@/traffic-injector-app', - enableAutostartConfiguration: false, - }, - disallowedRunningExternalApps: ['@/traffic-injector-app'], - }, + kind: 'addonCategory', + key: 'simbridge', + styles: ['align-bottom'], + }, + { + kind: 'externalApp', + key: 'mcdu-server', + prettyName: 'MCDU Server', + detectionType: 'ws', + url: 'ws://localhost:8380', + }, + { + kind: 'externalApp', + key: 'simbridge-app', + prettyName: 'SimBridge', + detectionType: 'http', + url: 'http://localhost:8380/health', + killUrl: 'http://localhost:8380/health/kill', + killMethod: 'GET', + }, + { + kind: 'externalApp', + key: 'msfs', + prettyName: 'MSFS', + detectionType: 'tcp', + port: 500, + }, + ], + addons: [ + { + key: 'A32NX', + name: 'A32NX', + repoOwner: 'flybywiresim', + repoName: 'a32nx', + category: '@aircraft', + aircraftName: 'A320-251N', + titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-a32nx/dark.svg', + titleImageUrlSelected: + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-a32nx/light.svg', + enabled: true, + // TODO: Change this + backgroundImageUrls: ['https://flybywiresim.b-cdn.net/installer/media-assets/addon-headers/fbw-a32nx/1.png'], + shortDescription: 'Airbus A320neo Series', + description: + 'The A320neo (new engine option) is one of many upgrades introduced by Airbus to help maintain ' + + 'its A320 product line’s position as the world’s most advanced and fuel-efficient single-aisle ' + + 'aircraft family. The baseline A320neo jetliner has a choice of two new-generation engines ' + + '(the PurePower PW1100G-JM from Pratt and Whitney and the LEAP-1A from CFM International) ' + + 'and features large, fuel-saving wingtip devices known as Sharklets.', + techSpecs: [ + { + name: 'Engines', + value: 'CFM LEAP 1A-26', + }, + { + name: 'APU', + value: 'APS3200', + }, + ], + targetDirectory: 'flybywire-aircraft-a320-neo', + alternativeNames: ['A32NX', 'a32nx'], + tracks: [ + { + name: 'Stable', + key: 'a32nx-stable', + url: 'https://flybywirecdn.com/addons/a32nx/stable', + alternativeUrls: [ + 'external/a32nx/stable', + // move bunnycdn users to cloudflare + 'https://cdn.flybywiresim.com/addons/a32nx/stable', + ], + description: + 'Stable is our variant that has the least bugs and best performance. ' + + 'This version will not always be up to date but we guarantee its compatibility ' + + 'with each major patch from MSFS.', + isExperimental: false, + releaseModel: { + type: 'githubRelease', + }, + }, + { + name: 'Development', + key: 'a32nx-dev', + url: 'https://flybywirecdn.com/addons/a32nx/master', + alternativeUrls: [ + 'external/a32nx/master', + // move old experimental users over to dev + 'https://cdn.flybywiresim.com/addons/a32nx/cfbw-cap', + 'https://cdn.flybywiresim.com/addons/a32nx/cfbw', + // move bunnycdn users to cloudflare + 'https://cdn.flybywiresim.com/addons/a32nx/master', + ], + description: + 'Development will have the latest features that will end up in the next stable. ' + + "Bugs are to be expected. It updates whenever something is added to the 'master' " + + 'branch on Github. Please visit our discord for support.', + isExperimental: false, + releaseModel: { + type: 'githubBranch', + branch: 'master', + }, + }, + { + name: 'Experimental', + key: 'experimental', + url: 'https://flybywirecdn.com/addons/a32nx/experimental', + alternativeUrls: [ + 'external/a32nx/experimental', + 'https://cdn.flybywiresim.com/addons/a32nx/experimental', + 'https://github.com/flybywiresim/a32nx/releases/download/assets/experimental/', + ], + description: + 'This version is similar to the Development version, ' + + 'but contains features that we wish to test publicly as we perfect them. ' + + 'The build is in practice stable most of the time, but you may encounter flight-breaking bugs, ' + + 'performance loss, crashes or other issues as the features present in this version are not completely finished. ' + + 'Not advised for flying on online networks.', + isExperimental: true, + warningContent: + 'The experimental version contains custom systems that more closely matches ' + + 'real-life behaviour of an A320neo. Those are in development and bugs are to be expected.\n\n' + + 'To understand what you are getting into and the potential issues you might experience, ' + + 'please read [this guide](https://docs.flybywiresim.com/fbw-a32nx/support/exp/).\n\n' + + '**Please be aware that no support will be offered via Discord support channels.**', + releaseModel: { + type: 'githubBranch', + branch: 'experimental', + }, + }, + ], + dependencies: [ + { + addon: '@flybywiresim/simbridge', + optional: true, + modalText: + 'SimBridge allows the A32NX to expose remote tools like the Web MCDU, as well as use the external terrain database.', + }, + ], + incompatibleAddons: [ + // title: the exact title as it appears in the manifest.json + // creator: the exact creator as it appears in the manifest.json + // packageVersion syntax follows: https://www.npmjs.com/package/semver + // description: a short description of why the addon is incompatible + { + title: 'Horizon Simulations A319ceo', + packageVersion: '<0.6.1', + description: + 'It is recommended to upgrade to the latest version (0.6.1 or later) or to remove this add-on before installing and using the A32NX. The older versions of this add-on are known to override A32NX components and to break the A32NX.', + }, + { + title: 'Horizon Simulations A321neo', + // packageVersion: '<0.4.0', see https://discord.com/channels/738864299392630914/785976111875751956/1055617417189011546 + description: + 'It is recommended to remove this add-on before installing and using the A32NX. This add-on is known to override A32NX components and cause unexpected behavior and issues when flying the A32NX.', + }, + { + title: 'LVFR A321neo FBW A32NX Compatibility Mod', + description: + 'It is recommended to remove this add-on before installing and using the A32NX. This add-on is known to override A32NX components and to break the A32NX.', + }, + { + title: 'LVFR A321neo Extreme', + description: + 'It is recommended to remove this add-on before installing and using the A32NX. This add-on is known to override A32NX components and cause unexpected behavior and issues when flying the A32NX.', + }, + { + title: 'lvfr-airbus-a319-fbw-standalone', + packageVersion: '<0.6.1', + description: + 'It is recommended to upgrade to the latest version (0.6.1 or later) or to remove this add-on before installing and using the A32NX. The older versions of this add-on are known to override A32NX components and to break the A32NX.', + }, + { + title: '[MOD] Mugz FBW A32NX Dev', + description: + 'It is required to remove this add-on before installing and using the A32NX. This add-on overrides A32NX components and renders the A32NX unusable.', + }, + { + title: '[MOD] Mugz FBW A32NX Stable', + description: + 'It is required to remove this add-on before installing and using the A32NX. This add-on overrides A32NX components and renders the A32NX unusable.', + }, + { + title: 'Toolbar Pushback', + creator: 'AmbitiousPilots', + description: + 'This add-on sometimes causes performance issues and also sometimes prevents the A32NX from taxiing. Consider removing it if you experience these issues.', + }, + { + title: 'Asobo_A320_A (A32NX Converted)', + creator: 'UnitDeath', + description: + 'It is required to remove this livery before installing and using the A32NX as it breaks the A32NX', + }, + ], + myInstallPage: { + links: [ + { + url: 'https://docs.flybywiresim.com/fbw-a32nx/', + title: 'Documentation', + }, ], - buttons: [ - { - text: 'Website', - action: 'openBrowser', - url: 'https://www.fslivetrafficliveries.com/', - }, - { - text: 'Discord', - action: 'openBrowser', - url: 'https://discord.gg/suMR56wCrn', - inline: true, + directories: [ + { + location: { + in: 'packageCache', + path: 'work', }, + title: 'Work Folder', + }, ], + }, + disallowedRunningExternalApps: ['@/msfs', '@/mcdu-server'], }, { - name: 'Synaptic Simulations', - key: 'synaptic', - logoUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/publisher-icons/synaptic/0.png', - defs: [ - { - kind: 'addonCategory', - key: 'aircraft', - title: 'Aircraft', - }, - ], - addons: [ - { - name: 'A22X', - repoOwner: 'Synaptic-Simulations', - repoName: 'a22x', - category: '@aircraft', - aircraftName: 'A220-300', - titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/synaptic-a22x/dark.svg', - titleImageUrlSelected: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/synaptic-a22x/light.svg', - key: 'A22X', - enabled: false, - backgroundImageUrls: ['https://nyc3.digitaloceanspaces.com/fselite/2020/11/123263426_126778999193686_7966913238295950901_o.png'], - shortDescription: 'Airbus A220-300 (CSeries 300)', - description: '', - targetDirectory: 'A22X', - alternativeNames: [], - tracks: [], - }, + name: 'A380X', + key: 'A380X', + repoOwner: 'flybywiresim', + repoName: 'a380x', + category: '@aircraft', + aircraftName: 'A380-841', + titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-a380x/dark.svg', + titleImageUrlSelected: + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-a380x/light.svg', + enabled: false, + backgroundImageUrls: [ + 'https://nyc3.digitaloceanspaces.com/fselite/2020/11/123263426_126778999193686_7966913238295950901_o.png', + ], + shortDescription: 'Airbus A380-800', + description: '', + targetDirectory: 'A380', + tracks: [], + disallowedRunningExternalApps: ['@/msfs'], + }, + { + name: 'KFBW', + key: 'KFBW', + category: '@scenery', + aircraftName: 'FBW Headquarters', + enabled: true, + overrideAddonWhileHidden: 'A380X', + backgroundImageUrls: ['https://flybywiresim.b-cdn.net/installer/media-assets/addon-headers/fbw-kfbw/0.png'], + titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-kfbw/dark.svg', + titleImageUrlSelected: + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-kfbw/light.svg', + shortDescription: 'FlyByWire Headquarters', + description: + 'Welcome to KFBW! \n\n' + + 'This is a showcase of the A380 project. Spawn at KFBW or fly there! The nearest airport is KTNP (Twenty-Nine Palms, California, USA). ' + + 'There is an ILS without waypoints to Runway 10. Freq: 108.9 CRS: 100 \n' + + 'The airport is designed to be used by our developers for flight testing of the A380 and also designed to match the real-world A380 testing airport in Hamburg, Germany (EDHI). \n' + + 'The location allows for quick test flights to LAX, which is also serviced by the A380. \n' + + 'Use the developer or drone camera to explore! \n\n' + + 'Happy holidays and enjoy! -FBW Team', + targetDirectory: 'flybywire-airport-kfbw-flybywire-field', + tracks: [ + { + name: 'Release', + key: 'kfbw-release', + url: 'https://cdn.flybywiresim.com/addons/kfbw/release/', + isExperimental: false, + releaseModel: { + type: 'CDN', + }, + description: + 'FlyByWire Headquarters is transformed into a winter wonderland - complete with a plethora of festive decorations in addition to the standard progress showcase.', + }, + ], + }, + { + name: 'SimBridge', + key: 'simbridge', + category: '@simbridge', + repoOwner: 'flybywiresim', + repoName: 'simbridge', + aircraftName: 'FBW SimBridge', + titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-simbridge/dark.svg', + titleImageUrlSelected: + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fbw-simbridge/light.svg', + enabled: true, + backgroundImageUrls: [ + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-headers/fbw-simbridge/0.png', + ], + backgroundImageShadow: false, + shortDescription: 'Airbus A380-800', + description: + 'SimBridge is an external application which allows FBW aircraft to communicate with components located outside the simulator. SimBridge will be used for a number of features requiring external data (such as TAWS terrain display), as well as for functionality providing remote access to aircraft systems or data.', + targetDirectory: 'flybywire-externaltools-simbridge', + tracks: [ + { + name: 'Release', + key: 'release', + releaseModel: { + type: 'githubRelease', + }, + url: 'https://cdn.flybywiresim.com/addons/simbridge/release/', + isExperimental: false, + description: + 'SimBridge is an external app that enables FlyByWire Simulations aircraft to communicate outside your simulator. From remote displays to external terrain display rendering, it is used for a variety of optional features.', + }, + ], + disallowedRunningExternalApps: ['@/simbridge-app'], + backgroundService: { + executableFileBasename: 'fbw-simbridge', + runCheckExternalAppRef: '@/simbridge-app', + commandLineArgs: ['--hide'], + }, + myInstallPage: { + links: [ + { + url: 'https://docs.flybywiresim.com/simbridge/', + title: 'Documentation', + }, ], - buttons: [ - { - text: 'Website', - action: 'openBrowser', - url: 'https://www.synapticsim.com/', - }, - { - text: 'Discord', - action: 'openBrowser', - url: 'https://discord.gg/acQkSvrePG', - }, - { - text: 'Twitter', - action: 'openBrowser', - url: 'https://twitter.com/synapticsim', - inline: true, - + directories: [ + { + location: { + in: 'package', + path: 'resources', }, + title: 'Resources', + }, ], + }, + }, + ], + buttons: [ + { + text: 'Documentation', + action: 'openBrowser', + url: 'https://docs.flybywiresim.com/', + }, + { + text: 'Website', + action: 'openBrowser', + url: 'https://flybywiresim.com/', + }, + { + text: 'Discord', + action: 'openBrowser', + url: 'https://discord.gg/flybywire', + }, + { + text: 'Twitter', + action: 'openBrowser', + url: 'https://twitter.com/FlyByWireSim', + inline: true, + }, + ], + }, + { + name: 'Salty Simulations', + key: 'salty', + logoUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/publisher-icons/salty/0.svg', + defs: [ + { + kind: 'addonCategory', + key: 'aircraft', + title: 'Aircraft', + }, + ], + addons: [ + { + key: '74S', + name: '74S', + repoOwner: 'saltysimulations', + repoName: 'salty-747', + category: '@aircraft', + aircraftName: 'B747-8I', + titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/salty-74S/dark.svg', + titleImageUrlSelected: + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/salty-74S/light.svg', + enabled: true, + backgroundImageUrls: [ + 'https://raw.githubusercontent.com/saltysimulations/branding/main/png/salty_banner.png', + ], + shortDescription: 'Boeing 747-8I', + description: + 'The Boeing 747-8 is the largest variant of the 747. ' + + 'It features a thicker and wider wing, allowing it to hold more fuel, as well as raked wingtips. ' + + 'The aircraft, powered by the more efficient General Electric GEnx engines, ' + + 'can carry 467 passengers in a typical three-class configuration, and has a range of 7,730 nautical miles.', + techSpecs: [ + { + name: 'Engines', + value: 'GEnx-2B', + }, + ], + targetDirectory: 'salty-747', + tracks: [ + { + name: 'Stable', + key: '74S-stable', + url: 'https://github.com/saltysimulations/salty-747/releases/download/vinstaller-stable/', + description: + 'Stable is our variant that has the least bugs and best performance. ' + + 'This version will not always be up to date but we guarantee its compatibility ' + + 'with each major patch from MSFS.', + isExperimental: false, + releaseModel: { + type: 'githubRelease', + }, + }, + { + name: 'Development', + key: '74S-dev', + url: 'https://github.com/saltysimulations/salty-747/releases/download/vinstaller/', + description: + 'The development version has all the latest features that will end up in the next stable. ' + + 'You may encounter bugs more frequently.', + isExperimental: false, + releaseModel: { + type: 'githubBranch', + branch: 'master', + }, + }, + ], + }, + ], + buttons: [ + { + text: 'Discord', + action: 'openBrowser', + url: 'https://discord.gg/S4PJDwk', + }, + { + text: 'Twitter', + action: 'openBrowser', + url: 'https://twitter.com/Salty_Sim', + inline: true, + }, + ], + }, + { + name: 'FSLTL', + key: 'fsltl', + logoUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/publisher-icons/fsltl/fsltl-test-2.png', + logoSize: 36, + defs: [ + { + kind: 'externalApp', + key: 'traffic-injector-app', + prettyName: 'FSLTL Traffic Injector', + detectionType: 'http', + url: 'http://localhost:42888', + killUrl: 'http://localhost:42888/kill', + killMethod: 'POST', + }, + { + kind: 'externalApp', + key: 'msfs', + prettyName: 'MSFS', + detectionType: 'tcp', + port: 500, + }, + ], + addons: [ + { + key: 'traffic-base-models', + name: 'FSLTL Traffic', + aircraftName: 'FSLTL Traffic', + titleImageUrl: + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fsltl/base-models/dark.svg', + titleImageUrlSelected: + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fsltl/base-models/light.svg', + enabled: true, + backgroundImageUrls: [ + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-headers/fsltl/traffic/0.png', + ], + shortDescription: 'FSLTL Traffic Base Models', + description: + 'FSLTL is a free standalone real-time online traffic overhaul and VATSIM model-matching solution for MSFS. Utilising native glTF models and MSFS independent online IFR/VFR traffic injection system with stock ATC interaction based on Flightradar24.\n', + targetDirectory: 'fsltl-traffic-base', + alternativeNames: [], + tracks: [ + { + name: 'Release', + key: 'release', + url: 'https://github.com/FSLiveTrafficLiveries/base/releases/latest/download/', + isExperimental: false, + releaseModel: { + type: 'CDN', + }, + description: + 'FSLTL is a free standalone real-time online traffic overhaul and VATSIM model-matching solution for MSFS. Utilising native glTF models and MSFS independent online IFR/VFR traffic injection system with stock ATC interaction based on Flightradar24.\n', + }, + ], + disallowedRunningExternalApps: ['@/msfs'], + }, + { + key: 'traffic-injector', + name: 'FSLTL Injector', + aircraftName: 'FSLTL Traffic', + titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fsltl/injector/dark.svg', + titleImageUrlSelected: + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/fsltl/injector/light.svg', + enabled: true, + backgroundImageUrls: [ + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-headers/fsltl/traffic/0.png', + ], + shortDescription: 'FSLTL Traffic Injector Software', + description: + 'FSLTL Live Traffic Injector - giving you a more immersive experience at airports globally!\n\n* Live IFR and VFR traffic based on Flightradar24\n* Parked aircraft based on historic real data for immersive full airports\n* Ability to have any combination of IFR, VFR and parked aircraft', + targetDirectory: 'fsltl-traffic-injector', + tracks: [ + { + name: 'Release', + key: 'release', + url: 'https://packages.fslivetrafficliveries.com/injector/', + isExperimental: false, + releaseModel: { + type: 'CDN', + }, + description: + 'FSLTL Live Traffic Injector - giving you a more immersive experience at airports globally!\n\n* Live IFR and VFR traffic based on Flightradar24\n* Parked aircraft based on historic real data for immersive full airports\n* Ability to have any combination of IFR, VFR and parked aircraft', + }, + ], + backgroundService: { + executableFileBasename: 'fsltl-trafficinjector', + runCheckExternalAppRef: '@/traffic-injector-app', + enableAutostartConfiguration: false, + }, + disallowedRunningExternalApps: ['@/traffic-injector-app'], + }, + ], + buttons: [ + { + text: 'Website', + action: 'openBrowser', + url: 'https://www.fslivetrafficliveries.com/', + }, + { + text: 'Discord', + action: 'openBrowser', + url: 'https://discord.gg/suMR56wCrn', + inline: true, + }, + ], + }, + { + name: 'Synaptic Simulations', + key: 'synaptic', + logoUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/publisher-icons/synaptic/0.png', + defs: [ + { + kind: 'addonCategory', + key: 'aircraft', + title: 'Aircraft', + }, + ], + addons: [ + { + name: 'A22X', + repoOwner: 'Synaptic-Simulations', + repoName: 'a22x', + category: '@aircraft', + aircraftName: 'A220-300', + titleImageUrl: 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/synaptic-a22x/dark.svg', + titleImageUrlSelected: + 'https://flybywiresim.b-cdn.net/installer/media-assets/addon-titles/synaptic-a22x/light.svg', + key: 'A22X', + enabled: false, + backgroundImageUrls: [ + 'https://nyc3.digitaloceanspaces.com/fselite/2020/11/123263426_126778999193686_7966913238295950901_o.png', + ], + shortDescription: 'Airbus A220-300 (CSeries 300)', + description: '', + targetDirectory: 'A22X', + alternativeNames: [], + tracks: [], + }, + ], + buttons: [ + { + text: 'Website', + action: 'openBrowser', + url: 'https://www.synapticsim.com/', + }, + { + text: 'Discord', + action: 'openBrowser', + url: 'https://discord.gg/acQkSvrePG', + }, + { + text: 'Twitter', + action: 'openBrowser', + url: 'https://twitter.com/synapticsim', + inline: true, }, - ], + ], + }, + ], }; diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index dc488dcc..0489c037 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -32,98 +32,103 @@ import { BrowserTracing } from '@sentry/tracing'; import { Provider } from 'react-redux'; import App, { fetchLatestVersionNames } from 'renderer/components/App'; import { Configuration, InstallerConfiguration } from 'renderer/utils/InstallerConfiguration'; -import { ipcRenderer } from "electron"; -import { Directories } from "renderer/utils/Directories"; -import channels from "common/channels"; +import { ipcRenderer } from 'electron'; +import { Directories } from 'renderer/utils/Directories'; +import channels from 'common/channels'; import { MemoryRouter } from 'react-router-dom'; -import { store } from "renderer/redux/store"; +import { store } from 'renderer/redux/store'; import { setConfiguration } from './redux/features/configuration'; -import { GitVersions } from "@flybywiresim/api-client"; -import { addReleases } from "renderer/redux/features/releaseNotes"; -import { ModalProvider } from "renderer/components/Modal"; -import { setSentrySessionID } from "renderer/redux/features/sentrySessionID"; +import { GitVersions } from '@flybywiresim/api-client'; +import { addReleases } from 'renderer/redux/features/releaseNotes'; +import { ModalProvider } from 'renderer/components/Modal'; +import { setSentrySessionID } from 'renderer/redux/features/sentrySessionID'; import packageJson from '../../package.json'; -import 'simplebar/dist/simplebar.min.css'; +import 'simplebar-react/dist/simplebar.min.css'; import './index.scss'; -import { Button, ButtonType } from "renderer/components/Button"; +import { Button, ButtonType } from 'renderer/components/Button'; // Setup Sentry Sentry.init({ - release: packageJson.version, - integrations: [ - new BrowserTracing(), - ], - tracesSampleRate: 1.0, - beforeBreadcrumb: (event) => { - if (event.category === 'fetch' && event.data.url.startsWith('http://localhost')) { - return null; - } + release: packageJson.version, + integrations: [new BrowserTracing()], + tracesSampleRate: 1.0, + beforeBreadcrumb: (event) => { + if (event.category === 'fetch' && event.data.url.startsWith('http://localhost')) { + return null; + } - return event; - }, - // sampleRate: 0.1, + return event; + }, + // sampleRate: 0.1, }); // Request Sentry session ID ipcRenderer.invoke(channels.sentry.requestSessionID).then((sessionID) => { - store.dispatch(setSentrySessionID(sessionID)); + store.dispatch(setSentrySessionID(sessionID)); }); // Obtain configuration and use it -InstallerConfiguration.obtain().then((config: Configuration) => { +InstallerConfiguration.obtain() + .then((config: Configuration) => { store.dispatch(setConfiguration({ configuration: config })); for (const publisher of config.publishers) { - for (const addon of publisher.addons) { - if (addon.repoOwner && addon.repoName) { - GitVersions.getReleases(addon.repoOwner, addon.repoName, false, 0, 5).then(res => { - const content = res.map(release => ({ - name: release.name, - publishedAt: release.publishedAt.getTime(), - htmlUrl: release.htmlUrl, - body: release.body, - })); + for (const addon of publisher.addons) { + if (addon.repoOwner && addon.repoName) { + GitVersions.getReleases(addon.repoOwner, addon.repoName, false, 0, 5).then((res) => { + const content = res.map((release) => ({ + name: release.name, + publishedAt: release.publishedAt.getTime(), + htmlUrl: release.htmlUrl, + body: release.body, + })); - if (content.length) { - store.dispatch(addReleases({ key: addon.key, releases: content })); - } else { - store.dispatch(addReleases({ key: addon.key, releases: [] })); - } - }); + if (content.length) { + store.dispatch(addReleases({ key: addon.key, releases: content })); } else { - store.dispatch(addReleases({ key: addon.key, releases: [] })); + store.dispatch(addReleases({ key: addon.key, releases: [] })); } - - fetchLatestVersionNames(addon); + }); + } else { + store.dispatch(addReleases({ key: addon.key, releases: [] })); } + + fetchLatestVersionNames(addon); + } } - console.log("Using this configuration:", config); + console.log('Using this configuration:', config); Directories.removeAllTemp(); ReactDOM.render( - - - - - - - , - document.getElementById('root'), + + + + + + + , + document.getElementById('root'), ); -}).catch((error: Error) => { + }) + .catch((error: Error) => { ReactDOM.render( -
- Something went very wrong. - We could not configure your installer. Please seek support on the Discord #support channel or on GitHub and provide a screenshot of the following information: -
{error.stack}
+
+ Something went very wrong. + + We could not configure your installer. Please seek support on the Discord #support channel or on GitHub and + provide a screenshot of the following information: + +
+          {error.stack}
+        
- -
, - document.getElementById('root'), + +
, + document.getElementById('root'), ); -}); + }); diff --git a/src/renderer/redux/features/applicationStatus.ts b/src/renderer/redux/features/applicationStatus.ts index 5e70637b..6ca314ac 100644 --- a/src/renderer/redux/features/applicationStatus.ts +++ b/src/renderer/redux/features/applicationStatus.ts @@ -1,17 +1,20 @@ -import { createSlice } from "@reduxjs/toolkit"; -import { ApplicationStatus } from "renderer/components/AddonSection/Enums"; -import { TypedAction } from "renderer/redux/store"; +import { createSlice } from '@reduxjs/toolkit'; +import { ApplicationStatus } from 'renderer/components/AddonSection/Enums'; +import { TypedAction } from 'renderer/redux/store'; const initialState: Record = {}; export const applicationStatusSlice = createSlice({ - name: "applicationStatus", - initialState, - reducers: { - setApplicationStatus: (state, action: TypedAction<{ applicationName: string, applicationStatus: ApplicationStatus }>) => { - state[action.payload.applicationName] = action.payload.applicationStatus; - }, + name: 'applicationStatus', + initialState, + reducers: { + setApplicationStatus: ( + state, + action: TypedAction<{ applicationName: string; applicationStatus: ApplicationStatus }>, + ) => { + state[action.payload.applicationName] = action.payload.applicationStatus; }, + }, }); export const { setApplicationStatus } = applicationStatusSlice.actions; diff --git a/src/renderer/redux/features/configuration.ts b/src/renderer/redux/features/configuration.ts index 264c18f7..30a86acf 100644 --- a/src/renderer/redux/features/configuration.ts +++ b/src/renderer/redux/features/configuration.ts @@ -1,17 +1,17 @@ -import { createSlice } from "@reduxjs/toolkit"; -import { TypedAction } from "../store"; -import { Configuration } from "renderer/utils/InstallerConfiguration"; +import { createSlice } from '@reduxjs/toolkit'; +import { TypedAction } from '../store'; +import { Configuration } from 'renderer/utils/InstallerConfiguration'; const initialState: Configuration = { version: 0, publishers: [] }; export const configurationSlice = createSlice({ - name: "configuration", - initialState, - reducers: { - setConfiguration: (state, action: TypedAction<{ configuration: Configuration }>) => { - state.publishers = action.payload.configuration.publishers; - }, + name: 'configuration', + initialState, + reducers: { + setConfiguration: (state, action: TypedAction<{ configuration: Configuration }>) => { + state.publishers = action.payload.configuration.publishers; }, + }, }); export const { setConfiguration } = configurationSlice.actions; diff --git a/src/renderer/redux/features/downloads.ts b/src/renderer/redux/features/downloads.ts index 4a7463fe..5586af87 100644 --- a/src/renderer/redux/features/downloads.ts +++ b/src/renderer/redux/features/downloads.ts @@ -1,64 +1,77 @@ -import { createSlice } from "@reduxjs/toolkit"; -import { TypedAction } from "renderer/redux/store"; -import { DownloadProgress, DownloadsState } from "renderer/redux/types"; +import { createSlice } from '@reduxjs/toolkit'; +import { TypedAction } from 'renderer/redux/store'; +import { DownloadProgress, DownloadsState } from 'renderer/redux/types'; const initialState: DownloadsState = []; export const downloadSlice = createSlice({ - name: "downloads", - initialState, - reducers: { - updateDownloadProgress: (state, action: TypedAction<{ id: string, progress: DownloadProgress, module: string}>) => { - state.forEach(download => { - if (download.id === action.payload.id) { - download.progress = action.payload.progress; - download.module = action.payload.module; - } - }); - }, - setDownloadModuleIndex: (state, action: TypedAction<{ id: string, moduleIndex: number }>) => { - state.forEach(download => { - if (download.id === action.payload.id) { - download.moduleIndex = action.payload.moduleIndex; - } - }); - }, - setDownloadInterrupted: (state, action: TypedAction<{ id: string }>) => { - state.forEach(download => { - if (download.id === action.payload.id) { - download.progress.interrupted = true; - } - }); - }, - clearDownloadInterrupted: (state, action: TypedAction<{ id: string }>) => { - state.forEach(download => { - if (download.id === action.payload.id) { - download.progress.interrupted = false; - } - }); - }, - registerNewDownload: (state, action: TypedAction<{ id: string, module: string, moduleCount: number, abortControllerID: number }>) => { - state.push({ - id: action.payload.id, - progress: { - interrupted: false, - totalPercent: 0, - }, - module: action.payload.module, - moduleIndex: 0, - moduleCount: action.payload.moduleCount, - abortControllerID: action.payload.abortControllerID, - }); + name: 'downloads', + initialState, + reducers: { + updateDownloadProgress: ( + state, + action: TypedAction<{ id: string; progress: DownloadProgress; module: string }>, + ) => { + state.forEach((download) => { + if (download.id === action.payload.id) { + download.progress = action.payload.progress; + download.module = action.payload.module; + } + }); + }, + setDownloadModuleIndex: (state, action: TypedAction<{ id: string; moduleIndex: number }>) => { + state.forEach((download) => { + if (download.id === action.payload.id) { + download.moduleIndex = action.payload.moduleIndex; + } + }); + }, + setDownloadInterrupted: (state, action: TypedAction<{ id: string }>) => { + state.forEach((download) => { + if (download.id === action.payload.id) { + download.progress.interrupted = true; + } + }); + }, + clearDownloadInterrupted: (state, action: TypedAction<{ id: string }>) => { + state.forEach((download) => { + if (download.id === action.payload.id) { + download.progress.interrupted = false; + } + }); + }, + registerNewDownload: ( + state, + action: TypedAction<{ id: string; module: string; moduleCount: number; abortControllerID: number }>, + ) => { + state.push({ + id: action.payload.id, + progress: { + interrupted: false, + totalPercent: 0, }, - deleteDownload: (state, action: TypedAction<{ id: string }>) => { - const index = state.findIndex(download => download.id === action.payload.id); + module: action.payload.module, + moduleIndex: 0, + moduleCount: action.payload.moduleCount, + abortControllerID: action.payload.abortControllerID, + }); + }, + deleteDownload: (state, action: TypedAction<{ id: string }>) => { + const index = state.findIndex((download) => download.id === action.payload.id); - if (index !== -1) { - state.splice(index, 1); - } - }, + if (index !== -1) { + state.splice(index, 1); + } }, + }, }); -export const { updateDownloadProgress, setDownloadModuleIndex, setDownloadInterrupted, clearDownloadInterrupted, registerNewDownload, deleteDownload } = downloadSlice.actions; +export const { + updateDownloadProgress, + setDownloadModuleIndex, + setDownloadInterrupted, + clearDownloadInterrupted, + registerNewDownload, + deleteDownload, +} = downloadSlice.actions; export default downloadSlice.reducer; diff --git a/src/renderer/redux/features/installStatus.ts b/src/renderer/redux/features/installStatus.ts index a1a29dcb..f9663f18 100644 --- a/src/renderer/redux/features/installStatus.ts +++ b/src/renderer/redux/features/installStatus.ts @@ -1,47 +1,54 @@ -import { createSlice } from "@reduxjs/toolkit"; -import { TypedAction } from "renderer/redux/store"; -import { InstallStatus } from "renderer/components/AddonSection/Enums"; +import { createSlice } from '@reduxjs/toolkit'; +import { TypedAction } from 'renderer/redux/store'; +import { InstallStatus } from 'renderer/components/AddonSection/Enums'; interface BaseInstallState { - status: InstallStatus, + status: InstallStatus; } export interface GenericInstallState { - status: Exclude, + status: Exclude< + InstallStatus, + InstallStatus.InstallingDependency | InstallStatus.InstallingDependencyEnding | InstallStatus.Decompressing + >; } export interface InstallingDependencyInstallState extends BaseInstallState { - status: InstallStatus.InstallingDependency, - dependencyPublisherKey: string, - dependencyAddonKey: string, + status: InstallStatus.InstallingDependency; + dependencyPublisherKey: string; + dependencyAddonKey: string; } export interface InstallingDependencyEndingInstallState extends BaseInstallState { - status: InstallStatus.InstallingDependencyEnding, - dependencyPublisherKey: string, - dependencyAddonKey: string, - percent: number, - entry?: string, + status: InstallStatus.InstallingDependencyEnding; + dependencyPublisherKey: string; + dependencyAddonKey: string; + percent: number; + entry?: string; } export interface DecompressingInstallState extends BaseInstallState { - status: InstallStatus.Decompressing, - percent: number, - entry?: string, + status: InstallStatus.Decompressing; + percent: number; + entry?: string; } -export type InstallState = GenericInstallState | InstallingDependencyInstallState | InstallingDependencyEndingInstallState | DecompressingInstallState +export type InstallState = + | GenericInstallState + | InstallingDependencyInstallState + | InstallingDependencyEndingInstallState + | DecompressingInstallState; -const initialState : Record = {}; +const initialState: Record = {}; export const installStatus = createSlice({ - name: "installStatus", - initialState, - reducers: { - setInstallStatus: (state, action: TypedAction<{ addonKey: string, installState: InstallState }>) => { - state[action.payload.addonKey] = action.payload.installState; - }, + name: 'installStatus', + initialState, + reducers: { + setInstallStatus: (state, action: TypedAction<{ addonKey: string; installState: InstallState }>) => { + state[action.payload.addonKey] = action.payload.installState; }, + }, }); export const { setInstallStatus } = installStatus.actions; diff --git a/src/renderer/redux/features/installedTrack.ts b/src/renderer/redux/features/installedTrack.ts index 20358aa4..41c6523d 100644 --- a/src/renderer/redux/features/installedTrack.ts +++ b/src/renderer/redux/features/installedTrack.ts @@ -1,17 +1,17 @@ -import { createSlice } from "@reduxjs/toolkit"; -import { TypedAction } from "renderer/redux/store"; -import { AddonTrack } from "renderer/utils/InstallerConfiguration"; +import { createSlice } from '@reduxjs/toolkit'; +import { TypedAction } from 'renderer/redux/store'; +import { AddonTrack } from 'renderer/utils/InstallerConfiguration'; const initialState: Record = {}; export const installedTrackSlice = createSlice({ - name: "installedTrack", - initialState, - reducers: { - setInstalledTrack: (state, action: TypedAction<{ addonKey: string, installedTrack: AddonTrack }>) => { - state[action.payload.addonKey] = action.payload.installedTrack; - }, + name: 'installedTrack', + initialState, + reducers: { + setInstalledTrack: (state, action: TypedAction<{ addonKey: string; installedTrack: AddonTrack }>) => { + state[action.payload.addonKey] = action.payload.installedTrack; }, + }, }); export const { setInstalledTrack } = installedTrackSlice.actions; diff --git a/src/renderer/redux/features/latestVersionNames.ts b/src/renderer/redux/features/latestVersionNames.ts index f91bccd7..81c93da0 100644 --- a/src/renderer/redux/features/latestVersionNames.ts +++ b/src/renderer/redux/features/latestVersionNames.ts @@ -1,22 +1,25 @@ -import { createSlice } from "@reduxjs/toolkit"; -import { TypedAction } from "renderer/redux/store"; -import { AddonAndTrackLatestVersionNamesState } from "renderer/redux/types"; -import { ReleaseInfo } from "renderer/utils/AddonData"; +import { createSlice } from '@reduxjs/toolkit'; +import { TypedAction } from 'renderer/redux/store'; +import { AddonAndTrackLatestVersionNamesState } from 'renderer/redux/types'; +import { ReleaseInfo } from 'renderer/utils/AddonData'; const initialState: AddonAndTrackLatestVersionNamesState = {}; export const latestVersionNamesSlice = createSlice({ - name: "latestVersionNames", - initialState, - reducers: { - setAddonAndTrackLatestReleaseInfo: (state, action: TypedAction<{ addonKey: string, trackKey: string, info: ReleaseInfo }>) => { - if (!state[action.payload.addonKey]) { - state[action.payload.addonKey] = {}; - } + name: 'latestVersionNames', + initialState, + reducers: { + setAddonAndTrackLatestReleaseInfo: ( + state, + action: TypedAction<{ addonKey: string; trackKey: string; info: ReleaseInfo }>, + ) => { + if (!state[action.payload.addonKey]) { + state[action.payload.addonKey] = {}; + } - state[action.payload.addonKey][action.payload.trackKey] = action.payload.info; - }, + state[action.payload.addonKey][action.payload.trackKey] = action.payload.info; }, + }, }); export const { setAddonAndTrackLatestReleaseInfo } = latestVersionNamesSlice.actions; diff --git a/src/renderer/redux/features/releaseNotes.ts b/src/renderer/redux/features/releaseNotes.ts index 2857e7d7..ba5823c0 100644 --- a/src/renderer/redux/features/releaseNotes.ts +++ b/src/renderer/redux/features/releaseNotes.ts @@ -1,21 +1,21 @@ import { createSlice } from '@reduxjs/toolkit'; -import { TypedAction } from "renderer/redux/store"; -import { ReleaseData } from "renderer/redux/types"; +import { TypedAction } from 'renderer/redux/store'; +import { ReleaseData } from 'renderer/redux/types'; const initialState: Record = {}; export const releaseNotesSlice = createSlice({ - name: "releaseNotes", - initialState, - reducers: { - addReleases: (state, action: TypedAction<{ key: string, releases: ReleaseData[] }>) => { - if (!state[action.payload.key]) { - state[action.payload.key] = action.payload.releases; - } else { - state[action.payload.key].push(...action.payload.releases); - } - }, + name: 'releaseNotes', + initialState, + reducers: { + addReleases: (state, action: TypedAction<{ key: string; releases: ReleaseData[] }>) => { + if (!state[action.payload.key]) { + state[action.payload.key] = action.payload.releases; + } else { + state[action.payload.key].push(...action.payload.releases); + } }, + }, }); export const { addReleases } = releaseNotesSlice.actions; diff --git a/src/renderer/redux/features/selectedTrack.ts b/src/renderer/redux/features/selectedTrack.ts index 7ce32b9a..e1420113 100644 --- a/src/renderer/redux/features/selectedTrack.ts +++ b/src/renderer/redux/features/selectedTrack.ts @@ -1,17 +1,17 @@ -import { createSlice } from "@reduxjs/toolkit"; -import { TypedAction } from "renderer/redux/store"; -import { AddonTrack } from "renderer/utils/InstallerConfiguration"; +import { createSlice } from '@reduxjs/toolkit'; +import { TypedAction } from 'renderer/redux/store'; +import { AddonTrack } from 'renderer/utils/InstallerConfiguration'; -const initialState : Record = {}; +const initialState: Record = {}; export const selectedTrackSlice = createSlice({ - name: "selectedTrack", - initialState, - reducers: { - setSelectedTrack: (state, action: TypedAction<{ addonKey: string, track: AddonTrack }>) => { - state[action.payload.addonKey] = action.payload.track; - }, + name: 'selectedTrack', + initialState, + reducers: { + setSelectedTrack: (state, action: TypedAction<{ addonKey: string; track: AddonTrack }>) => { + state[action.payload.addonKey] = action.payload.track; }, + }, }); export const { setSelectedTrack } = selectedTrackSlice.actions; diff --git a/src/renderer/redux/features/sentrySessionID.ts b/src/renderer/redux/features/sentrySessionID.ts index 62584e97..45e8052a 100644 --- a/src/renderer/redux/features/sentrySessionID.ts +++ b/src/renderer/redux/features/sentrySessionID.ts @@ -1,16 +1,16 @@ -import { createSlice } from "@reduxjs/toolkit"; -import { TypedAction } from "renderer/redux/store"; +import { createSlice } from '@reduxjs/toolkit'; +import { TypedAction } from 'renderer/redux/store'; -const initialState: { sessionID: string | undefined; } = { sessionID: undefined }; +const initialState: { sessionID: string | undefined } = { sessionID: undefined }; export const sentrySessionIDSlice = createSlice({ - name: "sentrySessionID", - initialState, - reducers: { - setSentrySessionID: (state, action: TypedAction) => { - state.sessionID = action.payload; - }, + name: 'sentrySessionID', + initialState, + reducers: { + setSentrySessionID: (state, action: TypedAction) => { + state.sessionID = action.payload; }, + }, }); export const { setSentrySessionID } = sentrySessionIDSlice.actions; diff --git a/src/renderer/redux/reducer.ts b/src/renderer/redux/reducer.ts index 1010d2a7..3262ae3d 100644 --- a/src/renderer/redux/reducer.ts +++ b/src/renderer/redux/reducer.ts @@ -1,26 +1,26 @@ -import { AnyAction, combineReducers, Reducer } from "redux"; -import downloadsReducer from "renderer/redux/features/downloads"; -import installStatusReducer from "renderer/redux/features/installStatus"; -import installedTrackReducer from "renderer/redux/features/installedTrack"; -import latestVersionNamesReducer from "renderer/redux/features/latestVersionNames"; -import selectedTrackReducer from "renderer/redux/features/selectedTrack"; -import releaseNotesReducer from "renderer/redux/features/releaseNotes"; -import configurationReducer from "renderer/redux/features/configuration"; -import applicationStatusReducer from "renderer/redux/features/applicationStatus"; -import sentrySessionIDReducer from "renderer/redux/features/sentrySessionID"; +import { AnyAction, combineReducers, Reducer } from 'redux'; +import downloadsReducer from 'renderer/redux/features/downloads'; +import installStatusReducer from 'renderer/redux/features/installStatus'; +import installedTrackReducer from 'renderer/redux/features/installedTrack'; +import latestVersionNamesReducer from 'renderer/redux/features/latestVersionNames'; +import selectedTrackReducer from 'renderer/redux/features/selectedTrack'; +import releaseNotesReducer from 'renderer/redux/features/releaseNotes'; +import configurationReducer from 'renderer/redux/features/configuration'; +import applicationStatusReducer from 'renderer/redux/features/applicationStatus'; +import sentrySessionIDReducer from 'renderer/redux/features/sentrySessionID'; -import { RootState } from "renderer/redux/store"; +import { RootState } from 'renderer/redux/store'; export const combinedReducer = combineReducers({ - downloads: downloadsReducer, - installStatus: installStatusReducer, - installedTracks: installedTrackReducer, - latestVersionNames: latestVersionNamesReducer, - selectedTracks: selectedTrackReducer, - releaseNotes: releaseNotesReducer, - configuration: configurationReducer, - applicationStatus: applicationStatusReducer, - sentrySessionID: sentrySessionIDReducer, + downloads: downloadsReducer, + installStatus: installStatusReducer, + installedTracks: installedTrackReducer, + latestVersionNames: latestVersionNamesReducer, + selectedTracks: selectedTrackReducer, + releaseNotes: releaseNotesReducer, + configuration: configurationReducer, + applicationStatus: applicationStatusReducer, + sentrySessionID: sentrySessionIDReducer, }); export const rootReducer: Reducer = (state: RootState, action: AnyAction) => combinedReducer(state, action); diff --git a/src/renderer/redux/store.ts b/src/renderer/redux/store.ts index 0790ddad..4beca53c 100644 --- a/src/renderer/redux/store.ts +++ b/src/renderer/redux/store.ts @@ -1,38 +1,27 @@ import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; -import { - DownloadsState, - AddonAndTrackLatestVersionNamesState, - ReleaseNotesState, -} from "renderer/redux/types"; -import { AddonTrack, Configuration } from "renderer/utils/InstallerConfiguration"; +import { DownloadsState, AddonAndTrackLatestVersionNamesState, ReleaseNotesState } from 'renderer/redux/types'; +import { AddonTrack, Configuration } from 'renderer/utils/InstallerConfiguration'; import { configureStore } from '@reduxjs/toolkit'; -import { combinedReducer, rootReducer } from "renderer/redux/reducer"; -import { InstallStatus } from "renderer/components/AddonSection/Enums"; +import { combinedReducer, rootReducer } from 'renderer/redux/reducer'; +import { InstallStatus } from 'renderer/components/AddonSection/Enums'; -export type TypedAction = { type: string, payload: T }; +export type TypedAction = { type: string; payload: T }; export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; export const store = configureStore({ - reducer: rootReducer, + reducer: rootReducer, }); -if (module.hot) { - module.hot.accept('./reducer', () => { - const nextRootReducer = require('./reducer').default; - store.replaceReducer(nextRootReducer); - }); -} - export type InstallerStore = { - downloads: DownloadsState, - installStatus: Record, - selectedTracks: Record, - installedTracks: Record, - latestVersionNames: AddonAndTrackLatestVersionNamesState, - releaseNotes: ReleaseNotesState, - configuration: Configuration, - sentrySessionID: { sessionID: string }, + downloads: DownloadsState; + installStatus: Record; + selectedTracks: Record; + installedTracks: Record; + latestVersionNames: AddonAndTrackLatestVersionNamesState; + releaseNotes: ReleaseNotesState; + configuration: Configuration; + sentrySessionID: { sessionID: string }; }; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types diff --git a/src/renderer/redux/types.ts b/src/renderer/redux/types.ts index 420956ab..089073cb 100644 --- a/src/renderer/redux/types.ts +++ b/src/renderer/redux/types.ts @@ -1,31 +1,31 @@ -import { ReleaseInfo } from "renderer/utils/AddonData"; +import { ReleaseInfo } from 'renderer/utils/AddonData'; export interface DownloadProgress { - interrupted: boolean, - totalPercent: number, - splitPartPercent?: number, - splitPartIndex?: number, - splitPartCount?: number, + interrupted: boolean; + totalPercent: number; + splitPartPercent?: number; + splitPartIndex?: number; + splitPartCount?: number; } export interface DownloadItem { - id: string - progress: DownloadProgress, - module: string, - moduleIndex: number, - moduleCount: number, - abortControllerID: number, + id: string; + progress: DownloadProgress; + module: string; + moduleIndex: number; + moduleCount: number; + abortControllerID: number; } export type DownloadsState = DownloadItem[]; export interface ReleaseData { - name: string; - publishedAt: number; - htmlUrl: string; - body: string; + name: string; + publishedAt: number; + htmlUrl: string; + body: string; } export type ReleaseNotesState = ReleaseData[]; -export type AddonAndTrackLatestVersionNamesState = { [addonKey: string]: { [trackKey: string]: ReleaseInfo } } +export type AddonAndTrackLatestVersionNamesState = { [addonKey: string]: { [trackKey: string]: ReleaseInfo } }; diff --git a/src/renderer/style/theme.ts b/src/renderer/style/theme.ts index b69c5f2f..d0e69a61 100644 --- a/src/renderer/style/theme.ts +++ b/src/renderer/style/theme.ts @@ -1,39 +1,39 @@ -import { css } from "styled-components"; +import { css } from 'styled-components'; export const fontSizes = { - huge: '20px', + huge: '20px', }; export const colors = { - positive: '#00b853', - red: '#fc3a3a', - redDark: '#F70404', - redDarker: '#E40303', - redDarkest: "#D10303", - - title: '#FFFFFFDD', - titleContrast: '#FFFFFF', - - mutedText: '#929292', - mutedTextDark: '#666666', - - listItem: '#393939', - listItemSelected: '#474747', - - cardBackground: '#313131', - cardBackgroundHover: '#3b3b3b', - cardForeground: '#FFFFFFDD', - cardSelected: '#00C2CB', - cardInstalled: '#2E995E', - - gray50: '#f9fafb', - navy: '#1b2434', - navyLightest: '#273347', - navyLighter: '#222c3d', - navy400: '#1f2633', - teal50: '#c2cbcc', - tealLight: '#00c2cc', - tealLightContrast: '#00afb7', + positive: '#00b853', + red: '#fc3a3a', + redDark: '#F70404', + redDarker: '#E40303', + redDarkest: '#D10303', + + title: '#FFFFFFDD', + titleContrast: '#FFFFFF', + + mutedText: '#929292', + mutedTextDark: '#666666', + + listItem: '#393939', + listItemSelected: '#474747', + + cardBackground: '#313131', + cardBackgroundHover: '#3b3b3b', + cardForeground: '#FFFFFFDD', + cardSelected: '#00C2CB', + cardInstalled: '#2E995E', + + gray50: '#f9fafb', + navy: '#1b2434', + navyLightest: '#273347', + navyLighter: '#222c3d', + navy400: '#1f2633', + teal50: '#c2cbcc', + tealLight: '#00c2cc', + tealLightContrast: '#00afb7', }; export const dropShadow = css` @@ -41,7 +41,7 @@ export const dropShadow = css` `; export const smallCard = css` - padding: .5em 1.15em; + padding: 0.5em 1.15em; border-radius: 5px; background-color: ${colors.cardBackground}; @@ -51,6 +51,6 @@ export const smallCard = css` color: ${colors.cardForeground}; transition: background-color linear 200ms; - + ${dropShadow}; `; diff --git a/src/renderer/utils/AddonData.ts b/src/renderer/utils/AddonData.ts index 234b7ef5..2d8e75ba 100644 --- a/src/renderer/utils/AddonData.ts +++ b/src/renderer/utils/AddonData.ts @@ -1,174 +1,180 @@ -import { Addon, AddonTrack, GithubBranchReleaseModel } from "renderer/utils/InstallerConfiguration"; -import { GitVersions } from "@flybywiresim/api-client"; -import { Directories } from "./Directories"; +import { Addon, AddonTrack, GithubBranchReleaseModel } from 'renderer/utils/InstallerConfiguration'; +import { GitVersions } from '@flybywiresim/api-client'; +import { Directories } from './Directories'; import fs from 'fs-extra'; -import { getCurrentInstall, FragmenterUpdateChecker } from "@flybywiresim/fragmenter"; -import settings from "common/settings"; -import { store } from "renderer/redux/store"; -import { setInstalledTrack } from "renderer/redux/features/installedTrack"; -import { setSelectedTrack } from "renderer/redux/features/selectedTrack"; -import { InstallState, setInstallStatus } from "renderer/redux/features/installStatus"; +import { getCurrentInstall, FragmenterUpdateChecker } from '@flybywiresim/fragmenter'; +import settings from 'common/settings'; +import { store } from 'renderer/redux/store'; +import { setInstalledTrack } from 'renderer/redux/features/installedTrack'; +import { setSelectedTrack } from 'renderer/redux/features/selectedTrack'; +import { InstallState, setInstallStatus } from 'renderer/redux/features/installStatus'; import yaml from 'js-yaml'; -import { InstallStatus } from "renderer/components/AddonSection/Enums"; +import { InstallStatus } from 'renderer/components/AddonSection/Enums'; export type ReleaseInfo = { - name: string, - releaseDate: number, - changelogUrl?: string, -} + name: string; + releaseDate: number; + changelogUrl?: string; +}; export class AddonData { - static async latestVersionForTrack(addon: Addon, track: AddonTrack): Promise { - switch (track.releaseModel.type) { - case 'githubRelease': - return this.latestVersionForReleasedTrack(addon); - case 'githubBranch': - return this.latestVersionForRollingTrack(addon, track.releaseModel); - case 'CDN': - return this.latestVersionForCDN(track); - } + static async latestVersionForTrack(addon: Addon, track: AddonTrack): Promise { + switch (track.releaseModel.type) { + case 'githubRelease': + return this.latestVersionForReleasedTrack(addon); + case 'githubBranch': + return this.latestVersionForRollingTrack(addon, track.releaseModel); + case 'CDN': + return this.latestVersionForCDN(track); } + } + + private static async latestVersionForReleasedTrack(addon: Addon): Promise { + return GitVersions.getReleases(addon.repoOwner, addon.repoName).then((releases) => ({ + name: releases[0].name, + releaseDate: releases[0].publishedAt.getTime(), + changelogUrl: releases[0].htmlUrl, + })); + } + + private static async latestVersionForRollingTrack( + addon: Addon, + releaseModel: GithubBranchReleaseModel, + ): Promise { + return GitVersions.getNewestCommit(addon.repoOwner, addon.repoName, releaseModel.branch).then((commit) => ({ + name: commit.sha.substring(0, 7), + releaseDate: commit.timestamp.getTime(), + })); + } + + private static async latestVersionForCDN(track: AddonTrack): Promise { + return fetch(track.url + '/releases.yaml') + .then((res) => res.blob()) + .then((blob) => blob.text()) + .then((stream) => ({ + name: 'v' + (yaml.load(stream) as { releases: Array<{ name: string; date: Date }> }).releases[0].name, + releaseDate: ( + yaml.load(stream) as { releases: Array<{ name: string; date: Date }> } + ).releases[0].date.getTime(), + })); + } + + static async configureInitialAddonState(addon: Addon): Promise { + const dispatch = store.dispatch; + + const setCurrentlyInstalledTrack = (newInstalledTrack: AddonTrack) => { + dispatch(setInstalledTrack({ addonKey: addon.key, installedTrack: newInstalledTrack })); + }; + + const setCurrentlySelectedTrack = (newSelectedTrack: AddonTrack) => { + dispatch(setSelectedTrack({ addonKey: addon.key, track: newSelectedTrack })); + }; + + const setCurrentInstallStatus = (new_state: InstallState) => { + dispatch(setInstallStatus({ addonKey: addon.key, installState: new_state })); + }; + + let selectedTrack: AddonTrack; + if (!Directories.isFragmenterInstall(addon)) { + console.log(addon.key, 'is not installed'); + selectedTrack = addon.tracks[0]; + setCurrentlySelectedTrack(selectedTrack); + } else { + console.log(addon.key, 'is installed'); + try { + const manifest = getCurrentInstall(Directories.inInstallLocation(addon.targetDirectory)); + console.log('Currently installed', manifest); + + let track = addon.tracks.find((track) => track.url.includes(manifest.source)); + if (!track) { + track = addon.tracks.find((track) => track.alternativeUrls?.includes(manifest.source)); + } - private static async latestVersionForReleasedTrack(addon: Addon): Promise { - return GitVersions.getReleases(addon.repoOwner, addon.repoName) - .then((releases) => ({ - name: releases[0].name, - releaseDate: releases[0].publishedAt.getTime(), - changelogUrl: releases[0].htmlUrl, - })); + console.log('Currently installed', track); + setCurrentlyInstalledTrack(track); + setCurrentlySelectedTrack(track); + selectedTrack = track; + } catch (e) { + console.error(e); + console.log('Not installed'); + setCurrentlySelectedTrack(addon.tracks[0]); + selectedTrack = addon.tracks[0]; + } } - private static async latestVersionForRollingTrack(addon: Addon, releaseModel: GithubBranchReleaseModel): Promise { - return GitVersions.getNewestCommit(addon.repoOwner, addon.repoName, releaseModel.branch) - .then((commit) => ({ - name: commit.sha.substring(0, 7), - releaseDate: commit.timestamp.getTime(), - })); - } + const addonDiscovered = settings.get('cache.main.discoveredAddons.' + addon.key); - private static async latestVersionForCDN(track: AddonTrack): Promise { - return fetch(track.url + '/releases.yaml') - .then(res => res.blob()) - .then(blob => blob.text()) - .then(stream => ({ - name: 'v' + (yaml.load(stream) as {releases: Array<{name: string, date: Date}>}).releases[0].name, - releaseDate: (yaml.load(stream) as {releases: Array<{name: string, date: Date}>}).releases[0].date.getTime(), - })); + if (addon.hidden && !addonDiscovered) { + setCurrentInstallStatus({ status: InstallStatus.Hidden }); + return; } - static async configureInitialAddonState(addon: Addon): Promise { - const dispatch = store.dispatch; - - const setCurrentlyInstalledTrack = (newInstalledTrack: AddonTrack) => { - dispatch(setInstalledTrack({ addonKey: addon.key, installedTrack: newInstalledTrack })); - }; - - const setCurrentlySelectedTrack = (newSelectedTrack: AddonTrack) => { - dispatch(setSelectedTrack({ addonKey: addon.key, track: newSelectedTrack })); - }; - - const setCurrentInstallStatus = (new_state: InstallState) => { - dispatch(setInstallStatus({ addonKey: addon.key, installState: new_state })); - }; - - let selectedTrack: AddonTrack; - if (!Directories.isFragmenterInstall(addon)) { - console.log (addon.key, 'is not installed'); - selectedTrack = addon.tracks[0]; - setCurrentlySelectedTrack(selectedTrack); - } else { - console.log (addon.key, 'is installed'); - try { - const manifest = getCurrentInstall(Directories.inInstallLocation(addon.targetDirectory)); - console.log('Currently installed', manifest); - - let track = addon.tracks.find(track => track.url.includes(manifest.source)); - if (!track) { - track = addon.tracks.find(track => track.alternativeUrls?.includes(manifest.source)); - } - - console.log('Currently installed', track); - setCurrentlyInstalledTrack(track); - setCurrentlySelectedTrack(track); - selectedTrack = track; - } catch (e) { - console.error(e); - console.log('Not installed'); - setCurrentlySelectedTrack(addon.tracks[0]); - selectedTrack = addon.tracks[0]; - } - } - - const addonDiscovered = settings.get('cache.main.discoveredAddons.' + addon.key); - - if (addon.hidden && !addonDiscovered) { - setCurrentInstallStatus({ status: InstallStatus.Hidden }); - return; - } - - if (!selectedTrack) { - console.log (addon.key, 'has unknown install status'); - setCurrentInstallStatus({ status: InstallStatus.Unknown }); - return; - } + if (!selectedTrack) { + console.log(addon.key, 'has unknown install status'); + setCurrentInstallStatus({ status: InstallStatus.Unknown }); + return; + } - console.log('Checking install status'); + console.log('Checking install status'); - const installDir = Directories.inInstallLocation(addon.targetDirectory); + const installDir = Directories.inInstallLocation(addon.targetDirectory); - if (!fs.existsSync(installDir)) { - console.log ('no existing install dir for', addon.key); - setCurrentInstallStatus({ status: InstallStatus.NotInstalled }); - return; - } + if (!fs.existsSync(installDir)) { + console.log('no existing install dir for', addon.key); + setCurrentInstallStatus({ status: InstallStatus.NotInstalled }); + return; + } - console.log('Checking for git install'); - if (Directories.isGitInstall(installDir)) { - setCurrentInstallStatus({ status: InstallStatus.GitInstall }); - return; - } + console.log('Checking for git install'); + if (Directories.isGitInstall(installDir)) { + setCurrentInstallStatus({ status: InstallStatus.GitInstall }); + return; + } - try { - const updateInfo = await new FragmenterUpdateChecker().needsUpdate(selectedTrack.url, installDir, { - forceCacheBust: true, - }); - - if (updateInfo.isFreshInstall) { - setCurrentInstallStatus({ status: InstallStatus.NotInstalled }); - return; - } - - if (updateInfo.needsUpdate) { - setCurrentInstallStatus({ status: InstallStatus.NeedsUpdate }); - return; - } - - setCurrentInstallStatus({ status: InstallStatus.UpToDate }); - return; - } catch (e) { - console.error(e); - setCurrentInstallStatus({ status: InstallStatus.Unknown }); - return; - } + try { + const updateInfo = await new FragmenterUpdateChecker().needsUpdate(selectedTrack.url, installDir, { + forceCacheBust: true, + }); + + if (updateInfo.isFreshInstall) { + setCurrentInstallStatus({ status: InstallStatus.NotInstalled }); + return; + } + + if (updateInfo.needsUpdate) { + setCurrentInstallStatus({ status: InstallStatus.NeedsUpdate }); + return; + } + + setCurrentInstallStatus({ status: InstallStatus.UpToDate }); + return; + } catch (e) { + console.error(e); + setCurrentInstallStatus({ status: InstallStatus.Unknown }); + return; } + } - static async checkForUpdates(addon: Addon): Promise { - console.log("Checking for updates for " + addon.key); + static async checkForUpdates(addon: Addon): Promise { + console.log('Checking for updates for ' + addon.key); - const dispatch = store.dispatch; + const dispatch = store.dispatch; - const installDir = Directories.inInstallLocation(addon.targetDirectory); + const installDir = Directories.inInstallLocation(addon.targetDirectory); - const state = store.getState(); + const state = store.getState(); - if (state.installStatus[addon.key].status === InstallStatus.UpToDate) { - const updateInfo = await new FragmenterUpdateChecker().needsUpdate(state.selectedTracks[addon.key].url, installDir, { - forceCacheBust: true, - }); - if (updateInfo.needsUpdate) { - dispatch(setInstallStatus({ addonKey: addon.key, installState: { status: InstallStatus.NeedsUpdate } })); - } - } + if (state.installStatus[addon.key].status === InstallStatus.UpToDate) { + const updateInfo = await new FragmenterUpdateChecker().needsUpdate( + state.selectedTracks[addon.key].url, + installDir, + { + forceCacheBust: true, + }, + ); + if (updateInfo.needsUpdate) { + dispatch(setInstallStatus({ addonKey: addon.key, installState: { status: InstallStatus.NeedsUpdate } })); + } } - + } } diff --git a/src/renderer/utils/BackgroundServices.ts b/src/renderer/utils/BackgroundServices.ts index 92964c79..3a711277 100644 --- a/src/renderer/utils/BackgroundServices.ts +++ b/src/renderer/utils/BackgroundServices.ts @@ -1,134 +1,148 @@ -import { Addon, ExternalApplicationDefinition, Publisher } from "renderer/utils/InstallerConfiguration"; -import { Resolver } from "renderer/utils/Resolver"; -import { store } from "renderer/redux/store"; -import { ApplicationStatus } from "renderer/components/AddonSection/Enums"; -import { ExternalApps } from "renderer/utils/ExternalApps"; -import path from "path"; -import { Directories } from "renderer/utils/Directories"; -import { shell } from "@electron/remote"; -import { promises } from "fs-extra"; +import { Addon, ExternalApplicationDefinition, Publisher } from 'renderer/utils/InstallerConfiguration'; +import { Resolver } from 'renderer/utils/Resolver'; +import { store } from 'renderer/redux/store'; +import { ApplicationStatus } from 'renderer/components/AddonSection/Enums'; +import { ExternalApps } from 'renderer/utils/ExternalApps'; +import path from 'path'; +import { Directories } from 'renderer/utils/Directories'; +import { shell } from '@electron/remote'; +import { promises } from 'fs-extra'; export const STARTUP_FOLDER_PATH = 'Microsoft\\Windows\\Start Menu\\Programs\\Startup\\'; export class BackgroundServices { - private static validateExecutablePath(path: string): boolean { - return /^[a-zA-Z\d_-]+$/.test(path); - } - - public static getExternalAppFromBackgroundService(addon: Addon, publisher: Publisher): ExternalApplicationDefinition { - if (!addon.backgroundService) { - throw new Error('Addon has no background service'); - } - - const appRef = addon.backgroundService.runCheckExternalAppRef; - const app = Resolver.findDefinition(appRef, publisher); + private static validateExecutablePath(path: string): boolean { + return /^[a-zA-Z\d_-]+$/.test(path); + } - if (!app || app.kind !== 'externalApp') { - throw new Error(`Attempted to find external app for background service, but runCheckExternalAppRef=${appRef} does not refer to a valid external app`); - } - - return app; + public static getExternalAppFromBackgroundService(addon: Addon, publisher: Publisher): ExternalApplicationDefinition { + if (!addon.backgroundService) { + throw new Error('Addon has no background service'); } - static isRunning(addon: Addon, publisher: Publisher): boolean { - const app = this.getExternalAppFromBackgroundService(addon, publisher); - - const state = store.getState().applicationStatus[app.key]; + const appRef = addon.backgroundService.runCheckExternalAppRef; + const app = Resolver.findDefinition(appRef, publisher); - return state === ApplicationStatus.Open; + if (!app || app.kind !== 'externalApp') { + throw new Error( + `Attempted to find external app for background service, but runCheckExternalAppRef=${appRef} does not refer to a valid external app`, + ); } - static async isAutoStartEnabled(addon: Addon): Promise { - const backgroundService = addon.backgroundService; + return app; + } + + static isRunning(addon: Addon, publisher: Publisher): boolean { + const app = this.getExternalAppFromBackgroundService(addon, publisher); - if (!backgroundService) { - throw new Error('Addon has no background service'); - } + const state = store.getState().applicationStatus[app.key]; - let folderEntries; - try { - folderEntries = await promises.readdir(path.join(process.env.APPDATA, STARTUP_FOLDER_PATH), { withFileTypes: true }); - } catch (e) { - console.error('[BackgroundServices](isAutoStartEnabled) Could not read contents of startup folder. See exception below'); - console.error(e); - } + return state === ApplicationStatus.Open; + } - if (!folderEntries) { - return false; - } + static async isAutoStartEnabled(addon: Addon): Promise { + const backgroundService = addon.backgroundService; - const shortcuts = folderEntries.filter((it) => it.isFile() && path.extname(it.name) === '.lnk'); - const matchingShortcut = shortcuts.find((it) => path.parse(it.name).name === backgroundService.executableFileBasename); + if (!backgroundService) { + throw new Error('Addon has no background service'); + } - return matchingShortcut !== undefined; + let folderEntries; + try { + folderEntries = await promises.readdir(path.join(process.env.APPDATA, STARTUP_FOLDER_PATH), { + withFileTypes: true, + }); + } catch (e) { + console.error( + '[BackgroundServices](isAutoStartEnabled) Could not read contents of startup folder. See exception below', + ); + console.error(e); } - static async setAutoStartEnabled(addon: Addon, publisher: Publisher, enabled: boolean): Promise { - const backgroundService = addon.backgroundService; - - if (!backgroundService) { - throw new Error('Addon has no background service'); - } - - if (!this.validateExecutablePath(backgroundService.executableFileBasename)) { - throw new Error('Executable path much match /^[a-zA-Z\\d_-]+$/.'); - } - - const exePath = path.join(Directories.inInstallLocation(addon.targetDirectory), backgroundService.executableFileBasename); - const commandLineArgs = backgroundService.commandLineArgs - ? ` ${backgroundService.commandLineArgs.join(' ')}` - : ''; - - const shortcutDir = path.join(process.env.APPDATA, STARTUP_FOLDER_PATH); - const shortcutPath = path.join(shortcutDir, `${backgroundService.executableFileBasename}.lnk`); - - if (enabled) { - const created = shell.writeShortcutLink(shortcutPath, 'create', { - target: exePath, - args: commandLineArgs, - cwd: path.dirname(exePath), - }); - - if (!created) { - console.error('[BackgroundServices](setAutoStartEnabled) Could not create shortcut'); - } else { - console.log('[BackgroundServices](setAutoStartEnabled) Shortcut created'); - } - } else { - promises.rm(shortcutPath).catch((e) => { - console.error('[BackgroundServices](setAutoStartEnabled) Could not remove shortcut. See exception below.'); - console.error(e); - }); - } + if (!folderEntries) { + return false; } - static async start(addon: Addon): Promise { - const backgroundService = addon.backgroundService; + const shortcuts = folderEntries.filter((it) => it.isFile() && path.extname(it.name) === '.lnk'); + const matchingShortcut = shortcuts.find( + (it) => path.parse(it.name).name === backgroundService.executableFileBasename, + ); - if (!backgroundService) { - throw new Error('Addon has no background service'); - } + return matchingShortcut !== undefined; + } - if (!this.validateExecutablePath(backgroundService.executableFileBasename)) { - throw new Error('Executable path much match /^[a-zA-Z\\d_-]+$/.'); - } + static async setAutoStartEnabled(addon: Addon, publisher: Publisher, enabled: boolean): Promise { + const backgroundService = addon.backgroundService; - const exePath = path.normalize(path.join(Directories.inInstallLocation(addon.targetDirectory), `${backgroundService.executableFileBasename}.exe`)); + if (!backgroundService) { + throw new Error('Addon has no background service'); + } - await shell.openPath(exePath); + if (!this.validateExecutablePath(backgroundService.executableFileBasename)) { + throw new Error('Executable path much match /^[a-zA-Z\\d_-]+$/.'); + } - // if (exePath.startsWith('..')) { - // throw new Error('Validated and normalized path still traversed directory.'); - // } - // - // const commandLineArgs = backgroundService.commandLineArgs ?? []; - // - // spawn(exePath, commandLineArgs, { cwd: Directories.inCommunity(addon.targetDirectory), shell: true, detached: true }); + const exePath = path.join( + Directories.inInstallLocation(addon.targetDirectory), + backgroundService.executableFileBasename, + ); + const commandLineArgs = backgroundService.commandLineArgs ? ` ${backgroundService.commandLineArgs.join(' ')}` : ''; + + const shortcutDir = path.join(process.env.APPDATA, STARTUP_FOLDER_PATH); + const shortcutPath = path.join(shortcutDir, `${backgroundService.executableFileBasename}.lnk`); + + if (enabled) { + const created = shell.writeShortcutLink(shortcutPath, 'create', { + target: exePath, + args: commandLineArgs, + cwd: path.dirname(exePath), + }); + + if (!created) { + console.error('[BackgroundServices](setAutoStartEnabled) Could not create shortcut'); + } else { + console.log('[BackgroundServices](setAutoStartEnabled) Shortcut created'); + } + } else { + promises.rm(shortcutPath).catch((e) => { + console.error('[BackgroundServices](setAutoStartEnabled) Could not remove shortcut. See exception below.'); + console.error(e); + }); } + } - static async kill(addon: Addon, publisher: Publisher): Promise { - const app = this.getExternalAppFromBackgroundService(addon, publisher); + static async start(addon: Addon): Promise { + const backgroundService = addon.backgroundService; - return ExternalApps.kill(app); + if (!backgroundService) { + throw new Error('Addon has no background service'); } + + if (!this.validateExecutablePath(backgroundService.executableFileBasename)) { + throw new Error('Executable path much match /^[a-zA-Z\\d_-]+$/.'); + } + + const exePath = path.normalize( + path.join( + Directories.inInstallLocation(addon.targetDirectory), + `${backgroundService.executableFileBasename}.exe`, + ), + ); + + await shell.openPath(exePath); + + // if (exePath.startsWith('..')) { + // throw new Error('Validated and normalized path still traversed directory.'); + // } + // + // const commandLineArgs = backgroundService.commandLineArgs ?? []; + // + // spawn(exePath, commandLineArgs, { cwd: Directories.inCommunity(addon.targetDirectory), shell: true, detached: true }); + } + + static async kill(addon: Addon, publisher: Publisher): Promise { + const app = this.getExternalAppFromBackgroundService(addon, publisher); + + return ExternalApps.kill(app); + } } diff --git a/src/renderer/utils/DataCache.ts b/src/renderer/utils/DataCache.ts index d237eafc..016ce1b1 100644 --- a/src/renderer/utils/DataCache.ts +++ b/src/renderer/utils/DataCache.ts @@ -1,31 +1,29 @@ export class DataCache { + key: string; + limit: number; - key: string; - limit: number; + constructor(key: string, limit: number) { + this.key = key; + this.limit = limit; + } - constructor(key: string, limit: number) { - this.key = key; - this.limit = limit; - } - - static from(key: string, limit: number): DataCache { - return new DataCache(key, limit); - } + static from(key: string, limit: number): DataCache { + return new DataCache(key, limit); + } - async fetchOrCompute(fetcher: () => Promise): Promise { - const cachedData = JSON.parse(localStorage.getItem(`data_cache_${this.key}`)); - const cachedDataTimestamp = Number(localStorage.getItem(`data_cache_${this.key}_timestamp`)); + async fetchOrCompute(fetcher: () => Promise): Promise { + const cachedData = JSON.parse(localStorage.getItem(`data_cache_${this.key}`)); + const cachedDataTimestamp = Number(localStorage.getItem(`data_cache_${this.key}_timestamp`)); - if (cachedData && Date.now() - cachedDataTimestamp < this.limit) { - return cachedData as T; - } else { - const data = await fetcher(); + if (cachedData && Date.now() - cachedDataTimestamp < this.limit) { + return cachedData as T; + } else { + const data = await fetcher(); - localStorage.setItem(`data_cache_${this.key}`, JSON.stringify(data)); - localStorage.setItem(`data_cache_${this.key}_timestamp`, String(Date.now())); + localStorage.setItem(`data_cache_${this.key}`, JSON.stringify(data)); + localStorage.setItem(`data_cache_${this.key}_timestamp`, String(Date.now())); - return data; - } + return data; } - + } } diff --git a/src/renderer/utils/Directories.ts b/src/renderer/utils/Directories.ts index a6e94543..25cd814a 100644 --- a/src/renderer/utils/Directories.ts +++ b/src/renderer/utils/Directories.ts @@ -1,158 +1,156 @@ -import path from "path"; -import { Addon } from "renderer/utils/InstallerConfiguration"; -import fs from "fs-extra"; -import settings from "common/settings"; +import path from 'path'; +import { Addon } from 'renderer/utils/InstallerConfiguration'; +import fs from 'fs-extra'; +import settings from 'common/settings'; const TEMP_DIRECTORY_PREFIX = 'flybywire-current-install'; -const TEMP_DIRECTORY_PREFIXES_FOR_CLEANUP = [ - 'flybywire_current_install', - TEMP_DIRECTORY_PREFIX, -]; +const TEMP_DIRECTORY_PREFIXES_FOR_CLEANUP = ['flybywire_current_install', TEMP_DIRECTORY_PREFIX]; const MSFS_APPDATA_PATH = 'Packages\\Microsoft.FlightSimulator_8wekyb3d8bbwe\\LocalState\\packages\\'; const MSFS_STEAM_PATH = 'Microsoft Flight Simulator\\Packages'; export class Directories { - private static sanitize(suffix: string): string { - return path.normalize(suffix).replace(/^(\.\.(\/|\\|$))+/, ''); - } + private static sanitize(suffix: string): string { + return path.normalize(suffix).replace(/^(\.\.(\/|\\|$))+/, ''); + } - static communityLocation(): string { - return settings.get('mainSettings.msfsCommunityPath') as string; - } + static communityLocation(): string { + return settings.get('mainSettings.msfsCommunityPath') as string; + } - static inCommunityLocation(targetDir: string): string { - return path.join(Directories.communityLocation(), this.sanitize(targetDir)); - } + static inCommunityLocation(targetDir: string): string { + return path.join(Directories.communityLocation(), this.sanitize(targetDir)); + } - static inCommunityPackage(addon: Addon, targetDir: string): string { - const baseDir = this.inCommunityLocation(this.sanitize(addon.targetDirectory)); - return path.join(baseDir, this.sanitize(targetDir)); - } + static inCommunityPackage(addon: Addon, targetDir: string): string { + const baseDir = this.inCommunityLocation(this.sanitize(addon.targetDirectory)); + return path.join(baseDir, this.sanitize(targetDir)); + } - static installLocation(): string { - return settings.get('mainSettings.installPath') as string; - } + static installLocation(): string { + return settings.get('mainSettings.installPath') as string; + } - static inInstallLocation(targetDir: string): string { - return path.join(Directories.installLocation(), this.sanitize(targetDir)); - } + static inInstallLocation(targetDir: string): string { + return path.join(Directories.installLocation(), this.sanitize(targetDir)); + } - static inInstallPackage(addon: Addon, targetDir: string): string { - const baseDir = this.inInstallLocation(this.sanitize(addon.targetDirectory)); - return path.join(baseDir, this.sanitize(targetDir)); - } + static inInstallPackage(addon: Addon, targetDir: string): string { + const baseDir = this.inInstallLocation(this.sanitize(addon.targetDirectory)); + return path.join(baseDir, this.sanitize(targetDir)); + } - static tempLocation(): string { - return settings.get('mainSettings.separateTempLocation') ? settings.get('mainSettings.tempLocation') as string : settings.get('mainSettings.installPath') as string; - } + static tempLocation(): string { + return settings.get('mainSettings.separateTempLocation') + ? (settings.get('mainSettings.tempLocation') as string) + : (settings.get('mainSettings.installPath') as string); + } - static inTempLocation(targetDir: string): string { - return path.join(Directories.tempLocation(), this.sanitize(targetDir)); - } + static inTempLocation(targetDir: string): string { + return path.join(Directories.tempLocation(), this.sanitize(targetDir)); + } - static liveries(): string { - return settings.get('mainSettings.liveriesPath') as string; - } + static liveries(): string { + return settings.get('mainSettings.liveriesPath') as string; + } - static inLiveries(targetDir: string): string { - return path.join(settings.get('mainSettings.liveriesPath') as string, this.sanitize(targetDir)); - } + static inLiveries(targetDir: string): string { + return path.join(settings.get('mainSettings.liveriesPath') as string, this.sanitize(targetDir)); + } - static inPackagesMicrosoftStore(targetDir: string): string { - return path.join(process.env.LOCALAPPDATA, MSFS_APPDATA_PATH, this.sanitize(targetDir)); - } + static inPackagesMicrosoftStore(targetDir: string): string { + return path.join(process.env.LOCALAPPDATA, MSFS_APPDATA_PATH, this.sanitize(targetDir)); + } - static inPackagesSteam(targetDir: string): string { - return path.join(process.env.APPDATA, MSFS_STEAM_PATH, this.sanitize(targetDir)); - } + static inPackagesSteam(targetDir: string): string { + return path.join(process.env.APPDATA, MSFS_STEAM_PATH, this.sanitize(targetDir)); + } + + static inPackageCache(addon: Addon, targetDir: string): string { + const baseDir = this.inPackagesSteam(this.sanitize(addon.targetDirectory)); - static inPackageCache(addon: Addon, targetDir: string): string { - const baseDir = this.inPackagesSteam(this.sanitize(addon.targetDirectory)); + return path.join(baseDir, this.sanitize(targetDir)); + } - return path.join(baseDir, this.sanitize(targetDir)); + static temp(): string { + const dir = path.join(Directories.tempLocation(), `${TEMP_DIRECTORY_PREFIX}-${(Math.random() * 1000).toFixed(0)}`); + if (fs.existsSync(dir)) { + return Directories.temp(); } + return dir; + } - static temp(): string { - const dir = path.join(Directories.tempLocation(), `${TEMP_DIRECTORY_PREFIX}-${(Math.random() * 1000).toFixed(0)}`); - if (fs.existsSync(dir)) { - return Directories.temp(); - } - return dir; + static removeAllTemp(): void { + console.log('[CLEANUP] Removing all temp directories'); + + if (!fs.existsSync(Directories.tempLocation())) { + console.warn('[CLEANUP] Location of temporary folders does not exist. Aborting'); + return; } - static removeAllTemp(): void { - console.log('[CLEANUP] Removing all temp directories'); + try { + const dirents = fs + .readdirSync(Directories.tempLocation(), { withFileTypes: true }) + .filter((dirEnt) => dirEnt.isDirectory()) + .filter((dirEnt) => TEMP_DIRECTORY_PREFIXES_FOR_CLEANUP.some((it) => dirEnt.name.startsWith(it))); - if (!fs.existsSync(Directories.tempLocation())) { - console.warn('[CLEANUP] Location of temporary folders does not exist. Aborting'); - return; - } + for (const dir of dirents) { + const fullPath = Directories.inTempLocation(dir.name); + console.log('[CLEANUP] Removing', fullPath); try { - const dirents = fs.readdirSync(Directories.tempLocation(), { withFileTypes: true }) - .filter(dirEnt => dirEnt.isDirectory()) - .filter(dirEnt => TEMP_DIRECTORY_PREFIXES_FOR_CLEANUP.some((it) => dirEnt.name.startsWith(it))); - - for (const dir of dirents) { - const fullPath = Directories.inTempLocation(dir.name); - - console.log('[CLEANUP] Removing', fullPath); - try { - fs.removeSync(fullPath); - console.log('[CLEANUP] Removed', fullPath); - } catch (e) { - console.error('[CLEANUP] Could not remove', fullPath, e); - } - } - - console.log('[CLEANUP] Finished removing all temp directories'); + fs.removeSync(fullPath); + console.log('[CLEANUP] Removed', fullPath); } catch (e) { - console.error('[CLEANUP] Could not scan folder', Directories.tempLocation(), e); + console.error('[CLEANUP] Could not remove', fullPath, e); } + } + console.log('[CLEANUP] Finished removing all temp directories'); + } catch (e) { + console.error('[CLEANUP] Could not scan folder', Directories.tempLocation(), e); } + } - static removeAlternativesForAddon(addon: Addon): void { - addon.alternativeNames?.forEach(altName => { - const altDir = Directories.inInstallLocation(altName); + static removeAlternativesForAddon(addon: Addon): void { + addon.alternativeNames?.forEach((altName) => { + const altDir = Directories.inInstallLocation(altName); - if (fs.existsSync(altDir)) { - console.log('Removing alternative', altDir); - fs.removeSync(altDir); - } - }); - } + if (fs.existsSync(altDir)) { + console.log('Removing alternative', altDir); + fs.removeSync(altDir); + } + }); + } - static removeTargetForAddon(addon: Addon): void { - const dir = Directories.inInstallLocation(addon.targetDirectory); + static removeTargetForAddon(addon: Addon): void { + const dir = Directories.inInstallLocation(addon.targetDirectory); - if (fs.existsSync(dir)) { - console.log('Removing', dir); - fs.removeSync(dir); - } + if (fs.existsSync(dir)) { + console.log('Removing', dir); + fs.removeSync(dir); } + } - static isFragmenterInstall(target: string | Addon): boolean { - const targetDir = typeof target === 'string' ? target : Directories.inInstallLocation(target.targetDirectory); - - return fs.existsSync(path.join(targetDir, 'install.json')); - } + static isFragmenterInstall(target: string | Addon): boolean { + const targetDir = typeof target === 'string' ? target : Directories.inInstallLocation(target.targetDirectory); - static isGitInstall(target: string | Addon): boolean { - const targetDir = typeof target === 'string' ? target : Directories.inInstallLocation(target.targetDirectory); + return fs.existsSync(path.join(targetDir, 'install.json')); + } - try { - const symlinkPath = fs.readlinkSync(targetDir); - if (symlinkPath && fs.existsSync(path.join(symlinkPath, '/../../../.git'))) { - console.log('Is git repo', targetDir); - return true; - } - } catch { - console.log('Is not git repo', targetDir); - return false; - } + static isGitInstall(target: string | Addon): boolean { + const targetDir = typeof target === 'string' ? target : Directories.inInstallLocation(target.targetDirectory); + try { + const symlinkPath = fs.readlinkSync(targetDir); + if (symlinkPath && fs.existsSync(path.join(symlinkPath, '/../../../.git'))) { + console.log('Is git repo', targetDir); + return true; + } + } catch { + console.log('Is not git repo', targetDir); + return false; } + } } diff --git a/src/renderer/utils/ExternalApps.ts b/src/renderer/utils/ExternalApps.ts index 135d5796..d508cc07 100644 --- a/src/renderer/utils/ExternalApps.ts +++ b/src/renderer/utils/ExternalApps.ts @@ -1,70 +1,74 @@ -import { Addon, ExternalApplicationDefinition, Publisher } from "renderer/utils/InstallerConfiguration"; +import { Addon, ExternalApplicationDefinition, Publisher } from 'renderer/utils/InstallerConfiguration'; import net from 'net'; import ws from 'ws'; -import { Resolver } from "renderer/utils/Resolver"; +import { Resolver } from 'renderer/utils/Resolver'; export class ExternalApps { - static forAddon(addon: Addon, publisher: Publisher): ExternalApplicationDefinition[] { - return addon.disallowedRunningExternalApps?.map((reference) => { - const def = Resolver.findDefinition(reference, publisher); + static forAddon(addon: Addon, publisher: Publisher): ExternalApplicationDefinition[] { + return ( + addon.disallowedRunningExternalApps?.map((reference) => { + const def = Resolver.findDefinition(reference, publisher); - if (def.kind !== 'externalApp') { - throw new Error(`definition (key=${def.key}) has kind=${def.kind}, expected kind=externalApp`); - } + if (def.kind !== 'externalApp') { + throw new Error(`definition (key=${def.key}) has kind=${def.kind}, expected kind=externalApp`); + } - return def; - }) ?? []; - } + return def; + }) ?? [] + ); + } - static async determineStateWithWS(app: ExternalApplicationDefinition): Promise { - return new Promise((resolve, reject) => { - try { - const wbs = new ws.WebSocket(app.url); + static async determineStateWithWS(app: ExternalApplicationDefinition): Promise { + return new Promise((resolve, reject) => { + try { + const wbs = new ws.WebSocket(app.url); - wbs.addEventListener('open', () => resolve(true)); - wbs.addEventListener('error', () => resolve(false)); - } catch (e) { - reject(new Error('Error while establishing WS external app state, see exception above')); - console.error(e); - } - }); - } + wbs.addEventListener('open', () => resolve(true)); + wbs.addEventListener('error', () => resolve(false)); + } catch (e) { + reject(new Error('Error while establishing WS external app state, see exception above')); + console.error(e); + } + }); + } - static async determineStateWithHttp(app: ExternalApplicationDefinition): Promise { - return new Promise((resolve) => { - fetch(app.url).then((resp) => { - resolve(resp.status === 200); - }).catch(() => { - resolve(false); - }); + static async determineStateWithHttp(app: ExternalApplicationDefinition): Promise { + return new Promise((resolve) => { + fetch(app.url) + .then((resp) => { + resolve(resp.status === 200); + }) + .catch(() => { + resolve(false); }); - } + }); + } - static async determineStateWithTcp(app: ExternalApplicationDefinition): Promise { - return new Promise((resolve, reject) => { - try { - const socket = net.connect(app.port); + static async determineStateWithTcp(app: ExternalApplicationDefinition): Promise { + return new Promise((resolve, reject) => { + try { + const socket = net.connect(app.port); - socket.on('connect', () => { - resolve(true); - socket.destroy(); - }); - socket.on('error', () => { - resolve(false); - socket.destroy(); - }); - } catch (e) { - reject(new Error('Error while establishing TCP external app state, see exception above')); - console.error(e); - } + socket.on('connect', () => { + resolve(true); + socket.destroy(); }); - } - - static async kill(app: ExternalApplicationDefinition): Promise { - if (!app.killUrl) { - throw new Error('Cannot kill external app if it has no killUrl value'); - } + socket.on('error', () => { + resolve(false); + socket.destroy(); + }); + } catch (e) { + reject(new Error('Error while establishing TCP external app state, see exception above')); + console.error(e); + } + }); + } - return fetch(app.killUrl, { method: app.killMethod ?? 'POST' }).then(); + static async kill(app: ExternalApplicationDefinition): Promise { + if (!app.killUrl) { + throw new Error('Cannot kill external app if it has no killUrl value'); } + + return fetch(app.killUrl, { method: app.killMethod ?? 'POST' }).then(); + } } diff --git a/src/renderer/utils/ExternalAppsUI.tsx b/src/renderer/utils/ExternalAppsUI.tsx index ed41bd88..e685690b 100644 --- a/src/renderer/utils/ExternalAppsUI.tsx +++ b/src/renderer/utils/ExternalAppsUI.tsx @@ -1,9 +1,9 @@ -import React from "react"; -import { Addon, ExternalApplicationDefinition, Publisher } from "renderer/utils/InstallerConfiguration"; -import { store, useAppSelector } from "renderer/redux/store"; -import { ApplicationStatus } from "renderer/components/AddonSection/Enums"; -import { ExternalApps } from "renderer/utils/ExternalApps"; -import { CannotInstallDialog } from "renderer/components/Modal/CannotInstallDialog"; +import React from 'react'; +import { Addon, ExternalApplicationDefinition, Publisher } from 'renderer/utils/InstallerConfiguration'; +import { store, useAppSelector } from 'renderer/redux/store'; +import { ApplicationStatus } from 'renderer/components/AddonSection/Enums'; +import { ExternalApps } from 'renderer/utils/ExternalApps'; +import { CannotInstallDialog } from 'renderer/components/Modal/CannotInstallDialog'; export type AddonExternalAppsInfo = [running: ExternalApplicationDefinition[], all: ExternalApplicationDefinition[]]; @@ -14,35 +14,39 @@ export type AddonExternalAppsInfo = [running: ExternalApplicationDefinition[], a * @param publisher the publisher of the addon */ export const useAddonExternalApps = (addon: Addon, publisher: Publisher): AddonExternalAppsInfo => { - const applicationStatus = useAppSelector((state) => state.applicationStatus); + const applicationStatus = useAppSelector((state) => state.applicationStatus); - const disallowedRunningExternalApps = ExternalApps.forAddon(addon, publisher); + const disallowedRunningExternalApps = ExternalApps.forAddon(addon, publisher); - return [ - disallowedRunningExternalApps.filter((it) => applicationStatus[it.key] === ApplicationStatus.Open), - disallowedRunningExternalApps, - ]; + return [ + disallowedRunningExternalApps.filter((it) => applicationStatus[it.key] === ApplicationStatus.Open), + disallowedRunningExternalApps, + ]; }; export class ExternalAppsUI { - /** - * Shows a modal presenting the user with running external apps for this addon that must be closed. When all apps are closed, - * and the user presses "Confirm", the promise is resolved with `true`. In all other cases, it is resolved with `false`. - * - * @param addon the addon - * @param publisher the publisher of the addon - * @param showModal a function to show a modal and return a promise based on the button clicked - */ - static async ensureNoneRunningForAddon(addon: Addon, publisher: Publisher, showModal: (modal: JSX.Element) => Promise): Promise { - const addonExternalApps = ExternalApps.forAddon(addon, publisher); - const runningExternalApps = addonExternalApps.filter((app) => store.getState().applicationStatus[app.key] == ApplicationStatus.Open); + /** + * Shows a modal presenting the user with running external apps for this addon that must be closed. When all apps are closed, + * and the user presses "Confirm", the promise is resolved with `true`. In all other cases, it is resolved with `false`. + * + * @param addon the addon + * @param publisher the publisher of the addon + * @param showModal a function to show a modal and return a promise based on the button clicked + */ + static async ensureNoneRunningForAddon( + addon: Addon, + publisher: Publisher, + showModal: (modal: JSX.Element) => Promise, + ): Promise { + const addonExternalApps = ExternalApps.forAddon(addon, publisher); + const runningExternalApps = addonExternalApps.filter( + (app) => store.getState().applicationStatus[app.key] == ApplicationStatus.Open, + ); - if (runningExternalApps.length > 0) { - return await showModal( - , - ); - } - - return true; + if (runningExternalApps.length > 0) { + return await showModal(); } + + return true; + } } diff --git a/src/renderer/utils/FreeDiskSpace.ts b/src/renderer/utils/FreeDiskSpace.ts index 7618bbd2..4954d727 100644 --- a/src/renderer/utils/FreeDiskSpace.ts +++ b/src/renderer/utils/FreeDiskSpace.ts @@ -1,82 +1,81 @@ -import { Directories } from "renderer/utils/Directories"; +import { Directories } from 'renderer/utils/Directories'; import fs from 'fs-extra'; -import checkDiskSpace from "check-disk-space"; +import checkDiskSpace from 'check-disk-space'; export enum FreeDiskSpaceStatus { - Unknown, - NotLimited, - LimitedByDestination, - LimitedByTemporary, - LimitedByBoth, + Unknown, + NotLimited, + LimitedByDestination, + LimitedByTemporary, + LimitedByBoth, } export interface FreeDiskSpaceInfo { - freeSpaceInDest: number, - freeSpaceInTemp: number, - status: FreeDiskSpaceStatus, + freeSpaceInDest: number; + freeSpaceInTemp: number; + status: FreeDiskSpaceStatus; } export class FreeDiskSpace { + static async analyse(requiredSpace: number): Promise { + if (!Number.isFinite(requiredSpace)) { + return { + freeSpaceInTemp: -1, + freeSpaceInDest: -1, + status: FreeDiskSpaceStatus.Unknown, + }; + } - static async analyse(requiredSpace: number): Promise { - if (!Number.isFinite(requiredSpace)) { - return { - freeSpaceInTemp: -1, - freeSpaceInDest: -1, - status: FreeDiskSpaceStatus.Unknown, - }; - } - - let resolvedDestDir = Directories.installLocation(); - let resolvedTempDir = Directories.tempLocation(); - - try { - resolvedDestDir = await fs.readlink(resolvedDestDir); - } catch (e) { - // noop - it's probably not a link - } + let resolvedDestDir = Directories.installLocation(); + let resolvedTempDir = Directories.tempLocation(); - try { - resolvedTempDir = await fs.readlink(resolvedTempDir); - } catch (e) { - // noop - it's probably not a link - } + try { + resolvedDestDir = await fs.readlink(resolvedDestDir); + } catch (e) { + // noop - it's probably not a link + } - let freeDestDirSpace = NaN; - try { - freeDestDirSpace = (await checkDiskSpace(resolvedDestDir)).free; - } catch (e) { - // noop - user probably does not have `wmic` on their system - } + try { + resolvedTempDir = await fs.readlink(resolvedTempDir); + } catch (e) { + // noop - it's probably not a link + } - let freeTempDirSpace = NaN; - try { - freeTempDirSpace = (await checkDiskSpace(resolvedTempDir)).free; - } catch (e) { - // noop - user probably does not have `wmic` on their system - } + let freeDestDirSpace = NaN; + try { + freeDestDirSpace = (await checkDiskSpace(resolvedDestDir)).free; + } catch (e) { + // noop - user probably does not have `wmic` on their system + } - if (!Number.isFinite(freeDestDirSpace) || !Number.isFinite(freeTempDirSpace)) { - return { - freeSpaceInTemp: -1, - freeSpaceInDest: -1, - status: FreeDiskSpaceStatus.Unknown, - }; - } + let freeTempDirSpace = NaN; + try { + freeTempDirSpace = (await checkDiskSpace(resolvedTempDir)).free; + } catch (e) { + // noop - user probably does not have `wmic` on their system + } - let status = FreeDiskSpaceStatus.NotLimited; - if (requiredSpace >= freeDestDirSpace && requiredSpace >= freeTempDirSpace) { - status = FreeDiskSpaceStatus.LimitedByBoth; - } else if (requiredSpace >= freeDestDirSpace) { - status = FreeDiskSpaceStatus.LimitedByDestination; - } else if (requiredSpace >= freeTempDirSpace) { - status = FreeDiskSpaceStatus.LimitedByTemporary; - } + if (!Number.isFinite(freeDestDirSpace) || !Number.isFinite(freeTempDirSpace)) { + return { + freeSpaceInTemp: -1, + freeSpaceInDest: -1, + status: FreeDiskSpaceStatus.Unknown, + }; + } - return { - freeSpaceInDest: freeDestDirSpace, - freeSpaceInTemp: freeTempDirSpace, - status, - }; + let status = FreeDiskSpaceStatus.NotLimited; + if (requiredSpace >= freeDestDirSpace && requiredSpace >= freeTempDirSpace) { + status = FreeDiskSpaceStatus.LimitedByBoth; + } else if (requiredSpace >= freeDestDirSpace) { + status = FreeDiskSpaceStatus.LimitedByDestination; + } else if (requiredSpace >= freeTempDirSpace) { + status = FreeDiskSpaceStatus.LimitedByTemporary; } + + return { + freeSpaceInDest: freeDestDirSpace, + freeSpaceInTemp: freeTempDirSpace, + status, + }; + } } diff --git a/src/renderer/utils/IncompatibleAddOnsCheck.ts b/src/renderer/utils/IncompatibleAddOnsCheck.ts index 437c6768..beffc04a 100644 --- a/src/renderer/utils/IncompatibleAddOnsCheck.ts +++ b/src/renderer/utils/IncompatibleAddOnsCheck.ts @@ -1,72 +1,72 @@ -import { Directories } from "renderer/utils/Directories"; -import { Addon, AddonIncompatibleAddon } from "renderer/utils/InstallerConfiguration"; -import fs from "fs-extra"; -import path from "path"; +import { Directories } from 'renderer/utils/Directories'; +import { Addon, AddonIncompatibleAddon } from 'renderer/utils/InstallerConfiguration'; +import fs from 'fs-extra'; +import path from 'path'; import semverSatisfies from 'semver/functions/satisfies'; export class IncompatibleAddOnsCheck { + /** + * Find incompatible add-ons + * This iterates through the first level of folders of the MSFS Community folder looking for the manifest.json + * file. It compares the manifest.json file content with the configured incompatible add-ons (data.ts) and if it + * finds one or more matches, it will issue a warning. + */ + static async checkIncompatibleAddOns(addon: Addon): Promise { + console.log('Searching incompatible add-ons'); - /** - * Find incompatible add-ons - * This iterates through the first level of folders of the MSFS Community folder looking for the manifest.json - * file. It compares the manifest.json file content with the configured incompatible add-ons (data.ts) and if it - * finds one or more matches, it will issue a warning. - */ - static async checkIncompatibleAddOns(addon: Addon): Promise { - console.log('Searching incompatible add-ons'); + const incompatibleAddons: AddonIncompatibleAddon[] = []; + const manifestFileName = 'manifest.json'; + const comDir = Directories.communityLocation(); - const incompatibleAddons: AddonIncompatibleAddon[] = []; - const manifestFileName = 'manifest.json'; - const comDir = Directories.communityLocation(); + try { + const addonFolders = fs.readdirSync(comDir); - try { - const addonFolders = fs.readdirSync(comDir); + for (const entry of addonFolders) { + const filePath = path.join(comDir, entry); + const stat = fs.statSync(filePath); - for (const entry of addonFolders) { - const filePath = path.join(comDir, entry); - const stat = fs.statSync(filePath); + if (stat.isDirectory()) { + const dirEntries = fs.readdirSync(filePath); - if (stat.isDirectory()) { - const dirEntries = fs.readdirSync(filePath); + if (dirEntries.includes(manifestFileName)) { + const manifest = JSON.parse(fs.readFileSync(path.join(filePath, manifestFileName), 'utf8')); - if (dirEntries.includes(manifestFileName)) { - const manifest = JSON.parse(fs.readFileSync(path.join(filePath, manifestFileName), 'utf8')); + for (const item of addon.incompatibleAddons) { + // This checks the configuration item properties (if set) against the manifest.json file + // entry property values. If all properties match, the add-on is considered incompatible. + // packageVersion syntax follows: https://www.npmjs.com/package/semver + // Future improvement would be to allow for regular expressions in the configuration item. + const titleMatches = !item.title || manifest.title === item.title; + const creatorMatches = !item.creator || manifest.creator === item.creator; + const packageVersionTargeted = + !item.packageVersion || semverSatisfies(manifest.package_version, item.packageVersion); - for (const item of addon.incompatibleAddons) { - // This checks the configuration item properties (if set) against the manifest.json file - // entry property values. If all properties match, the add-on is considered incompatible. - // packageVersion syntax follows: https://www.npmjs.com/package/semver - // Future improvement would be to allow for regular expressions in the configuration item. - const titleMatches = !item.title || manifest.title === item.title; - const creatorMatches = !item.creator || manifest.creator === item.creator; - const packageVersionTargeted = !item.packageVersion || semverSatisfies(manifest.package_version, item.packageVersion); + if (titleMatches && creatorMatches && packageVersionTargeted) { + // Also write this to the log as this info might be useful for support. + console.log(`Incompatible Add-On found: ${manifest.title}: ${item.description}`); - if (titleMatches && creatorMatches && packageVersionTargeted) { - // Also write this to the log as this info might be useful for support. - console.log(`Incompatible Add-On found: ${manifest.title}: ${item.description}`); - - incompatibleAddons.push({ - title: item.title, - creator: item.creator, - packageVersion: item.packageVersion, - folder: entry, - description: item.description, - }); - } - } - } - } + incompatibleAddons.push({ + title: item.title, + creator: item.creator, + packageVersion: item.packageVersion, + folder: entry, + description: item.description, + }); + } } - } catch (e) { - console.error("Error searching incompatible add-ons in %s: %s", comDir, e); - } - - if (incompatibleAddons.length > 0) { - console.log('Incompatible add-ons found'); - } else { - console.log('No incompatible add-ons found'); + } } + } + } catch (e) { + console.error('Error searching incompatible add-ons in %s: %s', comDir, e); + } - return incompatibleAddons; + if (incompatibleAddons.length > 0) { + console.log('Incompatible add-ons found'); + } else { + console.log('No incompatible add-ons found'); } + + return incompatibleAddons; + } } diff --git a/src/renderer/utils/InstallManager.tsx b/src/renderer/utils/InstallManager.tsx index 87d6cae6..9d846ea9 100644 --- a/src/renderer/utils/InstallManager.tsx +++ b/src/renderer/utils/InstallManager.tsx @@ -1,694 +1,711 @@ -import React from "react"; -import { Addon, AddonTrack, Publisher } from "renderer/utils/InstallerConfiguration"; -import { PromptModal } from "renderer/components/Modal"; -import { ButtonType } from "renderer/components/Button"; +import React from 'react'; +import { Addon, AddonTrack, Publisher } from 'renderer/utils/InstallerConfiguration'; +import { PromptModal } from 'renderer/components/Modal'; +import { ButtonType } from 'renderer/components/Button'; import { - clearDownloadInterrupted, - deleteDownload, - registerNewDownload, - setDownloadInterrupted, - setDownloadModuleIndex, - updateDownloadProgress, -} from "renderer/redux/features/downloads"; -import { Directories } from "renderer/utils/Directories"; -import fs from "fs-extra"; -import { ApplicationStatus, InstallStatus } from "renderer/components/AddonSection/Enums"; + clearDownloadInterrupted, + deleteDownload, + registerNewDownload, + setDownloadInterrupted, + setDownloadModuleIndex, + updateDownloadProgress, +} from 'renderer/redux/features/downloads'; +import { Directories } from 'renderer/utils/Directories'; +import fs from 'fs-extra'; +import { ApplicationStatus, InstallStatus } from 'renderer/components/AddonSection/Enums'; import { - FragmenterContextEvents, - FragmenterError, - FragmenterInstallerEvents, - FragmenterOperation, - FragmenterUpdateChecker, -} from "@flybywiresim/fragmenter"; -import settings from "common/settings"; -import { store } from "renderer/redux/store"; -import { InstallState, setInstallStatus } from "renderer/redux/features/installStatus"; -import { setSelectedTrack } from "renderer/redux/features/selectedTrack"; -import { setInstalledTrack } from "renderer/redux/features/installedTrack"; -import path from "path"; -import { DependencyDialogBody } from "renderer/components/Modal/DependencyDialog"; -import { IncompatibleAddonDialogBody } from "renderer/components/Modal/IncompatibleAddonDialog"; -import { Resolver } from "renderer/utils/Resolver"; -import { AutostartDialog } from "renderer/components/Modal/AutostartDialog"; -import { BackgroundServices } from "renderer/utils/BackgroundServices"; -import { CannotInstallDialog } from "renderer/components/Modal/CannotInstallDialog"; -import { ExternalApps } from "renderer/utils/ExternalApps"; -import { ExternalAppsUI } from "./ExternalAppsUI"; -import { ipcRenderer } from "electron"; + FragmenterContextEvents, + FragmenterError, + FragmenterInstallerEvents, + FragmenterOperation, + FragmenterUpdateChecker, +} from '@flybywiresim/fragmenter'; +import settings from 'common/settings'; +import { store } from 'renderer/redux/store'; +import { InstallState, setInstallStatus } from 'renderer/redux/features/installStatus'; +import { setSelectedTrack } from 'renderer/redux/features/selectedTrack'; +import { setInstalledTrack } from 'renderer/redux/features/installedTrack'; +import path from 'path'; +import { DependencyDialogBody } from 'renderer/components/Modal/DependencyDialog'; +import { IncompatibleAddonDialogBody } from 'renderer/components/Modal/IncompatibleAddonDialog'; +import { Resolver } from 'renderer/utils/Resolver'; +import { AutostartDialog } from 'renderer/components/Modal/AutostartDialog'; +import { BackgroundServices } from 'renderer/utils/BackgroundServices'; +import { CannotInstallDialog } from 'renderer/components/Modal/CannotInstallDialog'; +import { ExternalApps } from 'renderer/utils/ExternalApps'; +import { ExternalAppsUI } from './ExternalAppsUI'; +import { ipcRenderer } from 'electron'; import channels from 'common/channels'; import * as Sentry from '@sentry/electron/renderer'; -import { ErrorDialog } from "renderer/components/Modal/ErrorDialog"; -import { InstallSizeDialog } from "renderer/components/Modal/InstallSizeDialog"; -import { IncompatibleAddOnsCheck } from "renderer/utils/IncompatibleAddOnsCheck"; -import { FreeDiskSpace, FreeDiskSpaceStatus } from "renderer/utils/FreeDiskSpace"; +import { ErrorDialog } from 'renderer/components/Modal/ErrorDialog'; +import { InstallSizeDialog } from 'renderer/components/Modal/InstallSizeDialog'; +import { IncompatibleAddOnsCheck } from 'renderer/utils/IncompatibleAddOnsCheck'; +import { FreeDiskSpace, FreeDiskSpaceStatus } from 'renderer/utils/FreeDiskSpace'; -type FragmenterEventArguments = Parameters<(FragmenterInstallerEvents & FragmenterContextEvents)[K]> +type FragmenterEventArguments = Parameters< + (FragmenterInstallerEvents & FragmenterContextEvents)[K] +>; export enum InstallResult { - Success, - Failure, - Cancelled, + Success, + Failure, + Cancelled, } export class InstallManager { - private static abortControllers = (() => { - const arr = new Array(20); - - arr.fill(new AbortController); - - return arr; - })(); - - private static lowestAvailableAbortControllerID(): number { - for (let i = 0; i < this.abortControllers.length; i++) { - if (!store.getState().downloads.map(download => download.abortControllerID).includes(i)) { - return i; - } - } + private static abortControllers = (() => { + const arr = new Array(20); + + arr.fill(new AbortController()); + + return arr; + })(); + + private static lowestAvailableAbortControllerID(): number { + for (let i = 0; i < this.abortControllers.length; i++) { + if ( + !store + .getState() + .downloads.map((download) => download.abortControllerID) + .includes(i) + ) { + return i; + } } + } - static async determineAddonInstallState(addon: Addon): Promise { - const addonSelectedTrack = this.getAddonSelectedTrack(addon); - const addonInstalledTrack = this.getAddonInstalledTrack(addon); - - if (!addonSelectedTrack) { - return { status: InstallStatus.Unknown }; - } - - console.log("Checking install status"); - - const installDir = Directories.inInstallLocation(addon.targetDirectory); - - if (!fs.existsSync(installDir)) { - return { status: InstallStatus.NotInstalled }; - } - - console.log("Checking for git install"); - if (Directories.isGitInstall(installDir)) { - return { status: InstallStatus.GitInstall }; - } + static async determineAddonInstallState(addon: Addon): Promise { + const addonSelectedTrack = this.getAddonSelectedTrack(addon); + const addonInstalledTrack = this.getAddonInstalledTrack(addon); - try { - const updateInfo = await new FragmenterUpdateChecker().needsUpdate(addonSelectedTrack.url, installDir, { - forceCacheBust: true, - }); - console.log("Update info", updateInfo); + if (!addonSelectedTrack) { + return { status: InstallStatus.Unknown }; + } - if (addonSelectedTrack !== addonInstalledTrack && addonInstalledTrack) { - return { status: InstallStatus.TrackSwitch }; - } - if (updateInfo.isFreshInstall) { - return { status: InstallStatus.NotInstalled }; - } + console.log('Checking install status'); - if (updateInfo.needsUpdate) { - return { status: InstallStatus.NeedsUpdate }; - } + const installDir = Directories.inInstallLocation(addon.targetDirectory); - return { status: InstallStatus.UpToDate }; - } catch (e) { - console.error(e); - return { status: InstallStatus.Unknown }; - } + if (!fs.existsSync(installDir)) { + return { status: InstallStatus.NotInstalled }; } - static async getAddonInstallState(addon: Addon): Promise { - try { - return store.getState().installStatus[addon.key] as InstallState; - } catch (e) { - const state = await this.determineAddonInstallState(addon); - this.setCurrentInstallState(addon, state); - return state; - } + console.log('Checking for git install'); + if (Directories.isGitInstall(installDir)) { + return { status: InstallStatus.GitInstall }; } - static getAddonSelectedTrack(addon: Addon): AddonTrack { - try { - return store.getState().selectedTracks[addon.key] as AddonTrack; - } catch (e) { - this.setCurrentSelectedTrack(addon, null); - return null; - } + try { + const updateInfo = await new FragmenterUpdateChecker().needsUpdate(addonSelectedTrack.url, installDir, { + forceCacheBust: true, + }); + console.log('Update info', updateInfo); + + if (addonSelectedTrack !== addonInstalledTrack && addonInstalledTrack) { + return { status: InstallStatus.TrackSwitch }; + } + if (updateInfo.isFreshInstall) { + return { status: InstallStatus.NotInstalled }; + } + + if (updateInfo.needsUpdate) { + return { status: InstallStatus.NeedsUpdate }; + } + + return { status: InstallStatus.UpToDate }; + } catch (e) { + console.error(e); + return { status: InstallStatus.Unknown }; } - - static getAddonInstalledTrack(addon: Addon): AddonTrack { - try { - return store.getState().installedTracks[addon.key] as AddonTrack; - } catch (e) { - this.setCurrentlyInstalledTrack(addon, null); - return null; - } + } + + static async getAddonInstallState(addon: Addon): Promise { + try { + return store.getState().installStatus[addon.key] as InstallState; + } catch (e) { + const state = await this.determineAddonInstallState(addon); + this.setCurrentInstallState(addon, state); + return state; } - - private static setCurrentInstallState(addon: Addon, installState: InstallState): void { - store.dispatch(setInstallStatus({ addonKey: addon.key, installState })); + } + + static getAddonSelectedTrack(addon: Addon): AddonTrack { + try { + return store.getState().selectedTracks[addon.key] as AddonTrack; + } catch (e) { + this.setCurrentSelectedTrack(addon, null); + return null; } - - private static setCurrentSelectedTrack(addon: Addon, track: AddonTrack): void { - store.dispatch(setSelectedTrack({ addonKey: addon.key, track: track })); + } + + static getAddonInstalledTrack(addon: Addon): AddonTrack { + try { + return store.getState().installedTracks[addon.key] as AddonTrack; + } catch (e) { + this.setCurrentlyInstalledTrack(addon, null); + return null; } + } - private static setCurrentlyInstalledTrack(addon: Addon, track: AddonTrack): void { - store.dispatch(setInstalledTrack({ addonKey: addon.key, installedTrack: track })); - } + private static setCurrentInstallState(addon: Addon, installState: InstallState): void { + store.dispatch(setInstallStatus({ addonKey: addon.key, installState })); + } - static async installAddon(addon: Addon, publisher: Publisher, showModal: (modal: JSX.Element) => Promise, dependencyOf?: Addon): Promise { - this.setCurrentInstallState(addon, { status: InstallStatus.DownloadPending }); + private static setCurrentSelectedTrack(addon: Addon, track: AddonTrack): void { + store.dispatch(setSelectedTrack({ addonKey: addon.key, track: track })); + } - const setErrorState = () => { - this.setCurrentInstallState(addon, { status: InstallStatus.DownloadError }); - }; + private static setCurrentlyInstalledTrack(addon: Addon, track: AddonTrack): void { + store.dispatch(setInstalledTrack({ addonKey: addon.key, installedTrack: track })); + } - const setCancelledState = () => { - this.setCurrentInstallState(addon, { status: InstallStatus.DownloadCanceled }); - }; + static async installAddon( + addon: Addon, + publisher: Publisher, + showModal: (modal: JSX.Element) => Promise, + dependencyOf?: Addon, + ): Promise { + this.setCurrentInstallState(addon, { status: InstallStatus.DownloadPending }); - const startResetStateTimer = (timeout = 3_000) => { - setTimeout(async () => { - store.dispatch(deleteDownload({ id: addon.key })); - this.setCurrentInstallState(addon, await this.determineAddonInstallState(addon)); - }, timeout); - }; + const setErrorState = () => { + this.setCurrentInstallState(addon, { status: InstallStatus.DownloadError }); + }; - const removeDownloadState = () => { - store.dispatch(deleteDownload({ id: addon.key })); - }; + const setCancelledState = () => { + this.setCurrentInstallState(addon, { status: InstallStatus.DownloadCanceled }); + }; - const track = this.getAddonSelectedTrack(addon); + const startResetStateTimer = (timeout = 3_000) => { + setTimeout(async () => { + store.dispatch(deleteDownload({ id: addon.key })); + this.setCurrentInstallState(addon, await this.determineAddonInstallState(addon)); + }, timeout); + }; - const disallowedRunningExternalApps = ExternalApps.forAddon(addon, publisher); + const removeDownloadState = () => { + store.dispatch(deleteDownload({ id: addon.key })); + }; - const runningExternalApps = disallowedRunningExternalApps.filter((it) => store.getState().applicationStatus[it.key] === ApplicationStatus.Open); + const track = this.getAddonSelectedTrack(addon); - if (runningExternalApps.length > 0) { - const doInstall = await showModal( - , - ); + const disallowedRunningExternalApps = ExternalApps.forAddon(addon, publisher); - if (!doInstall) { - startResetStateTimer(0); + const runningExternalApps = disallowedRunningExternalApps.filter( + (it) => store.getState().applicationStatus[it.key] === ApplicationStatus.Open, + ); - return InstallResult.Cancelled; - } - } + if (runningExternalApps.length > 0) { + const doInstall = await showModal(); - // Find dependencies - for (const dependency of addon.dependencies ?? []) { - const [, publisherKey, addonKey] = dependency.addon.match(/@(\w+)\/(\w+)/); + if (!doInstall) { + startResetStateTimer(0); - const dependencyPublisher = Resolver.findPublisher(publisherKey); - const dependencyAddon = Resolver.findAddon(publisherKey, addonKey); + return InstallResult.Cancelled; + } + } - if (!dependencyAddon) { - console.error(`Addon specified dependency for unknown addon: @${publisherKey}/${addonKey}`); - return InstallResult.Failure; - } + // Find dependencies + for (const dependency of addon.dependencies ?? []) { + const [, publisherKey, addonKey] = dependency.addon.match(/@(\w+)\/(\w+)/); + + const dependencyPublisher = Resolver.findPublisher(publisherKey); + const dependencyAddon = Resolver.findAddon(publisherKey, addonKey); + + if (!dependencyAddon) { + console.error(`Addon specified dependency for unknown addon: @${publisherKey}/${addonKey}`); + return InstallResult.Failure; + } + + const dependencyInstallState = await this.getAddonInstallState(dependencyAddon); + + const isDependencyUptoDate = dependencyInstallState.status === InstallStatus.UpToDate; + const isDependencyNotInstalled = dependencyInstallState.status === InstallStatus.NotInstalled; - const dependencyInstallState = await this.getAddonInstallState(dependencyAddon); - - const isDependencyUptoDate = dependencyInstallState.status === InstallStatus.UpToDate; - const isDependencyNotInstalled = dependencyInstallState.status === InstallStatus.NotInstalled; - - if (!isDependencyUptoDate) { - let doInstallDependency = true; - - if (dependency.optional && isDependencyNotInstalled) { - const settingString = `mainSettings.disableDependencyPrompt.${publisher.key}.${addon.key}.@${dependencyPublisher.key}/${dependencyAddon.key}`; - const doNotAsk = settings.get(settingString); - - doInstallDependency = false; - - if (!doNotAsk) { - doInstallDependency = await showModal( - - } - cancelText="No" - confirmText="Yes" - confirmColor={ButtonType.Positive} - dontShowAgainSettingName={settingString} - />, - ); - } + if (!isDependencyUptoDate) { + let doInstallDependency = true; + if (dependency.optional && isDependencyNotInstalled) { + const settingString = `mainSettings.disableDependencyPrompt.${publisher.key}.${addon.key}.@${dependencyPublisher.key}/${dependencyAddon.key}`; + const doNotAsk = settings.get(settingString); + + doInstallDependency = false; + + if (!doNotAsk) { + doInstallDependency = await showModal( + } + cancelText="No" + confirmText="Yes" + confirmColor={ButtonType.Positive} + dontShowAgainSettingName={settingString} + />, + ); + } + } - if (doInstallDependency) { - this.setCurrentInstallState(addon, { - status: InstallStatus.InstallingDependency, - dependencyAddonKey: dependencyAddon.key, - dependencyPublisherKey: dependencyPublisher.key, - }); + if (doInstallDependency) { + this.setCurrentInstallState(addon, { + status: InstallStatus.InstallingDependency, + dependencyAddonKey: dependencyAddon.key, + dependencyPublisherKey: dependencyPublisher.key, + }); - const result = await this.installAddon(dependencyAddon, dependencyPublisher, showModal, addon); + const result = await this.installAddon(dependencyAddon, dependencyPublisher, showModal, addon); - if (result === InstallResult.Failure) { - console.error('Error while installing dependency - aborting'); + if (result === InstallResult.Failure) { + console.error('Error while installing dependency - aborting'); - setErrorState(); - startResetStateTimer(); + setErrorState(); + startResetStateTimer(); - return InstallResult.Failure; - } else if (result === InstallResult.Cancelled) { - console.log('Dependency install cancelled, canceling main addon too.'); + return InstallResult.Failure; + } else if (result === InstallResult.Cancelled) { + console.log('Dependency install cancelled, canceling main addon too.'); - setCancelledState(); - startResetStateTimer(); + setCancelledState(); + startResetStateTimer(); - return InstallResult.Cancelled; - } else { - console.log(`Dependency @${publisherKey}/${addonKey} installed successfully.`); - } - } - } + return InstallResult.Cancelled; + } else { + console.log(`Dependency @${publisherKey}/${addonKey} installed successfully.`); + } } + } + } - if (addon.incompatibleAddons && addon.incompatibleAddons.length > 0) { - const incompatibleAddons = await IncompatibleAddOnsCheck.checkIncompatibleAddOns(addon); - if (incompatibleAddons.length > 0) { - const continueInstall = await showModal( - - } - cancelText="No" - confirmText="Yes" - confirmColor={ButtonType.Positive} - />, - ); - if (!continueInstall) { - startResetStateTimer(); - return InstallResult.Cancelled; - } - } + if (addon.incompatibleAddons && addon.incompatibleAddons.length > 0) { + const incompatibleAddons = await IncompatibleAddOnsCheck.checkIncompatibleAddOns(addon); + if (incompatibleAddons.length > 0) { + const continueInstall = await showModal( + } + cancelText="No" + confirmText="Yes" + confirmColor={ButtonType.Positive} + />, + ); + if (!continueInstall) { + startResetStateTimer(); + return InstallResult.Cancelled; } + } + } - const destDir = Directories.inInstallLocation(addon.targetDirectory); - const tempDir = Directories.temp(); + const destDir = Directories.inInstallLocation(addon.targetDirectory); + const tempDir = Directories.temp(); - const fragmenterUpdateChecker = new FragmenterUpdateChecker(); - const updateInfo = await fragmenterUpdateChecker.needsUpdate(track.url, destDir, { forceCacheBust: true }); + const fragmenterUpdateChecker = new FragmenterUpdateChecker(); + const updateInfo = await fragmenterUpdateChecker.needsUpdate(track.url, destDir, { forceCacheBust: true }); - // Confirm download size and required disk space with user - const requiredDiskSpace = updateInfo.requiredDiskSpace; + // Confirm download size and required disk space with user + const requiredDiskSpace = updateInfo.requiredDiskSpace; - const freeDeskSpaceInfo = await FreeDiskSpace.analyse(requiredDiskSpace); + const freeDeskSpaceInfo = await FreeDiskSpace.analyse(requiredDiskSpace); - const diskSpaceModalSettingString = `mainSettings.disableAddonDiskSpaceModal.${publisher.key}.${addon.key}`; - const dontAsk = settings.get(diskSpaceModalSettingString); + const diskSpaceModalSettingString = `mainSettings.disableAddonDiskSpaceModal.${publisher.key}.${addon.key}`; + const dontAsk = settings.get(diskSpaceModalSettingString); - if ((!dontAsk || freeDeskSpaceInfo.status !== FreeDiskSpaceStatus.NotLimited) && freeDeskSpaceInfo.status !== FreeDiskSpaceStatus.Unknown) { - const continueInstall = await showModal( - , - ); + if ( + (!dontAsk || freeDeskSpaceInfo.status !== FreeDiskSpaceStatus.NotLimited) && + freeDeskSpaceInfo.status !== FreeDiskSpaceStatus.Unknown + ) { + const continueInstall = await showModal( + , + ); - if (!continueInstall) { - startResetStateTimer(); + if (!continueInstall) { + startResetStateTimer(); - return InstallResult.Cancelled; - } - } + return InstallResult.Cancelled; + } + } - // Initialize abort controller for downloads - const abortControllerID = this.lowestAvailableAbortControllerID(); + // Initialize abort controller for downloads + const abortControllerID = this.lowestAvailableAbortControllerID(); - this.abortControllers[abortControllerID] = new AbortController(); - const signal = this.abortControllers[abortControllerID].signal; + this.abortControllers[abortControllerID] = new AbortController(); + const signal = this.abortControllers[abortControllerID].signal; - let moduleCount = updateInfo.isFreshInstall ? 1 : updateInfo.updatedModules.length + updateInfo.addedModules.length; - if (!updateInfo.isFreshInstall && updateInfo.baseChanged) { - moduleCount++; - } + let moduleCount = updateInfo.isFreshInstall ? 1 : updateInfo.updatedModules.length + updateInfo.addedModules.length; + if (!updateInfo.isFreshInstall && updateInfo.baseChanged) { + moduleCount++; + } - store.dispatch(registerNewDownload({ - id: addon.key, - module: '', - moduleCount, - abortControllerID: abortControllerID, - })); + store.dispatch( + registerNewDownload({ + id: addon.key, + module: '', + moduleCount, + abortControllerID: abortControllerID, + }), + ); + + if (tempDir === Directories.installLocation()) { + console.error('Community directory equals temp directory'); + this.notifyDownload(addon, false); + return InstallResult.Failure; + } - if (tempDir === Directories.installLocation()) { - console.error('Community directory equals temp directory'); - this.notifyDownload(addon, false); - return InstallResult.Failure; + console.log(`Installing track=${track.key}`); + console.log('Installing into:'); + console.log('---'); + console.log(`installDir: ${destDir}`); + console.log(`tempDir: ${tempDir}`); + console.log('---'); + + try { + // Create dest dir if it doesn't exist + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir); + } + + let lastPercent = 0; + + this.setCurrentInstallState(addon, { status: InstallStatus.DownloadPrep }); + + // Generate a random install iD to keep track of events related to our install + const ourInstallID = Math.floor(Math.random() * 1_000_000); + + const handleForwardedFragmenterEvent = ( + _: unknown, + installID: number, + event: keyof FragmenterInstallerEvents | keyof FragmenterContextEvents, + ...args: unknown[] + ) => { + if (installID !== ourInstallID) { + return; } - console.log(`Installing track=${track.key}`); - console.log("Installing into:"); - console.log('---'); - console.log(`installDir: ${destDir}`); - console.log(`tempDir: ${tempDir}`); - console.log('---'); - - try { - // Create dest dir if it doesn't exist - if (!fs.existsSync(destDir)) { - fs.mkdirSync(destDir); - } - - let lastPercent = 0; - - this.setCurrentInstallState(addon, { status: InstallStatus.DownloadPrep }); + switch (event) { + case 'downloadStarted': { + const [module] = args as FragmenterEventArguments; + + console.log('Downloading started for module', module.name); + + this.setCurrentInstallState(addon, { status: InstallStatus.Downloading }); + + store.dispatch( + updateDownloadProgress({ + id: addon.key, + module: module.name, + progress: { + interrupted: false, + totalPercent: 0, + splitPartPercent: 0, + splitPartIndex: 0, + splitPartCount: 0, + }, + }), + ); - // Generate a random install iD to keep track of events related to our install - const ourInstallID = Math.floor(Math.random() * 1_000_000); + break; + } + case 'phaseChange': { + const [phase] = args as FragmenterEventArguments; - const handleForwardedFragmenterEvent = (_: unknown, installID: number, event: keyof FragmenterInstallerEvents | keyof FragmenterContextEvents, ...args: unknown[]) => { - if (installID !== ourInstallID) { - return; - } + if (phase.op === FragmenterOperation.InstallFinish) { + this.setCurrentInstallState(addon, { status: InstallStatus.DownloadEnding }); + return; + } - switch (event) { - case 'downloadStarted': { - const [module] = args as FragmenterEventArguments; - - console.log("Downloading started for module", module.name); - - this.setCurrentInstallState(addon, { status: InstallStatus.Downloading }); - - store.dispatch( - updateDownloadProgress({ - id: addon.key, - module: module.name, - progress: { - interrupted: false, - totalPercent: 0, - splitPartPercent: 0, - splitPartIndex: 0, - splitPartCount: 0, - }, - })); - - break; - } - case 'phaseChange': { - const [phase] = args as FragmenterEventArguments; - - if (phase.op === FragmenterOperation.InstallFinish) { - this.setCurrentInstallState(addon, { status: InstallStatus.DownloadEnding }); - return; - } - - if ('moduleIndex' in phase) { - store.dispatch( - setDownloadModuleIndex({ - id: addon.key, - moduleIndex: phase.moduleIndex, - }), - ); - } - break; - } - case 'downloadProgress': { - const [module, progress] = args as FragmenterEventArguments; - - if (lastPercent !== progress.percent) { - lastPercent = progress.percent; - store.dispatch( - updateDownloadProgress({ - id: addon.key, - module: module.name, - progress: { - interrupted: false, - totalPercent: progress.percent, - splitPartPercent: progress.partPercent, - splitPartIndex: progress.partIndex, - splitPartCount: progress.numParts, - }, - })); - } - break; - } - case 'downloadInterrupted': { - store.dispatch( - setDownloadInterrupted({ id: addon.key }), - ); - - break; - } - case 'unzipStarted': { - const [module] = args as FragmenterEventArguments; - - console.log("Started unzipping module", module.name); - this.setCurrentInstallState(addon, { status: InstallStatus.Decompressing, percent: 0 }); - - if (dependencyOf) { - this.setCurrentInstallState(dependencyOf, { - status: InstallStatus.InstallingDependencyEnding, - dependencyAddonKey: addon.key, dependencyPublisherKey: publisher.key, - percent: 0, - }); - } - break; - } - case 'unzipProgress': { - const [, progress] = args as FragmenterEventArguments; - - const percent = Math.round(((progress.entryIndex + 1) / progress.entryCount) * 100); - - this.setCurrentInstallState(addon, { - status: InstallStatus.Decompressing, - percent, - entry: progress.entryName, - }); - - if (dependencyOf) { - this.setCurrentInstallState(dependencyOf, { - status: InstallStatus.InstallingDependencyEnding, - dependencyAddonKey: addon.key, dependencyPublisherKey: publisher.key, - percent, - }); - } - break; - } - case 'copyStarted': { - const [module] = args as FragmenterEventArguments; - - console.log("Started moving over module", module.name); - - if (module.name === 'full') { - this.setCurrentInstallState(addon, { status: InstallStatus.DownloadEnding }); - } - - break; - } - case 'retryScheduled': { - const [module, retryCount, waitSeconds] = args as FragmenterEventArguments; - - console.log("Scheduling a retry for module", module.name); - console.log("Retry count", retryCount); - console.log("Waiting for", waitSeconds, "seconds"); - - store.dispatch( - clearDownloadInterrupted({ id: addon.key }), - ); - - this.setCurrentInstallState(addon, { status: InstallStatus.DownloadRetry }); - break; - } - case 'retryStarted': { - const [module, retryCount] = args as FragmenterEventArguments; - - console.log("Starting a retry for module", module.name); - console.log("Retry count", retryCount); - - this.setCurrentInstallState(addon, { status: InstallStatus.Downloading }); - break; - } - case 'cancelled': { - this.setCurrentInstallState(addon, { status: InstallStatus.DownloadCanceled }); - break; - } - case 'error': { - const [error] = args as FragmenterEventArguments; - - console.error('Error from Fragmenter:', error); - Sentry.captureException(error); - } - } - }; + if ('moduleIndex' in phase) { + store.dispatch( + setDownloadModuleIndex({ + id: addon.key, + moduleIndex: phase.moduleIndex, + }), + ); + } + break; + } + case 'downloadProgress': { + const [module, progress] = args as FragmenterEventArguments; + + if (lastPercent !== progress.percent) { + lastPercent = progress.percent; + store.dispatch( + updateDownloadProgress({ + id: addon.key, + module: module.name, + progress: { + interrupted: false, + totalPercent: progress.percent, + splitPartPercent: progress.partPercent, + splitPartIndex: progress.partIndex, + splitPartCount: progress.numParts, + }, + }), + ); + } + break; + } + case 'downloadInterrupted': { + store.dispatch(setDownloadInterrupted({ id: addon.key })); + + break; + } + case 'unzipStarted': { + const [module] = args as FragmenterEventArguments; + + console.log('Started unzipping module', module.name); + this.setCurrentInstallState(addon, { status: InstallStatus.Decompressing, percent: 0 }); + + if (dependencyOf) { + this.setCurrentInstallState(dependencyOf, { + status: InstallStatus.InstallingDependencyEnding, + dependencyAddonKey: addon.key, + dependencyPublisherKey: publisher.key, + percent: 0, + }); + } + break; + } + case 'unzipProgress': { + const [, progress] = args as FragmenterEventArguments; - // Listen to forwarded fragmenter events - ipcRenderer.on(channels.installManager.fragmenterEvent, handleForwardedFragmenterEvent); + const percent = Math.round(((progress.entryIndex + 1) / progress.entryCount) * 100); - // Send cancel message when abort controller is aborted - this.abortControllers[abortControllerID].signal.addEventListener('abort', () => { - ipcRenderer.send(channels.installManager.cancelInstall, ourInstallID); + this.setCurrentInstallState(addon, { + status: InstallStatus.Decompressing, + percent, + entry: progress.entryName, }); - console.log("Starting fragmenter download for URL", track.url); + if (dependencyOf) { + this.setCurrentInstallState(dependencyOf, { + status: InstallStatus.InstallingDependencyEnding, + dependencyAddonKey: addon.key, + dependencyPublisherKey: publisher.key, + percent, + }); + } + break; + } + case 'copyStarted': { + const [module] = args as FragmenterEventArguments; - const installResult = await ipcRenderer.invoke(channels.installManager.installFromUrl, ourInstallID, track.url, tempDir, destDir); + console.log('Started moving over module', module.name); - // Throw any error so we can display the error dialog - if (typeof installResult === 'object') { - throw installResult; + if (module.name === 'full') { + this.setCurrentInstallState(addon, { status: InstallStatus.DownloadEnding }); } - console.log("Fragmenter download finished for URL", track.url); + break; + } + case 'retryScheduled': { + const [module, retryCount, waitSeconds] = args as FragmenterEventArguments; - // Stop listening to forwarded fragmenter events - ipcRenderer.removeListener(channels.installManager.fragmenterEvent, handleForwardedFragmenterEvent); + console.log('Scheduling a retry for module', module.name); + console.log('Retry count', retryCount); + console.log('Waiting for', waitSeconds, 'seconds'); - // Remove installs existing under alternative names - console.log("Removing installs existing under alternative names"); - Directories.removeAlternativesForAddon(addon); - console.log("Finished removing installs existing under alternative names"); + store.dispatch(clearDownloadInterrupted({ id: addon.key })); - this.notifyDownload(addon, true); + this.setCurrentInstallState(addon, { status: InstallStatus.DownloadRetry }); + break; + } + case 'retryStarted': { + const [module, retryCount] = args as FragmenterEventArguments; - // Flash completion text - this.setCurrentInstallState(addon, { status: InstallStatus.DownloadDone }); - this.setCurrentlyInstalledTrack(addon, track); + console.log('Starting a retry for module', module.name); + console.log('Retry count', retryCount); - // If we have a background service, ask if we want to enable it - if (addon.backgroundService && (addon.backgroundService.enableAutostartConfiguration ?? true)) { - const app = BackgroundServices.getExternalAppFromBackgroundService(addon, publisher); + this.setCurrentInstallState(addon, { status: InstallStatus.Downloading }); + break; + } + case 'cancelled': { + this.setCurrentInstallState(addon, { status: InstallStatus.DownloadCanceled }); + break; + } + case 'error': { + const [error] = args as FragmenterEventArguments; + + console.error('Error from Fragmenter:', error); + Sentry.captureException(error); + } + } + }; - const isAutoStartEnabled = await BackgroundServices.isAutoStartEnabled(addon); - const doNotAskAgain = settings.get(`mainSettings.disableBackgroundServiceAutoStartPrompt.${publisher.key}.${addon.key}`); + // Listen to forwarded fragmenter events + ipcRenderer.on(channels.installManager.fragmenterEvent, handleForwardedFragmenterEvent); - if (!isAutoStartEnabled && !doNotAskAgain) { - await showModal( - , - ); - } - } - } catch (e) { - const isFragmenterError = FragmenterError.isFragmenterError(e); + // Send cancel message when abort controller is aborted + this.abortControllers[abortControllerID].signal.addEventListener('abort', () => { + ipcRenderer.send(channels.installManager.cancelInstall, ourInstallID); + }); - if (signal.aborted) { - console.warn('Download was cancelled'); + console.log('Starting fragmenter download for URL', track.url); - setCancelledState(); - startResetStateTimer(); + const installResult = await ipcRenderer.invoke( + channels.installManager.installFromUrl, + ourInstallID, + track.url, + tempDir, + destDir, + ); - return InstallResult.Cancelled; - } else { - console.error('Download failed, see exception below'); - console.error(e); + // Throw any error so we can display the error dialog + if (typeof installResult === 'object') { + throw installResult; + } - setErrorState(); + console.log('Fragmenter download finished for URL', track.url); - Sentry.captureException(e); - await showModal(); + // Stop listening to forwarded fragmenter events + ipcRenderer.removeListener(channels.installManager.fragmenterEvent, handleForwardedFragmenterEvent); - startResetStateTimer(); + // Remove installs existing under alternative names + console.log('Removing installs existing under alternative names'); + Directories.removeAlternativesForAddon(addon); + console.log('Finished removing installs existing under alternative names'); - Sentry.captureException(e); - await showModal(); + this.notifyDownload(addon, true); - return InstallResult.Failure; - } - } + // Flash completion text + this.setCurrentInstallState(addon, { status: InstallStatus.DownloadDone }); + this.setCurrentlyInstalledTrack(addon, track); - removeDownloadState(); + // If we have a background service, ask if we want to enable it + if (addon.backgroundService && (addon.backgroundService.enableAutostartConfiguration ?? true)) { + const app = BackgroundServices.getExternalAppFromBackgroundService(addon, publisher); - return InstallResult.Success; - } + const isAutoStartEnabled = await BackgroundServices.isAutoStartEnabled(addon); + const doNotAskAgain = settings.get( + `mainSettings.disableBackgroundServiceAutoStartPrompt.${publisher.key}.${addon.key}`, + ); - static cancelDownload(addon: Addon): void { - let download = store.getState().downloads.find((it) => it.id === addon.key); - if (!download) { - for (const dependency of addon.dependencies ?? []) { - const [, publisherKey, addonKey] = dependency.addon.match(/@(\w+)\/(\w+)/); + if (!isAutoStartEnabled && !doNotAskAgain) { + await showModal(); + } + } + } catch (e) { + const isFragmenterError = FragmenterError.isFragmenterError(e); - const dependencyAddon = Resolver.findAddon(publisherKey, addonKey); + if (signal.aborted) { + console.warn('Download was cancelled'); - const dependencyDownload = store.getState().downloads.find((it) => it.id === dependencyAddon.key); + setCancelledState(); + startResetStateTimer(); - if (dependencyDownload) { - download = dependencyDownload; - } - } - } + return InstallResult.Cancelled; + } else { + console.error('Download failed, see exception below'); + console.error(e); - if (!download) { - throw new Error('Cannot cancel when no addon or dependency download is ongoing'); - } + setErrorState(); - const abortController = this.abortControllers[download.abortControllerID]; + Sentry.captureException(e); + await showModal(); - abortController?.abort(); + startResetStateTimer(); + + Sentry.captureException(e); + await showModal(); + + return InstallResult.Failure; + } } - static async uninstallAddon(addon: Addon, publisher: Publisher, showModal: (modal: JSX.Element) => Promise): Promise { - const doUninstall = await showModal( - , - ); + removeDownloadState(); - if (!doUninstall) { - return; - } + return InstallResult.Success; + } - // Make sure no disallowed external apps are running - const noExternalAppsRunning = await ExternalAppsUI.ensureNoneRunningForAddon(addon, publisher, showModal); + static cancelDownload(addon: Addon): void { + let download = store.getState().downloads.find((it) => it.id === addon.key); + if (!download) { + for (const dependency of addon.dependencies ?? []) { + const [, publisherKey, addonKey] = dependency.addon.match(/@(\w+)\/(\w+)/); - if (!noExternalAppsRunning) { - return; - } + const dependencyAddon = Resolver.findAddon(publisherKey, addonKey); + + const dependencyDownload = store.getState().downloads.find((it) => it.id === dependencyAddon.key); - // Remove autostart of the background service if the addon has one - if (addon.backgroundService && (addon.backgroundService.enableAutostartConfiguration ?? true)) { - await BackgroundServices.setAutoStartEnabled(addon, publisher, false); + if (dependencyDownload) { + download = dependencyDownload; } + } + } - const installDir = Directories.inInstallLocation(addon.targetDirectory); + if (!download) { + throw new Error('Cannot cancel when no addon or dependency download is ongoing'); + } - await ipcRenderer.invoke( - channels.installManager.uninstall, - installDir, - [ - Directories.inPackagesMicrosoftStore(addon.targetDirectory), - Directories.inPackagesSteam(addon.targetDirectory), - ], - ); + const abortController = this.abortControllers[download.abortControllerID]; + + abortController?.abort(); + } + + static async uninstallAddon( + addon: Addon, + publisher: Publisher, + showModal: (modal: JSX.Element) => Promise, + ): Promise { + const doUninstall = await showModal( + , + ); + + if (!doUninstall) { + return; + } + + // Make sure no disallowed external apps are running + const noExternalAppsRunning = await ExternalAppsUI.ensureNoneRunningForAddon(addon, publisher, showModal); - this.setCurrentInstallState(addon, { status: InstallStatus.NotInstalled }); - this.setCurrentlyInstalledTrack(addon, null); + if (!noExternalAppsRunning) { + return; } - private static notifyDownload(addon: Addon, successful: boolean): void { - console.log("Requesting notification"); - Notification.requestPermission() - .then(() => { - console.log("Showing notification"); - if (successful) { - new Notification(`${addon.name} download complete!`, { - icon: path.join( - process.resourcesPath, - "extraResources", - "icon.ico", - ), - body: "Take to the skies!", - }); - } else { - new Notification("Download failed!", { - icon: path.join( - process.resourcesPath, - "extraResources", - "icon.ico", - ), - body: "Oops, something went wrong", - }); - } - }) - .catch((e) => console.log(e)); + // Remove autostart of the background service if the addon has one + if (addon.backgroundService && (addon.backgroundService.enableAutostartConfiguration ?? true)) { + await BackgroundServices.setAutoStartEnabled(addon, publisher, false); } + + const installDir = Directories.inInstallLocation(addon.targetDirectory); + + await ipcRenderer.invoke(channels.installManager.uninstall, installDir, [ + Directories.inPackagesMicrosoftStore(addon.targetDirectory), + Directories.inPackagesSteam(addon.targetDirectory), + ]); + + this.setCurrentInstallState(addon, { status: InstallStatus.NotInstalled }); + this.setCurrentlyInstalledTrack(addon, null); + } + + private static notifyDownload(addon: Addon, successful: boolean): void { + console.log('Requesting notification'); + Notification.requestPermission() + .then(() => { + console.log('Showing notification'); + if (successful) { + new Notification(`${addon.name} download complete!`, { + icon: path.join(process.resourcesPath, 'extraResources', 'icon.ico'), + body: 'Take to the skies!', + }); + } else { + new Notification('Download failed!', { + icon: path.join(process.resourcesPath, 'extraResources', 'icon.ico'), + body: 'Oops, something went wrong', + }); + } + }) + .catch((e) => console.log(e)); + } } diff --git a/src/renderer/utils/InstallerConfiguration.ts b/src/renderer/utils/InstallerConfiguration.ts index 5e524a60..361d1b91 100644 --- a/src/renderer/utils/InstallerConfiguration.ts +++ b/src/renderer/utils/InstallerConfiguration.ts @@ -1,405 +1,405 @@ -import { defaultConfiguration } from "renderer/data"; -import settings from "common/settings"; +import { defaultConfiguration } from 'renderer/data'; +import settings from 'common/settings'; export interface ExternalLink { - url: string, - title: string, + url: string; + title: string; } export interface DirectoryDefinition { - location: { - in: 'community' | 'packageCache' | 'package', - path: string, - }, + location: { + in: 'community' | 'packageCache' | 'package'; + path: string; + }; } export interface NamedDirectoryDefinition extends DirectoryDefinition { - title: string, + title: string; } export type AddonVersion = { - title: string, - date: Date, - type: 'major' | 'minor' | 'patch' -} + title: string; + date: Date; + type: 'major' | 'minor' | 'patch'; +}; export type GithubReleaseReleaseModel = { - type: 'githubRelease', -} + type: 'githubRelease'; +}; export type GithubBranchReleaseModel = { - type: 'githubBranch', - branch: string, -} + type: 'githubBranch'; + branch: string; +}; export type CDNReleaseModel = { - type: 'CDN', -} + type: 'CDN'; +}; -export type ReleaseModel = GithubReleaseReleaseModel | GithubBranchReleaseModel | CDNReleaseModel +export type ReleaseModel = GithubReleaseReleaseModel | GithubBranchReleaseModel | CDNReleaseModel; type BaseAddonTrack = { - name: string, - key: string, - url: string, - alternativeUrls?: string[], - description?: string, - releaseModel: ReleaseModel, -} + name: string; + key: string; + url: string; + alternativeUrls?: string[]; + description?: string; + releaseModel: ReleaseModel; +}; -export type MainlineAddonTrack = BaseAddonTrack & { isExperimental: false } +export type MainlineAddonTrack = BaseAddonTrack & { isExperimental: false }; -export type ExperimentalAddonTrack = BaseAddonTrack & { isExperimental: true, warningContent: string } +export type ExperimentalAddonTrack = BaseAddonTrack & { isExperimental: true; warningContent: string }; export type AddonTrack = MainlineAddonTrack | ExperimentalAddonTrack; export interface AddonBackgroundService { - /** - * Defines the executable file base name for the background service. This is relative to the community folder package - * of the addon, must match /^[a-zA-Z\d_-]+$/, and must not contain a file extension. - */ - executableFileBasename: string, - - /** - * Reference to an external app which is used to check if this service is running - */ - runCheckExternalAppRef: string, - - /** - * Whether autostart configuration is available for the background service - */ - enableAutostartConfiguration?: boolean, - - /** - * Command line arguments to run this background service with - * - * Defaults to `true`. - */ - commandLineArgs?: string[], + /** + * Defines the executable file base name for the background service. This is relative to the community folder package + * of the addon, must match /^[a-zA-Z\d_-]+$/, and must not contain a file extension. + */ + executableFileBasename: string; + + /** + * Reference to an external app which is used to check if this service is running + */ + runCheckExternalAppRef: string; + + /** + * Whether autostart configuration is available for the background service + */ + enableAutostartConfiguration?: boolean; + + /** + * Command line arguments to run this background service with + * + * Defaults to `true`. + */ + commandLineArgs?: string[]; } /** * Configuration for an addon's "My Install" page */ export interface AddonMyInstallPageConfiguration { - /** - * Links to show on the page. Those will be shown in a section on top, without a header, and open the user's browser. - */ - links: ExternalLink[], - - /** - * Folder quick-links to show. Those will be shown in a section on the bottom, with a header, and open the file explorer. - */ - directories: NamedDirectoryDefinition[], + /** + * Links to show on the page. Those will be shown in a section on top, without a header, and open the user's browser. + */ + links: ExternalLink[]; + + /** + * Folder quick-links to show. Those will be shown in a section on the bottom, with a header, and open the file explorer. + */ + directories: NamedDirectoryDefinition[]; } export interface Addon { - key: string, - name: string, - repoOwner?: string, - repoName?: string, - category?: `@${string}`, - aircraftName: string, - titleImageUrl: string, - titleImageUrlSelected: string, - backgroundImageUrls: string[], - backgroundImageShadow?: boolean, - shortDescription: string, - description: string, - techSpecs?: AddonTechSpec[], - targetDirectory: string, - alternativeNames?: string[], - tracks: AddonTrack[], - dependencies?: AddonDependency[], - incompatibleAddons?: AddonIncompatibleAddon[], - configurationAspects?: ConfigurationAspect[], - disallowedRunningExternalApps?: string[], - backgroundService?: AddonBackgroundService, - - /** - * Configuration for the "My Install" page of this addon. If not provided, a default page described below will be shown: - * - * Links: none - * - * Directories: Package in community directory - * - * If it is specified, the above elements are appended to the specified page contents. - */ - myInstallPage?: AddonMyInstallPageConfiguration, - - enabled: boolean, - hidesAddon?: string, - hidden?: boolean, - hiddenName?: string, - overrideAddonWhileHidden?: string, - gitHubReleaseBaseURL?: string, + key: string; + name: string; + repoOwner?: string; + repoName?: string; + category?: `@${string}`; + aircraftName: string; + titleImageUrl: string; + titleImageUrlSelected: string; + backgroundImageUrls: string[]; + backgroundImageShadow?: boolean; + shortDescription: string; + description: string; + techSpecs?: AddonTechSpec[]; + targetDirectory: string; + alternativeNames?: string[]; + tracks: AddonTrack[]; + dependencies?: AddonDependency[]; + incompatibleAddons?: AddonIncompatibleAddon[]; + configurationAspects?: ConfigurationAspect[]; + disallowedRunningExternalApps?: string[]; + backgroundService?: AddonBackgroundService; + + /** + * Configuration for the "My Install" page of this addon. If not provided, a default page described below will be shown: + * + * Links: none + * + * Directories: Package in community directory + * + * If it is specified, the above elements are appended to the specified page contents. + */ + myInstallPage?: AddonMyInstallPageConfiguration; + + enabled: boolean; + hidesAddon?: string; + hidden?: boolean; + hiddenName?: string; + overrideAddonWhileHidden?: string; + gitHubReleaseBaseURL?: string; } export interface AddonDependency { - /** - * Path to the addon, with the format `@/`` - */ - addon: `@${string}/${string}`, - - /** - * Whether this dependency is optional. If `false`, the dependency addon will be installed before the parent addon, and removing the dependency - * will cause the parent addon to be removed. - */ - optional: boolean, - - /** - * Modal text that shows below the dependency / parent addon on the pre-install modal (if optional) and the removal dialog (if not optional). - */ - modalText?: string, + /** + * Path to the addon, with the format `@/`` + */ + addon: `@${string}/${string}`; + + /** + * Whether this dependency is optional. If `false`, the dependency addon will be installed before the parent addon, and removing the dependency + * will cause the parent addon to be removed. + */ + optional: boolean; + + /** + * Modal text that shows below the dependency / parent addon on the pre-install modal (if optional) and the removal dialog (if not optional). + */ + modalText?: string; } /** * Fields from the addon's manifest.json */ export interface AddonIncompatibleAddon { - /** - * Field from the addon's manifest.json - * This need to be configured identically to the addon's manifest.json - * Leaving a field empty ignores it for the search. - */ - title?: string, - - /** - * Field from the addon's manifest.json - * This need to be configured identically to the addon's manifest.json - * Leaving a field empty ignores it for the search. - */ - creator?: string, - - /** - * Field from the addon's manifest.json - * This need to be configured identically to the addon's manifest.json - * Leaving a field empty ignores it for the search. - * - * This supports semver notation. - */ - packageVersion?: string, - - /** - * folder name in community - added later to show the user the corresponding folder name - not used for searching - */ - folder?: string, - - /** - * Description of the nature of the incompatibility to display to the user in a warning dialog - */ - description?: string + /** + * Field from the addon's manifest.json + * This need to be configured identically to the addon's manifest.json + * Leaving a field empty ignores it for the search. + */ + title?: string; + + /** + * Field from the addon's manifest.json + * This need to be configured identically to the addon's manifest.json + * Leaving a field empty ignores it for the search. + */ + creator?: string; + + /** + * Field from the addon's manifest.json + * This need to be configured identically to the addon's manifest.json + * Leaving a field empty ignores it for the search. + * + * This supports semver notation. + */ + packageVersion?: string; + + /** + * folder name in community - added later to show the user the corresponding folder name - not used for searching + */ + folder?: string; + + /** + * Description of the nature of the incompatibility to display to the user in a warning dialog + */ + description?: string; } export interface AddonTechSpec { - name: string, - value: string, + name: string; + value: string; } /** * Describes a configuration aspect, allowing to customize an addon install */ export interface ConfigurationAspect { - /** - * A unique key for this configuration aspect - */ - key: string, - - /** - * The name of the configuration aspect, shown under the supertitle, in the associated tab - */ - tabTitle: string, - - /** - * The supertitle of the associated tab - */ - tabSupertitle: string, - - /** - * The title of the page containing the choices - */ - title: string - - /** - * What to apply the list of desired choices to - */ - applyChoiceKeyTo: 'optionalFragmenterModule', - - /** - * The kind of choice to permit - */ - choiceKind: 'yesNo' | 'multipleChoice' | 'selectOne' | 'selectOneOrZero', - - /** - * The possible choices. Must always be at least two, and if using `yesNo`, must be exactly 2 keyed `yes` and `no`. - */ - choices: ConfigurationAspectChoice[], + /** + * A unique key for this configuration aspect + */ + key: string; + + /** + * The name of the configuration aspect, shown under the supertitle, in the associated tab + */ + tabTitle: string; + + /** + * The supertitle of the associated tab + */ + tabSupertitle: string; + + /** + * The title of the page containing the choices + */ + title: string; + + /** + * What to apply the list of desired choices to + */ + applyChoiceKeyTo: 'optionalFragmenterModule'; + + /** + * The kind of choice to permit + */ + choiceKind: 'yesNo' | 'multipleChoice' | 'selectOne' | 'selectOneOrZero'; + + /** + * The possible choices. Must always be at least two, and if using `yesNo`, must be exactly 2 keyed `yes` and `no`. + */ + choices: ConfigurationAspectChoice[]; } interface ConfigurationAspectChoice { - /** - * A unique key for this choice - */ - key: string, - - /** - * The title of the choice, displayed on the card - */ - title: string, - - /** - * The subtitle of the choice, displayed on the card - */ - subtitle?: string, - - /** - * A longer description of the choice, displayed below the cards - */ - description?: string, - - /** - * An URL to an image representing the choice - */ - imageUrl?: string, + /** + * A unique key for this choice + */ + key: string; + + /** + * The title of the choice, displayed on the card + */ + title: string; + + /** + * The subtitle of the choice, displayed on the card + */ + subtitle?: string; + + /** + * A longer description of the choice, displayed below the cards + */ + description?: string; + + /** + * An URL to an image representing the choice + */ + imageUrl?: string; } interface DefinitionBase { - kind: string, + kind: string; } export type AddonCategoryDefinition = DefinitionBase & { - kind: 'addonCategory', - key: string, - title?: string, - styles?: ('align-bottom')[], -} + kind: 'addonCategory'; + key: string; + title?: string; + styles?: 'align-bottom'[]; +}; export type ExternalApplicationDefinition = DefinitionBase & { - kind: 'externalApp', - - /** - * Key of this external app. Must be unique - */ - key: string, - - /** - * Display name shown in the UI - */ - prettyName: string, - - /** - * Type of detection to figure out if the external app is running - */ - detectionType: 'http' | 'ws' | 'tcp', - - /** - * External app URL, only for `http` and `ws ` {@link detectionType} values. For `ws`, must start with `ws` or `wss`. - */ - url?: string, - - /** - * External app port, only for `tcp` {@link detectionType} values - */ - port?: number, - - /** - * URL on which to make a request to stop the external app - */ - killUrl?: string, - - /** - * HTTP method to use on the kill endpoint - */ - killMethod?: string, -} - -export type Definition = AddonCategoryDefinition | ExternalApplicationDefinition + kind: 'externalApp'; + + /** + * Key of this external app. Must be unique + */ + key: string; + + /** + * Display name shown in the UI + */ + prettyName: string; + + /** + * Type of detection to figure out if the external app is running + */ + detectionType: 'http' | 'ws' | 'tcp'; + + /** + * External app URL, only for `http` and `ws ` {@link detectionType} values. For `ws`, must start with `ws` or `wss`. + */ + url?: string; + + /** + * External app port, only for `tcp` {@link detectionType} values + */ + port?: number; + + /** + * URL on which to make a request to stop the external app + */ + killUrl?: string; + + /** + * HTTP method to use on the kill endpoint + */ + killMethod?: string; +}; + +export type Definition = AddonCategoryDefinition | ExternalApplicationDefinition; interface BasePublisherButton { - text: string, - style?: 'normal' | 'fbw-local-api-config', - icon?: string, - inline?: boolean, - inop?: true, - forceStroke?: true, - action: string, + text: string; + style?: 'normal' | 'fbw-local-api-config'; + icon?: string; + inline?: boolean; + inop?: true; + forceStroke?: true; + action: string; } type UrlPublisherButton = BasePublisherButton & { - action: 'openBrowser', - url: string, -} + action: 'openBrowser'; + url: string; +}; type InternalAction = BasePublisherButton & { - action: 'internal', - call: 'fbw-local-api-config', -} + action: 'internal'; + call: 'fbw-local-api-config'; +}; -export type PublisherButton = UrlPublisherButton | InternalAction +export type PublisherButton = UrlPublisherButton | InternalAction; export type Publisher = { - name: string, - key: string, - logoUrl: string, - logoSize?: number, - defs?: Definition[], - addons: Addon[], - buttons?: PublisherButton[], -} + name: string; + key: string; + logoUrl: string; + logoSize?: number; + defs?: Definition[]; + addons: Addon[]; + buttons?: PublisherButton[]; +}; export interface Configuration { - version: number, - publishers: Publisher[], + version: number; + publishers: Publisher[]; } export class InstallerConfiguration { - - static async obtain(): Promise { - return this.fetchConfigurationFromCdn().then((config) => { + static async obtain(): Promise { + return this.fetchConfigurationFromCdn() + .then((config) => { + if (this.isConfigurationValid(config)) { + console.log('Configuration from CDN is valid'); + return config; + } else { + console.warn('CDN configuration was invalid, using local configuration'); + return this.loadConfigurationFromLocalStorage().then((config) => { if (this.isConfigurationValid(config)) { - console.log("Configuration from CDN is valid"); - return config; + console.log('Configuration from local storage is valid'); + return config; } else { - console.warn('CDN configuration was invalid, using local configuration'); - return this.loadConfigurationFromLocalStorage().then((config) => { - if (this.isConfigurationValid(config)) { - console.log("Configuration from local storage is valid"); - return config; - } else { - return Promise.reject('Both CDN and local configurations are invalid'); - } - }); + return Promise.reject('Both CDN and local configurations are invalid'); } - }).catch(() => { - return this.loadConfigurationFromLocalStorage().then((config) => { - if (this.isConfigurationValid(config)) { - console.warn('CDN configuration could not be loaded, using local configuration'); - return config; - } else { - return Promise.reject('Could not retrieve CDN configuration, and local configuration is invalid'); - } - }); + }); + } + }) + .catch(() => { + return this.loadConfigurationFromLocalStorage().then((config) => { + if (this.isConfigurationValid(config)) { + console.warn('CDN configuration could not be loaded, using local configuration'); + return config; + } else { + return Promise.reject('Could not retrieve CDN configuration, and local configuration is invalid'); + } }); - } - - private static async fetchConfigurationFromCdn(): Promise { - const url = settings.get('mainSettings.configDownloadUrl') as string; - console.log("Obtaining configuration from CDN (%s)", url); - return await fetch(url) - .then(res => res.blob()) - .then(blob => blob.text()) - .then(text => JSON.parse(text)) - .catch(() => { - return Promise.reject('Could not retrieve CDN configuration'); - }); - } - - private static async loadConfigurationFromLocalStorage(): Promise { - return defaultConfiguration; - } - - private static isConfigurationValid(config: Configuration): boolean { - return !!(config.publishers); - } - + }); + } + + private static async fetchConfigurationFromCdn(): Promise { + const url = settings.get('mainSettings.configDownloadUrl') as string; + console.log('Obtaining configuration from CDN (%s)', url); + return await fetch(url) + .then((res) => res.blob()) + .then((blob) => blob.text()) + .then((text) => JSON.parse(text)) + .catch(() => { + return Promise.reject('Could not retrieve CDN configuration'); + }); + } + + private static async loadConfigurationFromLocalStorage(): Promise { + return defaultConfiguration; + } + + private static isConfigurationValid(config: Configuration): boolean { + return !!config.publishers; + } } diff --git a/src/renderer/utils/McduServer.ts b/src/renderer/utils/McduServer.ts index 7f46f420..00c4d69f 100644 --- a/src/renderer/utils/McduServer.ts +++ b/src/renderer/utils/McduServer.ts @@ -1,20 +1,18 @@ -import net from "net"; +import net from 'net'; export class McduServer { + static async isRunning(): Promise { + return new Promise((resolve) => { + const socket = net.connect(8380); - static async isRunning(): Promise { - return new Promise((resolve) => { - const socket = net.connect(8380); - - socket.on('connect', () => { - resolve(true); - socket.destroy(); - }); - socket.on('error', () => { - resolve(false); - socket.destroy(); - }); - }); - } - + socket.on('connect', () => { + resolve(true); + socket.destroy(); + }); + socket.on('error', () => { + resolve(false); + socket.destroy(); + }); + }); + } } diff --git a/src/renderer/utils/Msfs.ts b/src/renderer/utils/Msfs.ts index 80eec235..c3de4f5e 100644 --- a/src/renderer/utils/Msfs.ts +++ b/src/renderer/utils/Msfs.ts @@ -1,20 +1,18 @@ import net from 'net'; export class Msfs { + static async isRunning(): Promise { + return new Promise((resolve) => { + const socket = net.connect(500); - static async isRunning(): Promise { - return new Promise((resolve) => { - const socket = net.connect(500); - - socket.on('connect', () => { - resolve(true); - socket.destroy(); - }); - socket.on('error', () => { - resolve(false); - socket.destroy(); - }); - }); - } - + socket.on('connect', () => { + resolve(true); + socket.destroy(); + }); + socket.on('error', () => { + resolve(false); + socket.destroy(); + }); + }); + } } diff --git a/src/renderer/utils/Resolver.ts b/src/renderer/utils/Resolver.ts index 37413b00..dac74eec 100644 --- a/src/renderer/utils/Resolver.ts +++ b/src/renderer/utils/Resolver.ts @@ -1,85 +1,84 @@ import { - Addon, - Configuration, - Definition, - ExternalApplicationDefinition, - Publisher, -} from "renderer/utils/InstallerConfiguration"; -import { store } from "renderer/redux/store"; + Addon, + Configuration, + Definition, + ExternalApplicationDefinition, + Publisher, +} from 'renderer/utils/InstallerConfiguration'; +import { store } from 'renderer/redux/store'; const _cache: { [k: string]: Definition } = {}; export class Resolver { - private static getConfiguration(): Configuration { - return store.getState().configuration; - } - - public static findPublisher(key: string): Publisher | undefined { - const config = this.getConfiguration(); + private static getConfiguration(): Configuration { + return store.getState().configuration; + } - return config.publishers.find((it) => it.key === key); - } + public static findPublisher(key: string): Publisher | undefined { + const config = this.getConfiguration(); - public static findAddon(publisherKey: string, addonKey: string): Addon | undefined { - const publisher = this.findPublisher(publisherKey); + return config.publishers.find((it) => it.key === key); + } - if (!publisher) { - return undefined; - } + public static findAddon(publisherKey: string, addonKey: string): Addon | undefined { + const publisher = this.findPublisher(publisherKey); - return publisher.addons.find((it) => it.key === addonKey); + if (!publisher) { + return undefined; } - public static findDefinition(ref: string, publisher: Publisher): Definition { - if (_cache[ref]) { - return _cache[ref]; - } + return publisher.addons.find((it) => it.key === addonKey); + } - const isOwnPublisher = ref[1] === '/'; + public static findDefinition(ref: string, publisher: Publisher): Definition { + if (_cache[ref]) { + return _cache[ref]; + } - if (isOwnPublisher) { - const defKey = ref.substring(2); + const isOwnPublisher = ref[1] === '/'; - const found = publisher.defs.find((it) => it.key === defKey); + if (isOwnPublisher) { + const defKey = ref.substring(2); - _cache[ref] = found; + const found = publisher.defs.find((it) => it.key === defKey); - return found; - } else { - const parsedRef = /@(\w+)\/(\w+)/; + _cache[ref] = found; - if (!parsedRef) { - throw new Error(`Could not parse reference=${ref}`); - } + return found; + } else { + const parsedRef = /@(\w+)\/(\w+)/; - const [, publisherKey, defKey] = parsedRef[Symbol.match](ref); + if (!parsedRef) { + throw new Error(`Could not parse reference=${ref}`); + } - const publisher = this.findPublisher(publisherKey); + const [, publisherKey, defKey] = parsedRef[Symbol.match](ref); - if (!publisher) { - throw new Error(`Cannot find publisher with key=${publisherKey}`); - } + const publisher = this.findPublisher(publisherKey); - const found = publisher.defs.find((it) => it.key === defKey); + if (!publisher) { + throw new Error(`Cannot find publisher with key=${publisherKey}`); + } - if (!found) { - throw new Error(`Cannot find definition with key=${defKey} for publisher=${publisher.key}`); - } + const found = publisher.defs.find((it) => it.key === defKey); - _cache[ref] = found; + if (!found) { + throw new Error(`Cannot find definition with key=${defKey} for publisher=${publisher.key}`); + } - return found; - } + _cache[ref] = found; + return found; } + } - public static getExternalApps(publisherKey: string): ExternalApplicationDefinition[] { - const publisher = this.findPublisher(publisherKey); - - if (!publisher) { - throw new Error(`Cannot find publisher with key=${publisherKey}`); - } + public static getExternalApps(publisherKey: string): ExternalApplicationDefinition[] { + const publisher = this.findPublisher(publisherKey); - return publisher.defs.filter((it) => it.kind === 'externalApp') as ExternalApplicationDefinition[]; + if (!publisher) { + throw new Error(`Cannot find publisher with key=${publisherKey}`); } + + return publisher.defs.filter((it) => it.kind === 'externalApp') as ExternalApplicationDefinition[]; + } } diff --git a/tailwind.config.js b/tailwind.config.js index 678f086b..a40ec848 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,11 +1,8 @@ 'use strict'; -const reactComponentsSafeList = require('@flybywiresim/react-components/build/usedCSSClasses.json'); - module.exports = { purge: { mode: 'jit', - options: { safelist: [...reactComponentsSafeList] }, content: [ './src/**/*.html', './src/**/*.jsx',