diff options
| author | Oskar <oskar@mullvad.net> | 2024-09-24 13:18:32 +0200 |
|---|---|---|
| committer | Oskar <oskar@mullvad.net> | 2024-09-24 13:18:32 +0200 |
| commit | 78aea0344617e0e013af49645d3d5563f124df31 (patch) | |
| tree | ed5371911d23ba2bab8e701f351d822202d63d57 | |
| parent | cd4f743ed5ce75e975594bf3071074756c538f53 (diff) | |
| parent | 4bb25616f02af4e15b0140767c07b61e49e8aa83 (diff) | |
| download | mullvadvpn-78aea0344617e0e013af49645d3d5563f124df31.tar.xz mullvadvpn-78aea0344617e0e013af49645d3d5563f124df31.zip | |
Merge branch 'make-eslint-and-other-tools-run-for-all-parts-of-the-project-des-1254'
60 files changed, 1312 insertions, 1206 deletions
diff --git a/gui/.eslintignore b/gui/.eslintignore deleted file mode 100644 index 07b7d976c2..0000000000 --- a/gui/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -.eslintrc.js -gulpfile.js -init.js -tasks/*.js diff --git a/gui/.eslintrc.js b/gui/.eslintrc.js deleted file mode 100644 index 4e4b1e0abe..0000000000 --- a/gui/.eslintrc.js +++ /dev/null @@ -1,120 +0,0 @@ -const namingConvention = [ - { - selector: 'default', - format: ['camelCase'], - }, - { - selector: 'variable', - modifiers: ['const'], - format: ['camelCase', 'PascalCase', 'UPPER_CASE'], - leadingUnderscore: 'allow', - }, - { - selector: 'variableLike', - format: ['camelCase'], - leadingUnderscore: 'allow', - }, - { - selector: 'import', - format: ['camelCase', 'PascalCase', 'snake_case'], - }, - { - selector: 'parameter', - format: ['camelCase', 'PascalCase'], - leadingUnderscore: 'allow', - }, - { - selector: 'function', - format: ['camelCase', 'PascalCase'], - }, - { - selector: 'memberLike', - format: ['camelCase'], - }, - { - selector: 'typeProperty', - format: ['camelCase'], - filter: { - regex: "^(data-testid|aria-labelledby|aria-describedby)$", - match: false, - }, - }, - { - selector: 'typeLike', - format: ['PascalCase'], - }, - { - selector: 'property', - format: null, - }, -]; - -const memberOrdering = { - default: [ - 'public-field', - 'protected-field', - 'private-field', - - 'public-constructor', - 'protected-constructor', - 'private-constructor', - - 'public-method', - 'protected-method', - 'private-method', - ], -}; - -module.exports = { - env: { - es6: true, - node: true, - }, - parserOptions: { - parser: '@typescript-eslint/parser', - project: './tsconfig.json', - tsconfigRootDir: __dirname, - ecmaVersion: '2018', - sourceType: 'module', - ecmaFeatures: { - jsx: true, - }, - }, - ignorePatterns: ['test/*', 'scripts/*'], - plugins: ['prettier', 'simple-import-sort'], - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - ], - settings: { - react: { - createClass: 'createReactClass', - pragma: 'React', - version: 'detect', - }, - }, - rules: { - quotes: ['error', 'single', { avoidEscape: true }], - 'prettier/prettier': 'error', - '@typescript-eslint/no-unused-vars': [ - 'error', - { argsIgnorePattern: '^_', ignoreRestSiblings: true }, - ], - '@typescript-eslint/require-await': 'error', - '@typescript-eslint/no-floating-promises': 'error', - '@typescript-eslint/no-unused-expressions': 'error', - '@typescript-eslint/member-ordering': ['error', memberOrdering], - 'no-return-await': 'error', - 'react/jsx-no-bind': 'error', - '@typescript-eslint/naming-convention': ['error', ...namingConvention], - '@typescript-eslint/ban-ts-comment': ['error', { 'ts-ignore': false }], - 'simple-import-sort/imports': 'error', - - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - 'react/prop-types': 'off', - }, -}; diff --git a/gui/.prettierignore b/gui/.prettierignore deleted file mode 100644 index b38db2f296..0000000000 --- a/gui/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -build/ diff --git a/gui/.prettierrc.yml b/gui/.prettierrc.yml deleted file mode 100644 index 7c4217913d..0000000000 --- a/gui/.prettierrc.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -# .prettierrc.yml -# see: https://prettier.io/docs/en/options.html -printWidth: 100 -semi: true -singleQuote: true -trailingComma: all -bracketSpacing: true -jsxBracketSameLine: true -arrowParens: always -proseWrap: always diff --git a/gui/eslint.config.mjs b/gui/eslint.config.mjs new file mode 100644 index 0000000000..5c2b5d848c --- /dev/null +++ b/gui/eslint.config.mjs @@ -0,0 +1,166 @@ +import eslint from '@eslint/js'; +import prettier from 'eslint-plugin-prettier/recommended'; +import react from 'eslint-plugin-react'; +import simpleImportSort from 'eslint-plugin-simple-import-sort'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +const namingConvention = [ + { + selector: 'default', + format: ['camelCase'], + }, + { + selector: 'variable', + modifiers: ['const'], + format: ['camelCase', 'PascalCase', 'UPPER_CASE'], + leadingUnderscore: 'allow', + }, + { + selector: 'variableLike', + format: ['camelCase'], + leadingUnderscore: 'allow', + }, + { + selector: 'import', + format: ['camelCase', 'PascalCase', 'snake_case'], + }, + { + selector: 'parameter', + format: ['camelCase', 'PascalCase'], + leadingUnderscore: 'allow', + }, + { + selector: 'function', + format: ['camelCase', 'PascalCase'], + }, + { + selector: 'memberLike', + format: ['camelCase'], + }, + { + selector: 'typeProperty', + format: ['camelCase'], + filter: { + regex: '^(data-testid|aria-labelledby|aria-describedby)$', + match: false, + }, + }, + { + selector: 'typeLike', + format: ['PascalCase'], + }, + { + selector: 'property', + format: null, + }, +]; + +const memberOrdering = { + default: [ + 'public-field', + 'protected-field', + 'private-field', + + 'public-constructor', + 'protected-constructor', + 'private-constructor', + + 'public-method', + 'protected-method', + 'private-method', + ], +}; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + react.configs.flat.recommended, + prettier, + { ignores: ['build/*'] }, + { + settings: { + react: { + createClass: 'createReactClass', + pragma: 'React', + version: 'detect', + }, + }, + }, + { + files: ['**/*'], + ignores: ['src/renderer/**/*'], + languageOptions: { + globals: globals.node, + }, + }, + { + files: ['src/renderer/**/*'], + languageOptions: { + globals: globals.browser, + }, + }, + { + files: ['test/**/*'], + languageOptions: { + globals: globals.mocha, + }, + }, + { + files: ['src/**/*.{js,mjs,ts,tsx}'], + languageOptions: { + parserOptions: { + parser: '@typescript-eslint/parser', + project: './tsconfig.json', + ecmaVersion: '2018', + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + }, + rules: { + '@typescript-eslint/require-await': 'error', + '@typescript-eslint/no-floating-promises': 'error', + }, + }, + { + files: ['**/*.{js,mjs,ts,tsx}'], + plugins: { + 'simple-import-sort': simpleImportSort, + }, + rules: { + quotes: ['error', 'single', { avoidEscape: true }], + // 'prettier/prettier': 'error', + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', ignoreRestSiblings: true }, + ], + '@typescript-eslint/no-unused-expressions': 'error', + '@typescript-eslint/member-ordering': ['error', memberOrdering], + 'no-return-await': 'error', + 'react/jsx-no-bind': 'error', + '@typescript-eslint/naming-convention': ['error', ...namingConvention], + '@typescript-eslint/ban-ts-comment': ['error', { 'ts-ignore': false }], + 'simple-import-sort/imports': 'error', + + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + 'react/prop-types': 'off', + 'react/react-in-jsx-scope': 'off', + }, + }, + { + files: ['test/**/*.spec.ts'], + rules: { + '@typescript-eslint/no-unused-expressions': 'off', + }, + }, + { + files: ['tasks/*', 'scripts/*', 'gulpfile.js', 'init.js'], + rules: { + '@typescript-eslint/no-require-imports': 'off', + }, + }, +); diff --git a/gui/package-lock.json b/gui/package-lock.json index d3fa7f4208..6ff861c565 100644 --- a/gui/package-lock.json +++ b/gui/package-lock.json @@ -26,10 +26,12 @@ "styled-components": "^6.1.0" }, "devDependencies": { + "@eslint/js": "^9.10.0", "@playwright/test": "^1.41.1", "@types/chai": "^4.3.3", "@types/chai-as-promised": "^7.1.5", "@types/chai-spies": "^1.0.3", + "@types/eslint__js": "^8.42.3", "@types/gettext-parser": "^4.0.1", "@types/google-protobuf": "^3.15.6", "@types/history": "^4.7.11", @@ -42,8 +44,6 @@ "@types/sinon": "^10.0.13", "@types/sprintf-js": "^1.1.2", "@types/topojson-specification": "^1.0.2", - "@typescript-eslint/eslint-plugin": "^7.13.0", - "@typescript-eslint/parser": "^7.13.0", "browserify": "^17.0.0", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", @@ -52,11 +52,13 @@ "electron": "^30.0.4", "electron-builder": "^24.13.3", "electron-devtools-installer": "^3.2.0", - "eslint": "^8.57.0", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-react": "^7.34.2", - "eslint-plugin-simple-import-sort": "^12.1.0", + "eslint": "^9.10.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-react": "^7.36.1", + "eslint-plugin-simple-import-sort": "^12.1.1", "gettext-extractor": "^3.5.4", + "globals": "^15.9.0", "grpc_tools_node_protoc_ts": "^5.3.2", "gulp": "^4.0.2", "gulp-inject-string": "^1.1.2", @@ -65,11 +67,12 @@ "mocha": "^10.2.0", "playwright": "^1.41.1", "postject": "^1.0.0-alpha.6", - "prettier": "^3.3.2", + "prettier": "^3.3.3", "sinon": "^14.0.1", "ts-node": "^10.9.2", "tsc-watch": "^5.0.3", "typescript": "^5.4.5", + "typescript-eslint": "^8.6.0", "vinyl-buffer": "^1.0.1", "vinyl-source-stream": "^2.0.0", "xvfb-maybe": "^0.2.1" @@ -432,24 +435,61 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", - "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -457,19 +497,19 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -480,19 +520,52 @@ } } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "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==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz", + "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz", + "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@grpc/grpc-js": { @@ -596,44 +669,6 @@ "node": ">=0.10.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -647,12 +682,18 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -1260,6 +1301,31 @@ "@types/ms": "*" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint__js": { + "version": "8.42.3", + "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", + "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", + "dev": true, + "dependencies": { + "@types/eslint": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, "node_modules/@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -1328,6 +1394,12 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -1492,31 +1564,31 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.0.tgz", - "integrity": "sha512-FX1X6AF0w8MdVFLSdqwqN/me2hyhuQg4ykN6ZpVhh1ij/80pTvDKclX1sZB9iqex8SjQfVhwMKs3JtnnMLzG9w==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz", + "integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/type-utils": "7.13.0", - "@typescript-eslint/utils": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/type-utils": "8.6.0", + "@typescript-eslint/utils": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1524,26 +1596,27 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.0.tgz", - "integrity": "sha512-xMEtMzxq9eRkZy48XuxlBFzpVMDurUAfDu5Rz16GouAtXm0TaAoTFzqWUFPPuQYXI/CDaH/Bgx/fk/84t/Bc9A==", + "node_modules/@typescript-eslint/parser": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz", + "integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.13.0", - "@typescript-eslint/utils": "7.13.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", + "debug": "^4.3.4" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1551,35 +1624,13 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.0.tgz", - "integrity": "sha512-jceD8RgdKORVnB4Y6BqasfIkFhl4pajB1wVxrF4akxD2QPM8GNYjgGwEzYS+437ewlqqrg7Dw+6dhdpjMpeBFQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/typescript-estree": "7.13.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1590,33 +1641,46 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/@typescript-eslint/parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/@typescript-eslint/parser": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.0.tgz", - "integrity": "sha512-EjMfl69KOS9awXXe83iRN7oIEXy9yYdqWfqdrFAYAAr6syP8eLEFI7ZE4939antx2mNgPRW/o1ybm2SFYkbTVA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz", + "integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/typescript-estree": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", - "debug": "^4.3.4" + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz", + "integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/utils": "8.6.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" }, - "peerDependencies": { - "eslint": "^8.56.0" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependenciesMeta": { "typescript": { @@ -1624,13 +1688,13 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1641,36 +1705,19 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.0.tgz", - "integrity": "sha512-ZrMCe1R6a01T94ilV13egvcnvVJ1pxShkE0+NDjDzH4nvG1wXpwsVI5bZCvE7AEDH1mXEx5tJSVR68bLgG7Dng==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/types": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.0.tgz", - "integrity": "sha512-QWuwm9wcGMAuTsxP+qz6LBBd3Uq8I5Nv8xb0mk54jmNoCyDspnMvVsOxI6IsMmway5d1S9Su2+sCKv1st2l6eA==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz", + "integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==", "dev": true, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -1678,22 +1725,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.0.tgz", - "integrity": "sha512-cAvBvUoobaoIcoqox1YatXOnSl3gx92rCZoMRPzMNisDiM12siGilSM4+dJAekuuHTibI2hVC2fYK79iSFvWjw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz", + "integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -1715,12 +1762,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1732,9 +1779,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -1747,34 +1794,50 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/@typescript-eslint/utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz", + "integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.0.tgz", - "integrity": "sha512-nxn+dozQx+MK61nn/JP+M4eCkHDSxSLDpgE3WcQo0+fkjEolnaB5jswvIKC4K56By8MMgIho7f1PVxERHEo8rw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz", + "integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/types": "8.6.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.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/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -2356,15 +2419,6 @@ "node": ">=0.10.0" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", @@ -2412,18 +2466,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, "node_modules/array.prototype.tosorted": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", @@ -4518,27 +4560,6 @@ "minimatch": "^3.0.4" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dir-glob/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/dmg-builder": { "version": "24.13.3", "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", @@ -5063,43 +5084,39 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz", + "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", "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", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.10.0", + "@eslint/plugin-kit": "^0.1.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.0.2", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.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", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", @@ -5111,20 +5128,40 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "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, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, "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==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "synckit": "^0.9.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -5148,35 +5185,35 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz", - "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", + "version": "7.36.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.36.1.tgz", + "integrity": "sha512-/qwbqNXZoq+VP30s1d4Nc1C5GTxjJQjk4Jzs4Wq2qzxFM7dSmuG2UkIjg2USMLh3A/aVcUNrK7v0J5U1XEGGwA==", "dev": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.hasown": "^1.1.4", "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11" + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react/node_modules/resolve": { @@ -5206,25 +5243,25 @@ } }, "node_modules/eslint-plugin-simple-import-sort": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.0.tgz", - "integrity": "sha512-Y2fqAfC11TcG/WP3TrI1Gi3p3nc8XJyEOJYHyEPEGI/UAgNx6akxxlX74p7SbAQdLcgASKhj8M0GKvH3vq/+ig==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz", + "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==", "dev": true, "peerDependencies": { "eslint": ">=5.0.0" } }, "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==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5268,16 +5305,16 @@ } } }, - "node_modules/eslint/node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, "engines": { - "node": ">=6.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/find-up": { @@ -5357,26 +5394,26 @@ "dev": true }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree/node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -5385,6 +5422,18 @@ "node": ">=0.4.0" } }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -5862,15 +5911,15 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/file-uri-to-path": { @@ -6000,17 +6049,16 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -6554,15 +6602,12 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", + "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6583,26 +6628,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "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.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/glogg": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", @@ -9624,23 +9649,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", @@ -10301,9 +10309,9 @@ } }, "node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -11463,15 +11471,6 @@ "node": ">=8" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", @@ -12002,6 +12001,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -12281,9 +12290,9 @@ } }, "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", "dev": true, "dependencies": { "@pkgr/core": "^0.1.0", @@ -12771,18 +12780,6 @@ "node": ">=4" } }, - "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typed-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", @@ -12875,6 +12872,29 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.6.0.tgz", + "integrity": "sha512-eEhhlxCEpCd4helh3AO1hk0UP2MvbRi9CtIAJTVPQjuSXOOO2jsEacNi4UdcJzZJbeuVg1gMhtZ8UYb+NFYPrA==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.6.0", + "@typescript-eslint/parser": "8.6.0", + "@typescript-eslint/utils": "8.6.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/uglify-js": { "version": "3.13.6", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.6.tgz", @@ -14089,21 +14109,49 @@ } }, "@eslint-community/regexpp": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", - "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true }, + "@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "requires": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -14112,28 +14160,49 @@ }, "dependencies": { "debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, + "globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true + }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "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==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz", + "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==", "dev": true }, + "@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true + }, + "@eslint/plugin-kit": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz", + "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", + "dev": true, + "requires": { + "levn": "^0.4.1" + } + }, "@grpc/grpc-js": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.2.tgz", @@ -14212,44 +14281,16 @@ } } }, - "@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" - }, - "dependencies": { - "debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "@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.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", "dev": true }, "@isaacs/cliui": { @@ -14731,6 +14772,31 @@ "@types/ms": "*" } }, + "@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint__js": { + "version": "8.42.3", + "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", + "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", + "dev": true, + "requires": { + "@types/eslint": "*" + } + }, + "@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -14799,6 +14865,12 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -14963,119 +15035,107 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.0.tgz", - "integrity": "sha512-FX1X6AF0w8MdVFLSdqwqN/me2hyhuQg4ykN6ZpVhh1ij/80pTvDKclX1sZB9iqex8SjQfVhwMKs3JtnnMLzG9w==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz", + "integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/type-utils": "7.13.0", - "@typescript-eslint/utils": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/type-utils": "8.6.0", + "@typescript-eslint/utils": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^1.3.0" + } + }, + "@typescript-eslint/parser": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz", + "integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", + "debug": "^4.3.4" }, "dependencies": { - "@typescript-eslint/type-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.0.tgz", - "integrity": "sha512-xMEtMzxq9eRkZy48XuxlBFzpVMDurUAfDu5Rz16GouAtXm0TaAoTFzqWUFPPuQYXI/CDaH/Bgx/fk/84t/Bc9A==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "7.13.0", - "@typescript-eslint/utils": "7.13.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - } - }, - "@typescript-eslint/utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.0.tgz", - "integrity": "sha512-jceD8RgdKORVnB4Y6BqasfIkFhl4pajB1wVxrF4akxD2QPM8GNYjgGwEzYS+437ewlqqrg7Dw+6dhdpjMpeBFQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/typescript-estree": "7.13.0" - } - }, "debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, - "@typescript-eslint/parser": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.0.tgz", - "integrity": "sha512-EjMfl69KOS9awXXe83iRN7oIEXy9yYdqWfqdrFAYAAr6syP8eLEFI7ZE4939antx2mNgPRW/o1ybm2SFYkbTVA==", + "@typescript-eslint/scope-manager": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz", + "integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/typescript-estree": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", - "debug": "^4.3.4" + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz", + "integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/utils": "8.6.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" }, "dependencies": { "debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, - "@typescript-eslint/scope-manager": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.0.tgz", - "integrity": "sha512-ZrMCe1R6a01T94ilV13egvcnvVJ1pxShkE0+NDjDzH4nvG1wXpwsVI5bZCvE7AEDH1mXEx5tJSVR68bLgG7Dng==", - "dev": true, - "requires": { - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0" - } - }, "@typescript-eslint/types": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.0.tgz", - "integrity": "sha512-QWuwm9wcGMAuTsxP+qz6LBBd3Uq8I5Nv8xb0mk54jmNoCyDspnMvVsOxI6IsMmway5d1S9Su2+sCKv1st2l6eA==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz", + "integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.0.tgz", - "integrity": "sha512-cAvBvUoobaoIcoqox1YatXOnSl3gx92rCZoMRPzMNisDiM12siGilSM4+dJAekuuHTibI2hVC2fYK79iSFvWjw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz", + "integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==", "dev": true, "requires": { - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", @@ -15092,47 +15152,53 @@ } }, "debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, + "@typescript-eslint/utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz", + "integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0" + } + }, "@typescript-eslint/visitor-keys": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.0.tgz", - "integrity": "sha512-nxn+dozQx+MK61nn/JP+M4eCkHDSxSLDpgE3WcQo0+fkjEolnaB5jswvIKC4K56By8MMgIho7f1PVxERHEo8rw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz", + "integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==", "dev": true, "requires": { - "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/types": "8.6.0", "eslint-visitor-keys": "^3.4.3" } }, - "@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 - }, "@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -15590,12 +15656,6 @@ } } }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", @@ -15628,18 +15688,6 @@ "es-shim-unscopables": "^1.0.0" } }, - "array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, "array.prototype.tosorted": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", @@ -17376,23 +17424,6 @@ "minimatch": "^3.0.4" } }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - }, - "dependencies": { - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - } - } - }, "dmg-builder": { "version": "24.13.3", "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", @@ -17835,43 +17866,39 @@ "dev": true }, "eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz", + "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", "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", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.10.0", + "@eslint/plugin-kit": "^0.1.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.0.2", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.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", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", @@ -17895,14 +17922,11 @@ "ms": "2.1.2" } }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } + "eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true }, "find-up": { "version": "5.0.0", @@ -17946,40 +17970,47 @@ } } }, + "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": {} + }, "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==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, "requires": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "synckit": "^0.9.1" } }, "eslint-plugin-react": { - "version": "7.34.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz", - "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", + "version": "7.36.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.36.1.tgz", + "integrity": "sha512-/qwbqNXZoq+VP30s1d4Nc1C5GTxjJQjk4Jzs4Wq2qzxFM7dSmuG2UkIjg2USMLh3A/aVcUNrK7v0J5U1XEGGwA==", "dev": true, "requires": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.hasown": "^1.1.4", "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11" + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "dependencies": { "resolve": { @@ -18002,16 +18033,16 @@ } }, "eslint-plugin-simple-import-sort": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.0.tgz", - "integrity": "sha512-Y2fqAfC11TcG/WP3TrI1Gi3p3nc8XJyEOJYHyEPEGI/UAgNx6akxxlX74p7SbAQdLcgASKhj8M0GKvH3vq/+ig==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz", + "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==", "dev": true, "requires": {} }, "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==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -18045,20 +18076,26 @@ } }, "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", "dev": true, "requires": { - "acorn": "^8.9.0", + "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "dependencies": { "acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true } } @@ -18455,12 +18492,12 @@ } }, "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "requires": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" } }, "file-uri-to-path": { @@ -18570,14 +18607,13 @@ "dev": true }, "flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "requires": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" } }, "flatted": { @@ -18987,13 +19023,10 @@ } }, "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" - } + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", + "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", + "dev": true }, "globalthis": { "version": "1.0.3", @@ -19004,20 +19037,6 @@ "define-properties": "^1.1.3" } }, - "globby": { - "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.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, "glogg": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", @@ -21338,17 +21357,6 @@ "es-object-atoms": "^1.0.0" } }, - "object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "requires": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - } - }, "object.map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", @@ -21836,9 +21844,9 @@ "dev": true }, "prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true }, "prettier-linter-helpers": { @@ -22760,12 +22768,6 @@ } } }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, "slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", @@ -23213,6 +23215,16 @@ "side-channel": "^1.0.6" } }, + "string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -23398,9 +23410,9 @@ } }, "synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", "dev": true, "requires": { "@pkgr/core": "^0.1.0", @@ -23773,12 +23785,6 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, - "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 - }, "typed-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", @@ -23843,6 +23849,17 @@ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true }, + "typescript-eslint": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.6.0.tgz", + "integrity": "sha512-eEhhlxCEpCd4helh3AO1hk0UP2MvbRi9CtIAJTVPQjuSXOOO2jsEacNi4UdcJzZJbeuVg1gMhtZ8UYb+NFYPrA==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "8.6.0", + "@typescript-eslint/parser": "8.6.0", + "@typescript-eslint/utils": "8.6.0" + } + }, "uglify-js": { "version": "3.13.6", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.6.tgz", diff --git a/gui/package.json b/gui/package.json index 0e0fa86d63..457a5a97d1 100644 --- a/gui/package.json +++ b/gui/package.json @@ -32,10 +32,12 @@ "nseventmonitor": "^1.0.5" }, "devDependencies": { + "@eslint/js": "^9.10.0", "@playwright/test": "^1.41.1", "@types/chai": "^4.3.3", "@types/chai-as-promised": "^7.1.5", "@types/chai-spies": "^1.0.3", + "@types/eslint__js": "^8.42.3", "@types/gettext-parser": "^4.0.1", "@types/google-protobuf": "^3.15.6", "@types/history": "^4.7.11", @@ -48,8 +50,6 @@ "@types/sinon": "^10.0.13", "@types/sprintf-js": "^1.1.2", "@types/topojson-specification": "^1.0.2", - "@typescript-eslint/eslint-plugin": "^7.13.0", - "@typescript-eslint/parser": "^7.13.0", "browserify": "^17.0.0", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", @@ -58,11 +58,13 @@ "electron": "^30.0.4", "electron-builder": "^24.13.3", "electron-devtools-installer": "^3.2.0", - "eslint": "^8.57.0", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-react": "^7.34.2", - "eslint-plugin-simple-import-sort": "^12.1.0", + "eslint": "^9.10.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-react": "^7.36.1", + "eslint-plugin-simple-import-sort": "^12.1.1", "gettext-extractor": "^3.5.4", + "globals": "^15.9.0", "grpc_tools_node_protoc_ts": "^5.3.2", "gulp": "^4.0.2", "gulp-inject-string": "^1.1.2", @@ -71,11 +73,12 @@ "mocha": "^10.2.0", "playwright": "^1.41.1", "postject": "^1.0.0-alpha.6", - "prettier": "^3.3.2", + "prettier": "^3.3.3", "sinon": "^14.0.1", "ts-node": "^10.9.2", "tsc-watch": "^5.0.3", "typescript": "^5.4.5", + "typescript-eslint": "^8.6.0", "vinyl-buffer": "^1.0.1", "vinyl-source-stream": "^2.0.0", "xvfb-maybe": "^0.2.1" @@ -86,7 +89,7 @@ "build-proto": "gulp build-proto", "pack-test-executable": "./scripts/build-test-executable.sh", "build-test-executable": "npm run build && npm run pack-test-executable", - "lint": "eslint --ext tsx,ts .", + "lint": "eslint .", "format": "prettier \"**/*.{js,css,ts,tsx}\" --write", "tsc": "tsc -p . --noEmit", "e2e": "npm run build && npm run e2e:no-build", diff --git a/gui/prettier.config.mjs b/gui/prettier.config.mjs new file mode 100644 index 0000000000..150678b825 --- /dev/null +++ b/gui/prettier.config.mjs @@ -0,0 +1,7 @@ +// see: https://prettier.io/docs/en/options.html +export default { + printWidth: 100, + singleQuote: true, + bracketSameLine: true, + proseWrap: 'always', +}; diff --git a/gui/scripts/extract-translations.js b/gui/scripts/extract-translations.js index 30e04e298a..e101ca03a5 100644 --- a/gui/scripts/extract-translations.js +++ b/gui/scripts/extract-translations.js @@ -1,4 +1,4 @@ -const { GettextExtractor, JsExtractors, HtmlExtractors } = require('gettext-extractor'); +const { GettextExtractor, JsExtractors } = require('gettext-extractor'); const path = require('path'); const extractor = new GettextExtractor(); diff --git a/gui/scripts/verify-translations-format.ts b/gui/scripts/verify-translations-format.ts index e44997d579..eaba3ee7b7 100644 --- a/gui/scripts/verify-translations-format.ts +++ b/gui/scripts/verify-translations-format.ts @@ -62,14 +62,14 @@ function checkFormatSpecifiers(translation: GetTextTranslation): boolean { .every((result) => result); } -function checkHtmlTagsImpl(value: string): { correct: boolean, amount: number } { - const tagsRegexp = new RegExp("<.*?>", "g"); +function checkHtmlTagsImpl(value: string): { correct: boolean; amount: number } { + const tagsRegexp = new RegExp('<.*?>', 'g'); const tags = value.match(tagsRegexp) ?? []; const tagTypes = tags.map((tag) => tag.slice(1, -1)); // Make sure tags match by pushing start-tags to a stack and matching closing tags with the last // item. - let tagStack: string[] = []; + const tagStack: string[] = []; for (let tag of tagTypes) { const selfClosing = tag.endsWith('/'); const endTag = tag.startsWith('/'); @@ -94,21 +94,23 @@ function checkHtmlTagsImpl(value: string): { correct: boolean, amount: number } } if (tagStack.length > 0) { - console.error(`Missing closing-tags (${tagStack}) in "${value}"`); - return { correct: false, amount: NaN }; + console.error(`Missing closing-tags (${tagStack}) in "${value}"`); + return { correct: false, amount: NaN }; } return { correct: true, amount: tags.length / 2 }; } function checkHtmlTags(translation: GetTextTranslation): boolean { - let { correct, amount: sourceAmount } = checkHtmlTagsImpl(translation.msgid); + const { correct, amount: sourceAmount } = checkHtmlTagsImpl(translation.msgid); - let translationsCorrect = translation.msgstr.every((value) => { - let { correct, amount } = checkHtmlTagsImpl(value); + const translationsCorrect = translation.msgstr.every((value) => { + const { correct, amount } = checkHtmlTagsImpl(value); // The amount doesn't make sense if the string isn't correctly formatted. if (correct && amount !== sourceAmount) { - console.error(`Incorrect amount of tags in translation for "${translation.msgid}": "${value}"`); + console.error( + `Incorrect amount of tags in translation for "${translation.msgid}": "${value}"`, + ); } return correct && amount === sourceAmount; }); diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index 31f6ddcfab..1c96be12bb 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -791,7 +791,6 @@ export class DaemonRpc { } private channelOptions(): grpc.ClientOptions { - /* eslint-disable @typescript-eslint/naming-convention */ return { 'grpc.max_reconnect_backoff_ms': 3000, 'grpc.initial_reconnect_backoff_ms': 3000, @@ -799,7 +798,6 @@ export class DaemonRpc { 'grpc.keepalive_timeout_ms': Math.pow(2, 30), 'grpc.client_idle_timeout_ms': Math.pow(2, 30), }; - /* eslint-enable @typescript-eslint/naming-convention */ } private connectivityChangeCallback(timeoutErr?: Error) { diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts index c5144ee99e..be6c6609a0 100644 --- a/gui/src/main/index.ts +++ b/gui/src/main/index.ts @@ -70,6 +70,7 @@ import Version, { GUI_VERSION } from './version'; const execAsync = util.promisify(exec); // Only import split tunneling library on correct OS. +// eslint-disable-next-line @typescript-eslint/no-require-imports const linuxSplitTunneling = process.platform === 'linux' && require('./linux-split-tunneling'); // This is used on Windows and macOS and will be undefined on Linux. const splitTunneling: ISplitTunnelingAppListRetriever | undefined = importSplitTunneling(); @@ -1115,11 +1116,11 @@ class ApplicationMain function importSplitTunneling() { if (process.platform === 'win32') { - // eslint-disable-next-line @typescript-eslint/no-var-requires + // eslint-disable-next-line @typescript-eslint/no-require-imports const { WindowsSplitTunnelingAppListRetriever } = require('./windows-split-tunneling'); return new WindowsSplitTunnelingAppListRetriever(); } else if (process.platform === 'darwin') { - // eslint-disable-next-line @typescript-eslint/no-var-requires + // eslint-disable-next-line @typescript-eslint/no-require-imports const { MacOsSplitTunnelingAppListRetriever } = require('./macos-split-tunneling'); return new MacOsSplitTunnelingAppListRetriever(); } diff --git a/gui/src/main/linux-desktop-entry.ts b/gui/src/main/linux-desktop-entry.ts index d6c11c7943..7cc46862a5 100644 --- a/gui/src/main/linux-desktop-entry.ts +++ b/gui/src/main/linux-desktop-entry.ts @@ -265,7 +265,6 @@ function getGtkThemeDirectories(): Promise<DirectoryDescription[]> { process.env.ORIGINAL_XDG_CURRENT_DESKTOP ?? process.env.XDG_CURRENT_DESKTOP ?? ''; child_process.exec( 'gsettings get org.gnome.desktop.interface icon-theme', - // eslint-disable-next-line @typescript-eslint/naming-convention { env: { XDG_CURRENT_DESKTOP: xdgCurrentDesktop } }, (error, stdout) => { if (error) { diff --git a/gui/src/main/linux-split-tunneling.ts b/gui/src/main/linux-split-tunneling.ts index 3ae1a15390..6d690aa453 100644 --- a/gui/src/main/linux-split-tunneling.ts +++ b/gui/src/main/linux-split-tunneling.ts @@ -113,7 +113,7 @@ export async function getApplications(locale: string): Promise<ILinuxSplitTunnel for (const entryPath of desktopEntryPaths) { try { desktopEntries.push(await readDesktopEntry(entryPath, locale)); - } catch (e) { + } catch { // no-op } } @@ -141,7 +141,7 @@ async function replaceIconNameWithDataUrl( } return { ...app, icon: await getImageDataUrl(iconPath) }; - } catch (e) { + } catch { return app; } } diff --git a/gui/src/main/logging.ts b/gui/src/main/logging.ts index f71e3a62f0..a1942d5007 100644 --- a/gui/src/main/logging.ts +++ b/gui/src/main/logging.ts @@ -113,7 +113,7 @@ function fileExists(filePath: string): boolean { try { fs.accessSync(filePath); return true; - } catch (e) { + } catch { return false; } } diff --git a/gui/src/main/user-interface.ts b/gui/src/main/user-interface.ts index a0b4662c48..f7b81247ff 100644 --- a/gui/src/main/user-interface.ts +++ b/gui/src/main/user-interface.ts @@ -494,7 +494,7 @@ export default class UserInterface implements WindowControllerDelegate { return; } - // eslint-disable-next-line @typescript-eslint/no-var-requires + // eslint-disable-next-line @typescript-eslint/no-require-imports const { NSEventMonitor, NSEventMask } = require('nseventmonitor'); const macEventMonitor = new NSEventMonitor(); const eventMask = NSEventMask.leftMouseDown | NSEventMask.rightMouseDown; diff --git a/gui/src/main/windows-split-tunneling.ts b/gui/src/main/windows-split-tunneling.ts index 5ba594f7cc..7ae73827d0 100644 --- a/gui/src/main/windows-split-tunneling.ts +++ b/gui/src/main/windows-split-tunneling.ts @@ -288,7 +288,7 @@ export class WindowsSplitTunnelingAppListRetriever implements ISplitTunnelingApp let fileHandle: fs.promises.FileHandle; try { fileHandle = await fs.promises.open(path, fs.constants.O_RDONLY); - } catch (e) { + } catch { return false; } diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx index a4d5fc2fad..c961d07258 100644 --- a/gui/src/renderer/app.tsx +++ b/gui/src/renderer/app.tsx @@ -400,7 +400,7 @@ export default class AppRenderer { this.loginState = 'too many devices'; this.history.reset(RoutePath.tooManyDevices, { transition: transitions.push }); - } catch (e) { + } catch { log.error('Failed to fetch device list'); actions.account.loginFailed('list-devices'); } diff --git a/gui/src/renderer/components/ClipboardLabel.tsx b/gui/src/renderer/components/ClipboardLabel.tsx index 582e03b02d..e9f760fd07 100644 --- a/gui/src/renderer/components/ClipboardLabel.tsx +++ b/gui/src/renderer/components/ClipboardLabel.tsx @@ -62,7 +62,7 @@ export default function ClipboardLabel(props: IProps) { return ( <StyledLabelContainer> <StyledLabel aria-hidden={obscured} {...otherProps}> - {obscured ? '●●●● ●●●● ●●●● ●●●●' : displayValue ?? value} + {obscured ? '●●●● ●●●● ●●●● ●●●●' : (displayValue ?? value)} </StyledLabel> {obscureValue !== false && ( <StyledButton diff --git a/gui/src/renderer/components/EditApiAccessMethod.tsx b/gui/src/renderer/components/EditApiAccessMethod.tsx index 97996d7f42..a8602675a0 100644 --- a/gui/src/renderer/components/EditApiAccessMethod.tsx +++ b/gui/src/renderer/components/EditApiAccessMethod.tsx @@ -62,7 +62,7 @@ function AccessMethodForm() { const onSave = useCallback( async (newMethod: NamedCustomProxy) => { - const enabled = id === undefined ? true : method?.enabled ?? true; + const enabled = id === undefined ? true : (method?.enabled ?? true); updatedMethod.current = { ...newMethod, enabled }; if ( updatedMethod.current !== undefined && diff --git a/gui/src/renderer/components/ImageView.tsx b/gui/src/renderer/components/ImageView.tsx index fa3855b21a..f40a93fbbc 100644 --- a/gui/src/renderer/components/ImageView.tsx +++ b/gui/src/renderer/components/ImageView.tsx @@ -46,7 +46,6 @@ export default function ImageView(props: IImageViewProps) { ? props.source : `../../assets/images/${props.source}.svg`; - // eslint-disable-next-line @typescript-eslint/naming-convention const style = useMemo(() => ({ WebkitMaskImage: `url('${url}')` }), [url]); if (props.tintColor) { diff --git a/gui/src/renderer/components/Login.tsx b/gui/src/renderer/components/Login.tsx index 32eb5bbe82..e5f981eafb 100644 --- a/gui/src/renderer/components/Login.tsx +++ b/gui/src/renderer/components/Login.tsx @@ -281,7 +281,7 @@ export default class Login extends React.Component<IProps, IState> { await this.props.clearAccountHistory(); // TODO: Remove account from memory - } catch (error) { + } catch { // TODO: Show error } } diff --git a/gui/src/renderer/components/NotificationBanner.tsx b/gui/src/renderer/components/NotificationBanner.tsx index ad84ad3697..f79855f004 100644 --- a/gui/src/renderer/components/NotificationBanner.tsx +++ b/gui/src/renderer/components/NotificationBanner.tsx @@ -167,7 +167,7 @@ export function NotificationBanner(props: INotificationBannerProps) { useEffect(() => { const newHeight = - props.children !== undefined ? contentRef.current?.getBoundingClientRect().height ?? 0 : 0; + props.children !== undefined ? (contentRef.current?.getBoundingClientRect().height ?? 0) : 0; if (newHeight !== contentHeight) { setContentHeight(newHeight); setAlignBottom((alignBottom) => alignBottom || contentHeight === 0 || newHeight === 0); diff --git a/gui/src/renderer/components/ProblemReport.tsx b/gui/src/renderer/components/ProblemReport.tsx index 3a0a9bbd0e..fd481cde07 100644 --- a/gui/src/renderer/components/ProblemReport.tsx +++ b/gui/src/renderer/components/ProblemReport.tsx @@ -137,7 +137,7 @@ function Form() { try { const reportId = await collectLog(); await viewLog(reportId); - } catch (error) { + } catch { // TODO: handle error } finally { setDisableActions(false); @@ -431,7 +431,7 @@ const ProblemReportContextProvider = ({ children }: { children: ReactNode }) => await sendProblemReport(email, message, reportId); clearReportForm(); setSendState(SendState.success); - } catch (error) { + } catch { setSendState(SendState.failed); } }, [email, message]); @@ -447,7 +447,7 @@ const ProblemReportContextProvider = ({ children }: { children: ReactNode }) => try { setSendState(SendState.sending); await sendReport(); - } catch (error) { + } catch { // No-op } } diff --git a/gui/src/renderer/components/main-view/FeatureIndicators.tsx b/gui/src/renderer/components/main-view/FeatureIndicators.tsx index 1696a6fd20..a4bf8659c6 100644 --- a/gui/src/renderer/components/main-view/FeatureIndicators.tsx +++ b/gui/src/renderer/components/main-view/FeatureIndicators.tsx @@ -116,7 +116,7 @@ export default function FeatureIndicators(props: FeatureIndicatorsProps) { tunnelState.state === 'connected' || tunnelState.state === 'connecting'; const featureIndicators = useRef( - featureIndicatorsVisible ? tunnelState.featureIndicators ?? [] : [], + featureIndicatorsVisible ? (tunnelState.featureIndicators ?? []) : [], ); if (featureIndicatorsVisible && tunnelState.featureIndicators) { diff --git a/gui/src/renderer/lib/ip.ts b/gui/src/renderer/lib/ip.ts index 94eb807d49..db458aa256 100644 --- a/gui/src/renderer/lib/ip.ts +++ b/gui/src/renderer/lib/ip.ts @@ -15,7 +15,7 @@ export abstract class IpAddress<G extends number[]> { public static fromString(ip: string): IPv4Address | IPv6Address { try { return IPv4Address.fromString(ip); - } catch (e) { + } catch { return IPv6Address.fromString(ip); } } @@ -80,7 +80,7 @@ export class IPv4Address extends IpAddress<IPv4Octets> { try { const octets = IPv4Address.octetsFromString(ip); return new IPv4Address(octets); - } catch (e) { + } catch { throw new Error(`Invalid ip: ${ip}`); } } @@ -94,7 +94,7 @@ export class IPv4Address extends IpAddress<IPv4Octets> { return parsedOctets; } } - } catch (e) { + } catch { // no-op } @@ -105,7 +105,7 @@ export class IPv4Address extends IpAddress<IPv4Octets> { try { IPv4Address.fromString(ip); return true; - } catch (e) { + } catch { return false; } } @@ -135,7 +135,7 @@ export class IPv4Range extends IpRange<IPv4Octets> { const prefixSize = parseInt(parts[1]); return new IPv4Range(octets, prefixSize); } - } catch (e) { + } catch { // no-op } @@ -167,7 +167,7 @@ export class IPv6Address extends IpAddress<IPv6Groups> { try { const groups = IPv6Address.groupsFromString(ip); return new IPv6Address(groups); - } catch (e) { + } catch { throw new Error(`Invalid ip: ${ip}`); } } @@ -200,7 +200,7 @@ export class IPv6Address extends IpAddress<IPv6Groups> { } } } - } catch (e) { + } catch { // no-op } @@ -211,7 +211,7 @@ export class IPv6Address extends IpAddress<IPv6Groups> { try { IPv6Address.fromString(ip); return true; - } catch (e) { + } catch { return false; } } @@ -241,7 +241,7 @@ export class IPv6Range extends IpRange<IPv6Groups> { const prefixSize = parseInt(parts[1], 10); return new IPv6Range(groups, prefixSize); } - } catch (e) { + } catch { // no-op } diff --git a/gui/tasks/assets.js b/gui/tasks/assets.js index 56b0c7a8ad..8028124180 100644 --- a/gui/tasks/assets.js +++ b/gui/tasks/assets.js @@ -31,7 +31,14 @@ copyHtml.displayName = 'copy-html'; copyLocales.displayName = 'copy-locales'; copyGeoData.displayName = 'copy-geo-data'; -exports.copyAll = parallel(copyStaticAssets, copyConfig, copyCss, copyHtml, copyLocales, copyGeoData); +exports.copyAll = parallel( + copyStaticAssets, + copyConfig, + copyCss, + copyHtml, + copyLocales, + copyGeoData, +); exports.copyStaticAssets = copyStaticAssets; exports.copyCss = copyCss; exports.copyHtml = copyHtml; diff --git a/gui/tasks/distribution.js b/gui/tasks/distribution.js index a7f7c29d6d..0e80c3c73f 100644 --- a/gui/tasks/distribution.js +++ b/gui/tasks/distribution.js @@ -42,7 +42,7 @@ const config = { name: 'mullvad-vpn', // We have to stick to semver on Windows for now due to: // https://github.com/electron-userland/electron-builder/issues/7173 - version: productVersion(process.platform === 'win32' ? ['semver'] : []) + version: productVersion(process.platform === 'win32' ? ['semver'] : []), }, files: [ @@ -139,23 +139,44 @@ const config = { publisherName: 'Mullvad VPN AB', extraResources: [ { from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad.exe')), to: '.' }, - { from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad-problem-report.exe')), to: '.' }, + { + from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad-problem-report.exe')), + to: '.', + }, { from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad-daemon.exe')), to: '.' }, { from: distAssets(path.join(getWindowsDistSubdir(), 'talpid_openvpn_plugin.dll')), to: '.' }, { - from: root(path.join('windows', 'winfw', 'bin', getWindowsTargetArch() + '-${env.CPP_BUILD_MODE}', 'winfw.dll')), + from: root( + path.join( + 'windows', + 'winfw', + 'bin', + getWindowsTargetArch() + '-${env.CPP_BUILD_MODE}', + 'winfw.dll', + ), + ), to: '.', }, // TODO: OpenVPN does not have an ARM64 build yet. { from: distAssets('binaries/x86_64-pc-windows-msvc/openvpn.exe'), to: '.' }, - { from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'apisocks5.exe')), to: '.' }, - { from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'wintun/wintun.dll')), to: '.' }, { - from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'split-tunnel/mullvad-split-tunnel.sys')), - to: '.' + from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'apisocks5.exe')), + to: '.', + }, + { + from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'wintun/wintun.dll')), + to: '.', + }, + { + from: distAssets( + path.join('binaries', getWindowsTargetSubdir(), 'split-tunnel/mullvad-split-tunnel.sys'), + ), + to: '.', }, { - from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'wireguard-nt/mullvad-wireguard.dll')), + from: distAssets( + path.join('binaries', getWindowsTargetSubdir(), 'wireguard-nt/mullvad-wireguard.dll'), + ), to: '.', }, { from: distAssets('maybenot_machines'), to: '.' }, @@ -180,7 +201,10 @@ const config = { extraResources: [ { from: distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-problem-report')), to: '.' }, { from: distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-setup')), to: '.' }, - { from: distAssets(path.join(getLinuxTargetSubdir(), 'libtalpid_openvpn_plugin.so')), to: '.' }, + { + from: distAssets(path.join(getLinuxTargetSubdir(), 'libtalpid_openvpn_plugin.so')), + to: '.', + }, { from: distAssets(path.join('linux', 'apparmor_mullvad')), to: '.' }, { from: distAssets(path.join('binaries', '${env.TARGET_TRIPLE}', 'openvpn')), to: '.' }, { from: distAssets(path.join('binaries', '${env.TARGET_TRIPLE}', 'apisocks5')), to: '.' }, @@ -197,8 +221,10 @@ const config = { distAssets('linux/before-install.sh'), '--before-remove', distAssets('linux/before-remove.sh'), - distAssets('linux/mullvad-daemon.service') +'=/usr/lib/systemd/system/mullvad-daemon.service', - distAssets('linux/mullvad-early-boot-blocking.service') +'=/usr/lib/systemd/system/mullvad-early-boot-blocking.service', + distAssets('linux/mullvad-daemon.service') + + '=/usr/lib/systemd/system/mullvad-daemon.service', + distAssets('linux/mullvad-early-boot-blocking.service') + + '=/usr/lib/systemd/system/mullvad-early-boot-blocking.service', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad')) + '=/usr/bin/', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-daemon')) + '=/usr/bin/', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-exclude')) + '=/usr/bin/', @@ -228,8 +254,10 @@ const config = { distAssets('linux/before-remove.sh'), '--rpm-posttrans', distAssets('linux/post-transaction.sh'), - distAssets('linux/mullvad-daemon.service') +'=/usr/lib/systemd/system/mullvad-daemon.service', - distAssets('linux/mullvad-early-boot-blocking.service') +'=/usr/lib/systemd/system/mullvad-early-boot-blocking.service', + distAssets('linux/mullvad-daemon.service') + + '=/usr/lib/systemd/system/mullvad-daemon.service', + distAssets('linux/mullvad-early-boot-blocking.service') + + '=/usr/lib/systemd/system/mullvad-early-boot-blocking.service', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad')) + '=/usr/bin/', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-daemon')) + '=/usr/bin/', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-exclude')) + '=/usr/bin/', @@ -265,7 +293,7 @@ function packWin() { process.env.SETUP_SUBDIR = 'aarch64-pc-windows-msvc'; break; default: - throw new Error(`Invalid or unknown target (only one may be specified)`); + throw new Error('Invalid or unknown target (only one may be specified)'); } return true; }, @@ -279,9 +307,12 @@ function packWin() { for (const artifactPath of buildResult.artifactPaths) { const artifactDir = path.dirname(artifactPath); const artifactSemverFilename = path.basename(artifactPath); - const artifactDesiredFilename = artifactSemverFilename.replace(productSemverVersion, productTargetVersion); + const artifactDesiredFilename = artifactSemverFilename.replace( + productSemverVersion, + productTargetVersion, + ); const targetArtifactPath = path.join(artifactDir, artifactDesiredFilename); - console.log("Moving", artifactSemverFilename, "=>", artifactDesiredFilename); + console.log('Moving', artifactSemverFilename, '=>', artifactDesiredFilename); fs.renameSync(artifactPath, targetArtifactPath); } }, @@ -310,9 +341,8 @@ function packMac() { break; } - process.env.BINARIES_PATH = hostTargetTriple !== process.env.TARGET_TRIPLE - ? process.env.TARGET_TRIPLE - : ''; + process.env.BINARIES_PATH = + hostTargetTriple !== process.env.TARGET_TRIPLE ? process.env.TARGET_TRIPLE : ''; return true; }, @@ -322,8 +352,12 @@ function packMac() { // if they're present for both targets. So make sure we remove libraries for other archs. // Remove the workaround once the issue has been fixed: // https://github.com/electron/universal/issues/41#issuecomment-1496288834 - await fs.promises.rm('node_modules/nseventmonitor/lib/binding/Release', { recursive: true }); - } catch {} + await fs.promises.rm('node_modules/nseventmonitor/lib/binding/Release', { + recursive: true, + }); + } catch { + // noop + } config.beforePack?.(context); }, afterPack: (context) => { @@ -336,13 +370,15 @@ function packMac() { return Promise.resolve(); }, - afterAllArtifactBuild: async (buildResult) => { + afterAllArtifactBuild: async (_buildResult) => { // Remove the folder that contains the unpacked app. Electron builder cleans up some of // these directories and it's changed between versions without a mention in the changelog. for (const dir of appOutDirs) { try { await fs.promises.rm(dir, { recursive: true }); - } catch {} + } catch { + // noop + } } }, afterSign: (context) => { @@ -359,7 +395,7 @@ function packLinux() { } if (targets && targets === 'aarch64-unknown-linux-gnu') { - config.rpm.fpm.unshift('--architecture', 'aarch64') + config.rpm.fpm.unshift('--architecture', 'aarch64'); } return builder.build({ @@ -422,7 +458,7 @@ function getWindowsTargetArch() { if (targets === 'aarch64-pc-windows-msvc') { return 'arm64'; } - throw new Error(`Invalid or unknown target (only one may be specified)`); + throw new Error('Invalid or unknown target (only one may be specified)'); } // Use host architecture (we assume this is x64 since building on Arm64 isn't supported). return 'x64'; @@ -433,7 +469,7 @@ function getWindowsTargetSubdir() { if (targets === 'aarch64-pc-windows-msvc') { return targets; } - throw new Error(`Invalid or unknown target (only one may be specified)`); + throw new Error('Invalid or unknown target (only one may be specified)'); } // Use host architecture (we assume this is x64 since building on Arm64 isn't supported). return 'x86_64-pc-windows-msvc'; @@ -444,7 +480,7 @@ function getLinuxTargetArch() { if (targets === 'aarch64-unknown-linux-gnu') { return 'arm64'; } - throw new Error(`Invalid or unknown target (only one may be specified)`); + throw new Error('Invalid or unknown target (only one may be specified)'); } // Use host architecture. return undefined; @@ -455,7 +491,7 @@ function getLinuxTargetSubdir() { if (targets === 'aarch64-unknown-linux-gnu') { return targets; } - throw new Error(`Invalid or unknown target (only one may be specified)`); + throw new Error('Invalid or unknown target (only one may be specified)'); } return ''; } @@ -489,8 +525,8 @@ function getLinuxVersion() { // Returns the product version. The `args` argument is optional. Set it to `'semver'` // to get the version in semver format. -function productVersion(extra_args) { - const args = ['run', '-q', '--bin', 'mullvad-version', ...extra_args]; +function productVersion(extraArgs) { + const args = ['run', '-q', '--bin', 'mullvad-version', ...extraArgs]; return execFileSync('cargo', args, { encoding: 'utf-8' }).trim(); } diff --git a/gui/tasks/scripts.js b/gui/tasks/scripts.js index 36b9def82d..20acc69c35 100644 --- a/gui/tasks/scripts.js +++ b/gui/tasks/scripts.js @@ -37,7 +37,9 @@ function makeWatchCompiler(onFirstSuccess, onSuccess) { ) { lastBundle = bundle; lastPreloadBundle = preloadBundle; - !wasFirstBuild && onSuccess(); + if (!wasFirstBuild) { + onSuccess(); + } } }), ); diff --git a/gui/test/e2e/installed/installed-utils.ts b/gui/test/e2e/installed/installed-utils.ts index 1a8cd4258e..9e4dd6c79c 100644 --- a/gui/test/e2e/installed/installed-utils.ts +++ b/gui/test/e2e/installed/installed-utils.ts @@ -2,7 +2,7 @@ import { startApp } from '../utils'; export const startInstalledApp = async (): ReturnType<typeof startApp> => { return startApp({ executablePath: getAppInstallPath() }); -} +}; function getAppInstallPath(): string { switch (process.platform) { diff --git a/gui/test/e2e/installed/playwright.config.ts b/gui/test/e2e/installed/playwright.config.ts index 9c1fa7fb87..9487e06905 100644 --- a/gui/test/e2e/installed/playwright.config.ts +++ b/gui/test/e2e/installed/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from "@playwright/test"; +import { defineConfig } from '@playwright/test'; export default defineConfig({ testDir: process.cwd(), diff --git a/gui/test/e2e/installed/state-dependent/api-access-methods.spec.ts b/gui/test/e2e/installed/state-dependent/api-access-methods.spec.ts index 0d6e8a0672..d17611261b 100644 --- a/gui/test/e2e/installed/state-dependent/api-access-methods.spec.ts +++ b/gui/test/e2e/installed/state-dependent/api-access-methods.spec.ts @@ -1,9 +1,9 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; -import { startInstalledApp } from '../installed-utils'; -import { TestUtils } from '../../utils'; import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { TestUtils } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; // This test expects the daemon to be logged in and only have "Direct" and "Mullvad Bridges" // access methods. @@ -31,10 +31,10 @@ test.afterAll(async () => { }); async function navigateToAccessMethods() { - await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); - await util.waitForNavigation(async () => await page.getByText('API access').click()); + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); + await util.waitForNavigation(() => page.getByText('API access').click()); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('API access'); } @@ -52,9 +52,9 @@ test('App should display access methods', async () => { }); test('App should add invalid access method', async () => { - await util.waitForNavigation(async () => await page.locator('button:has-text("Add")').click()); + await util.waitForNavigation(() => page.locator('button:has-text("Add")').click()); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Add method'); const inputs = page.locator('input'); @@ -71,13 +71,13 @@ test('App should add invalid access method', async () => { await inputs.nth(2).fill(process.env.SHADOWSOCKS_SERVER_PORT!); await expect(addButton).toBeEnabled(); - await addButton.click() + await addButton.click(); await expect(page.getByText('Testing method...')).toBeVisible(); await expect(page.getByText('API unreachable, add anyway?')).toBeVisible(); expect( - await util.waitForNavigation(async () => await page.locator('button:has-text("Save")').click()) + await util.waitForNavigation(() => page.locator('button:has-text("Save")').click()), ).toEqual(RoutePath.apiAccessMethods); const accessMethods = page.getByTestId('access-method'); @@ -107,7 +107,7 @@ test('App should edit access method', async () => { await customMethod.locator('button').last().click(); await util.waitForNavigation(() => customMethod.getByText('Edit').click()); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Edit method'); const inputs = page.locator('input'); @@ -125,11 +125,13 @@ test('App should edit access method', async () => { await inputs.nth(3).fill(process.env.SHADOWSOCKS_SERVER_PASSWORD!); await page.getByTestId('ciphers').click(); - await page.getByRole('option', { name: process.env.SHADOWSOCKS_SERVER_CIPHER!, exact: true }).click(); + await page + .getByRole('option', { name: process.env.SHADOWSOCKS_SERVER_CIPHER!, exact: true }) + .click(); - expect( - await util.waitForNavigation(async () => await saveButton.click()) - ).toEqual(RoutePath.apiAccessMethods); + expect(await util.waitForNavigation(() => saveButton.click())).toEqual( + RoutePath.apiAccessMethods, + ); const accessMethods = page.getByTestId('access-method'); await expect(accessMethods).toHaveCount(3); diff --git a/gui/test/e2e/installed/state-dependent/custom-bridge.spec.ts b/gui/test/e2e/installed/state-dependent/custom-bridge.spec.ts index 5efd110213..34efb60f26 100644 --- a/gui/test/e2e/installed/state-dependent/custom-bridge.spec.ts +++ b/gui/test/e2e/installed/state-dependent/custom-bridge.spec.ts @@ -1,10 +1,10 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; -import { startInstalledApp } from '../installed-utils'; -import { TestUtils } from '../../utils'; import { colors } from '../../../../src/config.json'; import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { TestUtils } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; // This test expects the daemon to be logged in and not have a custom bridge configured. // Env parameters: @@ -25,37 +25,35 @@ test.afterAll(async () => { }); test('App should enable bridge mode', async () => { - await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); - expect( - await util.waitForNavigation(async () => await page.getByText('VPN settings').click()), - ).toBe(RoutePath.vpnSettings); + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); + expect(await util.waitForNavigation(() => page.getByText('VPN settings').click())).toBe( + RoutePath.vpnSettings, + ); await page.getByRole('option', { name: 'OpenVPN' }).click(); - expect( - await util.waitForNavigation(async () => await page.getByText('OpenVPN settings').click()), - ).toBe(RoutePath.openVpnSettings); + expect(await util.waitForNavigation(() => page.getByText('OpenVPN settings').click())).toBe( + RoutePath.openVpnSettings, + ); await page.getByTestId('bridge-mode-on').click(); await expect(page.getByText('Enable bridge mode?')).toBeVisible(); - page.getByTestId('enable-confirm').click(); + await page.getByTestId('enable-confirm').click(); - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); - expect( - await util.waitForNavigation(async () => await page.click('button[aria-label="Close"]')), - ).toBe(RoutePath.main); + await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); + await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); + expect(await util.waitForNavigation(() => page.click('button[aria-label="Close"]'))).toBe( + RoutePath.main, + ); }); test('App display disabled custom bridge', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label^="Select location"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label^="Select location"]')), ).toBe(RoutePath.selectLocation); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Select location'); await page.getByText(/^Entry$/).click(); @@ -66,12 +64,10 @@ test('App display disabled custom bridge', async () => { test('App should add new custom bridge', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="Add new custom bridge"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="Add new custom bridge"]')), ).toBe(RoutePath.editCustomBridge); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Add custom bridge'); const inputs = page.locator('input'); @@ -88,11 +84,11 @@ test('App should add new custom bridge', async () => { await inputs.nth(2).fill(process.env.SHADOWSOCKS_SERVER_PASSWORD!); await page.getByTestId('ciphers').click(); - await page.getByRole('option', { name: process.env.SHADOWSOCKS_SERVER_CIPHER!, exact: true }).click(); + await page + .getByRole('option', { name: process.env.SHADOWSOCKS_SERVER_CIPHER!, exact: true }) + .click(); - expect( - await util.waitForNavigation(async () => await addButton.click()) - ).toEqual(RoutePath.selectLocation); + expect(await util.waitForNavigation(() => addButton.click())).toEqual(RoutePath.selectLocation); const customBridgeButton = page.getByText('Custom bridge'); await expect(customBridgeButton).toBeEnabled(); @@ -109,11 +105,9 @@ test('App should select custom bridge', async () => { await page.getByText(/^Entry$/).click(); await expect(customBridgeButton).not.toHaveCSS('background-color', colors.green); - await customBridgeButton.click(); await page.getByText(/^Entry$/).click(); await expect(customBridgeButton).toHaveCSS('background-color', colors.green); - }); test('App should edit custom bridge', async () => { @@ -122,12 +116,10 @@ test('App should edit custom bridge', async () => { await page.getByText(/^Entry$/).click(); expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="Edit custom bridge"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="Edit custom bridge"]')), ).toBe(RoutePath.editCustomBridge); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Edit custom bridge'); const inputs = page.locator('input'); @@ -138,10 +130,7 @@ test('App should edit custom bridge', async () => { await inputs.nth(1).fill(process.env.SHADOWSOCKS_SERVER_PORT!); await expect(saveButton).toBeEnabled(); - - expect( - await util.waitForNavigation(async () => await saveButton.click()) - ).toEqual(RoutePath.selectLocation); + expect(await util.waitForNavigation(() => saveButton.click())).toEqual(RoutePath.selectLocation); const customBridgeButton = page.locator('button:has-text("Custom bridge")'); await expect(customBridgeButton).toBeEnabled(); @@ -150,9 +139,7 @@ test('App should edit custom bridge', async () => { test('App should delete custom bridge', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="Edit custom bridge"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="Edit custom bridge"]')), ).toBe(RoutePath.editCustomBridge); const deleteButton = page.locator('button:has-text("Delete")'); @@ -163,9 +150,9 @@ test('App should delete custom bridge', async () => { await expect(page.getByText('Delete custom bridge?')).toBeVisible(); const confirmButton = page.getByTestId('delete-confirm'); - expect( - await util.waitForNavigation(async () => await confirmButton.click()) - ).toEqual(RoutePath.selectLocation); + expect(await util.waitForNavigation(() => confirmButton.click())).toEqual( + RoutePath.selectLocation, + ); const customBridgeButton = page.locator('button:has-text("Custom bridge")'); await expect(customBridgeButton).toBeDisabled(); diff --git a/gui/test/e2e/installed/state-dependent/device-revoked.spec.ts b/gui/test/e2e/installed/state-dependent/device-revoked.spec.ts index 22aa30b3d5..2b9f8d0c58 100644 --- a/gui/test/e2e/installed/state-dependent/device-revoked.spec.ts +++ b/gui/test/e2e/installed/state-dependent/device-revoked.spec.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; + import { RoutePath } from '../../../../src/renderer/lib/routes'; import { TestUtils } from '../../utils'; - import { startInstalledApp } from '../installed-utils'; // This test expects the daemon to be logged in to a revoked device. @@ -23,7 +23,7 @@ test('App should fail to login', async () => { await expect(page.getByTestId('title')).toHaveText('Device is inactive'); - expect(await util.waitForNavigation(() => { - page.getByText('Go to login').click(); - })).toEqual(RoutePath.login); + expect(await util.waitForNavigation(() => page.getByText('Go to login').click())).toEqual( + RoutePath.login, + ); }); diff --git a/gui/test/e2e/installed/state-dependent/disconnected.spec.ts b/gui/test/e2e/installed/state-dependent/disconnected.spec.ts index 360a88338d..253545dad7 100644 --- a/gui/test/e2e/installed/state-dependent/disconnected.spec.ts +++ b/gui/test/e2e/installed/state-dependent/disconnected.spec.ts @@ -1,7 +1,7 @@ import { test } from '@playwright/test'; import { Page } from 'playwright'; -import { expectDisconnected } from '../../shared/tunnel-state'; +import { expectDisconnected } from '../../shared/tunnel-state'; import { startInstalledApp } from '../installed-utils'; // This test expects the daemon to be logged into an account that has time left and to be diff --git a/gui/test/e2e/installed/state-dependent/location.spec.ts b/gui/test/e2e/installed/state-dependent/location.spec.ts index 229adb9acc..f0bd2e11f7 100644 --- a/gui/test/e2e/installed/state-dependent/location.spec.ts +++ b/gui/test/e2e/installed/state-dependent/location.spec.ts @@ -20,7 +20,6 @@ test('App should have a country', async () => { await expect(countryLabel).not.toBeEmpty(); const cityLabel = page.getByTestId('city'); - const noCityLabel = await cityLabel.count() === 0; + const noCityLabel = (await cityLabel.count()) === 0; expect(noCityLabel).toBeTruthy(); }); - diff --git a/gui/test/e2e/installed/state-dependent/login.spec.ts b/gui/test/e2e/installed/state-dependent/login.spec.ts index ba72a5b466..ad49c88edd 100644 --- a/gui/test/e2e/installed/state-dependent/login.spec.ts +++ b/gui/test/e2e/installed/state-dependent/login.spec.ts @@ -1,11 +1,11 @@ -import { exec, execSync } from 'child_process'; import { expect, test } from '@playwright/test'; +import { exec, execSync } from 'child_process'; import { Locator, Page } from 'playwright'; + import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { expectDisconnected } from '../../shared/tunnel-state'; import { TestUtils } from '../../utils'; - import { startInstalledApp } from '../installed-utils'; -import { expectDisconnected } from '../../shared/tunnel-state'; // This test expects the daemon to be logged out. // Env parameters: @@ -27,7 +27,7 @@ test.afterAll(async () => { test('App should fail to login', async () => { expect(await util.currentRoute()).toEqual(RoutePath.login); - const title = page.locator('h1') + const title = page.locator('h1'); const subtitle = page.getByTestId('subtitle'); const loginInput = getInput(page); @@ -46,15 +46,17 @@ test('App should fail to login', async () => { test('App should create account', async () => { expect(await util.currentRoute()).toEqual(RoutePath.login); - const title = page.locator('h1') + const title = page.locator('h1'); const subtitle = page.getByTestId('subtitle'); - expect(await util.waitForNavigation(async () => { - await page.getByText('Create account').click(); + expect( + await util.waitForNavigation(async () => { + await page.getByText('Create account').click(); - await expect(title).toHaveText('Account created'); - await expect(subtitle).toHaveText('Logged in'); - })).toEqual(RoutePath.expired); + await expect(title).toHaveText('Account created'); + await expect(subtitle).toHaveText('Logged in'); + }), + ).toEqual(RoutePath.expired); const outOfTimeTitle = page.getByTestId('title'); await expect(outOfTimeTitle).toHaveText('Congrats!'); @@ -65,15 +67,17 @@ test('App should create account', async () => { }); test('App should become logged out', async () => { - expect(await util.waitForNavigation(() => { - exec('mullvad account logout'); - })).toEqual(RoutePath.login); + expect( + await util.waitForNavigation(() => { + exec('mullvad account logout'); + }), + ).toEqual(RoutePath.login); }); test('App should log in', async () => { expect(await util.currentRoute()).toEqual(RoutePath.login); - const title = page.locator('h1') + const title = page.locator('h1'); const subtitle = page.getByTestId('subtitle'); const loginInput = getInput(page); @@ -82,25 +86,31 @@ test('App should log in', async () => { await loginInput.fill(process.env.ACCOUNT_NUMBER!); - expect(await util.waitForNavigation(async () => { - await loginInput.press('Enter'); + expect( + await util.waitForNavigation(async () => { + await loginInput.press('Enter'); - await expect(title).toHaveText('Logged in'); - await expect(subtitle).toHaveText('Valid account number'); - })).toEqual(RoutePath.main); + await expect(title).toHaveText('Logged in'); + await expect(subtitle).toHaveText('Valid account number'); + }), + ).toEqual(RoutePath.main); await expectDisconnected(page); }); test('App should log out', async () => { - expect(await util.waitForNavigation(() => { - void page.getByTestId('account-button').click(); - })).toEqual(RoutePath.account); + expect( + await util.waitForNavigation(() => { + void page.getByTestId('account-button').click(); + }), + ).toEqual(RoutePath.account); - expect(await util.waitForNavigation(() => { - void page.getByText('Log out').click(); - })).toEqual(RoutePath.login); + expect( + await util.waitForNavigation(() => { + void page.getByText('Log out').click(); + }), + ).toEqual(RoutePath.login); - const title = page.locator('h1') + const title = page.locator('h1'); const subtitle = page.getByTestId('subtitle'); await expect(title).toHaveText('Login'); await expect(subtitle).toHaveText('Enter your account number'); @@ -109,7 +119,7 @@ test('App should log out', async () => { test('App should log in to expired account', async () => { expect(await util.currentRoute()).toEqual(RoutePath.login); - const title = page.locator('h1') + const title = page.locator('h1'); const subtitle = page.getByTestId('subtitle'); const loginInput = getInput(page); @@ -118,9 +128,11 @@ test('App should log in to expired account', async () => { await loginInput.fill(accountNumber); - expect(await util.waitForNavigation(async () => { - await loginInput.press('Enter'); - })).toEqual(RoutePath.expired); + expect( + await util.waitForNavigation(async () => { + await loginInput.press('Enter'); + }), + ).toEqual(RoutePath.expired); const outOfTimeTitle = page.getByTestId('title'); await expect(outOfTimeTitle).toHaveText('Out of time'); diff --git a/gui/test/e2e/installed/state-dependent/macos-split-tunneling.spec.ts b/gui/test/e2e/installed/state-dependent/macos-split-tunneling.spec.ts index 0f1680bf44..6d49173bcd 100644 --- a/gui/test/e2e/installed/state-dependent/macos-split-tunneling.spec.ts +++ b/gui/test/e2e/installed/state-dependent/macos-split-tunneling.spec.ts @@ -1,10 +1,10 @@ -import { Locator, expect, test } from '@playwright/test'; -import { Page } from 'playwright'; +import { expect, Locator, test } from '@playwright/test'; import { execSync } from 'child_process'; +import { Page } from 'playwright'; -import { startInstalledApp } from '../installed-utils'; -import { TestUtils } from '../../utils'; import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { TestUtils } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; // macOS only. This test expects the daemon to be logged in and for split tunneling to be off and // have no split applications. @@ -21,13 +21,13 @@ test.afterAll(async () => { }); async function navigateToSplitTunneling() { - await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); - expect( - await util.waitForNavigation(async () => await page.getByText('Split tunneling').click()) - ).toEqual(RoutePath.splitTunneling); + expect(await util.waitForNavigation(() => page.getByText('Split tunneling').click())).toEqual( + RoutePath.splitTunneling, + ); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Split tunneling'); } @@ -46,7 +46,7 @@ test('App should enable split tunneling', async () => { const launchPadApp = page.getByText('launchpad'); await expect(launchPadApp).not.toBeVisible(); - toggle.click(); + await toggle.click(); await expect(toggle).toBeChecked(); await expect(splitList).not.toBeVisible(); await expect(nonSplitList).toBeVisible(); @@ -133,7 +133,7 @@ test('App should disable split tunneling', async () => { const launchPadApp = page.getByText('launchpad'); await expect(launchPadApp).toBeVisible(); - toggle.click(); + await toggle.click(); await expect(toggle).not.toBeChecked(); }); @@ -148,7 +148,7 @@ async function numberOfApplicationsInList(listTestid: string) { return 0; } - return await list.locator('button').count(); + return list.locator('button').count(); } function getDaemonSplitTunnelingApplications() { @@ -157,6 +157,7 @@ function getDaemonSplitTunnelingApplications() { } function isSplitInDaemon(app: string): boolean { - return !!getDaemonSplitTunnelingApplications() - .find((splitApp) => splitApp.toLowerCase().includes(app)); + return !!getDaemonSplitTunnelingApplications().find((splitApp) => + splitApp.toLowerCase().includes(app), + ); } diff --git a/gui/test/e2e/installed/state-dependent/obfuscation.spec.ts b/gui/test/e2e/installed/state-dependent/obfuscation.spec.ts index af72ded6d5..b9518f8717 100644 --- a/gui/test/e2e/installed/state-dependent/obfuscation.spec.ts +++ b/gui/test/e2e/installed/state-dependent/obfuscation.spec.ts @@ -2,10 +2,10 @@ import { expect, test } from '@playwright/test'; import { execSync } from 'child_process'; import { Page } from 'playwright'; -import { startInstalledApp } from '../installed-utils'; -import { TestUtils } from '../../utils'; import { colors } from '../../../../src/config.json'; import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { TestUtils } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; const SHADOWSOCKS_PORT = 65_000; const UDPOVERTCP_PORT = '80'; @@ -25,14 +25,14 @@ test.afterAll(async () => { }); test('App should have automatic obfuscation', async () => { - await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); - expect( - await util.waitForNavigation(async () => await page.getByText('VPN settings').click()), - ).toBe(RoutePath.vpnSettings); + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); + expect(await util.waitForNavigation(() => page.getByText('VPN settings').click())).toBe( + RoutePath.vpnSettings, + ); - expect( - await util.waitForNavigation(async () => await page.getByText('WireGuard settings').click()), - ).toBe(RoutePath.wireguardSettings); + expect(await util.waitForNavigation(() => page.getByText('WireGuard settings').click())).toBe( + RoutePath.wireguardSettings, + ); const automatic = page.getByTestId('automatic-obfuscation'); await expect(automatic).toHaveCSS('background-color', colors.green); @@ -45,9 +45,7 @@ test('App should have automatic obfuscation', async () => { test('App should set obfuscation to shadowsocks with custom port', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="Shadowsocks settings"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="Shadowsocks settings"]')), ).toBe(RoutePath.shadowsocks); const automatic = page.locator('button', { hasText: 'Automatic' }); @@ -61,7 +59,7 @@ test('App should set obfuscation to shadowsocks with custom port', async () => { const customItem = page.locator('div[role="option"]', { hasText: 'Custom' }); await expect(customItem).toHaveCSS('background-color', colors.green); - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); + await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); const shadowsocksItem = page.locator('button', { hasText: 'Shadowsocks' }); await shadowsocksItem.click(); @@ -74,22 +72,18 @@ test('App should set obfuscation to shadowsocks with custom port', async () => { test('App should still have shadowsocks custom port', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="Shadowsocks settings"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="Shadowsocks settings"]')), ).toBe(RoutePath.shadowsocks); const customItem = page.locator('div[role="option"]', { hasText: 'Custom' }); await expect(customItem).toHaveCSS('background-color', colors.green); - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); + await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); }); test('App should set obfuscation to UDP-over-TCP with port', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="UDP-over-TCP settings"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="UDP-over-TCP settings"]')), ).toBe(RoutePath.udpOverTcp); const automatic = page.locator('button', { hasText: 'Automatic' }); @@ -100,7 +94,7 @@ test('App should set obfuscation to UDP-over-TCP with port', async () => { await expect(portButton).toHaveCSS('background-color', colors.green); - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); + await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); const udpOverTcpItem = page.locator('button', { hasText: 'UDP-over-TCP' }); await udpOverTcpItem.click(); diff --git a/gui/test/e2e/installed/state-dependent/settings-import.spec.ts b/gui/test/e2e/installed/state-dependent/settings-import.spec.ts index 6b37b36244..d9a8859f76 100644 --- a/gui/test/e2e/installed/state-dependent/settings-import.spec.ts +++ b/gui/test/e2e/installed/state-dependent/settings-import.spec.ts @@ -1,9 +1,9 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; -import { startInstalledApp } from '../installed-utils'; -import { TestUtils } from '../../utils'; import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { TestUtils } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; const INVALID_JSON = 'invalid json'; const VALID_JSON = ` @@ -31,14 +31,14 @@ test.afterAll(async () => { }); async function navigateToSettingsImport() { - await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); - await util.waitForNavigation(async () => await page.getByText('VPN settings').click()); + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); + await util.waitForNavigation(() => page.getByText('VPN settings').click()); - expect( - await util.waitForNavigation(async () => await page.getByText('Server IP override').click()) - ).toEqual(RoutePath.settingsImport); + expect(await util.waitForNavigation(() => page.getByText('Server IP override').click())).toEqual( + RoutePath.settingsImport, + ); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Server IP override'); } @@ -49,14 +49,14 @@ test('App should display no overrides', async () => { }); test('App should fail to import text', async () => { - expect( - await util.waitForNavigation(async () => await page.getByText('Import via text').click()) - ).toEqual(RoutePath.settingsTextImport); + expect(await util.waitForNavigation(() => page.getByText('Import via text').click())).toEqual( + RoutePath.settingsTextImport, + ); await page.locator('textarea').fill(INVALID_JSON); - expect( - await util.waitForNavigation(async () => await page.click('button[aria-label="Save"]')) - ).toEqual(RoutePath.settingsImport); + expect(await util.waitForNavigation(() => page.click('button[aria-label="Save"]'))).toEqual( + RoutePath.settingsImport, + ); await expect(page.getByTestId('status-title')).toHaveText('NO OVERRIDES IMPORTED'); await expect(page.getByTestId('status-subtitle')).toBeVisible(); @@ -65,16 +65,16 @@ test('App should fail to import text', async () => { }); test('App should succeed to import text', async () => { - expect( - await util.waitForNavigation(async () => await page.getByText('Import via text').click()) - ).toEqual(RoutePath.settingsTextImport); + expect(await util.waitForNavigation(() => page.getByText('Import via text').click())).toEqual( + RoutePath.settingsTextImport, + ); const textarea = page.locator('textarea'); await expect(textarea).toHaveValue(INVALID_JSON); await textarea.fill(VALID_JSON); - expect( - await util.waitForNavigation(async () => await page.click('button[aria-label="Save"]')) - ).toEqual(RoutePath.settingsImport); + expect(await util.waitForNavigation(() => page.click('button[aria-label="Save"]'))).toEqual( + RoutePath.settingsImport, + ); await expect(page.getByTestId('status-title')).toHaveText('IMPORT SUCCESSFUL'); await expect(page.getByTestId('status-subtitle')).toBeVisible(); @@ -83,24 +83,24 @@ test('App should succeed to import text', async () => { await expect(page.getByTestId('status-title')).toHaveText('OVERRIDES ACTIVE'); - expect( - await util.waitForNavigation(async () => await page.getByText('Import via text').click()) - ).toEqual(RoutePath.settingsTextImport); + expect(await util.waitForNavigation(() => page.getByText('Import via text').click())).toEqual( + RoutePath.settingsTextImport, + ); await expect(textarea).toHaveValue(''); - expect( - await util.waitForNavigation(async () => await page.click('button[aria-label="Close"]')) - ).toEqual(RoutePath.settingsImport); + expect(await util.waitForNavigation(() => page.click('button[aria-label="Close"]'))).toEqual( + RoutePath.settingsImport, + ); }); test('App should show active overrides', async () => { - expect( - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')) - ).toEqual(RoutePath.vpnSettings); - expect( - await util.waitForNavigation(async () => await page.getByText('Server IP override').click()) - ).toEqual(RoutePath.settingsImport); + expect(await util.waitForNavigation(() => page.click('button[aria-label="Back"]'))).toEqual( + RoutePath.vpnSettings, + ); + expect(await util.waitForNavigation(() => page.getByText('Server IP override').click())).toEqual( + RoutePath.settingsImport, + ); await expect(page.getByTestId('status-title')).toHaveText('OVERRIDES ACTIVE'); await expect(page.getByText('Clear all overrides')).toBeEnabled(); diff --git a/gui/test/e2e/installed/state-dependent/too-many-devices.spec.ts b/gui/test/e2e/installed/state-dependent/too-many-devices.spec.ts index 9b6dbdbe3d..8ff8675b67 100644 --- a/gui/test/e2e/installed/state-dependent/too-many-devices.spec.ts +++ b/gui/test/e2e/installed/state-dependent/too-many-devices.spec.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; import { Locator, Page } from 'playwright'; + import { RoutePath } from '../../../../src/renderer/lib/routes'; import { TestUtils } from '../../utils'; - import { startInstalledApp } from '../installed-utils'; // This test expects the daemon to be logged out and the provided account to have five registered @@ -25,25 +25,26 @@ test('App should show too many devices', async () => { expect(await util.currentRoute()).toEqual(RoutePath.login); const loginInput = getInput(page); - await loginInput.type(process.env.ACCOUNT_NUMBER!); + await loginInput.fill(process.env.ACCOUNT_NUMBER!); - expect(await util.waitForNavigation(() => { - loginInput.press('Enter'); - })).toEqual(RoutePath.tooManyDevices); + expect(await util.waitForNavigation(() => loginInput.press('Enter'))).toEqual( + RoutePath.tooManyDevices, + ); const loginButton = page.getByText('Continue with login'); await expect(page.getByTestId('title')).toHaveText('Too many devices'); await expect(loginButton).toBeDisabled(); - await page.getByLabel(/^Remove device named/).first().click(); + await page + .getByLabel(/^Remove device named/) + .first() + .click(); await page.getByText('Yes, log out device').click(); await expect(loginButton).toBeEnabled(); // Trigger transition: too-many-devices -> login -> main - expect(await util.waitForNavigation(() => { - loginButton.click(); - })).toEqual(RoutePath.login); + expect(await util.waitForNavigation(() => loginButton.click())).toEqual(RoutePath.login); // Note: `util.waitForNavigation` won't return the navigation event when // transitioning from login -> main, so we need to observe the state of the diff --git a/gui/test/e2e/installed/state-dependent/tunnel-state.spec.ts b/gui/test/e2e/installed/state-dependent/tunnel-state.spec.ts index 53332ee6ab..15ce240e57 100644 --- a/gui/test/e2e/installed/state-dependent/tunnel-state.spec.ts +++ b/gui/test/e2e/installed/state-dependent/tunnel-state.spec.ts @@ -1,15 +1,11 @@ -import { exec as execAsync } from 'child_process'; -import { promisify } from 'util'; import { expect, test } from '@playwright/test'; +import { exec as execAsync } from 'child_process'; import { Page } from 'playwright'; -import { - expectConnected, - expectDisconnected, - expectError, -} from '../../shared/tunnel-state'; +import { promisify } from 'util'; -import { startInstalledApp } from '../installed-utils'; +import { expectConnected, expectDisconnected, expectError } from '../../shared/tunnel-state'; import { escapeRegExp } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; const exec = promisify(execAsync); @@ -164,7 +160,9 @@ test('App should show multihop', async () => { await exec('mullvad relay set tunnel wireguard --use-multihop=on'); await expectConnected(page); const relay = page.getByTestId('hostname-line'); - await expect(relay).toHaveText(new RegExp('^' + escapeRegExp(`${process.env.HOSTNAME} via`), 'i')); + await expect(relay).toHaveText( + new RegExp('^' + escapeRegExp(`${process.env.HOSTNAME} via`), 'i'), + ); await exec('mullvad relay set tunnel wireguard --use-multihop=off'); await page.getByText('Disconnect').click(); }); diff --git a/gui/test/e2e/mocked/expired-account-error-view.spec.ts b/gui/test/e2e/mocked/expired-account-error-view.spec.ts index 5c906a134b..e63e62f51f 100644 --- a/gui/test/e2e/mocked/expired-account-error-view.spec.ts +++ b/gui/test/e2e/mocked/expired-account-error-view.spec.ts @@ -1,10 +1,11 @@ -import { Page } from 'playwright'; -import { MockedTestUtils, startMockedApp } from './mocked-utils'; import { expect, test } from '@playwright/test'; -import { IAccountData } from '../../../src/shared/daemon-rpc-types'; -import { getBackgroundColor } from '../utils'; +import { Page } from 'playwright'; + import { colors } from '../../../src/config.json'; import { RoutePath } from '../../../src/renderer/lib/routes'; +import { IAccountData } from '../../../src/shared/daemon-rpc-types'; +import { getBackgroundColor } from '../utils'; +import { MockedTestUtils, startMockedApp } from './mocked-utils'; let page: Page; let util: MockedTestUtils; @@ -37,10 +38,12 @@ test('App should show out of time view after running out of time', async () => { const expiryDate = new Date(); expiryDate.setSeconds(expiryDate.getSeconds() + 2); - expect(await util.waitForNavigation(async () => { - await util.sendMockIpcResponse<IAccountData>({ - channel: 'account-', - response: { expiry: expiryDate.toISOString() }, - }); - })).toEqual(RoutePath.expired); + expect( + await util.waitForNavigation(async () => { + await util.sendMockIpcResponse<IAccountData>({ + channel: 'account-', + response: { expiry: expiryDate.toISOString() }, + }); + }), + ).toEqual(RoutePath.expired); }); diff --git a/gui/test/e2e/mocked/feature-indicators.spec.ts b/gui/test/e2e/mocked/feature-indicators.spec.ts index 1ba216bd52..6e0e034c35 100644 --- a/gui/test/e2e/mocked/feature-indicators.spec.ts +++ b/gui/test/e2e/mocked/feature-indicators.spec.ts @@ -1,9 +1,14 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; -import { MockedTestUtils, startMockedApp } from './mocked-utils'; -import { FeatureIndicator, ILocation, ITunnelEndpoint, TunnelState } from '../../../src/shared/daemon-rpc-types'; +import { + FeatureIndicator, + ILocation, + ITunnelEndpoint, + TunnelState, +} from '../../../src/shared/daemon-rpc-types'; import { expectConnected } from '../shared/tunnel-state'; +import { MockedTestUtils, startMockedApp } from './mocked-utils'; const endpoint: ITunnelEndpoint = { address: 'wg10:80', @@ -91,23 +96,23 @@ test('App should show feature indicators', async () => { await expect(ellipsis).toBeVisible(); await expectConnected(page); - await expectFeatureIndicators(page, ["DAITA", "Quantum resistance"], false); - await expectHiddenFeatureIndicator(page, "Mssfix"); + await expectFeatureIndicators(page, ['DAITA', 'Quantum resistance'], false); + await expectHiddenFeatureIndicator(page, 'Mssfix'); await page.getByTestId('connection-panel-chevron').click(); await expect(ellipsis).not.toBeVisible(); await expectFeatureIndicators(page, [ - "DAITA", - "Quantum resistance", - "Mssfix", - "MTU", - "Obfuscation", - "Local network sharing", - "Lockdown mode", - "Multihop", - "Custom DNS", - "Server IP override", + 'DAITA', + 'Quantum resistance', + 'Mssfix', + 'MTU', + 'Obfuscation', + 'Local network sharing', + 'Lockdown mode', + 'Multihop', + 'Custom DNS', + 'Server IP override', ]); }); @@ -123,11 +128,7 @@ async function expectHiddenFeatureIndicator(page: Page, hiddenIndicator: string) await expect(indicator).not.toBeVisible(); } -async function expectFeatureIndicators( - page: Page, - expectedIndicators: Array<string>, - only = true, -) { +async function expectFeatureIndicators(page: Page, expectedIndicators: Array<string>, only = true) { const indicators = page.getByTestId('feature-indicator'); if (only) { await expect(indicators).toHaveCount(expectedIndicators.length); diff --git a/gui/test/e2e/mocked/mocked-utils.ts b/gui/test/e2e/mocked/mocked-utils.ts index 1840ebbbb0..57998fae68 100644 --- a/gui/test/e2e/mocked/mocked-utils.ts +++ b/gui/test/e2e/mocked/mocked-utils.ts @@ -3,7 +3,7 @@ import { ElectronApplication } from 'playwright'; import { startApp, TestUtils } from '../utils'; interface StartMockedAppResponse extends Awaited<ReturnType<typeof startApp>> { - util: MockedTestUtils, + util: MockedTestUtils; } export interface MockedTestUtils extends TestUtils { @@ -22,7 +22,7 @@ export const startMockedApp = async (): Promise<StartMockedAppResponse> => { ...startAppResult.util, mockIpcHandle, sendMockIpcResponse, - } + }, }; }; diff --git a/gui/test/e2e/mocked/settings.spec.ts b/gui/test/e2e/mocked/settings.spec.ts index c6022678f5..52dbc72402 100644 --- a/gui/test/e2e/mocked/settings.spec.ts +++ b/gui/test/e2e/mocked/settings.spec.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; -import { MockedTestUtils, startMockedApp } from './mocked-utils'; import { IAccountData } from '../../../src/shared/daemon-rpc-types'; +import { MockedTestUtils, startMockedApp } from './mocked-utils'; let page: Page; let util: MockedTestUtils; @@ -21,7 +21,7 @@ test('Account button should be displayed correctly', async () => { }); test('Headerbar account info should be displayed correctly', async () => { - let expiryText = page.getByText(/^Time left:/); + const expiryText = page.getByText(/^Time left:/); await expect(expiryText).toContainText(/Time left: 29 days/i); /** diff --git a/gui/test/e2e/mocked/tunnel-state.spec.ts b/gui/test/e2e/mocked/tunnel-state.spec.ts index 3e20dab6ec..6ecf707ba6 100644 --- a/gui/test/e2e/mocked/tunnel-state.spec.ts +++ b/gui/test/e2e/mocked/tunnel-state.spec.ts @@ -1,9 +1,20 @@ import { test } from '@playwright/test'; import { Page } from 'playwright'; +import { + ErrorStateCause, + ILocation, + ITunnelEndpoint, + TunnelState, +} from '../../../src/shared/daemon-rpc-types'; +import { + expectConnected, + expectConnecting, + expectDisconnected, + expectDisconnecting, + expectError, +} from '../shared/tunnel-state'; import { MockedTestUtils, startMockedApp } from './mocked-utils'; -import { ErrorStateCause, ILocation, ITunnelEndpoint, TunnelState } from '../../../src/shared/daemon-rpc-types'; -import { expectConnected, expectConnecting, expectDisconnected, expectDisconnecting, expectError } from '../shared/tunnel-state'; const mockLocation: ILocation = { country: 'Sweden', diff --git a/gui/test/e2e/shared/tunnel-state.ts b/gui/test/e2e/shared/tunnel-state.ts index 4d75f79d01..6a5fbfaf09 100644 --- a/gui/test/e2e/shared/tunnel-state.ts +++ b/gui/test/e2e/shared/tunnel-state.ts @@ -1,5 +1,6 @@ import { expect } from '@playwright/test'; import { Page } from 'playwright'; + import { colors } from '../../../src/config.json'; import { anyOf } from '../utils'; diff --git a/gui/test/e2e/utils.ts b/gui/test/e2e/utils.ts index 082ccfcf3f..2fb33319f2 100644 --- a/gui/test/e2e/utils.ts +++ b/gui/test/e2e/utils.ts @@ -1,4 +1,4 @@ -import { Locator, Page, _electron as electron, ElectronApplication } from 'playwright'; +import { _electron as electron, ElectronApplication, Locator, Page } from 'playwright'; export interface StartAppResponse { app: ElectronApplication; @@ -8,7 +8,7 @@ export interface StartAppResponse { export interface TestUtils { currentRoute: () => Promise<string>; - waitForNavigation: (initiateNavigation?: () => Promise<void> | void) => Promise<string>; + waitForNavigation: (initiateNavigation?: () => Promise<void> | void) => Promise<string>; waitForNoTransition: () => Promise<void>; } @@ -38,34 +38,28 @@ export const startApp = async (options: LaunchOptions): Promise<StartAppResponse return { app, page, util }; }; -export const launch = async (options: LaunchOptions): Promise<ElectronApplication> => { +export const launch = (options: LaunchOptions): Promise<ElectronApplication> => { process.env.CI = 'e2e'; - return await electron.launch(options); -} + return electron.launch(options); +}; const currentRouteFactory = (app: ElectronApplication) => { - return async () => { - return await app.evaluate<string>(({ webContents }) => { - return webContents.getAllWebContents() - // Select window that isn't devtools - .find((webContents) => webContents.getURL().startsWith('file://'))! - .executeJavaScript('window.e2e.location'); - }); - }; -} + return () => + app.evaluate<string>(({ webContents }) => + webContents + .getAllWebContents() + // Select window that isn't devtools + .find((webContents) => webContents.getURL().startsWith('file://'))! + .executeJavaScript('window.e2e.location'), + ); +}; -const waitForNavigationFactory = ( - app: ElectronApplication, - page: Page, -) => { +const waitForNavigationFactory = (app: ElectronApplication, page: Page) => { // Wait for navigation animation to finish. A function can be provided that initiates the // navigation, e.g. clicks a button. return async (initiateNavigation?: () => Promise<void> | void) => { // Wait for route to change after optionally initiating the navigation. - const [route] = await Promise.all([ - waitForNextRoute(app), - initiateNavigation?.(), - ]); + const [route] = await Promise.all([waitForNextRoute(app), initiateNavigation?.()]); // Wait for view corresponding to new route to appear await page.getByTestId(route).isVisible(); @@ -77,7 +71,7 @@ const waitForNavigationFactory = ( const waitForNoTransition = async (page: Page) => { // Wait until there's only one transitionContents - let transitionContentsCount; + let transitionContentsCount; do { if (transitionContentsCount !== undefined) { await new Promise((resolve) => setTimeout(resolve, 5)); @@ -93,14 +87,15 @@ const waitForNoTransition = async (page: Page) => { }; // Returns the route when it changes -const waitForNextRoute = async (app: ElectronApplication): Promise<string> => { - return await app.evaluate(({ ipcMain }) => { - return new Promise((resolve) => { - ipcMain.once('navigation-setHistory', (_event, history: History) => { +const waitForNextRoute = (app: ElectronApplication): Promise<string> => { + return app.evaluate( + ({ ipcMain }) => + new Promise((resolve) => { + ipcMain.once('navigation-setHistory', (_event, history: History) => { resolve(history.entries[history.index].pathname); - }); - }); - }); + }); + }), + ); }; const getStyleProperty = (locator: Locator, property: string) => { @@ -125,5 +120,5 @@ export function anyOf(...values: string[]): RegExp { } export function escapeRegExp(regexp: string): string { - return regexp.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string + return regexp.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } diff --git a/gui/test/unit/account-data-cache.spec.ts b/gui/test/unit/account-data-cache.spec.ts index f3d6df3142..cf1c4299d8 100644 --- a/gui/test/unit/account-data-cache.spec.ts +++ b/gui/test/unit/account-data-cache.spec.ts @@ -1,7 +1,8 @@ +import { expect, spy } from 'chai'; +import sinon from 'sinon'; + import AccountDataCache, { AccountFetchError } from '../../src/main/account-data-cache'; import { AccountDataResponse, IAccountData } from '../../src/shared/daemon-rpc-types'; -import sinon from 'sinon'; -import { expect, spy } from 'chai'; describe('IAccountData cache', () => { const dummyAccountToken = '9876543210'; @@ -44,7 +45,7 @@ describe('IAccountData cache', () => { const watcher = new Promise<void>((resolve, reject) => { cache.fetch(dummyAccountToken, { - onFinish: (_reason?: any) => resolve(), + onFinish: () => resolve(), onError: (_error: AccountFetchError) => reject(), }); }); @@ -254,9 +255,7 @@ describe('IAccountData cache', () => { await new Promise((resolve) => setTimeout(resolve)); cache.fetch(dummyAccountToken, { - onFinish: async () => { - resolve(); - }, + onFinish: () => resolve(), onError: (_error: AccountFetchError) => reject(), }); }, @@ -298,9 +297,7 @@ describe('IAccountData cache', () => { await new Promise((resolve) => setTimeout(resolve)); cache.fetch(dummyAccountToken, { - onFinish: async () => { - resolve(); - }, + onFinish: () => resolve(), onError: (_error: AccountFetchError) => reject(), }); }, diff --git a/gui/test/unit/changelog.spec.ts b/gui/test/unit/changelog.spec.ts index 249c26e1db..774dca956b 100644 --- a/gui/test/unit/changelog.spec.ts +++ b/gui/test/unit/changelog.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { after, it, describe } from 'mocha'; +import { after, describe, it } from 'mocha'; + import { parseChangelog } from '../../src/main/changelog'; // It should be handled the same no matter if the platforms are split with a space or not. diff --git a/gui/test/unit/date-helper.spec.ts b/gui/test/unit/date-helper.spec.ts index 74500ff960..82c4faeac7 100644 --- a/gui/test/unit/date-helper.spec.ts +++ b/gui/test/unit/date-helper.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; + import * as date from '../../src/shared/date-helper'; describe('Date helper', () => { @@ -100,76 +101,61 @@ describe('Date helper', () => { }); it('should format positive difference as string', () => { - const diff1 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-01-01 13:37:20', - { displayMonths: true }, - ); + const diff1 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-01-01 13:37:20', { + displayMonths: true, + }); expect(diff1).to.equal('less than a day'); - const diff2 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-01-02 13:37:20', - { displayMonths: true }, - ); + const diff2 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-01-02 13:37:20', { + displayMonths: true, + }); expect(diff2).to.equal('1 day'); - const diff3 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-02-25 13:37:20', - { displayMonths: true }, - ); + const diff3 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-02-25 13:37:20', { + displayMonths: true, + }); expect(diff3).to.equal('55 days'); - const diff4 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-04-25 13:37:20', - { displayMonths: true }, - ); + const diff4 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-04-25 13:37:20', { + displayMonths: true, + }); expect(diff4).to.equal('3 months'); - const diff5 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2031-04-25 13:37:20', - { displayMonths: true }, - ); + const diff5 = date.formatRelativeDate('2021-01-01 13:37:10', '2031-04-25 13:37:20', { + displayMonths: true, + }); expect(diff5).to.equal('10 years'); }); it('should format positive difference as string suffixed with "left"', () => { - const diff1 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-01-01 13:37:20', - { suffix: true, displayMonths: true }, - ); + const diff1 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-01-01 13:37:20', { + suffix: true, + displayMonths: true, + }); expect(diff1).to.equal('less than a day left'); - const diff2 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-01-02 13:37:20', - { suffix: true, displayMonths: true }, - ); + const diff2 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-01-02 13:37:20', { + suffix: true, + displayMonths: true, + }); expect(diff2).to.equal('1 day left'); - const diff3 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-02-25 13:37:20', - { suffix: true, displayMonths: true }, - ); + const diff3 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-02-25 13:37:20', { + suffix: true, + displayMonths: true, + }); expect(diff3).to.equal('55 days left'); - const diff4 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-04-25 13:37:20', - { suffix: true, displayMonths: true }, - ); + const diff4 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-04-25 13:37:20', { + suffix: true, + displayMonths: true, + }); expect(diff4).to.equal('3 months left'); - const diff5 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2031-04-25 13:37:20', - { suffix: true, displayMonths: true }, - ); + const diff5 = date.formatRelativeDate('2021-01-01 13:37:10', '2031-04-25 13:37:20', { + suffix: true, + displayMonths: true, + }); expect(diff5).to.equal('10 years left'); }); diff --git a/gui/test/unit/history.spec.ts b/gui/test/unit/history.spec.ts index 03eabd80cc..739c65c5ca 100644 --- a/gui/test/unit/history.spec.ts +++ b/gui/test/unit/history.spec.ts @@ -1,5 +1,6 @@ import { expect, spy } from 'chai'; -import { it, describe, beforeEach } from 'mocha'; +import { beforeEach, describe, it } from 'mocha'; + import History from '../../src/renderer/lib/history'; import { RoutePath } from '../../src/renderer/lib/routes'; diff --git a/gui/test/unit/ip.spec.ts b/gui/test/unit/ip.spec.ts index 10b93bc890..5f6a265d25 100644 --- a/gui/test/unit/ip.spec.ts +++ b/gui/test/unit/ip.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; + import * as ip from '../../src/renderer/lib/ip'; const validIpv4Addresses = [ diff --git a/gui/test/unit/keyframe-animation.spec.ts b/gui/test/unit/keyframe-animation.spec.ts index 59b5c2d6f7..0ba302e65c 100644 --- a/gui/test/unit/keyframe-animation.spec.ts +++ b/gui/test/unit/keyframe-animation.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; + import KeyframeAnimation from '../../src/main/keyframe-animation'; describe('lib/keyframe-animation', function () { diff --git a/gui/test/unit/list-diff.spec.ts b/gui/test/unit/list-diff.spec.ts index 9357579072..e64d79b193 100644 --- a/gui/test/unit/list-diff.spec.ts +++ b/gui/test/unit/list-diff.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; + import { calculateItemList, RowDisplayData } from '../../src/renderer/components/List'; const prevItems: Array<RowDisplayData<undefined>> = [ diff --git a/gui/test/unit/logging.spec.ts b/gui/test/unit/logging.spec.ts index d107a23d3b..393b631ba5 100644 --- a/gui/test/unit/logging.spec.ts +++ b/gui/test/unit/logging.spec.ts @@ -1,10 +1,11 @@ import { expect, spy } from 'chai'; import fs from 'fs'; -import sinon from 'sinon'; -import { it, describe, before, beforeEach, after } from 'mocha'; +import { after, before, beforeEach, describe, it } from 'mocha'; import path from 'path'; -import { Logger } from '../../src/shared/logging'; +import sinon from 'sinon'; + import { backupLogFile, rotateOrDeleteFile } from '../../src/main/logging'; +import { Logger } from '../../src/shared/logging'; import { LogLevel } from '../../src/shared/logging-types'; const aPath = path.normalize('log-directory/a.log'); diff --git a/gui/test/unit/notification-evaluation.spec.ts b/gui/test/unit/notification-evaluation.spec.ts index 27de83cf29..723307b3d7 100644 --- a/gui/test/unit/notification-evaluation.spec.ts +++ b/gui/test/unit/notification-evaluation.spec.ts @@ -1,21 +1,25 @@ import { expect } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; import sinon from 'sinon'; -import { - UnsupportedVersionNotificationProvider, - UpdateAvailableNotificationProvider, -} from '../../src/shared/notifications/notification'; import NotificationController from '../../src/main/notification-controller'; import { TunnelState } from '../../src/shared/daemon-rpc-types'; import { ErrorStateCause } from '../../src/shared/daemon-rpc-types'; import { FirewallPolicyErrorType } from '../../src/shared/daemon-rpc-types'; +import { + UnsupportedVersionNotificationProvider, + UpdateAvailableNotificationProvider, +} from '../../src/shared/notifications/notification'; function createController() { return new NotificationController({ - openApp: () => { /* no-op */ }, + openApp: () => { + /* no-op */ + }, openLink: (_url: string, _withAuth?: boolean) => Promise.resolve(), - showNotificationIcon: (_value: boolean) => { /* no-op */ }, + showNotificationIcon: (_value: boolean) => { + /* no-op */ + }, }); } @@ -26,10 +30,18 @@ describe('System notifications', () => { sandbox = sinon.createSandbox(); // @ts-ignore sandbox.stub(NotificationController.prototype, 'createElectronNotification').returns({ - show: () => { /* no-op */ }, - close: () => { /* no-op */ }, - on: () => { /* no-op */ }, - removeAllListeners: () => { /* no-op */ }, + show: () => { + /* no-op */ + }, + close: () => { + /* no-op */ + }, + on: () => { + /* no-op */ + }, + removeAllListeners: () => { + /* no-op */ + }, }); }); @@ -133,7 +145,7 @@ describe('System notifications', () => { cause: ErrorStateCause.isOffline, blockingError: { type: FirewallPolicyErrorType.generic, - } + }, }, }; const result6 = controller.notifyTunnelState(nonBlockingErrorState, false, false, false, false); diff --git a/gui/test/unit/setup.ts b/gui/test/unit/setup.ts index 65644e28d9..a565ec50ca 100644 --- a/gui/test/unit/setup.ts +++ b/gui/test/unit/setup.ts @@ -1,6 +1,6 @@ import chai from 'chai'; -import spies from 'chai-spies'; import chaiAsPromised from 'chai-as-promised'; +import spies from 'chai-spies'; chai.use(spies); chai.use(chaiAsPromised); diff --git a/gui/test/unit/tunnel-state.spec.ts b/gui/test/unit/tunnel-state.spec.ts index d4629ebc64..a6013523b8 100644 --- a/gui/test/unit/tunnel-state.spec.ts +++ b/gui/test/unit/tunnel-state.spec.ts @@ -1,6 +1,7 @@ import { expect, spy } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; import sinon from 'sinon'; + import TunnelStateHandler from '../../src/main/tunnel-state'; import { TunnelState } from '../../src/shared/daemon-rpc-types'; @@ -106,4 +107,3 @@ describe('Tunnel state', () => { clock.restore(); }); }); - |
