diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ec4cf291..f35dbb900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated to Java 21 (#272) - Updated to Quarkus 3.8.x LTS (#272) - Bumpd build time dependencies +- Migrated remaining commonjs modules in frontend build to ESM - Memoize infrequently changing data, reducing XHR roundtrips - Switched to JWK thumbprint format in user profile - Switched to Repository Pattern (#273) diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js deleted file mode 100644 index 753bfdaad..000000000 --- a/frontend/.eslintrc.js +++ /dev/null @@ -1,51 +0,0 @@ -module.exports = { - plugins: ['@typescript-eslint'], - parserOptions: { - parser: require.resolve('@typescript-eslint/parser'), - extraFileExtensions: ['.vue'], - ecmaFeatures: { - jsx: true - } - }, - env: { - 'browser': true - }, - extends: [ - 'eslint:recommended', - 'plugin:vue/vue3-recommended', - ], - globals: { - defineProps: 'readonly', - defineEmits: 'readonly', - defineExpose: 'readonly', - }, - overrides: [ - { - files: ['*.vue'], - rules: { - 'vue/block-tag-newline': ['error', { 'singleline': 'consistent', 'multiline': 'consistent' }], - 'vue/html-closing-bracket-spacing': 'off', - 'vue/html-self-closing': 'off', - 'vue/max-attributes-per-line': 'off', - 'vue/padding-line-between-blocks': 'error', - 'vue/singleline-html-element-content-newline': 'off', - 'vue/space-infix-ops': 'error', - } - }, - ], - rules: { - 'keyword-spacing': ['error', { 'before': true, 'after': true }], - 'linebreak-style': ['error', 'unix'], - 'lines-between-class-members': ['error', 'always', { 'exceptAfterSingleLine': true }], - 'no-multiple-empty-lines': ['error', { 'max': 1, 'maxEOF': 0, 'maxBOF': 0 }], - 'no-unused-vars': 'off', - 'object-curly-spacing': ['error', 'always'], - 'padded-blocks': ['error', 'never'], - 'quotes': ['error', 'single'], - 'semi': ['error', 'always'], - 'space-infix-ops': 'error', - '@typescript-eslint/indent': ['error', 2], - '@typescript-eslint/no-unused-vars': 'off', // is checked by noUnusedLocals in tsconfig.json - 'no-undef': 'off' // types checked by typescript already - } -}; diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json index 1c096d3bb..fbd6dd8c8 100644 --- a/frontend/.vscode/settings.json +++ b/frontend/.vscode/settings.json @@ -20,11 +20,13 @@ "html.format.wrapLineLength": 0, "typescript.preferences.importModuleSpecifier": "relative", "typescript.preferences.quoteStyle": "single", - "mochaExplorer.env": { - "TS_NODE_COMPILER_OPTIONS": "{\"module\": \"commonjs\" }", - // "TS_NODE_PROJECT": "./test/tsconfig.json" - }, + "mochaExplorer.nodeArgv": [ + "--loader=ts-node/esm", + "--no-warnings=ExperimentalWarning", + "--experimental-specifier-resolution=node" + ], "mochaExplorer.files": "test/**/*.spec.ts", "mochaExplorer.require": "ts-node/register", - "eslint.validate": ["typescript", "javascript", "vue"] + "eslint.validate": ["typescript", "javascript", "vue"], + "eslint.useFlatConfig": true } diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 000000000..1904a7fe5 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,97 @@ +// @ts-check +import eslint from "@eslint/js"; +import pluginVue from 'eslint-plugin-vue'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( +{ + files: ['src/**/*.ts', 'test/**/*.ts'], + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ], + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + languageOptions: { + parser: tseslint.parser, + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + ecmaVersion: 'latest', + sourceType: 'module', + }, + }, + rules: { + '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unsafe-function-type': 'warn', + '@typescript-eslint/no-unused-vars': 'off', // is checked by noUnusedLocals in tsconfig.json // or use ['error', { 'ignoreRestSiblings': true }] + 'no-unused-vars': 'off', // is checked by noUnusedLocals in tsconfig.json + 'no-undef': 'off', // types checked by typescript already + 'keyword-spacing': ['error', { before: true, after: true}], + 'linebreak-style': ['error', 'unix'], + 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true, }], + 'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0, maxBOF: 0 }], + 'object-curly-spacing': ['error', 'always'], + 'padded-blocks': ['error', 'never'], + 'quotes': ['error', 'single'], + 'semi': ['error', 'always'], + 'space-infix-ops': 'error', + 'indent': ['error', 2, { SwitchCase: 1 }], + } +}, +{ + files: ['test/**/*.ts'], + rules: { + '@typescript-eslint/no-unused-expressions': 'off' + } +}, +{ + files: ['src/**/*.vue'], + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ...pluginVue.configs["flat/recommended"], + ], + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + languageOptions: { + parserOptions: { + parser: tseslint.parser, + ecmaVersion: 'latest', + sourceType: 'module', + extraFileExtensions: ['.vue'], + ecmaFeatures: { + jsx: true + } + }, + }, + rules: { + '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unsafe-function-type': 'warn', + '@typescript-eslint/no-unused-vars': 'off', // is checked by noUnusedLocals in tsconfig.json // or use ['error', { 'ignoreRestSiblings': true }] + 'no-unused-vars': 'off', // is checked by noUnusedLocals in tsconfig.json + 'no-undef': 'off', // types checked by typescript already + 'keyword-spacing': ['error', { before: true, after: true}], + 'linebreak-style': ['error', 'unix'], + 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true, }], + 'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0, maxBOF: 0 }], + 'object-curly-spacing': ['error', 'always'], + 'padded-blocks': ['error', 'never'], + 'quotes': ['error', 'single'], + 'semi': ['error', 'always'], + 'space-infix-ops': 'error', + 'indent': ['error', 2, { SwitchCase: 1 }], + 'vue/block-tag-newline': ['error', { singleline: 'consistent', multiline: 'consistent', }], + 'vue/html-closing-bracket-spacing': 'off', + 'vue/html-self-closing': 'off', + 'vue/max-attributes-per-line': 'off', + 'vue/padding-line-between-blocks': 'error', + 'vue/singleline-html-element-content-newline': 'off', + 'vue/space-infix-ops': 'error', + }, +} +); \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1581e81e9..e809a26b9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -34,21 +34,20 @@ "@types/mocha": "^10.0.7", "@types/node": "^20.12.7", "@types/semver": "^7.5.8", - "@typescript-eslint/eslint-plugin": "^8.1.0", - "@typescript-eslint/parser": "^8.1.0", "@vitejs/plugin-vue": "^5.1.2", "@vue/compiler-sfc": "^3.4.37", "autoprefixer": "^10.4.20", - "chai": "^4.5.0", - "chai-as-promised": "^7.1.2", - "eslint": "^8.57.0", - "eslint-plugin-vue": "^9.25.0", + "chai": "^5.1.1", + "chai-as-promised": "^8.0.0", + "eslint": "^9.9.0", + "eslint-plugin-vue": "^9.27.0", "mocha": "^10.7.3", "nyc": "^17.0.0", "postcss": "^8.4.41", "tailwindcss": "^3.4.10", "ts-node": "^10.9.2", "typescript": "^5.5.4", + "typescript-eslint": "^8.2.0", "vite": "^5.4.0", "vue-tsc": "^2.0.29" }, @@ -520,17 +519,56 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", + "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", + "dev": true, + "license": "Apache-2.0", + "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/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@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, "license": "MIT", "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", @@ -538,7 +576,7 @@ "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" @@ -555,6 +593,50 @@ "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/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, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/espree": { + "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, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -569,13 +651,23 @@ } }, "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.9.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz", + "integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==", "dev": true, "license": "MIT", "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, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@headlessui/tailwindcss": { @@ -614,46 +706,6 @@ "vue": ">= 3" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -668,13 +720,19 @@ "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", + "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, - "license": "BSD-3-Clause" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@intlify/bundle-utils": { "version": "8.0.0", @@ -1274,17 +1332,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.1.0.tgz", - "integrity": "sha512-LlNBaHFCEBPHyD4pZXb35mzjGkuGKXU5eeCA1SxvHfiRES0E82dOounfVpL4DCqYvJEKab0bZIA0gCRpdLKkCw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz", + "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.1.0", - "@typescript-eslint/type-utils": "8.1.0", - "@typescript-eslint/utils": "8.1.0", - "@typescript-eslint/visitor-keys": "8.1.0", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/type-utils": "8.2.0", + "@typescript-eslint/utils": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1308,16 +1366,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.1.0.tgz", - "integrity": "sha512-U7iTAtGgJk6DPX9wIWPPOlt1gO57097G06gIcl0N0EEnNw8RGD62c+2/DiP/zL7KrkqnnqF7gtFGR7YgzPllTA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz", + "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.1.0", - "@typescript-eslint/types": "8.1.0", - "@typescript-eslint/typescript-estree": "8.1.0", - "@typescript-eslint/visitor-keys": "8.1.0", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4" }, "engines": { @@ -1337,14 +1395,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.1.0.tgz", - "integrity": "sha512-DsuOZQji687sQUjm4N6c9xABJa7fjvfIdjqpSIIVOgaENf2jFXiM9hIBZOL3hb6DHK9Nvd2d7zZnoMLf9e0OtQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz", + "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.1.0", - "@typescript-eslint/visitor-keys": "8.1.0" + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1355,14 +1413,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.1.0.tgz", - "integrity": "sha512-oLYvTxljVvsMnldfl6jIKxTaU7ok7km0KDrwOt1RHYu6nxlhN3TIx8k5Q52L6wR33nOwDgM7VwW1fT1qMNfFIA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz", + "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.1.0", - "@typescript-eslint/utils": "8.1.0", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/utils": "8.2.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1380,9 +1438,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.1.0.tgz", - "integrity": "sha512-q2/Bxa0gMOu/2/AKALI0tCKbG2zppccnRIRCW6BaaTlRVaPKft4oVYPp7WOPpcnsgbr0qROAVCVKCvIQ0tbWog==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz", + "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==", "dev": true, "license": "MIT", "engines": { @@ -1394,14 +1452,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.1.0.tgz", - "integrity": "sha512-NTHhmufocEkMiAord/g++gWKb0Fr34e9AExBRdqgWdVBaKoei2dIyYKD9Q0jBnvfbEA5zaf8plUFMUH6kQ0vGg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz", + "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.1.0", - "@typescript-eslint/visitor-keys": "8.1.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1423,16 +1481,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.1.0.tgz", - "integrity": "sha512-ypRueFNKTIFwqPeJBfeIpxZ895PQhNyH4YID6js0UoBImWYoSjBsahUn9KMiJXh94uOjVBgHD9AmkyPsPnFwJA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz", + "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.1.0", - "@typescript-eslint/types": "8.1.0", - "@typescript-eslint/typescript-estree": "8.1.0" + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1446,13 +1504,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.1.0.tgz", - "integrity": "sha512-ba0lNI19awqZ5ZNKh6wCModMwoZs457StTebQ0q1NP58zSi2F6MOZRXwfKZy+jB78JNJ/WH8GSh2IQNzXX8Nag==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz", + "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.1.0", + "@typescript-eslint/types": "8.2.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1463,13 +1521,6 @@ "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, - "license": "ISC" - }, "node_modules/@vitejs/plugin-vue": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.2.tgz", @@ -1819,13 +1870,13 @@ } }, "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": ">=12" } }, "node_modules/asynckit": { @@ -2045,32 +2096,30 @@ } }, "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", "dev": true, "license": "MIT", "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=12" } }, "node_modules/chai-as-promised": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", - "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-8.0.0.tgz", + "integrity": "sha512-sMsGXTrS3FunP/wbqh/KxM8Kj/aLPXQGkNtvE5wPfSToq8wkkvBpTZo1LIiEVmC4BwkKpag+l5h/20lBMk6nUg==", "dev": true, "license": "WTFPL", "dependencies": { - "check-error": "^1.0.2" + "check-error": "^2.0.0" }, "peerDependencies": { "chai": ">= 2.1.2 < 6" @@ -2094,16 +2143,13 @@ } }, "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, "engines": { - "node": "*" + "node": ">= 16" } }, "node_modules/chokidar": { @@ -2319,14 +2365,11 @@ } }, "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, "engines": { "node": ">=6" } @@ -2398,19 +2441,6 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "license": "MIT" }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2534,42 +2564,38 @@ } }, "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.9.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz", + "integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.17.1", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.9.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", @@ -2583,10 +2609,18 @@ "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-plugin-vue": { @@ -2653,6 +2687,54 @@ "concat-map": "0.0.1" } }, + "node_modules/eslint/node_modules/eslint-scope": { + "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, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/espree": { + "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, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2809,16 +2891,16 @@ } }, "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, "license": "MIT", "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-saver": { @@ -2885,18 +2967,17 @@ } }, "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, "license": "MIT", "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": { @@ -3817,9 +3898,9 @@ } }, "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", + "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", "dev": true, "license": "MIT", "dependencies": { @@ -4597,13 +4678,13 @@ "license": "MIT" }, "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": ">= 14.16" } }, "node_modules/picocolors": { @@ -5728,16 +5809,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -5775,6 +5846,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.2.0.tgz", + "integrity": "sha512-DmnqaPcML0xYwUzgNbM1XaKXpEb7BShYf2P1tkUmmcl8hyeG7Pj08Er7R9bNy6AufabywzJcOybQAtnD/c9DGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.2.0", + "@typescript-eslint/parser": "8.2.0", + "@typescript-eslint/utils": "8.2.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/ufo": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index e5e1fa63e..88d8bdf1e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,4 +1,5 @@ { + "type": "module", "name": "cryptomator-hub", "version": "1.4.0", "description": "Web-Frontend for Cryptomator Hub", @@ -9,7 +10,7 @@ "scripts": { "dev": "vite", "build": "vue-tsc --noEmit && vite build", - "lint": "eslint -c .eslintrc.js", + "lint": "eslint", "test": "nyc --reporter lcov mocha", "serve": "vite preview", "dist": "vue-tsc --noEmit && vite build --sourcemap --outDir=\"../backend/src/main/resources/META-INF/resources\" --emptyOutDir" @@ -19,9 +20,7 @@ "test": "./test" }, "mocha": { - "require": [ - "ts-node/register" - ], + "loader": "ts-node/esm", "spec": "test/**/*.spec.ts", "timeout": 60000 }, @@ -35,21 +34,20 @@ "@types/mocha": "^10.0.7", "@types/node": "^20.12.7", "@types/semver": "^7.5.8", - "@typescript-eslint/eslint-plugin": "^8.1.0", - "@typescript-eslint/parser": "^8.1.0", "@vitejs/plugin-vue": "^5.1.2", "@vue/compiler-sfc": "^3.4.37", "autoprefixer": "^10.4.20", - "chai": "^4.5.0", - "chai-as-promised": "^7.1.2", - "eslint": "^8.57.0", - "eslint-plugin-vue": "^9.25.0", + "chai": "^5.1.1", + "chai-as-promised": "^8.0.0", + "eslint": "^9.9.0", + "eslint-plugin-vue": "^9.27.0", "mocha": "^10.7.3", "nyc": "^17.0.0", "postcss": "^8.4.41", "tailwindcss": "^3.4.10", "ts-node": "^10.9.2", "typescript": "^5.5.4", + "typescript-eslint": "^8.2.0", "vite": "^5.4.0", "vue-tsc": "^2.0.29" }, diff --git a/frontend/postcss.config.js b/frontend/postcss.config.cjs similarity index 100% rename from frontend/postcss.config.js rename to frontend/postcss.config.cjs diff --git a/frontend/src/common/auditlog.ts b/frontend/src/common/auditlog.ts index 5ccdbdd95..c9531d260 100644 --- a/frontend/src/common/auditlog.ts +++ b/frontend/src/common/auditlog.ts @@ -116,7 +116,7 @@ export class AuditLogEntityCache { return this.getEntity(deviceId, this.devices, this.debouncedResolvePendingDevices); } - private async getEntity(entityId: string, entities: Map>, debouncedResolvePendingEntities: Function): Promise { + private async getEntity(entityId: string, entities: Map>, debouncedResolvePendingEntities: () => void): Promise { const cachedEntity = entities.get(entityId); if (!cachedEntity) { const deferredEntity = new Deferred(); @@ -133,8 +133,8 @@ export class AuditLogEntityCache { private debouncedResolvePendingDevices = debounce(async () => await this.resolvePendingEntities(this.devices, backend.devices.listSome), 100); private async resolvePendingEntities(entities: Map>, listSome: (ids: string[]) => Promise): Promise { - const pendingEntities = Array.from(entities.entries()).filter(([_, v]) => v.status === 'pending'); - const entitiesResult = await listSome(pendingEntities.map(([k, _]) => k)); + const pendingEntities = Array.from(entities.entries()).filter(([, v]) => v.status === 'pending'); + const entitiesResult = await listSome(pendingEntities.map(([k,]) => k)); for (const [entityId, deferredEntity] of pendingEntities) { const entity = entitiesResult.find(v => v.id === entityId); if (entity) { diff --git a/frontend/src/common/backend.ts b/frontend/src/common/backend.ts index b833e7541..47cbc11f3 100644 --- a/frontend/src/common/backend.ts +++ b/frontend/src/common/backend.ts @@ -27,7 +27,7 @@ axiosAuth.interceptors.request.use(async request => { request.headers = AxiosHeaders.from({ 'Authorization': `Bearer ${token}` }); } return request; - } catch (err: unknown) { + } catch { // only things from auth module can throw errors here throw new UnauthorizedError(); } @@ -160,7 +160,7 @@ class VaultService { public async get(vaultId: string): Promise { return axiosAuth.get(`/vaults/${vaultId}`) .then(response => { - let dateString = response.data.creationTime; + const dateString = response.data.creationTime; response.data.creationTime = new Date(dateString); return response.data; }) @@ -228,12 +228,12 @@ class DeviceService { return axiosAuth.get(`/devices?${query}`).then(response => response.data); } - public async removeDevice(deviceId: string): Promise> { + public async removeDevice(deviceId: string): Promise> { return axiosAuth.delete(`/devices/${deviceId}`) .catch((error) => rethrowAndConvertIfExpected(error, 404)); } - public async putDevice(device: DeviceDto): Promise> { + public async putDevice(device: DeviceDto): Promise> { return axiosAuth.put(`/devices/${device.id}`, device); } } @@ -260,6 +260,7 @@ class TrustService { public async trustUser(userId: string, signature: string): Promise { return axiosAuth.put(`/users/trusted/${userId}`, signature, { headers: { 'Content-Type': 'text/plain' } }); } + public async get(userId: string): Promise { return axiosAuth.get(`/users/trusted/${userId}`).then(response => response.data) .catch(e => { @@ -288,9 +289,9 @@ class AuthorityService { return { ...authority, pictureUrl: authority.pictureUrl - } + }; } else { - let cfg = AuthorityService.getJdenticonConfig(authority.type); + const cfg = AuthorityService.getJdenticonConfig(authority.type); const svg = toSvg(authority.id, 100, cfg); const bytes = new TextEncoder().encode(svg); const url = `data:image/svg+xml;base64,${base64.stringify(bytes)}`; @@ -407,7 +408,7 @@ function convertExpectedToBackendError(status: number): BackendError { * @param error A thrown object * @param expectedStatusCodes The expected http status codes of the backend call */ -export function rethrowAndConvertIfExpected(error: unknown, ...expectedStatusCodes: number[]): Promise { +export function rethrowAndConvertIfExpected(error: unknown, ...expectedStatusCodes: number[]): never { if (AxiosStatic.isAxiosError(error) && error.response != null && expectedStatusCodes.includes(error.response.status)) { throw convertExpectedToBackendError(error.response.status); } else { diff --git a/frontend/src/common/crypto.ts b/frontend/src/common/crypto.ts index 351741ff6..f7bd3a5b0 100644 --- a/frontend/src/common/crypto.ts +++ b/frontend/src/common/crypto.ts @@ -3,9 +3,9 @@ import { base16, base32, base64, base64url } from 'rfc4648'; import { JWEBuilder, JWEParser } from './jwe'; import { CRC32, DB, wordEncoder } from './util'; export class UnwrapKeyError extends Error { - readonly actualError: any; + readonly actualError: unknown; - constructor(actualError: any) { + constructor(actualError: unknown) { super('Unwrapping key failed'); this.actualError = actualError; } @@ -270,7 +270,6 @@ export class UserKeys { public static readonly ECDSA_KEY_DESIGNATION: EcKeyImportParams | EcKeyGenParams = { name: 'ECDSA', namedCurve: 'P-384' }; - protected constructor(readonly ecdhKeyPair: CryptoKeyPair, readonly ecdsaKeyPair: CryptoKeyPair) { } /** diff --git a/frontend/src/common/jwe.ts b/frontend/src/common/jwe.ts index b73cb8e8e..e4af50cd3 100644 --- a/frontend/src/common/jwe.ts +++ b/frontend/src/common/jwe.ts @@ -81,7 +81,7 @@ export class JWEParser { * @param recipientPrivateKey The recipient's private key * @returns Decrypted payload */ - public async decryptEcdhEs(recipientPrivateKey: CryptoKey): Promise { + public async decryptEcdhEs(recipientPrivateKey: CryptoKey): Promise { if (this.header.alg != 'ECDH-ES' || this.header.enc != 'A256GCM' || !this.header.epk) { throw new Error('unsupported alg or enc'); } @@ -96,7 +96,7 @@ export class JWEParser { * @returns Decrypted payload * @throws {UnwrapKeyError} if decryption failed (wrong password?) */ - public async decryptPbes2(password: string): Promise { + public async decryptPbes2(password: string): Promise { if (this.header.alg != 'PBES2-HS512+A256KW' || /* this.header.enc != 'A256GCM' || */ !this.header.p2s || !this.header.p2c) { throw new Error('unsupported alg or enc'); } @@ -110,7 +110,7 @@ export class JWEParser { } } - private async decrypt(cek: CryptoKey): Promise { + private async decrypt(cek: CryptoKey): Promise { const utf8enc = new TextEncoder(); const m = new Uint8Array(this.ciphertext.length + this.tag.length); m.set(this.ciphertext, 0); diff --git a/frontend/src/common/jwt.ts b/frontend/src/common/jwt.ts index f7a7d00a1..be174c899 100644 --- a/frontend/src/common/jwt.ts +++ b/frontend/src/common/jwt.ts @@ -18,7 +18,7 @@ export class JWT { * @param payload The payload * @param signerPrivateKey The signers's private key */ - public static async build(header: JWTHeader, payload: any, signerPrivateKey: CryptoKey): Promise { + public static async build(header: JWTHeader, payload: object, signerPrivateKey: CryptoKey): Promise { const encodedHeader = base64url.stringify(new TextEncoder().encode(JSON.stringify(header)), { pad: false }); const encodedPayload = base64url.stringify(new TextEncoder().encode(JSON.stringify(payload)), { pad: false }); const encodedSignature = await this.es384sign(encodedHeader, encodedPayload, signerPrivateKey); @@ -46,8 +46,8 @@ export class JWT { * @returns header and payload * @throws Error if the JWT is invalid */ - public static async parse(jwt: string, signerPublicKey: CryptoKey): Promise<[JWTHeader, any]> { - const [encodedHeader, encodedPayload, encodedSignature] = jwt.split('.'); + public static async parse(jwt: string, signerPublicKey: CryptoKey): Promise<[JWTHeader, object]> { + const [encodedHeader, encodedPayload] = jwt.split('.'); const header: JWTHeader = JSON.parse(new TextDecoder().decode(base64url.parse(encodedHeader, { loose: true }))); if (header.alg !== 'ES384') { throw new Error('Unsupported algorithm'); diff --git a/frontend/src/common/updatecheck.ts b/frontend/src/common/updatecheck.ts index 94bebbbdb..40810fafa 100644 --- a/frontend/src/common/updatecheck.ts +++ b/frontend/src/common/updatecheck.ts @@ -9,7 +9,7 @@ export type LatestVersionDto = { class UpdatesService { public async get(localVersion: string): Promise { - let config = { + const config = { headers: { 'Content-Type': 'application/json', 'Cryptomator-Hub-Version': localVersion, diff --git a/frontend/src/common/userdata.ts b/frontend/src/common/userdata.ts index 800f6c80b..2cb8cb694 100644 --- a/frontend/src/common/userdata.ts +++ b/frontend/src/common/userdata.ts @@ -4,7 +4,6 @@ import { BrowserKeys, UserKeys } from './crypto'; import { JWEParser } from './jwe'; class UserData { - #me?: Promise; #browserKeys?: Promise; @@ -136,7 +135,6 @@ class UserData { await backend.users.putMe(me); } } - } const instance = new UserData(); diff --git a/frontend/src/common/util.ts b/frontend/src/common/util.ts index 1eafa76fb..a0a731113 100644 --- a/frontend/src/common/util.ts +++ b/frontend/src/common/util.ts @@ -3,7 +3,7 @@ import { dictionary } from './4096words_en'; export class DB { private static readonly NAME = 'hub'; - public static async transaction(objectStore: string, mode: IDBTransactionMode, query: (transaction: IDBTransaction) => IDBRequest): Promise { + public static async transaction(objectStore: string, mode: IDBTransactionMode, query: (transaction: IDBTransaction) => IDBRequest): Promise { const db = await new Promise((resolve, reject) => { const req = indexedDB.open(DB.NAME); req.onsuccess = () => resolve(req.result); @@ -23,7 +23,7 @@ export class DB { export class Deferred { public promise: Promise; - public reject: (reason?: any) => void; + public reject: (reason?: unknown) => void; public resolve: (value: T) => void; public status: 'pending' | 'resolved' | 'rejected' = 'pending'; @@ -43,9 +43,10 @@ export class Deferred { * @param wait time to wait before calling function * @returns debounced function */ +// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type export const debounce = (func: Function, wait = 300) => { let timeoutId: ReturnType; - function debounceCore(this: any, ...args: any[]) { + function debounceCore(this: unknown, ...args: unknown[]) { cancel(); timeoutId = setTimeout(() => func.apply(this, args), wait); } diff --git a/frontend/src/common/wot.ts b/frontend/src/common/wot.ts index dcf5fb675..2fb89e6ea 100644 --- a/frontend/src/common/wot.ts +++ b/frontend/src/common/wot.ts @@ -48,7 +48,7 @@ async function sign(user: UserDto): Promise { * @param allegedSignedKey The public key that should be signed by the last signature in the chain */ async function verify(signatureChain: string[], allegedSignedKey: SignedKeys) { - let signerPublicKey = await userdata.decryptUserKeysWithBrowser().then(keys => keys.ecdsaKeyPair.publicKey); + const signerPublicKey = await userdata.decryptUserKeysWithBrowser().then(keys => keys.ecdsaKeyPair.publicKey); await verifyRescursive(signatureChain, signerPublicKey, allegedSignedKey); } @@ -62,7 +62,7 @@ async function verify(signatureChain: string[], allegedSignedKey: SignedKeys) { async function verifyRescursive(signatureChain: string[], signerPublicKey: CryptoKey, allegedSignedKey: SignedKeys) { // get first element of signature chain: const [signature, ...remainingChain] = signatureChain; - const [_, signedKeys] = await JWT.parse(signature, signerPublicKey) as [JWTHeader, SignedKeys]; + const [, signedKeys] = await JWT.parse(signature, signerPublicKey) as [JWTHeader, SignedKeys]; if (remainingChain.length === 0) { // last element in chain should match signed public key if (!deeplyEqual(signedKeys, allegedSignedKey)) { diff --git a/frontend/src/components/AdminSettings.vue b/frontend/src/components/AdminSettings.vue index 88724efab..cf264f5f2 100644 --- a/frontend/src/components/AdminSettings.vue +++ b/frontend/src/components/AdminSettings.vue @@ -238,7 +238,7 @@ const numberOfExceededSeats = computed(() => { }); onMounted(async () => { - let cfg = config.get(); + const cfg = config.get(); keycloakAdminRealmURL.value = `${cfg.keycloakUrl}/admin/${cfg.keycloakRealm}/console`; if (props.token) { await setToken(props.token); @@ -256,8 +256,8 @@ async function setToken(token: string) { async function fetchData() { try { - let versionDto = backend.version.get(); - let versionAvailable = versionDto.then(versionDto => updateChecker.get(versionDto.hubVersion)); + const versionDto = backend.version.get(); + const versionAvailable = versionDto.then(versionDto => updateChecker.get(versionDto.hubVersion)); admin.value = await backend.billing.get(); version.value = await versionDto; latestVersion.value = await versionAvailable; diff --git a/frontend/src/components/AuditLogDetailsSignedWotId.vue b/frontend/src/components/AuditLogDetailsSignedWotId.vue index a537e6fae..89f6de459 100644 --- a/frontend/src/components/AuditLogDetailsSignedWotId.vue +++ b/frontend/src/components/AuditLogDetailsSignedWotId.vue @@ -77,7 +77,7 @@ onMounted(async () => { try { const signerPublicKey = await asPublicKey(base64.parse(props.event.signerKey), UserKeys.ECDSA_KEY_DESIGNATION, UserKeys.ECDSA_PUB_KEY_USAGES); const [_, signedKeys] = await JWT.parse(props.event.signature, signerPublicKey) as [JWTHeader, SignedKeys]; - signedFingerprint.value = await wot.computeFingerprint({ecdhPublicKey: signedKeys.ecdhPublicKey, ecdsaPublicKey: signedKeys.ecdsaPublicKey}); + signedFingerprint.value = await wot.computeFingerprint({ ecdhPublicKey: signedKeys.ecdhPublicKey, ecdsaPublicKey: signedKeys.ecdsaPublicKey }); if (props.event.signerKey === signingUser?.ecdsaPublicKey && signedFingerprint.value === currentFingerprint.value) { signatureStatus.value = SignatureStatus.STILL_VALID; } else if (props.event.signerKey !== signingUser?.ecdsaPublicKey) { diff --git a/frontend/src/components/DeviceList.vue b/frontend/src/components/DeviceList.vue index abb6e7591..c11e6a976 100644 --- a/frontend/src/components/DeviceList.vue +++ b/frontend/src/components/DeviceList.vue @@ -120,7 +120,7 @@ async function removeDevice(device: DeviceDto) { if (error instanceof NotFoundError) { // if device is already missing in backend → ignore and proceed to then() } else { - let e = error instanceof Error ? error : new Error('Unknown Error'); + const e = error instanceof Error ? error : new Error('Unknown Error'); onRemoveDeviceError.value[device.id] = e; throw e; } diff --git a/frontend/src/components/GrantPermissionDialog.vue b/frontend/src/components/GrantPermissionDialog.vue index 224d71899..6da9e8557 100644 --- a/frontend/src/components/GrantPermissionDialog.vue +++ b/frontend/src/components/GrantPermissionDialog.vue @@ -73,7 +73,6 @@ import backend, { AccessGrant, ConflictError, NotFoundError, TrustDto, UserDto, import { VaultKeys } from '../common/crypto'; import TrustDetails from './TrustDetails.vue'; - const { t } = useI18n({ useScope: 'global' }); const open = ref(false); @@ -122,7 +121,7 @@ async function grantAccess() { } async function giveUsersAccess(users: UserDto[]) { - let tokens: AccessGrant[] = []; + const tokens: AccessGrant[] = []; for (const user of users) { if (user.ecdhPublicKey) { // some users might not have set up their key pair, so we can't share secrets with them yet const publicKey = base64.parse(user.ecdhPublicKey); diff --git a/frontend/src/components/NavigationBar.vue b/frontend/src/components/NavigationBar.vue index 8edd89672..d0225299f 100644 --- a/frontend/src/components/NavigationBar.vue +++ b/frontend/src/components/NavigationBar.vue @@ -68,8 +68,8 @@ diff --git a/frontend/src/components/VaultDetails.vue b/frontend/src/components/VaultDetails.vue index abcdb3ce8..2d944e1f7 100644 --- a/frontend/src/components/VaultDetails.vue +++ b/frontend/src/components/VaultDetails.vue @@ -364,7 +364,7 @@ async function reloadDevicesRequiringAccessGrant() { } } -function isAuthorityDto(toCheck: any): toCheck is AuthorityDto { +function isAuthorityDto(toCheck: unknown): toCheck is AuthorityDto { return (toCheck as AuthorityDto).type != null; } @@ -391,7 +391,7 @@ async function addAuthority(authority: AuthorityDto) { async function addAuthorityBackend(authority: AuthorityDto) { try { - switch(authority.type) { + switch (authority.type) { case 'USER': await backend.vaults.addUser(props.vaultId, authority.id); break; diff --git a/frontend/src/components/VaultList.vue b/frontend/src/components/VaultList.vue index 0efdf3285..a57981d6d 100644 --- a/frontend/src/components/VaultList.vue +++ b/frontend/src/components/VaultList.vue @@ -8,7 +8,7 @@ - +

{{ t('vaultList.title') }} diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts index beea492c6..ce67dd38c 100644 --- a/frontend/src/i18n/index.ts +++ b/frontend/src/i18n/index.ts @@ -36,4 +36,4 @@ export const numberFormats: I18nOptions['numberFormats'] = { style: 'percent', useGrouping: false } } -} +}; diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index c422ee44d..8522fe71f 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -1,4 +1,4 @@ -import { createRouter, createWebHistory, RouteLocationRaw, RouteLocationResolvedGeneric, RouteRecordRaw } from 'vue-router'; +import { createRouter, createWebHistory, RouteLocationRaw, RouteRecordRaw } from 'vue-router'; import authPromise from '../common/auth'; import backend from '../common/backend'; import { baseURL } from '../common/config'; @@ -149,7 +149,7 @@ router.beforeEach((to, from, next) => { await backend.users.putMe(); } }).finally(() => { - let {sync_me: _, ...remainingQuery} = to.query; // remove sync_me query parameter to avoid endless recursion + const { sync_me: _, ...remainingQuery } = to.query; // remove sync_me query parameter to avoid endless recursion next({ path: to.path, query: remainingQuery, replace: true }); }); } else { diff --git a/frontend/src/shims-vue.d.ts b/frontend/src/shims-vue.d.ts index fe7917e04..b92e77e23 100644 --- a/frontend/src/shims-vue.d.ts +++ b/frontend/src/shims-vue.d.ts @@ -1,6 +1,6 @@ declare module '*.vue' { import { DefineComponent } from 'vue'; - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type const component: DefineComponent<{}, {}, any>; export default component; } diff --git a/frontend/test/common/crypto.spec.ts b/frontend/test/common/crypto.spec.ts index c767e6353..d259c8992 100644 --- a/frontend/test/common/crypto.spec.ts +++ b/frontend/test/common/crypto.spec.ts @@ -1,6 +1,7 @@ import { use as chaiUse, expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; import { before, describe } from 'mocha'; +import { webcrypto } from 'node:crypto'; import { base64, base64url } from 'rfc4648'; import { UnwrapKeyError, UserKeys, VaultKeys, getJwkThumbprint } from '../../src/common/crypto'; @@ -35,14 +36,13 @@ describe('crypto', () => { before(async () => { // since this test runs on Node, we need to replace window.crypto: - // @ts-ignore: global not defined (but will be available within Node) - Object.defineProperty(global, 'crypto', { value: require('node:crypto').webcrypto }); - // @ts-ignore: global not defined (but will be available within Node) + Object.defineProperty(global, 'crypto', { value: webcrypto }); + // @ts-expect-error: incomplete 'window' type global.window = { crypto: global.crypto }; // prepare some test key pairs: - let ecdhP384: EcKeyImportParams = { name: 'ECDH', namedCurve: 'P-384' }; - let ecdsaP384: EcKeyImportParams = { name: 'ECDSA', namedCurve: 'P-384' }; + const ecdhP384: EcKeyImportParams = { name: 'ECDH', namedCurve: 'P-384' }; + const ecdsaP384: EcKeyImportParams = { name: 'ECDSA', namedCurve: 'P-384' }; const aliceEcdhPrv = crypto.subtle.importKey('jwk', alicePrivate, ecdhP384, true, ['deriveKey', 'deriveBits']); const aliceEcdhPub = crypto.subtle.importKey('jwk', alicePublic, ecdhP384, true, []); const aliceEcdsaPrv = crypto.subtle.importKey('jwk', alicePrivate, ecdsaP384, true, ['sign']); @@ -62,7 +62,7 @@ describe('crypto', () => { }); it('recover() succeeds for valid key', async () => { - let recoveryKey = ` + const recoveryKey = ` pathway lift abuse plenty export texture gentleman landscape beyond ceiling around leaf cafe charity border breakdown victory surely computer cat linger restrict infer crowd live computer true written amazed investor boot depth left theory snow whereby terminal weekly reject happiness circuit partial cup ad @@ -238,12 +238,10 @@ describe('crypto', () => { }); }); - describe('JWK Thumbprint', () => { - // https://datatracker.ietf.org/doc/html/rfc7638#section-3.1 it('compute example thumbprint from RFC 7638, Section 3.1', async () => { - const input: JsonWebKey & any = { + const input: JsonWebKey & Record = { kty: 'RSA', n: '0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw', e: 'AQAB', @@ -255,7 +253,6 @@ describe('crypto', () => { expect(base64url.stringify(thumbprint, { pad: false })).to.eq('NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs'); }); - }); }); diff --git a/frontend/test/common/jwe.spec.ts b/frontend/test/common/jwe.spec.ts index 5977b87f6..57c320332 100644 --- a/frontend/test/common/jwe.spec.ts +++ b/frontend/test/common/jwe.spec.ts @@ -1,14 +1,14 @@ import { expect } from 'chai'; import { describe } from 'mocha'; +import { webcrypto } from 'node:crypto'; import { base64url } from 'rfc4648'; import { ConcatKDF, ECDH_ES, ECDH_P384, JWEBuilder, JWEHeader, JWEParser, PBES2 } from '../../src/common/jwe'; describe('JWE', () => { before(done => { // since this test runs on Node, we need to replace window.crypto: - // @ts-ignore: global not defined (but will be available within Node) - Object.defineProperty(global, 'crypto', { value: require('node:crypto').webcrypto }); - // @ts-ignore: global not defined (but will be available within Node) + Object.defineProperty(global, 'crypto', { value: webcrypto }); + // @ts-expect-error: incomplete 'window' type global.window = { crypto: global.crypto }; done(); }); @@ -22,7 +22,7 @@ describe('JWE', () => { const suppPubInfo = new Uint8Array([0, 0, 0, 128]); const otherInfo = new Uint8Array([...algorithmId, ...partyUInfo, ...partyVInfo, ...new Uint8Array(suppPubInfo)]); - let derivedKey = await ConcatKDF.kdf(z, 16, otherInfo); + const derivedKey = await ConcatKDF.kdf(z, 16, otherInfo); expect(new Uint8Array(derivedKey), 'derived key').to.eql(new Uint8Array([86, 170, 141, 234, 248, 35, 109, 32, 92, 34, 40, 205, 113, 167, 16, 26])); }); }); diff --git a/frontend/test/common/jwt.spec.ts b/frontend/test/common/jwt.spec.ts index dfc64a975..8d8a2b673 100644 --- a/frontend/test/common/jwt.spec.ts +++ b/frontend/test/common/jwt.spec.ts @@ -1,13 +1,13 @@ import { expect } from 'chai'; import { describe } from 'mocha'; +import { webcrypto } from 'node:crypto'; import { JWT, JWTHeader } from '../../src/common/jwt'; describe('JWT', () => { before(done => { // since this test runs on Node, we need to replace window.crypto: - // @ts-ignore: global not defined (but will be available within Node) - Object.defineProperty(global, 'crypto', { value: require('node:crypto').webcrypto }); - // @ts-ignore: global not defined (but will be available within Node) + Object.defineProperty(global, 'crypto', { value: webcrypto }); + // @ts-expect-error: incomplete 'window' type global.window = { crypto: global.crypto }; done(); }); @@ -85,6 +85,5 @@ describe('JWT', () => { expect(header).to.deep.equal({ alg: 'ES384', typ: 'JWT', b64: true }); expect(payload).to.deep.equal({ foo: 42, bar: 'lol', obj: { nested: true } }); }); - }); }); diff --git a/frontend/test/common/util.spec.ts b/frontend/test/common/util.spec.ts index d6865af83..2fedf3c59 100644 --- a/frontend/test/common/util.spec.ts +++ b/frontend/test/common/util.spec.ts @@ -22,9 +22,9 @@ describe('WordEncoder', () => { describe('CRC32', () => { it('crc32(\'123456789\') == 0xCBF43926', () => { - let input = new TextEncoder().encode('123456789'); + const input = new TextEncoder().encode('123456789'); - let result = CRC32.compute(input); + const result = CRC32.compute(input); expect(result).to.eql(0xCBF43926); }); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 381b7738e..e9073f959 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -14,9 +14,8 @@ "useUnknownInCatchVariables": false, // Workaround for `node_modules/miscreant/src/providers/webcrypto.ts:21:11 - error TS18046: 'e' is of type 'unknown'.` }, "ts-node": { - "compilerOptions": { - "module": "CommonJS" - } + "esm": true, + "experimentalSpecifierResolution": "node" }, - "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] + "include": ["src/**/*.ts", "src/**/*.vue", "test/**/*.ts"] }