From 02ea33520db390a23cc7acdd76ae218845b7609a Mon Sep 17 00:00:00 2001 From: Crimson Date: Sun, 30 Aug 2026 07:04:28 +0500 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=D0=B2=D0=B5=D1=80=D0=BD?= =?UTF-8?q?=D1=83=D1=82=D1=8C=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D1=83=20=D1=82=D0=B8=D0=BF=D0=BE=D0=B2=20SFC-=D1=88=D0=B0?= =?UTF-8?q?=D0=B1=D0=BB=D0=BE=D0=BD=D0=BE=D0=B2=20=D0=B8=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BA=D1=80=D1=8B=D1=82=D1=8C=20=D0=B2=D1=81=D0=B5=20142=20?= =?UTF-8?q?=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vue 3.2.45 -> 3.5.42, TypeScript 4.9.3 -> 5.9.3, vue-tsc 0.35.0 -> 3.3.11. Element Plus, Vue Router, Pinia, Vite и VueUse не тронуты: ни один из них не является предусловием работающего typechecker. vue-tsc 0.35 был не просто инертен — на Vue 3.5 он ломается сам (TS7026: нет JSX.IntrinsicElements), потому что не знает vue/jsx-runtime. То есть проверка, которая ничего не находила, ещё и не пережила бы обновление Vue. Современный vue-tsc даёт 142 ошибки, а не ~155, и картина однороднее ожидаемой: 141 x TS18048 и 1 x TS2322, всё в двух файлах представления Hysteria. Предсказанного класса DefaultRow/PeerVo в таблице пиров не оказалось вовсе. Все 142 — одно и то же: шаблон обращается к необязательным секциям конфига (dataForm.tls.cert, dataForm.acme.dns.config, dataForm.resolver.https.sni). Необязательны они правильно: так устроен upstream YAML. Инвариант «секция есть всегда» существовал, но держался на порядке присваиваний внутри компонента. Закрыто одним преобразованием на границе API вместо 141 `?.` или `as any`: api/config/hysteriaViewModel.ts даёт Hysteria2ServerConfigView, где присутствие каждой секции — свойство типа, и normalizeHysteriaViewModel(). types.ts остаётся описанием того, что приходит по сети. Побочно закрыто мёртвое UI: редактор outbounds (кнопка «+», диалог создания, удаление тегов, emit update:outbounds) не мог ничего сохранить — страница отрисована с :disabled="true", родитель передаёт :outbounds без v-model, а маршрутов записи серверного конфига в API нет. Оператор мог добавить outbound и уйти в уверенности, что изменил конфигурацию сервера. package.json: typecheck и build:prod разделены, verify запускает их по порядку. Раньше `vite build && vue-tsc` сначала тратил время на production bundle и только потом сообщал о типовой ошибке. --- apps/frontend/package.json | 10 +- apps/frontend/pnpm-lock.yaml | 625 +++++++++--------- .../src/api/config/hysteriaViewModel.ts | 314 +++++++++ apps/frontend/src/api/config/types.ts | 156 +---- apps/frontend/src/lang/package/en.ts | 3 +- apps/frontend/src/lang/package/ru.ts | 5 +- apps/frontend/src/types/components.d.ts | 1 + .../list/components/Outbounds/index.vue | 250 ++----- .../src/views/hysteria/list/index.vue | 92 ++- 9 files changed, 726 insertions(+), 730 deletions(-) create mode 100644 apps/frontend/src/api/config/hysteriaViewModel.ts diff --git a/apps/frontend/package.json b/apps/frontend/package.json index df111fc..4af2fc8 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -8,7 +8,9 @@ "packageManager": "pnpm@9.15.9", "scripts": { "dev": "vite serve --mode development", - "build:prod": "vite build --mode production && vue-tsc --noEmit", + "typecheck": "vue-tsc --noEmit", + "build:prod": "vite build --mode production", + "verify": "pnpm run typecheck && pnpm run build:prod", "lint:eslint": "eslint --fix --ext .ts,.js,.vue ./src ", "lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\"", "lint:stylelint": "stylelint \"**/*.{css,scss,vue}\" --fix" @@ -24,7 +26,7 @@ "path-browserify": "^1.0.1", "pinia": "^2.0.33", "qrcode.vue": "3.4.1", - "vue": "^3.2.45", + "vue": "^3.5.42", "vue-echarts": "^7.0.3", "vue-i18n": "9", "vue-router": "^4.1.6", @@ -54,14 +56,14 @@ "stylelint-config-recommended-vue": "^1.4.0", "stylelint-config-standard": "^32.0.0", "stylelint-config-standard-scss": "^8.0.0", - "typescript": "^4.9.3", + "typescript": "^5.9.3", "unocss": "^0.50.1", "unplugin-auto-import": "^0.13.0", "unplugin-icons": "^0.15.1", "unplugin-vue-components": "^0.23.0", "vite": "^4.3.1", "vite-plugin-svg-icons": "^2.0.1", - "vue-tsc": "^0.35.0" + "vue-tsc": "^3.3.11" }, "pnpm": { "overrides": { diff --git a/apps/frontend/pnpm-lock.yaml b/apps/frontend/pnpm-lock.yaml index 2176f45..e76e5c7 100644 --- a/apps/frontend/pnpm-lock.yaml +++ b/apps/frontend/pnpm-lock.yaml @@ -14,10 +14,10 @@ importers: dependencies: '@element-plus/icons-vue': specifier: ^1.0.0 - version: 1.0.0(vue@3.2.45) + version: 1.0.0(vue@3.5.42(typescript@5.9.3)) '@vueuse/core': specifier: ^9.1.1 - version: 9.1.1(vue@3.2.45) + version: 9.1.1(vue@3.5.42(typescript@5.9.3)) axios: specifier: ^1.20.0 version: 1.20.0 @@ -29,7 +29,7 @@ importers: version: 5.6.0 element-plus: specifier: ^2.3.1 - version: 2.3.1(vue@3.2.45) + version: 2.3.1(vue@3.5.42(typescript@5.9.3)) nprogress: specifier: ^0.2.0 version: 0.2.0 @@ -38,25 +38,25 @@ importers: version: 1.0.1 pinia: specifier: ^2.0.33 - version: 2.0.33(typescript@4.9.3)(vue@3.2.45) + version: 2.0.33(typescript@5.9.3)(vue@3.5.42(typescript@5.9.3)) qrcode.vue: specifier: 3.4.1 - version: 3.4.1(vue@3.2.45) + version: 3.4.1(vue@3.5.42(typescript@5.9.3)) vue: - specifier: ^3.2.45 - version: 3.2.45 + specifier: ^3.5.42 + version: 3.5.42(typescript@5.9.3) vue-echarts: specifier: ^7.0.3 - version: 7.0.3(@vue/runtime-core@3.2.45)(echarts@5.6.0)(vue@3.2.45) + version: 7.0.3(@vue/runtime-core@3.5.42)(echarts@5.6.0)(vue@3.5.42(typescript@5.9.3)) vue-i18n: specifier: '9' - version: 9.0.0(vue@3.2.45) + version: 9.0.0(vue@3.5.42(typescript@5.9.3)) vue-router: specifier: ^4.1.6 - version: 4.1.6(vue@3.2.45) + version: 4.1.6(vue@3.5.42(typescript@5.9.3)) vuedraggable: specifier: ^4.1.0 - version: 4.1.0(vue@3.2.45) + version: 4.1.0(vue@3.5.42(typescript@5.9.3)) devDependencies: '@iconify-json/ep': specifier: ^1.1.8 @@ -69,13 +69,13 @@ importers: version: 1.0.0 '@typescript-eslint/eslint-plugin': specifier: ^5.51.0 - version: 5.51.0(@typescript-eslint/parser@5.51.0(eslint@8.34.0)(typescript@4.9.3))(eslint@8.34.0)(typescript@4.9.3) + version: 5.51.0(@typescript-eslint/parser@5.51.0(eslint@8.34.0)(typescript@5.9.3))(eslint@8.34.0)(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^5.51.0 - version: 5.51.0(eslint@8.34.0)(typescript@4.9.3) + version: 5.51.0(eslint@8.34.0)(typescript@5.9.3) '@vitejs/plugin-vue': specifier: ^4.0.0 - version: 4.0.0(vite@4.3.1(@types/node@20.7.0)(sass@1.58.3))(vue@3.2.45) + version: 4.0.0(vite@4.3.1(@types/node@20.7.0)(sass@1.58.3))(vue@3.5.42(typescript@5.9.3)) autoprefixer: specifier: ^10.4.13 version: 10.4.13(postcss@8.5.26) @@ -108,40 +108,40 @@ importers: version: 1.58.3 stylelint: specifier: ^15.5.0 - version: 15.5.0(typescript@4.9.3) + version: 15.5.0(typescript@5.9.3) stylelint-config-html: specifier: ^1.1.0 - version: 1.1.0(postcss-html@1.5.0)(stylelint@15.5.0(typescript@4.9.3)) + version: 1.1.0(postcss-html@1.5.0)(stylelint@15.5.0(typescript@5.9.3)) stylelint-config-recess-order: specifier: ^4.0.0 - version: 4.0.0(stylelint@15.5.0(typescript@4.9.3)) + version: 4.0.0(stylelint@15.5.0(typescript@5.9.3)) stylelint-config-recommended-scss: specifier: ^9.0.1 - version: 9.0.1(postcss@8.5.26)(stylelint@15.5.0(typescript@4.9.3)) + version: 9.0.1(postcss@8.5.26)(stylelint@15.5.0(typescript@5.9.3)) stylelint-config-recommended-vue: specifier: ^1.4.0 - version: 1.4.0(postcss-html@1.5.0)(stylelint@15.5.0(typescript@4.9.3)) + version: 1.4.0(postcss-html@1.5.0)(stylelint@15.5.0(typescript@5.9.3)) stylelint-config-standard: specifier: ^32.0.0 - version: 32.0.0(stylelint@15.5.0(typescript@4.9.3)) + version: 32.0.0(stylelint@15.5.0(typescript@5.9.3)) stylelint-config-standard-scss: specifier: ^8.0.0 - version: 8.0.0(postcss@8.5.26)(stylelint@15.5.0(typescript@4.9.3)) + version: 8.0.0(postcss@8.5.26)(stylelint@15.5.0(typescript@5.9.3)) typescript: - specifier: ^4.9.3 - version: 4.9.3 + specifier: ^5.9.3 + version: 5.9.3 unocss: specifier: ^0.50.1 version: 0.50.1(rollup@3.29.3)(vite@4.3.1(@types/node@20.7.0)(sass@1.58.3)) unplugin-auto-import: specifier: ^0.13.0 - version: 0.13.0(@vueuse/core@9.1.1(vue@3.2.45))(rollup@3.29.3) + version: 0.13.0(@vueuse/core@9.1.1(vue@3.5.42(typescript@5.9.3)))(rollup@3.29.3) unplugin-icons: specifier: ^0.15.1 - version: 0.15.1(@vue/compiler-sfc@3.3.4) + version: 0.15.1(@vue/compiler-sfc@3.5.42) unplugin-vue-components: specifier: ^0.23.0 - version: 0.23.0(@babel/parser@7.23.0)(rollup@3.29.3)(vue@3.2.45) + version: 0.23.0(@babel/parser@7.29.8)(rollup@3.29.3)(vue@3.5.42(typescript@5.9.3)) vite: specifier: ^4.3.1 version: 4.3.1(@types/node@20.7.0)(sass@1.58.3) @@ -149,8 +149,8 @@ importers: specifier: ^2.0.1 version: 2.0.1(vite@4.3.1(@types/node@20.7.0)(sass@1.58.3)) vue-tsc: - specifier: ^0.35.0 - version: 0.35.0(typescript@4.9.3) + specifier: ^3.3.11 + version: 3.3.11(typescript@5.9.3) packages: @@ -222,10 +222,18 @@ packages: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.22.20': resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.22.15': resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} engines: {node: '>=6.9.0'} @@ -243,6 +251,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/standalone@7.23.1': resolution: {integrity: sha512-a4muOYz1qUaSoybuUKwK90mRG4sf5rBeUbuzpuGLzG32ZDE/Y2YEebHDODFJN+BtyOKi19hrLfq2qbNyKMx0TA==} engines: {node: '>=6.9.0'} @@ -259,6 +272,10 @@ packages: resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@csstools/css-parser-algorithms@2.3.2': resolution: {integrity: sha512-sLYGdAdEY2x7TSw9FtmdaTrh2wFtRJO5VMbBrA8tEqEod7GEggFmxTSK9XqExib3yMuYNcvcTdCZIP6ukdjAIA==} engines: {node: ^14 || ^16 || >=18} @@ -498,6 +515,9 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/trace-mapping@0.3.19': resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} @@ -709,75 +729,47 @@ packages: vite: ^4.0.0 vue: ^3.2.25 - '@volar/code-gen@0.35.0': - resolution: {integrity: sha512-MUKPjE6VL97R2Dg3MwUexjKsdHRhX2cEQUWDtl7Zgn7fe60mULQOHcZZ+IU49eTgyOjAj0Z2hm7hub/nBxduxw==} + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} - '@volar/source-map@0.35.0': - resolution: {integrity: sha512-VvVyCW8SEQ297zoeZNi4HOSLU8H7/xbGg4WpcpVOy080y/MXTxjIZshSlMHx45EB91U2vk0gbgd2R/d9uyY+3A==} + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} - '@volar/vue-code-gen@0.35.0': - resolution: {integrity: sha512-IqIkzs3eRlJjABVXXBf5ik4/a8lQ3wqKemWMR33+KzOmj5xtXS+lgMMLNXJpj0Mw2nOLVl0I2KuIaTHFy485DQ==} - deprecated: 'WARNING: This project has been renamed to @vue/language-core. Install using @vue/language-core instead.' + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} - '@volar/vue-typescript@0.35.0': - resolution: {integrity: sha512-Ojn98P888wWbk1qeR5f6spgtJZMwUos+OkJfZpzyafN5S21LxF58o7DhhNG8rMm1zMNUQHb+evlp5AXI5zHOmA==} - deprecated: 'WARNING: This project has been renamed to @vue/typescript. Install using @vue/typescript instead.' + '@vue/compiler-core@3.5.42': + resolution: {integrity: sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==} - '@vue/compiler-core@3.2.45': - resolution: {integrity: sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==} + '@vue/compiler-dom@3.5.42': + resolution: {integrity: sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==} - '@vue/compiler-core@3.3.4': - resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==} + '@vue/compiler-sfc@3.5.42': + resolution: {integrity: sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==} - '@vue/compiler-dom@3.2.45': - resolution: {integrity: sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==} - - '@vue/compiler-dom@3.3.4': - resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==} - - '@vue/compiler-sfc@3.2.45': - resolution: {integrity: sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==} - - '@vue/compiler-sfc@3.3.4': - resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==} - - '@vue/compiler-ssr@3.2.45': - resolution: {integrity: sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==} - - '@vue/compiler-ssr@3.3.4': - resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==} + '@vue/compiler-ssr@3.5.42': + resolution: {integrity: sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==} '@vue/devtools-api@6.5.0': resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==} - '@vue/reactivity-transform@3.2.45': - resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==} + '@vue/language-core@3.3.11': + resolution: {integrity: sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==} - '@vue/reactivity-transform@3.3.4': - resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==} + '@vue/reactivity@3.5.42': + resolution: {integrity: sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==} - '@vue/reactivity@3.2.45': - resolution: {integrity: sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==} + '@vue/runtime-core@3.5.42': + resolution: {integrity: sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==} - '@vue/reactivity@3.3.4': - resolution: {integrity: sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==} + '@vue/runtime-dom@3.5.42': + resolution: {integrity: sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==} - '@vue/runtime-core@3.2.45': - resolution: {integrity: sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==} + '@vue/server-renderer@3.5.42': + resolution: {integrity: sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==} - '@vue/runtime-dom@3.2.45': - resolution: {integrity: sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==} - - '@vue/server-renderer@3.2.45': - resolution: {integrity: sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==} - peerDependencies: - vue: 3.2.45 - - '@vue/shared@3.2.45': - resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==} - - '@vue/shared@3.3.4': - resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==} + '@vue/shared@3.5.42': + resolution: {integrity: sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==} '@vueuse/core@9.1.1': resolution: {integrity: sha512-QfuaNWRDMQcCUwXylCyYhPC3ScS9Tiiz4J0chdwr3vOemBwRToSywq8MP+ZegKYFnbETzRY8G/5zC+ca30wrRQ==} @@ -808,6 +800,9 @@ packages: ajv@8.12.0: resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + ansi-regex@2.1.1: resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} engines: {node: '>=0.10.0'} @@ -1090,8 +1085,8 @@ packages: resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} engines: {node: '>=8.0.0'} - csstype@2.6.21: - resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} dayjs@1.11.10: resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} @@ -1236,6 +1231,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + errno@0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true @@ -1929,13 +1928,13 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - magic-string@0.25.9: - resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - magic-string@0.27.0: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@0.30.3: resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==} engines: {node: '>=12'} @@ -2067,6 +2066,9 @@ packages: ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + nanoid@3.3.18: resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2225,6 +2227,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + pinia@2.0.33: resolution: {integrity: sha512-HOj1yVV2itw6rNIrR2f7+MirGNxhORjrULL8GWgRwXsGSvEqIQ+SE0MYt6cwtpegzCda3i+rVTZM+AM7CG+kRg==} peerDependencies: @@ -2541,10 +2547,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - sourcemap-codec@1.4.8: - resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - deprecated: Please use @jridgewell/sourcemap-codec instead - spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -2811,9 +2813,9 @@ packages: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} - typescript@4.9.3: - resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} - engines: {node: '>=4.2.0'} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} hasBin: true ufo@1.3.0: @@ -2955,6 +2957,9 @@ packages: terser: optional: true + vscode-uri@3.2.0: + resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} + vue-demi@0.13.11: resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} engines: {node: '>=12'} @@ -3005,14 +3010,19 @@ packages: peerDependencies: vue: ^3.2.0 - vue-tsc@0.35.0: - resolution: {integrity: sha512-RcL7PM3M1nSaVQU84mRPOXk1t/mAZ25QCx4L0rNQzozJYhz1wSVQWYqq9UfhE2lW8nMAQzQtLZgvjaPR4Q29ZA==} + vue-tsc@3.3.11: + resolution: {integrity: sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==} hasBin: true peerDependencies: - typescript: '*' + typescript: '>=5.0.0' - vue@3.2.45: - resolution: {integrity: sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==} + vue@3.5.42: + resolution: {integrity: sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true vuedraggable@4.1.0: resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==} @@ -3155,8 +3165,12 @@ snapshots: '@babel/helper-string-parser@7.22.5': {} + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.22.20': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.22.15': {} '@babel/helpers@7.23.1': @@ -3177,6 +3191,10 @@ snapshots: dependencies: '@babel/types': 7.23.0 + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + '@babel/standalone@7.23.1': {} '@babel/template@7.22.15': @@ -3206,6 +3224,11 @@ snapshots: '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@csstools/css-parser-algorithms@2.3.2(@csstools/css-tokenizer@2.2.1)': dependencies: '@csstools/css-tokenizer': 2.2.1 @@ -3223,13 +3246,13 @@ snapshots: '@ctrl/tinycolor@3.6.1': {} - '@element-plus/icons-vue@1.0.0(vue@3.2.45)': + '@element-plus/icons-vue@1.0.0(vue@3.5.42(typescript@5.9.3))': dependencies: - vue: 3.2.45 + vue: 3.5.42(typescript@5.9.3) - '@element-plus/icons-vue@2.1.0(vue@3.2.45)': + '@element-plus/icons-vue@2.1.0(vue@3.5.42(typescript@5.9.3))': dependencies: - vue: 3.2.45 + vue: 3.5.42(typescript@5.9.3) '@esbuild/android-arm64@0.17.19': optional: true @@ -3386,6 +3409,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/trace-mapping@0.3.19': dependencies: '@jridgewell/resolve-uri': 3.1.1 @@ -3488,12 +3513,12 @@ snapshots: '@types/web-bluetooth@0.0.15': {} - '@typescript-eslint/eslint-plugin@5.51.0(@typescript-eslint/parser@5.51.0(eslint@8.34.0)(typescript@4.9.3))(eslint@8.34.0)(typescript@4.9.3)': + '@typescript-eslint/eslint-plugin@5.51.0(@typescript-eslint/parser@5.51.0(eslint@8.34.0)(typescript@5.9.3))(eslint@8.34.0)(typescript@5.9.3)': dependencies: - '@typescript-eslint/parser': 5.51.0(eslint@8.34.0)(typescript@4.9.3) + '@typescript-eslint/parser': 5.51.0(eslint@8.34.0)(typescript@5.9.3) '@typescript-eslint/scope-manager': 5.51.0 - '@typescript-eslint/type-utils': 5.51.0(eslint@8.34.0)(typescript@4.9.3) - '@typescript-eslint/utils': 5.51.0(eslint@8.34.0)(typescript@4.9.3) + '@typescript-eslint/type-utils': 5.51.0(eslint@8.34.0)(typescript@5.9.3) + '@typescript-eslint/utils': 5.51.0(eslint@8.34.0)(typescript@5.9.3) debug: 4.3.4 eslint: 8.34.0 grapheme-splitter: 1.0.4 @@ -3501,21 +3526,21 @@ snapshots: natural-compare-lite: 1.4.0 regexpp: 3.2.0 semver: 7.5.4 - tsutils: 3.21.0(typescript@4.9.3) + tsutils: 3.21.0(typescript@5.9.3) optionalDependencies: - typescript: 4.9.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@5.51.0(eslint@8.34.0)(typescript@4.9.3)': + '@typescript-eslint/parser@5.51.0(eslint@8.34.0)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 5.51.0 '@typescript-eslint/types': 5.51.0 - '@typescript-eslint/typescript-estree': 5.51.0(typescript@4.9.3) + '@typescript-eslint/typescript-estree': 5.51.0(typescript@5.9.3) debug: 4.3.4 eslint: 8.34.0 optionalDependencies: - typescript: 4.9.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3524,21 +3549,21 @@ snapshots: '@typescript-eslint/types': 5.51.0 '@typescript-eslint/visitor-keys': 5.51.0 - '@typescript-eslint/type-utils@5.51.0(eslint@8.34.0)(typescript@4.9.3)': + '@typescript-eslint/type-utils@5.51.0(eslint@8.34.0)(typescript@5.9.3)': dependencies: - '@typescript-eslint/typescript-estree': 5.51.0(typescript@4.9.3) - '@typescript-eslint/utils': 5.51.0(eslint@8.34.0)(typescript@4.9.3) + '@typescript-eslint/typescript-estree': 5.51.0(typescript@5.9.3) + '@typescript-eslint/utils': 5.51.0(eslint@8.34.0)(typescript@5.9.3) debug: 4.3.4 eslint: 8.34.0 - tsutils: 3.21.0(typescript@4.9.3) + tsutils: 3.21.0(typescript@5.9.3) optionalDependencies: - typescript: 4.9.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color '@typescript-eslint/types@5.51.0': {} - '@typescript-eslint/typescript-estree@5.51.0(typescript@4.9.3)': + '@typescript-eslint/typescript-estree@5.51.0(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 5.51.0 '@typescript-eslint/visitor-keys': 5.51.0 @@ -3546,19 +3571,19 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - tsutils: 3.21.0(typescript@4.9.3) + tsutils: 3.21.0(typescript@5.9.3) optionalDependencies: - typescript: 4.9.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.51.0(eslint@8.34.0)(typescript@4.9.3)': + '@typescript-eslint/utils@5.51.0(eslint@8.34.0)(typescript@5.9.3)': dependencies: '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 5.51.0 '@typescript-eslint/types': 5.51.0 - '@typescript-eslint/typescript-estree': 5.51.0(typescript@4.9.3) + '@typescript-eslint/typescript-estree': 5.51.0(typescript@5.9.3) eslint: 8.34.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0(eslint@8.34.0) @@ -3688,155 +3713,104 @@ snapshots: transitivePeerDependencies: - rollup - '@vitejs/plugin-vue@4.0.0(vite@4.3.1(@types/node@20.7.0)(sass@1.58.3))(vue@3.2.45)': + '@vitejs/plugin-vue@4.0.0(vite@4.3.1(@types/node@20.7.0)(sass@1.58.3))(vue@3.5.42(typescript@5.9.3))': dependencies: vite: 4.3.1(@types/node@20.7.0)(sass@1.58.3) - vue: 3.2.45 + vue: 3.5.42(typescript@5.9.3) - '@volar/code-gen@0.35.0': + '@volar/language-core@2.4.28': dependencies: - '@volar/source-map': 0.35.0 + '@volar/source-map': 2.4.28 - '@volar/source-map@0.35.0': {} + '@volar/source-map@2.4.28': {} - '@volar/vue-code-gen@0.35.0': + '@volar/typescript@2.4.28': dependencies: - '@volar/code-gen': 0.35.0 - '@volar/source-map': 0.35.0 - '@vue/compiler-core': 3.3.4 - '@vue/compiler-dom': 3.3.4 - '@vue/shared': 3.3.4 + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.2.0 - '@volar/vue-typescript@0.35.0': + '@vue/compiler-core@3.5.42': dependencies: - '@volar/code-gen': 0.35.0 - '@volar/source-map': 0.35.0 - '@volar/vue-code-gen': 0.35.0 - '@vue/compiler-sfc': 3.3.4 - '@vue/reactivity': 3.3.4 - - '@vue/compiler-core@3.2.45': - dependencies: - '@babel/parser': 7.23.0 - '@vue/shared': 3.2.45 + '@babel/parser': 7.29.8 + '@vue/shared': 3.5.42 + entities: 7.0.1 estree-walker: 2.0.2 - source-map: 0.6.1 + source-map-js: 1.2.1 - '@vue/compiler-core@3.3.4': + '@vue/compiler-dom@3.5.42': dependencies: - '@babel/parser': 7.23.0 - '@vue/shared': 3.3.4 + '@vue/compiler-core': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/compiler-sfc@3.5.42': + dependencies: + '@babel/parser': 7.29.8 + '@vue/compiler-core': 3.5.42 + '@vue/compiler-dom': 3.5.42 + '@vue/compiler-ssr': 3.5.42 + '@vue/shared': 3.5.42 estree-walker: 2.0.2 - source-map-js: 1.0.2 - - '@vue/compiler-dom@3.2.45': - dependencies: - '@vue/compiler-core': 3.2.45 - '@vue/shared': 3.2.45 - - '@vue/compiler-dom@3.3.4': - dependencies: - '@vue/compiler-core': 3.3.4 - '@vue/shared': 3.3.4 - - '@vue/compiler-sfc@3.2.45': - dependencies: - '@babel/parser': 7.23.0 - '@vue/compiler-core': 3.2.45 - '@vue/compiler-dom': 3.2.45 - '@vue/compiler-ssr': 3.2.45 - '@vue/reactivity-transform': 3.2.45 - '@vue/shared': 3.2.45 - estree-walker: 2.0.2 - magic-string: 0.25.9 + magic-string: 0.30.21 postcss: 8.5.26 - source-map: 0.6.1 + source-map-js: 1.2.1 - '@vue/compiler-sfc@3.3.4': + '@vue/compiler-ssr@3.5.42': dependencies: - '@babel/parser': 7.23.0 - '@vue/compiler-core': 3.3.4 - '@vue/compiler-dom': 3.3.4 - '@vue/compiler-ssr': 3.3.4 - '@vue/reactivity-transform': 3.3.4 - '@vue/shared': 3.3.4 - estree-walker: 2.0.2 - magic-string: 0.30.3 - postcss: 8.5.26 - source-map-js: 1.0.2 - - '@vue/compiler-ssr@3.2.45': - dependencies: - '@vue/compiler-dom': 3.2.45 - '@vue/shared': 3.2.45 - - '@vue/compiler-ssr@3.3.4': - dependencies: - '@vue/compiler-dom': 3.3.4 - '@vue/shared': 3.3.4 + '@vue/compiler-dom': 3.5.42 + '@vue/shared': 3.5.42 '@vue/devtools-api@6.5.0': {} - '@vue/reactivity-transform@3.2.45': + '@vue/language-core@3.3.11': dependencies: - '@babel/parser': 7.23.0 - '@vue/compiler-core': 3.2.45 - '@vue/shared': 3.2.45 - estree-walker: 2.0.2 - magic-string: 0.25.9 + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.42 + '@vue/shared': 3.5.42 + alien-signals: 3.2.1 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.7 - '@vue/reactivity-transform@3.3.4': + '@vue/reactivity@3.5.42': dependencies: - '@babel/parser': 7.23.0 - '@vue/compiler-core': 3.3.4 - '@vue/shared': 3.3.4 - estree-walker: 2.0.2 - magic-string: 0.30.3 + '@vue/shared': 3.5.42 - '@vue/reactivity@3.2.45': + '@vue/runtime-core@3.5.42': dependencies: - '@vue/shared': 3.2.45 + '@vue/reactivity': 3.5.42 + '@vue/shared': 3.5.42 - '@vue/reactivity@3.3.4': + '@vue/runtime-dom@3.5.42': dependencies: - '@vue/shared': 3.3.4 + '@vue/reactivity': 3.5.42 + '@vue/runtime-core': 3.5.42 + '@vue/shared': 3.5.42 + csstype: 3.2.3 - '@vue/runtime-core@3.2.45': + '@vue/server-renderer@3.5.42': dependencies: - '@vue/reactivity': 3.2.45 - '@vue/shared': 3.2.45 + '@vue/compiler-ssr': 3.5.42 + '@vue/runtime-dom': 3.5.42 + '@vue/shared': 3.5.42 - '@vue/runtime-dom@3.2.45': - dependencies: - '@vue/runtime-core': 3.2.45 - '@vue/shared': 3.2.45 - csstype: 2.6.21 + '@vue/shared@3.5.42': {} - '@vue/server-renderer@3.2.45(vue@3.2.45)': - dependencies: - '@vue/compiler-ssr': 3.2.45 - '@vue/shared': 3.2.45 - vue: 3.2.45 - - '@vue/shared@3.2.45': {} - - '@vue/shared@3.3.4': {} - - '@vueuse/core@9.1.1(vue@3.2.45)': + '@vueuse/core@9.1.1(vue@3.5.42(typescript@5.9.3))': dependencies: '@types/web-bluetooth': 0.0.15 '@vueuse/metadata': 9.1.1 - '@vueuse/shared': 9.1.1(vue@3.2.45) - vue-demi: 0.14.6(vue@3.2.45) + '@vueuse/shared': 9.1.1(vue@3.5.42(typescript@5.9.3)) + vue-demi: 0.14.6(vue@3.5.42(typescript@5.9.3)) transitivePeerDependencies: - '@vue/composition-api' - vue '@vueuse/metadata@9.1.1': {} - '@vueuse/shared@9.1.1(vue@3.2.45)': + '@vueuse/shared@9.1.1(vue@3.5.42(typescript@5.9.3))': dependencies: - vue-demi: 0.14.6(vue@3.2.45) + vue-demi: 0.14.6(vue@3.5.42(typescript@5.9.3)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -3867,6 +3841,8 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 + alien-signals@3.2.1: {} + ansi-regex@2.1.1: {} ansi-regex@5.0.1: {} @@ -4126,14 +4102,14 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - cosmiconfig@8.3.6(typescript@4.9.3): + cosmiconfig@8.3.6(typescript@5.9.3): dependencies: import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: - typescript: 4.9.3 + typescript: 5.9.3 cross-spawn@7.0.3: dependencies: @@ -4169,7 +4145,7 @@ snapshots: dependencies: css-tree: 1.1.3 - csstype@2.6.21: {} + csstype@3.2.3: {} dayjs@1.11.10: {} @@ -4286,15 +4262,15 @@ snapshots: electron-to-chromium@1.4.529: {} - element-plus@2.3.1(vue@3.2.45): + element-plus@2.3.1(vue@3.5.42(typescript@5.9.3)): dependencies: '@ctrl/tinycolor': 3.6.1 - '@element-plus/icons-vue': 2.1.0(vue@3.2.45) + '@element-plus/icons-vue': 2.1.0(vue@3.5.42(typescript@5.9.3)) '@floating-ui/dom': 1.5.3 '@popperjs/core': '@sxzz/popperjs-es@2.11.8' '@types/lodash': 4.14.199 '@types/lodash-es': 4.17.9 - '@vueuse/core': 9.1.1(vue@3.2.45) + '@vueuse/core': 9.1.1(vue@3.5.42(typescript@5.9.3)) async-validator: 4.2.5 dayjs: 1.11.10 escape-html: 1.0.3 @@ -4303,7 +4279,7 @@ snapshots: lodash-unified: 1.0.3(@types/lodash-es@4.17.9)(lodash-es@4.18.1)(lodash@4.18.1) memoize-one: 6.0.0 normalize-wheel-es: 1.2.0 - vue: 3.2.45 + vue: 3.5.42(typescript@5.9.3) transitivePeerDependencies: - '@vue/composition-api' @@ -4323,6 +4299,8 @@ snapshots: entities@4.5.0: {} + entities@7.0.1: {} + errno@0.1.8: dependencies: prr: 1.0.1 @@ -5036,14 +5014,14 @@ snapshots: dependencies: yallist: 4.0.0 - magic-string@0.25.9: - dependencies: - sourcemap-codec: 1.4.8 - magic-string@0.27.0: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + magic-string@0.30.3: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -5178,6 +5156,8 @@ snapshots: ms@2.1.2: {} + muggle-string@0.4.1: {} + nanoid@3.3.18: {} nanomatch@1.2.13: @@ -5332,13 +5312,15 @@ snapshots: picomatch@2.3.1: {} - pinia@2.0.33(typescript@4.9.3)(vue@3.2.45): + picomatch@4.0.7: {} + + pinia@2.0.33(typescript@5.9.3)(vue@3.5.42(typescript@5.9.3)): dependencies: '@vue/devtools-api': 6.5.0 - vue: 3.2.45 - vue-demi: 0.14.6(vue@3.2.45) + vue: 3.5.42(typescript@5.9.3) + vue-demi: 0.14.6(vue@3.5.42(typescript@5.9.3)) optionalDependencies: - typescript: 4.9.3 + typescript: 5.9.3 pkg-types@1.0.3: dependencies: @@ -5438,9 +5420,9 @@ snapshots: punycode@2.3.0: {} - qrcode.vue@3.4.1(vue@3.2.45): + qrcode.vue@3.4.1(vue@3.5.42(typescript@5.9.3)): dependencies: - vue: 3.2.45 + vue: 3.5.42(typescript@5.9.3) query-string@4.3.4: dependencies: @@ -5636,8 +5618,6 @@ snapshots: source-map@0.6.1: {} - sourcemap-codec@1.4.8: {} - spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 @@ -5703,82 +5683,82 @@ snapshots: style-search@0.1.0: {} - stylelint-config-html@1.1.0(postcss-html@1.5.0)(stylelint@15.5.0(typescript@4.9.3)): + stylelint-config-html@1.1.0(postcss-html@1.5.0)(stylelint@15.5.0(typescript@5.9.3)): dependencies: postcss-html: 1.5.0 - stylelint: 15.5.0(typescript@4.9.3) + stylelint: 15.5.0(typescript@5.9.3) - stylelint-config-recess-order@4.0.0(stylelint@15.5.0(typescript@4.9.3)): + stylelint-config-recess-order@4.0.0(stylelint@15.5.0(typescript@5.9.3)): dependencies: - stylelint: 15.5.0(typescript@4.9.3) - stylelint-order: 6.0.3(stylelint@15.5.0(typescript@4.9.3)) + stylelint: 15.5.0(typescript@5.9.3) + stylelint-order: 6.0.3(stylelint@15.5.0(typescript@5.9.3)) - stylelint-config-recommended-scss@10.0.0(postcss@8.5.26)(stylelint@15.5.0(typescript@4.9.3)): + stylelint-config-recommended-scss@10.0.0(postcss@8.5.26)(stylelint@15.5.0(typescript@5.9.3)): dependencies: postcss-scss: 4.0.6(postcss@8.5.26) - stylelint: 15.5.0(typescript@4.9.3) - stylelint-config-recommended: 11.0.0(stylelint@15.5.0(typescript@4.9.3)) - stylelint-scss: 4.7.0(stylelint@15.5.0(typescript@4.9.3)) + stylelint: 15.5.0(typescript@5.9.3) + stylelint-config-recommended: 11.0.0(stylelint@15.5.0(typescript@5.9.3)) + stylelint-scss: 4.7.0(stylelint@15.5.0(typescript@5.9.3)) optionalDependencies: postcss: 8.5.26 - stylelint-config-recommended-scss@9.0.1(postcss@8.5.26)(stylelint@15.5.0(typescript@4.9.3)): + stylelint-config-recommended-scss@9.0.1(postcss@8.5.26)(stylelint@15.5.0(typescript@5.9.3)): dependencies: postcss-scss: 4.0.6(postcss@8.5.26) - stylelint: 15.5.0(typescript@4.9.3) - stylelint-config-recommended: 10.0.1(stylelint@15.5.0(typescript@4.9.3)) - stylelint-scss: 4.7.0(stylelint@15.5.0(typescript@4.9.3)) + stylelint: 15.5.0(typescript@5.9.3) + stylelint-config-recommended: 10.0.1(stylelint@15.5.0(typescript@5.9.3)) + stylelint-scss: 4.7.0(stylelint@15.5.0(typescript@5.9.3)) optionalDependencies: postcss: 8.5.26 - stylelint-config-recommended-vue@1.4.0(postcss-html@1.5.0)(stylelint@15.5.0(typescript@4.9.3)): + stylelint-config-recommended-vue@1.4.0(postcss-html@1.5.0)(stylelint@15.5.0(typescript@5.9.3)): dependencies: postcss-html: 1.5.0 semver: 7.5.4 - stylelint: 15.5.0(typescript@4.9.3) - stylelint-config-html: 1.1.0(postcss-html@1.5.0)(stylelint@15.5.0(typescript@4.9.3)) - stylelint-config-recommended: 13.0.0(stylelint@15.5.0(typescript@4.9.3)) + stylelint: 15.5.0(typescript@5.9.3) + stylelint-config-html: 1.1.0(postcss-html@1.5.0)(stylelint@15.5.0(typescript@5.9.3)) + stylelint-config-recommended: 13.0.0(stylelint@15.5.0(typescript@5.9.3)) - stylelint-config-recommended@10.0.1(stylelint@15.5.0(typescript@4.9.3)): + stylelint-config-recommended@10.0.1(stylelint@15.5.0(typescript@5.9.3)): dependencies: - stylelint: 15.5.0(typescript@4.9.3) + stylelint: 15.5.0(typescript@5.9.3) - stylelint-config-recommended@11.0.0(stylelint@15.5.0(typescript@4.9.3)): + stylelint-config-recommended@11.0.0(stylelint@15.5.0(typescript@5.9.3)): dependencies: - stylelint: 15.5.0(typescript@4.9.3) + stylelint: 15.5.0(typescript@5.9.3) - stylelint-config-recommended@13.0.0(stylelint@15.5.0(typescript@4.9.3)): + stylelint-config-recommended@13.0.0(stylelint@15.5.0(typescript@5.9.3)): dependencies: - stylelint: 15.5.0(typescript@4.9.3) + stylelint: 15.5.0(typescript@5.9.3) - stylelint-config-standard-scss@8.0.0(postcss@8.5.26)(stylelint@15.5.0(typescript@4.9.3)): + stylelint-config-standard-scss@8.0.0(postcss@8.5.26)(stylelint@15.5.0(typescript@5.9.3)): dependencies: - stylelint: 15.5.0(typescript@4.9.3) - stylelint-config-recommended-scss: 10.0.0(postcss@8.5.26)(stylelint@15.5.0(typescript@4.9.3)) - stylelint-config-standard: 32.0.0(stylelint@15.5.0(typescript@4.9.3)) + stylelint: 15.5.0(typescript@5.9.3) + stylelint-config-recommended-scss: 10.0.0(postcss@8.5.26)(stylelint@15.5.0(typescript@5.9.3)) + stylelint-config-standard: 32.0.0(stylelint@15.5.0(typescript@5.9.3)) optionalDependencies: postcss: 8.5.26 - stylelint-config-standard@32.0.0(stylelint@15.5.0(typescript@4.9.3)): + stylelint-config-standard@32.0.0(stylelint@15.5.0(typescript@5.9.3)): dependencies: - stylelint: 15.5.0(typescript@4.9.3) - stylelint-config-recommended: 11.0.0(stylelint@15.5.0(typescript@4.9.3)) + stylelint: 15.5.0(typescript@5.9.3) + stylelint-config-recommended: 11.0.0(stylelint@15.5.0(typescript@5.9.3)) - stylelint-order@6.0.3(stylelint@15.5.0(typescript@4.9.3)): + stylelint-order@6.0.3(stylelint@15.5.0(typescript@5.9.3)): dependencies: postcss: 8.5.26 postcss-sorting: 8.0.2(postcss@8.5.26) - stylelint: 15.5.0(typescript@4.9.3) + stylelint: 15.5.0(typescript@5.9.3) - stylelint-scss@4.7.0(stylelint@15.5.0(typescript@4.9.3)): + stylelint-scss@4.7.0(stylelint@15.5.0(typescript@5.9.3)): dependencies: postcss-media-query-parser: 0.2.3 postcss-resolve-nested-selector: 0.1.1 postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 - stylelint: 15.5.0(typescript@4.9.3) + stylelint: 15.5.0(typescript@5.9.3) - stylelint@15.5.0(typescript@4.9.3): + stylelint@15.5.0(typescript@5.9.3): dependencies: '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) '@csstools/css-tokenizer': 2.2.1 @@ -5786,7 +5766,7 @@ snapshots: '@csstools/selector-specificity': 2.2.0(postcss-selector-parser@6.0.13) balanced-match: 2.0.0 colord: 2.9.3 - cosmiconfig: 8.3.6(typescript@4.9.3) + cosmiconfig: 8.3.6(typescript@5.9.3) css-functions-list: 3.2.0 css-tree: 2.3.1 debug: 4.3.4 @@ -5932,10 +5912,10 @@ snapshots: tslib@2.3.0: {} - tsutils@3.21.0(typescript@4.9.3): + tsutils@3.21.0(typescript@5.9.3): dependencies: tslib: 1.14.1 - typescript: 4.9.3 + typescript: 5.9.3 type-check@0.4.0: dependencies: @@ -5949,7 +5929,7 @@ snapshots: type-fest@0.8.1: {} - typescript@4.9.3: {} + typescript@5.9.3: {} ufo@1.3.0: {} @@ -6032,7 +6012,7 @@ snapshots: - supports-color - vite - unplugin-auto-import@0.13.0(@vueuse/core@9.1.1(vue@3.2.45))(rollup@3.29.3): + unplugin-auto-import@0.13.0(@vueuse/core@9.1.1(vue@3.5.42(typescript@5.9.3)))(rollup@3.29.3): dependencies: '@antfu/utils': 0.7.6 '@rollup/pluginutils': 5.0.4(rollup@3.29.3) @@ -6041,11 +6021,11 @@ snapshots: unimport: 2.2.4(rollup@3.29.3) unplugin: 1.5.0 optionalDependencies: - '@vueuse/core': 9.1.1(vue@3.2.45) + '@vueuse/core': 9.1.1(vue@3.5.42(typescript@5.9.3)) transitivePeerDependencies: - rollup - unplugin-icons@0.15.1(@vue/compiler-sfc@3.3.4): + unplugin-icons@0.15.1(@vue/compiler-sfc@3.5.42): dependencies: '@antfu/install-pkg': 0.1.1 '@antfu/utils': 0.7.6 @@ -6055,11 +6035,11 @@ snapshots: local-pkg: 0.4.3 unplugin: 1.5.0 optionalDependencies: - '@vue/compiler-sfc': 3.3.4 + '@vue/compiler-sfc': 3.5.42 transitivePeerDependencies: - supports-color - unplugin-vue-components@0.23.0(@babel/parser@7.23.0)(rollup@3.29.3)(vue@3.2.45): + unplugin-vue-components@0.23.0(@babel/parser@7.29.8)(rollup@3.29.3)(vue@3.5.42(typescript@5.9.3)): dependencies: '@antfu/utils': 0.7.6 '@nuxt/kit': 3.7.4(rollup@3.29.3) @@ -6072,9 +6052,9 @@ snapshots: minimatch: 6.2.0 resolve: 1.22.6 unplugin: 1.5.0 - vue: 3.2.45 + vue: 3.5.42(typescript@5.9.3) optionalDependencies: - '@babel/parser': 7.23.0 + '@babel/parser': 7.29.8 transitivePeerDependencies: - rollup - supports-color @@ -6152,21 +6132,23 @@ snapshots: fsevents: 2.3.3 sass: 1.58.3 - vue-demi@0.13.11(vue@3.2.45): - dependencies: - vue: 3.2.45 + vscode-uri@3.2.0: {} - vue-demi@0.14.6(vue@3.2.45): + vue-demi@0.13.11(vue@3.5.42(typescript@5.9.3)): dependencies: - vue: 3.2.45 + vue: 3.5.42(typescript@5.9.3) - vue-echarts@7.0.3(@vue/runtime-core@3.2.45)(echarts@5.6.0)(vue@3.2.45): + vue-demi@0.14.6(vue@3.5.42(typescript@5.9.3)): + dependencies: + vue: 3.5.42(typescript@5.9.3) + + vue-echarts@7.0.3(@vue/runtime-core@3.5.42)(echarts@5.6.0)(vue@3.5.42(typescript@5.9.3)): dependencies: echarts: 5.6.0 - vue: 3.2.45 - vue-demi: 0.13.11(vue@3.2.45) + vue: 3.5.42(typescript@5.9.3) + vue-demi: 0.13.11(vue@3.5.42(typescript@5.9.3)) optionalDependencies: - '@vue/runtime-core': 3.2.45 + '@vue/runtime-core': 3.5.42 transitivePeerDependencies: - '@vue/composition-api' @@ -6183,35 +6165,38 @@ snapshots: transitivePeerDependencies: - supports-color - vue-i18n@9.0.0(vue@3.2.45): + vue-i18n@9.0.0(vue@3.5.42(typescript@5.9.3)): dependencies: '@intlify/core-base': 9.0.0 '@intlify/shared': 9.0.0 '@vue/devtools-api': 6.5.0 - vue: 3.2.45 + vue: 3.5.42(typescript@5.9.3) - vue-router@4.1.6(vue@3.2.45): + vue-router@4.1.6(vue@3.5.42(typescript@5.9.3)): dependencies: '@vue/devtools-api': 6.5.0 - vue: 3.2.45 + vue: 3.5.42(typescript@5.9.3) - vue-tsc@0.35.0(typescript@4.9.3): + vue-tsc@3.3.11(typescript@5.9.3): dependencies: - '@volar/vue-typescript': 0.35.0 - typescript: 4.9.3 + '@volar/typescript': 2.4.28 + '@vue/language-core': 3.3.11 + typescript: 5.9.3 - vue@3.2.45: + vue@3.5.42(typescript@5.9.3): dependencies: - '@vue/compiler-dom': 3.2.45 - '@vue/compiler-sfc': 3.2.45 - '@vue/runtime-dom': 3.2.45 - '@vue/server-renderer': 3.2.45(vue@3.2.45) - '@vue/shared': 3.2.45 + '@vue/compiler-dom': 3.5.42 + '@vue/compiler-sfc': 3.5.42 + '@vue/runtime-dom': 3.5.42 + '@vue/server-renderer': 3.5.42 + '@vue/shared': 3.5.42 + optionalDependencies: + typescript: 5.9.3 - vuedraggable@4.1.0(vue@3.2.45): + vuedraggable@4.1.0(vue@3.5.42(typescript@5.9.3)): dependencies: sortablejs: 1.14.0 - vue: 3.2.45 + vue: 3.5.42(typescript@5.9.3) webpack-sources@3.2.3: {} diff --git a/apps/frontend/src/api/config/hysteriaViewModel.ts b/apps/frontend/src/api/config/hysteriaViewModel.ts new file mode 100644 index 0000000..7db51a5 --- /dev/null +++ b/apps/frontend/src/api/config/hysteriaViewModel.ts @@ -0,0 +1,314 @@ +import type { + Hysteria2ServerConfig, + Hysteria2ServerConfigOutbound, +} from "./types"; + +/** + * Нормализация конфига Hysteria на границе API. + * + * Зачем этот файл существует. + * + * `Hysteria2ServerConfig` описывает то, что РЕАЛЬНО приходит по сети, и почти + * все его секции необязательны — потому что необязательны они и в upstream + * YAML. Панель при этом показывает их как обычную форму: `dataForm.tls.cert`, + * `dataForm.acme.dns.config`, `dataForm.resolver.https.sni`. + * + * Пока проверка типов SFC-шаблонов не работала, это выглядело безобидно. + * Современный `vue-tsc` даёт на этом 141 ошибку `TS18048` в двух файлах — и он + * прав: обращение через возможно отсутствующий объект в рантайме падает. + * Спасало только то, что форма строится merge'ем поверх полного объекта + * значений по умолчанию, то есть инвариант «секция есть всегда» существовал, + * но держался на порядке присваиваний внутри компонента и нигде не был + * выражен типом. + * + * Два способа это закрыть неверны: + * + * `?.` в 141 месте шаблона — прячет вопрос «а что показывать, если секции + * нет», не отвечая на него, и делает шаблон нечитаемым; + * + * `as any` — выключает ровно ту проверку, ради которой обновлялся + * typechecker. + * + * Здесь выбран третий: одно преобразование на входе. Ответ приходит в + * `Hysteria2ServerConfig` (как есть, с необязательными секциями), а форма + * работает с `Hysteria2ServerConfigView`, где присутствие каждой секции — + * свойство типа. Шаблону больше не нужно знать ни одного нюанса + * необязательности upstream-схемы. + * + * Важно, чего этот слой НЕ делает: он не участвует в экспорте. Выгрузка + * серверного конфига идёт на backend от исходного YAML и сохраняет поля, о + * которых HY2XS ещё не знает (см. docs/04). View-модель — только для + * отображения, поэтому потеря неизвестных полей здесь безвредна. + */ + +/** + * DeepRequired делает обязательными все поля на всех уровнях. + * + * Массивы обрабатываются отдельно: без этой ветки `T[]` попал бы в `object` и + * маппинг прошёлся бы по свойствам самого массива. + */ +type DeepRequired = T extends (infer U)[] + ? DeepRequired[] + : T extends object + ? { [K in keyof T]-?: DeepRequired> } + : T; + +/** Конфиг Hysteria в том виде, в котором его показывает панель. */ +export type Hysteria2ServerConfigView = DeepRequired; + +/** Один outbound в том же виде. */ +export type Hysteria2ServerConfigOutboundView = + DeepRequired; + +/** + * Полное значение по умолчанию: каждая секция заполнена. + * + * Тип здесь не декоративный. `Hysteria2ServerConfigView` требует все поля, и + * добавление секции в `Hysteria2ServerConfig` сломает компиляцию ровно здесь — + * то есть новое поле upstream нельзя молча не отобразить. + */ +export const defaultHysteria2ServerConfigView: Hysteria2ServerConfigView = { + listen: ":443", + tls: { + cert: "", + key: "", + sniGuard: "", + clientCA: "", + }, + ech: { + keyPath: "", + }, + acme: { + domains: [], + email: "", + ca: "letsencrypt", + listenHost: "0.0.0.0", + dir: "/var/lib/hysteria/acme", + type: "", + http: { + altPort: 8888, + }, + tls: { + altPort: 44333, + }, + dns: { + name: "cloudflare", + config: {}, + }, + disableHTTP: false, + disableTLSALPN: false, + altHTTPPort: 80, + altTLSALPNPort: 443, + }, + obfs: { + type: "gecko", + salamander: { + password: "", + }, + gecko: { + password: "", + minPacketSize: 512, + maxPacketSize: 1200, + }, + }, + quic: { + initStreamReceiveWindow: 8388608, + maxStreamReceiveWindow: 8388608, + initConnReceiveWindow: 20971520, + maxConnReceiveWindow: 20971520, + maxIdleTimeout: "30s", + maxIncomingStreams: 1024, + disablePathMTUDiscovery: false, + disableStatelessReset: false, + }, + bandwidth: { + up: "50 mbps", + down: "50 mbps", + disableLossCompensation: false, + }, + congestion: { + type: "bbr", + bbrProfile: "standard", + }, + ignoreClientBandwidth: false, + speedTest: false, + disableUDP: false, + udpIdleTimeout: "60s", + resolver: { + type: "", + tcp: { + addr: "8.8.8.8:53", + timeout: "4s", + }, + udp: { + addr: "8.8.4.4:53", + timeout: "4s", + }, + tls: { + addr: "1.1.1.1:853", + timeout: "10s", + sni: "cloudflare-dns.com", + insecure: false, + }, + https: { + addr: "1.1.1.1:443", + timeout: "10s", + sni: "cloudflare-dns.com", + insecure: false, + }, + }, + sniff: { + enable: true, + timeout: "2s", + rewriteDomain: false, + tcpPorts: "80,443,8000-9000", + udpPorts: "all", + }, + acl: { + file: "", + inline: [], + geoip: "", + geosite: "", + geoUpdateInterval: "168h", + }, + outbounds: [], + trafficStats: { + listen: ":9999", + }, + masquerade: { + type: "", + file: { + dir: "", + }, + proxy: { + url: "", + rewriteHost: true, + insecure: false, + xForwarded: false, + }, + string: { + content: "hello stupid world", + headers: {}, + statusCode: 200, + }, + listenHTTP: ":80", + listenHTTPS: ":443", + forceHTTPS: true, + }, + mimic: { + enabled: false, + interface: "", + xdpMode: "", + path: "", + extraArgs: [], + }, + realm: { + stunServers: [], + stunTimeout: "", + punchTimeout: "", + heartbeatInterval: "", + insecure: false, + ipMode: "", + portMapping: { + enabled: false, + timeout: "", + lifetime: "", + }, + }, +}; + +function isPlainObject(value: unknown): value is Record { + return ( + typeof value === "object" && value !== null && !Array.isArray(value) + ); +} + +/** + * Рекурсивное наложение ответа сервера на значение по умолчанию. + * + * `null` и `undefined` игнорируются намеренно: в YAML отсутствующая секция и + * секция со значением `null` означают одно и то же — «не задано», — и обе + * обязаны оставить значение по умолчанию, а не обнулить поле формы. + * + * Массивы заменяются целиком, а не сливаются поэлементно: список ACL-правил + * или outbounds с сервера — это весь список, а не патч к дефолтному. + */ +function mergeInto(target: Record, source: unknown): void { + if (!isPlainObject(source)) { + return; + } + + for (const [key, value] of Object.entries(source)) { + if (value === null || value === undefined) { + continue; + } + if (Array.isArray(value)) { + target[key] = value; + continue; + } + if (isPlainObject(value)) { + const existing = target[key]; + if (!isPlainObject(existing)) { + target[key] = {}; + } + mergeInto(target[key] as Record, value); + continue; + } + target[key] = value; + } +} + +function cloneDefaults(): Hysteria2ServerConfigView { + // structuredClone есть во всех целевых браузерах и, в отличие от + // JSON.parse(JSON.stringify(...)), не тратит проход на сериализацию. + return structuredClone(defaultHysteria2ServerConfigView); +} + +/** + * Приводит ответ сервера к модели, с которой работает форма. + * + * Пустой или отсутствующий ответ даёт полное значение по умолчанию: это то же + * состояние, в котором форма находится до первого запроса. + */ +export function normalizeHysteriaViewModel( + raw: Hysteria2ServerConfig | null | undefined +): Hysteria2ServerConfigView { + const view = cloneDefaults(); + mergeInto(view as unknown as Record, raw); + return view; +} + +/** Значение по умолчанию для одного outbound. */ +export const defaultHysteria2ServerConfigOutboundView: Hysteria2ServerConfigOutboundView = + { + name: "", + type: "socks5", + socks5: { + addr: "", + username: "", + password: "", + }, + http: { + url: "", + insecure: false, + }, + direct: { + mode: "auto", + bindIPv4: "", + bindIPv6: "", + bindDevice: "", + fastOpen: false, + }, + }; + +/** + * Тот же приём для одного outbound: список приходит с необязательными + * подблоками, а карточка показывает их как обычные поля. + */ +export function normalizeOutboundViewModel( + raw: Hysteria2ServerConfigOutbound | null | undefined +): Hysteria2ServerConfigOutboundView { + const view = structuredClone(defaultHysteria2ServerConfigOutboundView); + mergeInto(view as unknown as Record, raw); + return view; +} diff --git a/apps/frontend/src/api/config/types.ts b/apps/frontend/src/api/config/types.ts index 3772705..ddff1cb 100644 --- a/apps/frontend/src/api/config/types.ts +++ b/apps/frontend/src/api/config/types.ts @@ -19,6 +19,17 @@ export interface ConfigUpdateDto { configUpdateDtos: Array; } +/** + * Конфиг Hysteria в том виде, в котором он ПРИХОДИТ ПО СЕТИ. + * + * Необязательность секций здесь не оплошность: ровно так устроен upstream YAML, + * и модель обязана его повторять, иначе она врала бы об ответе сервера. + * + * Форма панели работает не с этим типом, а с `Hysteria2ServerConfigView` из + * `hysteriaViewModel.ts`, где все секции обязательны. Значения по умолчанию + * живут там же: пока они лежали здесь, рядом с описанием ответа сервера, они + * выглядели частью протокола, хотя относятся исключительно к отображению. + */ export interface Hysteria2ServerConfig { listen: string; tls?: { @@ -169,129 +180,6 @@ export interface Hysteria2ServerConfig { }; } -export const defaultHysteria2ServerConfig: Hysteria2ServerConfig = { - listen: ":443", - tls: { - cert: "", - key: "", - sniGuard: "", - }, - acme: { - domains: [], - email: "", - ca: "letsencrypt", - listenHost: "0.0.0.0", - dir: "/var/lib/hysteria/acme", - type: "", - http: { - altPort: 8888, - }, - tls: { - altPort: 44333, - }, - dns: { - name: "cloudflare", - config: {}, - }, - disableHTTP: false, - disableTLSALPN: false, - altHTTPPort: 80, - altTLSALPNPort: 443, - }, - obfs: { - type: "gecko", - gecko: { - password: "", - minPacketSize: 512, - maxPacketSize: 1200, - }, - }, - quic: { - initStreamReceiveWindow: 8388608, - maxStreamReceiveWindow: 8388608, - initConnReceiveWindow: 20971520, - maxConnReceiveWindow: 20971520, - maxIdleTimeout: "30s", - maxIncomingStreams: 1024, - disablePathMTUDiscovery: false, - disableStatelessReset: false, - }, - bandwidth: { - up: "50 mbps", - down: "50 mbps", - disableLossCompensation: false, - }, - congestion: { - type: "bbr", - bbrProfile: "standard", - }, - ignoreClientBandwidth: false, - speedTest: false, - disableUDP: false, - udpIdleTimeout: "60s", - resolver: { - type: "", - tcp: { - addr: "8.8.8.8:53", - timeout: "4s", - }, - udp: { - addr: "8.8.4.4:53", - timeout: "4s", - }, - tls: { - addr: "1.1.1.1:853", - timeout: "10s", - sni: "cloudflare-dns.com", - insecure: false, - }, - https: { - addr: "1.1.1.1:443", - timeout: "10s", - sni: "cloudflare-dns.com", - insecure: false, - }, - }, - sniff: { - enable: true, - timeout: "2s", - rewriteDomain: false, - tcpPorts: "80,443,8000-9000", - udpPorts: "all", - }, - acl: { - file: "", - inline: [], - geoip: "", - geosite: "", - geoUpdateInterval: "168h", - }, - outbounds: [], - trafficStats: { - listen: ":9999", - }, - masquerade: { - type: "", - file: { - dir: "", - }, - proxy: { - url: "", - rewriteHost: true, - insecure: false, - xForwarded: false, - }, - string: { - content: "hello stupid world", - headers: {}, - statusCode: 200, - }, - listenHTTP: ":80", - listenHTTPS: ":443", - forceHTTPS: true, - }, -}; - export interface Hysteria2ServerConfigOutbound { name: string; type: string; @@ -313,28 +201,6 @@ export interface Hysteria2ServerConfigOutbound { }; } -export const defaultHysteria2ServerConfigOutbound: Hysteria2ServerConfigOutbound = - { - name: "", - type: "socks5", - socks5: { - addr: "", - username: undefined, - password: undefined, - }, - http: { - url: "", - insecure: false, - }, - direct: { - mode: "auto", - bindIPv4: undefined, - bindIPv6: undefined, - bindDevice: undefined, - fastOpen: false, - }, - }; - export interface Tab { name: string; desc: string; diff --git a/apps/frontend/src/lang/package/en.ts b/apps/frontend/src/lang/package/en.ts index 338a8ed..af6cc7c 100644 --- a/apps/frontend/src/lang/package/en.ts +++ b/apps/frontend/src/lang/package/en.ts @@ -80,6 +80,7 @@ export default { add: "Add", confirm: "Confirm", cancel: "Cancel", + close: "Close", copySuccess: "Copy successful", nodeUrl: "Node URL", nodeQrCode: "Node QR Code", @@ -226,7 +227,7 @@ export default { addConfigItem: "Add Config Item", hysteria2Version: "Hysteria2 Version", hysteria2Running: "Hysteria2 Status", - addOutbound: "Add Outbound", + outboundsEmpty: "No outbounds are configured on the server", listen: "Listen", tls: "TLS", obfs: "Obfuscation", diff --git a/apps/frontend/src/lang/package/ru.ts b/apps/frontend/src/lang/package/ru.ts index 5b27225..665a348 100644 --- a/apps/frontend/src/lang/package/ru.ts +++ b/apps/frontend/src/lang/package/ru.ts @@ -77,6 +77,7 @@ export default { add: "Добавить", confirm: "Подтвердить", cancel: "Отмена", + close: "Закрыть", copySuccess: "Скопировано", nodeUrl: "URL узла", nodeQrCode: "QR узла", @@ -222,7 +223,9 @@ export default { addConfigItem: "Добавить параметр", hysteria2Version: "Версия Hysteria2", hysteria2Running: "Статус Hysteria2", - addOutbound: "Добавить outbound", + // addOutbound удалён вместе с редактором outbounds: страница read-only, + // маршрутов записи серверного конфига в API нет. + outboundsEmpty: "Outbounds в конфигурации сервера не заданы", listen: "Адрес прослушивания", tls: "TLS", obfs: "Маскировка", diff --git a/apps/frontend/src/types/components.d.ts b/apps/frontend/src/types/components.d.ts index 2ad23b0..f91dbdf 100644 --- a/apps/frontend/src/types/components.d.ts +++ b/apps/frontend/src/types/components.d.ts @@ -40,6 +40,7 @@ declare module '@vue/runtime-core' { ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabs: typeof import('element-plus/es')['ElTabs'] ElTag: typeof import('element-plus/es')['ElTag'] + ElText: typeof import('element-plus/es')['ElText'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElUpload: typeof import('element-plus/es')['ElUpload'] Hamburger: typeof import('./../components/Hamburger/index.vue')['default'] diff --git a/apps/frontend/src/views/hysteria/list/components/Outbounds/index.vue b/apps/frontend/src/views/hysteria/list/components/Outbounds/index.vue index 01acd0b..645c832 100644 --- a/apps/frontend/src/views/hysteria/list/components/Outbounds/index.vue +++ b/apps/frontend/src/views/hysteria/list/components/Outbounds/index.vue @@ -1,152 +1,17 @@