diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 6f40582..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-env node */ -require('@rushstack/eslint-patch/modern-module-resolution') - -module.exports = { - root: true, - 'extends': [ - 'plugin:vue/vue3-essential', - 'eslint:recommended', - '@vue/eslint-config-typescript', - '@vue/eslint-config-prettier/skip-formatting' - ], - parserOptions: { - ecmaVersion: 'latest' - } -} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..59d9cb3 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,33 @@ +name: Build frontend + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: frontend-build-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 9.15.9 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - run: pnpm install --frozen-lockfile + - run: pnpm build diff --git a/.gitignore b/.gitignore index 8ee54e8..2d29e0a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ node_modules dist dist-ssr coverage +output *.local /cypress/videos/ diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..a549f59 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "2.4.0" +} diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..8f1a7b8 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,95 @@ +# 前端架构说明 + +本文档记录前端代码的分层边界和导入规则,用于保持框架稳定、可扩展。 + +## 目录职责 + +- `src/views/`:页面编排层,只负责布局、组件组合和事件绑定。 +- `src/components/`:可复用 UI 组件,只接收 props、触发 emits,不直接访问接口。 +- `src/composables/`:业务流程层,承载页面状态、业务校验、服务调用和视图模型转换。 +- `src/services/`:API 访问层,只封装请求路径、请求参数和响应类型;`client.ts` 是统一 HTTP 客户端入口,`upload-client.ts` 只处理外部直传,`upload-strategy.ts` 承载上传协议编排。 +- `src/stores/`:跨页面共享状态。局部页面状态应优先放在对应 composable。 +- `src/types/`:领域类型定义,按 `api/auth/config/dashboard/file/presign-upload/ui` 拆分。 +- `src/utils/`:纯工具、存储 helper、URL 构造、剪贴板、格式化等无页面状态能力,不直接依赖 store。 + +## 导入规则 + +- 页面层统一从 `@/composables` 引入业务流程。 +- composable 内部引用同目录 composable 时使用相对路径,不通过 `@/composables/*` 绕回统一出口。 +- composable 统一从 `@/services` 引入 API 服务。 +- service 内部统一使用 `./client`,不要从 `@/utils/api` 或页面层创建请求客户端。 +- 裸 `axios` 只能出现在 `services/client.ts` 与 `services/upload-client.ts` 这类客户端封装内。 +- 类型默认从 `@/types` 引入;需要领域内复用时可在 `src/types/*` 内部相对引入。 +- 不在页面或组件中直接调用 `axios`、`fetch`、`FileService`、`ConfigService` 等服务。 +- 不在页面中直接读写 `localStorage`,持久化逻辑放在 `utils/*-storage.ts`。 +- 页面和组件不直接导入剪贴板 helper,复制动作由对应 composable 暴露。 + +## 状态归属 + +- 发送流程状态归 `useSendFlow`;路由跳转、返回首页等页面编排放在 view。 +- 发送记录列表归 `SentRecordList`,发送记录详情归 `SentRecordDetailModal`,发送页不内联记录抽屉明细模板。 +- 发送记录视图模型、过期时间校验和过期时间展示归 `utils/send-record.ts`,`useSendFlow` 不内联日期格式化和记录构造。 +- 发送记录复制和二维码取值动作归 `utils/sent-record-actions.ts`,`useSendFlow` 不直接调用底层剪贴板和二维码 URL helper。 +- 发送提交策略归 `useSendSubmit`,切片上传、预签名上传、文本上传和多文件打包不回流到 `useSendFlow`。 +- 取件流程状态归 `useRetrieveFlow`;路由参数读取、自动提交、页面跳转等页面编排放在 view。 +- 取件详情和内容预览的打开/关闭动作归 `useRetrieveFlow`,取件页不直接改内部状态 ref。 +- 管理文件列表状态归 `useAdminFiles`。 +- 文件管理编辑弹窗字段统一使用 `FileEditField`,页面不手写重复输入字段结构。 +- 仪表盘数据状态归 `useDashboardStats`,加载时机由 `DashboardView` 显式触发。 +- 登录表单和认证请求归 `useAdminLogin`,登录成功后的页面跳转放在 `LoginView`。 +- 可复用的底层 composable 不直接读取全局 store 或弹提示,应通过参数注入配置读取和通知回调。 +- 系统配置编辑状态归 `useSystemConfig`,公共配置缓存归 `configStore`。 +- 系统配置页的布尔/数字开关由 `useSystemConfig` 暴露语义动作,模板不直接写切换表达式。 +- 系统配置页的二值开关统一使用 `SettingSwitch`,页面不手写 switch 样式结构。 +- 系统配置页的简单数字项统一使用 `SettingNumberInput`,带单位下拉的复合输入可保留在页面编排层。 +- 系统配置页的单位表单状态和提交 payload 构造归 `useSystemConfig`,单位换算纯函数归 `utils/config-form.ts`。 +- 发送/接收历史记录归 `fileData` store。 +- Alert 计时和进度归 `alertStore`,`AlertComponent` 只负责渲染。 + +## 工具边界 + +- URL 构造统一放在 `utils/share-url.ts`。 +- 剪贴板能力统一放在 `utils/clipboard.ts`,复制结果提示通过调用方注入回调。 +- 粘贴事件的纯解析和文本插入计算统一放在 `utils/clipboard-paste.ts`。 +- 文件 hash、zip 打包等浏览器文件处理统一放在 `utils/file-processing.ts`。 +- Markdown 预览渲染与 HTML 清洗统一放在 `utils/content-preview.ts`。 +- 文件下载、文本另存等浏览器下载动作统一放在 `utils/download-action.ts`。 +- 配置表单单位转换统一放在 `utils/config-form.ts`。 +- 配置缓存读写统一放在 `utils/config-storage.ts`。 +- 认证缓存读写统一放在 `utils/auth-storage.ts`。 + +## 维护约束 + +- 新页面应先创建对应 composable,再由 view 绑定 composable 输出。 +- composable 默认不在初始化阶段自动发起远程请求,页面或上层流程应显式触发加载。 +- 新接口应先进入 `services/`,不要在 composable 中拼接散落的底层请求细节。 +- Blob、文件下载等需要保留响应头的接口使用 `rawApiClient`,普通 JSON 接口使用默认 `apiClient`。 +- 上传协议、断点续传、外部直传等流程优先放在 `services/*strategy*` 或客户端封装,composable 只传入状态、回调和文案。 +- 文件打包依赖 `JSZip` 只能出现在 `utils/file-processing.ts`,页面和 composable 不直接依赖打包库。 +- 预览渲染依赖 `marked` / `DOMPurify` 只能出现在 `utils/content-preview.ts`。 +- 下载动作依赖 `file-saver` 或 `window.open` 时,只能出现在 `utils/download-action.ts`。 +- 新共享状态进入 store 前,需要确认它确实跨页面使用。 +- 新类型应放入对应领域类型文件,再由 `types/index.ts` 汇总导出。 +- 删除旧代码优先于保留兼容别名,除非已有明确外部调用方。 + +## 验证 + +每次结构性改动后至少运行: + +```sh +pnpm build +``` + +`pnpm build` 会先执行 `pnpm check:architecture`,再执行类型检查和生产构建。 + +快速验证架构边界时可单独运行: + +```sh +pnpm check:architecture +``` + +必要时补充: + +```sh +pnpm lint +``` diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cf8695a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +此文件由 Release Please 根据 Conventional Commits 自动维护。 diff --git a/README.md b/README.md index 8ea06d4..8b6a280 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,13 @@ pnpm install pnpm dev ``` +The frontend version comes from `package.json`. Production builds also embed the +current Git commit. CI can override these values with `VITE_APP_VERSION` and +`VITE_GIT_COMMIT`. + +The FileCodeBox product release workflow builds the latest commit from this +repository's default branch. Every push is also checked with a production build. + ### Type-Check, Compile and Minify for Production ```sh diff --git a/env.d.ts b/env.d.ts index 11f02fe..53cbf48 100644 --- a/env.d.ts +++ b/env.d.ts @@ -1 +1,4 @@ /// + +declare const __APP_VERSION__: string +declare const __GIT_COMMIT__: string diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..35904b9 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,30 @@ +import js from '@eslint/js' +import vue from 'eslint-plugin-vue' +import typescript from '@vue/eslint-config-typescript' +import prettier from '@vue/eslint-config-prettier/skip-formatting' + +export default [ + js.configs.recommended, + ...vue.configs['flat/essential'], + ...typescript(), + prettier, + { + files: ['**/*.{js,mjs,cjs,vue,ts}'], + rules: { + // 你可以在这里添加自定义规则 + } + }, + { + files: ['scripts/**/*.mjs'], + languageOptions: { + globals: { + console: 'readonly', + process: 'readonly', + URL: 'readonly', + }, + }, + }, + { + ignores: ['dist/**', 'node_modules/**'] + } +] diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..44aa851 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5669 @@ +{ + "name": "filecodeboxfronted", + "version": "2.4.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "filecodeboxfronted", + "version": "2.4.0", + "dependencies": { + "@tailwindcss/typography": "^0.5.20", + "axios": "^1.18.0", + "dompurify": "^3.4.11", + "file-saver": "^2.0.5", + "jszip": "3.10.1", + "lru-cache": "^11.5.1", + "lucide-vue-next": "^0.535.0", + "marked": "^16.4.2", + "pinia": "^3.0.4", + "qrcode.vue": "^3.10.0", + "spark-md5": "^3.0.2", + "vue": "^3.5.38", + "vue-i18n": "^9.14.5", + "vue-router": "^4.6.4" + }, + "devDependencies": { + "@eslint/config-array": "^0.21.2", + "@eslint/js": "^9.39.4", + "@eslint/object-schema": "^2.1.7", + "@rushstack/eslint-patch": "^1.16.1", + "@tsconfig/node20": "^20.1.9", + "@types/file-saver": "^2.0.7", + "@types/node": "^24.13.2", + "@types/spark-md5": "^3.0.5", + "@vitejs/plugin-vue": "^6.0.7", + "@vitejs/plugin-vue-jsx": "^5.1.5", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.8.0", + "@vue/tsconfig": "^0.7.0", + "autoprefixer": "^10.5.0", + "eslint": "^9.39.4", + "eslint-plugin-vue": "^10.9.2", + "glob": "^13.0.6", + "npm-run-all2": "^8.0.4", + "postcss": "^8.5.15", + "prettier": "^3.8.4", + "rimraf": "^6.1.3", + "tailwindcss": "^3.4.19", + "typescript": "~5.8.3", + "vite": "^7.3.5", + "vue-tsc": "^3.3.5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", + "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.29.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.28.6", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.6", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", + "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@intlify/core-base": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz", + "integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==", + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "9.14.5", + "@intlify/shared": "9.14.5" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz", + "integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==", + "license": "MIT", + "dependencies": { + "@intlify/shared": "9.14.5", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz", + "integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.16.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz", + "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.20.tgz", + "integrity": "sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >=4.0.0 || insiders" + } + }, + "node_modules/@tsconfig/node20": { + "version": "20.1.9", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@tsconfig/node20/-/node20-20.1.9.tgz", + "integrity": "sha512-IjlTv1RsvnPtUcjTqtVsZExKVq+KQx4g5pCP5tI7rAs6Xesl2qFwSz/tPDBC4JajkL/MlezBu3gPUwqRHl+RIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/file-saver": { + "version": "2.0.7", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@types/file-saver/-/file-saver-2.0.7.tgz", + "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz", + "integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/spark-md5": { + "version": "3.0.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@types/spark-md5/-/spark-md5-3.0.5.tgz", + "integrity": "sha512-lWf05dnD42DLVKQJZrDHtWFidcLrHuip01CtnC2/S6AMhX4t9ZlEUj4iuRlAnts0PQk7KESOqKxeGE/b6sIPGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz", + "integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/type-utils": "8.61.1", + "@typescript-eslint/utils": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.61.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz", + "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz", + "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.61.1", + "@typescript-eslint/types": "^8.61.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz", + "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz", + "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz", + "integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/utils": "8.61.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz", + "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz", + "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.61.1", + "@typescript-eslint/tsconfig-utils": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz", + "integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", + "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz", + "integrity": "sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitejs/plugin-vue-jsx": { + "version": "5.1.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-5.1.5.tgz", + "integrity": "sha512-jIAsvHOEtWpslLOI2MeElGFxH7M8pM83BU/Tor4RLyiwH0FM4nUW3xdvbw20EeU9wc5IspQwMq225K3CMnJEpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-syntax-typescript": "^7.28.6", + "@babel/plugin-transform-typescript": "^7.28.6", + "@rolldown/pluginutils": "^1.0.0-rc.2", + "@vue/babel-plugin-jsx": "^2.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.0.0" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "2.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-2.0.1.tgz", + "integrity": "sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "2.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vue/babel-plugin-jsx/-/babel-plugin-jsx-2.0.1.tgz", + "integrity": "sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@vue/babel-helper-vue-transform-on": "2.0.1", + "@vue/babel-plugin-resolve-type": "2.0.1", + "@vue/shared": "^3.5.22" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "2.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-2.0.1.tgz", + "integrity": "sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.4", + "@vue/compiler-sfc": "^3.5.22" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.38.tgz", + "integrity": "sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/shared": "3.5.38", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.38.tgz", + "integrity": "sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.38", + "@vue/shared": "3.5.38" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.38.tgz", + "integrity": "sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/compiler-core": "3.5.38", + "@vue/compiler-dom": "3.5.38", + "@vue/compiler-ssr": "3.5.38", + "@vue/shared": "3.5.38", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.15", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.38.tgz", + "integrity": "sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.38", + "@vue/shared": "3.5.38" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/eslint-config-prettier": { + "version": "10.2.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz", + "integrity": "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2" + }, + "peerDependencies": { + "eslint": ">= 8.21.0", + "prettier": ">= 3.0.0" + } + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "14.8.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.8.0.tgz", + "integrity": "sha512-yIquzhXH7ZsrwSSm+rYvoGCRY6wcuF4qBi76e0l7hHLq7YU0f9aC+RcR5fL+XJNfmBZxgX5cVl4sppt4x7ZCBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.60.0", + "fast-glob": "^3.3.3", + "typescript-eslint": "^8.60.0", + "vue-eslint-parser": "^10.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0 || ^10.0.0", + "eslint-plugin-vue": "^9.28.0 || ^10.0.0", + "typescript": ">=4.8.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.5.tgz", + "integrity": "sha512-UkKu5nhX89fg4VhlG/FOeI10G3cj/7radKT/cy9BT4Q9qJmJlSTAc/dP63Xqs29aypN4f39xUV6PsLNk/dcD6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.0", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/language-core/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.38.tgz", + "integrity": "sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.38" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.38.tgz", + "integrity": "sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.38", + "@vue/shared": "3.5.38" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.38.tgz", + "integrity": "sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.38", + "@vue/runtime-core": "3.5.38", + "@vue/shared": "3.5.38", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.38.tgz", + "integrity": "sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.38", + "@vue/shared": "3.5.38" + }, + "peerDependencies": { + "vue": "3.5.38" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.38.tgz", + "integrity": "sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.7.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vue/tsconfig/-/tsconfig-0.7.0.tgz", + "integrity": "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", + "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", + "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/dompurify": { + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", + "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.422", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz", + "integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.9.2.tgz", + "integrity": "sha512-4g7ZP3pYcuqd7Zp0pzUKcos0W+RkjBz4EGdhJ92FcYk6v03Ti/GK5NwjgsjxHK+98eXDbHeK7VtX1az7/8doZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^7.1.0", + "semver": "^7.6.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "vue-eslint-parser": "^10.3.0" + }, + "peerDependenciesMeta": { + "@stylistic/eslint-plugin": { + "optional": true + }, + "@typescript-eslint/parser": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-vue/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "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-visitor-keys": { + "version": "3.4.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "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/espree": { + "version": "10.4.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "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/esquery": { + "version": "1.7.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/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/gopd": { + "version": "1.2.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/lucide-vue-next": { + "version": "0.535.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/lucide-vue-next/-/lucide-vue-next-0.535.0.tgz", + "integrity": "sha512-B79vTrPyVgp+yfnQe0xU0OcA5+xa9fz24psR8z01NZhbmn5VoxfYMwRm5E4iSk9mCRYgyFWqtx8DMp/bydiTcg==", + "license": "ISC", + "peerDependencies": { + "vue": ">=3.0.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", + "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "8.0.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/npm-run-all2/-/npm-run-all2-8.0.4.tgz", + "integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "picomatch": "^4.0.2", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^20.5.0 || >=22.0.0", + "npm": ">= 10" + } + }, + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm-run-all2/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinia": { + "version": "3.0.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/pinia/-/pinia-3.0.4.tgz", + "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.7" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.5.0", + "vue": "^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz", + "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode.vue": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-3.10.0.tgz", + "integrity": "sha512-1bjeBds9hRKMszZuBuYQZor9HdJYWtb0S44HG1JofZ9uicpJpdF+TtDvqo3+2ZlH0k80WVIkmiKB4hq0/N6/rA==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.60.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/rollup/-/rollup-4.60.3.tgz", + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.3", + "@rollup/rollup-android-arm64": "4.60.3", + "@rollup/rollup-darwin-arm64": "4.60.3", + "@rollup/rollup-darwin-x64": "4.60.3", + "@rollup/rollup-freebsd-arm64": "4.60.3", + "@rollup/rollup-freebsd-x64": "4.60.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", + "@rollup/rollup-linux-arm-musleabihf": "4.60.3", + "@rollup/rollup-linux-arm64-gnu": "4.60.3", + "@rollup/rollup-linux-arm64-musl": "4.60.3", + "@rollup/rollup-linux-loong64-gnu": "4.60.3", + "@rollup/rollup-linux-loong64-musl": "4.60.3", + "@rollup/rollup-linux-ppc64-gnu": "4.60.3", + "@rollup/rollup-linux-ppc64-musl": "4.60.3", + "@rollup/rollup-linux-riscv64-gnu": "4.60.3", + "@rollup/rollup-linux-riscv64-musl": "4.60.3", + "@rollup/rollup-linux-s390x-gnu": "4.60.3", + "@rollup/rollup-linux-x64-gnu": "4.60.3", + "@rollup/rollup-linux-x64-musl": "4.60.3", + "@rollup/rollup-openbsd-x64": "4.60.3", + "@rollup/rollup-openharmony-arm64": "4.60.3", + "@rollup/rollup-win32-arm64-msvc": "4.60.3", + "@rollup/rollup-win32-ia32-msvc": "4.60.3", + "@rollup/rollup-win32-x64-gnu": "4.60.3", + "@rollup/rollup-win32-x64-msvc": "4.60.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", + "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "license": "(WTFPL OR MIT)" + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.1.tgz", + "integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.61.1", + "@typescript-eslint/parser": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/utils": "8.61.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz", + "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.38.tgz", + "integrity": "sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.38", + "@vue/compiler-sfc": "3.5.38", + "@vue/runtime-dom": "3.5.38", + "@vue/server-renderer": "3.5.38", + "@vue/shared": "3.5.38" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "10.4.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/vue-eslint-parser/-/vue-eslint-parser-10.4.0.tgz", + "integrity": "sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "eslint-scope": "^8.2.0 || ^9.0.0", + "eslint-visitor-keys": "^4.2.0 || ^5.0.0", + "espree": "^10.3.0 || ^11.0.0", + "esquery": "^1.6.0", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vue-i18n": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz", + "integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==", + "deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html", + "license": "MIT", + "dependencies": { + "@intlify/core-base": "9.14.5", + "@intlify/shared": "9.14.5", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-i18n/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/vue-tsc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.5.tgz", + "integrity": "sha512-Rzh/G2MmNlMSAMTiQEjDrsb4dgB/jbtEM47rVN2NtidF1dfb/q4w4QvpQBtW5+y3y5H27Hjh7deVwk+YB02fNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.5" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://nexus3.bilibili.co/repository/npm-public/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index 9f4d4b5..815eae7 100644 --- a/package.json +++ b/package.json @@ -1,54 +1,79 @@ { "name": "filecodeboxfronted", - "version": "0.0.0", + "version": "2.4.0", "private": true, "type": "module", "scripts": { "dev": "vite", - "build": "run-p type-check \"build-only {@}\" --", + "build": "run-s check:architecture type-check build-only", "preview": "vite preview", "build-only": "vite build", "type-check": "vue-tsc --build --force", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", + "check:architecture": "node scripts/check-architecture.mjs", + "lint": "eslint . --fix", "format": "prettier --write src/" }, "dependencies": { - "@tailwindcss/typography": "^0.5.15", - "axios": "^1.7.7", + "@tailwindcss/typography": "^0.5.20", + "axios": "^1.18.0", + "dompurify": "^3.4.11", "file-saver": "^2.0.5", - "lru-cache": "^11.0.1", - "lucide-vue-next": "^0.445.0", - "marked": "^14.1.2", - "pinia": "^2.2.2", - "qrcode.vue": "^3.4.1", + "jszip": "3.10.1", + "lru-cache": "^11.5.1", + "lucide-vue-next": "^0.535.0", + "marked": "^16.4.2", + "pinia": "^3.0.4", + "qrcode.vue": "^3.10.0", "spark-md5": "^3.0.2", - "vue": "^3.5.8", - "vue-router": "^4.4.5" + "vue": "^3.5.38", + "vue-i18n": "^9.14.5", + "vue-router": "^4.6.4" }, "devDependencies": { - "@eslint/config-array": "^0.18.0", - "@eslint/object-schema": "^2.1.4", - "@rushstack/eslint-patch": "^1.10.4", - "@tsconfig/node20": "^20.1.4", - "@types/node": "^20.16.7", - "@types/spark-md5": "^3.0.4", - "@vitejs/plugin-vue": "^5.1.4", - "@vitejs/plugin-vue-jsx": "^4.0.1", - "@vue/eslint-config-prettier": "^9.0.0", - "@vue/eslint-config-typescript": "^13.0.0", - "@vue/tsconfig": "^0.5.1", - "autoprefixer": "^10.4.20", - "eslint": "^8.57.1", - "eslint-plugin-vue": "^9.28.0", - "glob": "^11.0.0", - "npm-run-all2": "^6.2.3", - "postcss": "^8.4.47", - "prettier": "^3.3.3", - "rimraf": "^6.0.1", - "tailwindcss": "^3.4.13", - "typescript": "~5.4.5", - "vite": "^5.4.7", - "vite-plugin-vue-devtools": "^7.4.6", - "vue-tsc": "^2.1.6" + "@eslint/config-array": "^0.21.2", + "@eslint/js": "^9.39.4", + "@eslint/object-schema": "^2.1.7", + "@rushstack/eslint-patch": "^1.16.1", + "@tsconfig/node20": "^20.1.9", + "@types/file-saver": "^2.0.7", + "@types/node": "^24.13.2", + "@types/spark-md5": "^3.0.5", + "@vitejs/plugin-vue": "^6.0.7", + "@vitejs/plugin-vue-jsx": "^5.1.5", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.8.0", + "@vue/tsconfig": "^0.7.0", + "autoprefixer": "^10.5.0", + "eslint": "^9.39.4", + "eslint-plugin-vue": "^10.9.2", + "glob": "^13.0.6", + "npm-run-all2": "^8.0.4", + "postcss": "^8.5.15", + "prettier": "^3.8.4", + "rimraf": "^6.1.3", + "tailwindcss": "^3.4.19", + "typescript": "~5.8.3", + "vite": "^7.3.5", + "vue-tsc": "^3.3.5" + }, + "overrides": { + "@babel/core": "7.29.7", + "brace-expansion": "5.0.6", + "esbuild": "0.28.1", + "form-data": "4.0.6", + "js-yaml": "4.2.0", + "shell-quote": "1.8.4", + "yaml": "2.9.0" + }, + "pnpm": { + "overrides": { + "@babel/core": "7.29.7", + "brace-expansion": "5.0.6", + "esbuild": "0.28.1", + "form-data": "4.0.6", + "js-yaml": "4.2.0", + "shell-quote": "1.8.4", + "yaml": "2.9.0" + } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 228e9c5..d862766 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,116 +4,137 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + '@babel/core': 7.29.7 + brace-expansion: 5.0.6 + esbuild: 0.28.1 + form-data: 4.0.6 + js-yaml: 4.2.0 + shell-quote: 1.8.4 + yaml: 2.9.0 + importers: .: dependencies: '@tailwindcss/typography': - specifier: ^0.5.15 - version: 0.5.16(tailwindcss@3.4.17) + specifier: ^0.5.20 + version: 0.5.20(tailwindcss@3.4.19) axios: - specifier: ^1.7.7 - version: 1.8.4 + specifier: ^1.18.0 + version: 1.18.0 + dompurify: + specifier: ^3.4.11 + version: 3.4.11 file-saver: specifier: ^2.0.5 version: 2.0.5 + jszip: + specifier: 3.10.1 + version: 3.10.1 lru-cache: - specifier: ^11.0.1 - version: 11.1.0 + specifier: ^11.5.1 + version: 11.5.1 lucide-vue-next: - specifier: ^0.445.0 - version: 0.445.0(vue@3.5.13(typescript@5.4.5)) + specifier: ^0.535.0 + version: 0.535.0(vue@3.5.38(typescript@5.8.3)) marked: - specifier: ^14.1.2 - version: 14.1.4 + specifier: ^16.4.2 + version: 16.4.2 pinia: - specifier: ^2.2.2 - version: 2.3.1(typescript@5.4.5)(vue@3.5.13(typescript@5.4.5)) + specifier: ^3.0.4 + version: 3.0.4(typescript@5.8.3)(vue@3.5.38(typescript@5.8.3)) qrcode.vue: - specifier: ^3.4.1 - version: 3.6.0(vue@3.5.13(typescript@5.4.5)) + specifier: ^3.10.0 + version: 3.10.0(vue@3.5.38(typescript@5.8.3)) spark-md5: specifier: ^3.0.2 version: 3.0.2 vue: - specifier: ^3.5.8 - version: 3.5.13(typescript@5.4.5) + specifier: ^3.5.38 + version: 3.5.38(typescript@5.8.3) + vue-i18n: + specifier: ^9.14.5 + version: 9.14.5(vue@3.5.38(typescript@5.8.3)) vue-router: - specifier: ^4.4.5 - version: 4.5.0(vue@3.5.13(typescript@5.4.5)) + specifier: ^4.6.4 + version: 4.6.4(vue@3.5.38(typescript@5.8.3)) devDependencies: '@eslint/config-array': - specifier: ^0.18.0 - version: 0.18.0 + specifier: ^0.21.2 + version: 0.21.2 + '@eslint/js': + specifier: ^9.39.4 + version: 9.39.4 '@eslint/object-schema': - specifier: ^2.1.4 - version: 2.1.6 + specifier: ^2.1.7 + version: 2.1.7 '@rushstack/eslint-patch': - specifier: ^1.10.4 - version: 1.11.0 + specifier: ^1.16.1 + version: 1.16.1 '@tsconfig/node20': - specifier: ^20.1.4 - version: 20.1.5 + specifier: ^20.1.9 + version: 20.1.9 + '@types/file-saver': + specifier: ^2.0.7 + version: 2.0.7 '@types/node': - specifier: ^20.16.7 - version: 20.17.30 + specifier: ^24.13.2 + version: 24.13.2 '@types/spark-md5': - specifier: ^3.0.4 + specifier: ^3.0.5 version: 3.0.5 '@vitejs/plugin-vue': - specifier: ^5.1.4 - version: 5.2.3(vite@5.4.18(@types/node@20.17.30))(vue@3.5.13(typescript@5.4.5)) + specifier: ^6.0.7 + version: 6.0.7(vite@7.3.5(@types/node@24.13.2)(jiti@1.21.7))(vue@3.5.38(typescript@5.8.3)) '@vitejs/plugin-vue-jsx': - specifier: ^4.0.1 - version: 4.1.2(vite@5.4.18(@types/node@20.17.30))(vue@3.5.13(typescript@5.4.5)) + specifier: ^5.1.5 + version: 5.1.5(vite@7.3.5(@types/node@24.13.2)(jiti@1.21.7))(vue@3.5.38(typescript@5.8.3)) '@vue/eslint-config-prettier': - specifier: ^9.0.0 - version: 9.0.0(eslint@8.57.1)(prettier@3.5.3) + specifier: ^10.2.0 + version: 10.2.0(eslint@9.39.4(jiti@1.21.7))(prettier@3.8.4) '@vue/eslint-config-typescript': - specifier: ^13.0.0 - version: 13.0.0(eslint-plugin-vue@9.33.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.4.5) + specifier: ^14.8.0 + version: 14.8.0(eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.4(jiti@1.21.7))(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@1.21.7))))(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) '@vue/tsconfig': - specifier: ^0.5.1 - version: 0.5.1 + specifier: ^0.7.0 + version: 0.7.0(typescript@5.8.3)(vue@3.5.38(typescript@5.8.3)) autoprefixer: - specifier: ^10.4.20 - version: 10.4.21(postcss@8.5.3) + specifier: ^10.5.0 + version: 10.5.0(postcss@8.5.15) eslint: - specifier: ^8.57.1 - version: 8.57.1 + specifier: ^9.39.4 + version: 9.39.4(jiti@1.21.7) eslint-plugin-vue: - specifier: ^9.28.0 - version: 9.33.0(eslint@8.57.1) + specifier: ^10.9.2 + version: 10.9.2(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.4(jiti@1.21.7))(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@1.21.7))) glob: - specifier: ^11.0.0 - version: 11.0.1 + specifier: ^13.0.6 + version: 13.0.6 npm-run-all2: - specifier: ^6.2.3 - version: 6.2.6 + specifier: ^8.0.4 + version: 8.0.4 postcss: - specifier: ^8.4.47 - version: 8.5.3 + specifier: ^8.5.15 + version: 8.5.15 prettier: - specifier: ^3.3.3 - version: 3.5.3 + specifier: ^3.8.4 + version: 3.8.4 rimraf: - specifier: ^6.0.1 - version: 6.0.1 + specifier: ^6.1.3 + version: 6.1.3 tailwindcss: - specifier: ^3.4.13 - version: 3.4.17 + specifier: ^3.4.19 + version: 3.4.19 typescript: - specifier: ~5.4.5 - version: 5.4.5 + specifier: ~5.8.3 + version: 5.8.3 vite: - specifier: ^5.4.7 - version: 5.4.18(@types/node@20.17.30) - vite-plugin-vue-devtools: - specifier: ^7.4.6 - version: 7.7.5(rollup@4.40.0)(vite@5.4.18(@types/node@20.17.30))(vue@3.5.13(typescript@5.4.5)) + specifier: ^7.3.5 + version: 7.3.5(@types/node@24.13.2)(jiti@1.21.7) vue-tsc: - specifier: ^2.1.6 - version: 2.2.10(typescript@5.4.5) + specifier: ^3.3.5 + version: 3.3.5(typescript@5.8.3) packages: @@ -121,347 +142,364 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - - '@antfu/utils@0.7.10': - resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} - - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.8': - resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} - '@babel/core@7.26.10': - resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.27.0': - resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==} + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.25.9': - resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.0': - resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==} + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.27.0': - resolution: {integrity: sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==} + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0 + '@babel/core': 7.29.7 - '@babel/helper-member-expression-to-functions@7.25.9': - resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.25.9': - resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.26.0': - resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0 + '@babel/core': 7.29.7 - '@babel/helper-optimise-call-expression@7.25.9': - resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.26.5': - resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} engines: {node: '>=6.9.0'} - '@babel/helper-replace-supers@7.26.5': - resolution: {integrity: sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==} + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0 + '@babel/core': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers@7.25.9': - resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.25.9': - resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.27.0': - resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==} + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.0': - resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-proposal-decorators@7.25.9': - resolution: {integrity: sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==} + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': 7.29.7 - '@babel/plugin-syntax-decorators@7.25.9': - resolution: {integrity: sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==} + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': 7.29.7 - '@babel/plugin-syntax-import-attributes@7.26.0': - resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} + '@babel/plugin-transform-typescript@7.29.7': + resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': 7.29.7 - '@babel/plugin-syntax-jsx@7.25.9': - resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.25.9': - resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.27.0': - resolution: {integrity: sha512-fRGGjO2UEGPjvEcyAZXRXAS8AfdaQoq7HnxAbJoAoW10B9xOKesmmndJv+Sym2a+9FHWZ9KbyyLCe9s0Sn5jtg==} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/template@7.27.0': - resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.27.0': - resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.27.0': - resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} - engines: {node: '>=6.9.0'} - - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.6.1': - resolution: {integrity: sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==} + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.18.0': - resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@8.57.1': - resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@humanwhocodes/config-array@0.13.0': - resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@intlify/core-base@9.14.5': + resolution: {integrity: sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==} + engines: {node: '>= 16'} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@intlify/message-compiler@9.14.5': + resolution: {integrity: sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==} + engines: {node: '>= 16'} + + '@intlify/shared@9.14.5': + resolution: {integrity: sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==} + engines: {node: '>= 16'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -475,368 +513,379 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@pkgr/core@0.2.4': - resolution: {integrity: sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@pkgr/core@0.3.6': + resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} + engines: {node: ^14.18.0 || >=16.0.0} - '@polka/url@1.0.0-next.29': - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@rollup/pluginutils@5.1.4': - resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/rollup-android-arm-eabi@4.40.0': - resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==} + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.40.0': - resolution: {integrity: sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==} + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.40.0': - resolution: {integrity: sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==} + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.40.0': - resolution: {integrity: sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==} + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.40.0': - resolution: {integrity: sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==} + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.40.0': - resolution: {integrity: sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==} + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.40.0': - resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==} + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.40.0': - resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==} + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.40.0': - resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==} + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.40.0': - resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==} + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loongarch64-gnu@4.40.0': - resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==} + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': - resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==} + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.40.0': - resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==} + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.40.0': - resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==} + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.40.0': - resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==} + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.40.0': - resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==} + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.40.0': - resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==} + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-win32-arm64-msvc@4.40.0': - resolution: {integrity: sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==} + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.40.0': - resolution: {integrity: sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==} + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.40.0': - resolution: {integrity: sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==} + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} cpu: [x64] os: [win32] - '@rushstack/eslint-patch@1.11.0': - resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==} - - '@sec-ant/readable-stream@0.4.1': - resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} + cpu: [x64] + os: [win32] - '@sindresorhus/merge-streams@4.0.0': - resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} - engines: {node: '>=18'} + '@rushstack/eslint-patch@1.16.1': + resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==} - '@tailwindcss/typography@0.5.16': - resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==} + '@tailwindcss/typography@0.5.20': + resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==} peerDependencies: - tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + tailwindcss: '>=3.0.0 || >=4.0.0 || insiders' - '@tsconfig/node20@20.1.5': - resolution: {integrity: sha512-Vm8e3WxDTqMGPU4GATF9keQAIy1Drd7bPwlgzKJnZtoOsTm1tduUTbDjg0W5qERvGuxPI2h9RbMufH0YdfBylA==} + '@tsconfig/node20@20.1.9': + resolution: {integrity: sha512-IjlTv1RsvnPtUcjTqtVsZExKVq+KQx4g5pCP5tI7rAs6Xesl2qFwSz/tPDBC4JajkL/MlezBu3gPUwqRHl+RIg==} - '@types/estree@1.0.7': - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/node@20.17.30': - resolution: {integrity: sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/file-saver@2.0.7': + resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@24.13.2': + resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} '@types/spark-md5@3.0.5': resolution: {integrity: sha512-lWf05dnD42DLVKQJZrDHtWFidcLrHuip01CtnC2/S6AMhX4t9ZlEUj4iuRlAnts0PQk7KESOqKxeGE/b6sIPGg==} - '@typescript-eslint/eslint-plugin@7.18.0': - resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@typescript-eslint/parser@7.18.0': - resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/eslint-plugin@8.61.1': + resolution: {integrity: sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser': ^8.61.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@7.18.0': - resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/parser@8.61.1': + resolution: {integrity: sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@7.18.0': - resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/project-service@8.61.1': + resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@7.18.0': - resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/scope-manager@8.61.1': + resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@7.18.0': - resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/tsconfig-utils@8.61.1': + resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.61.1': + resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.61.1': + resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/utils@7.18.0': - resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/typescript-estree@8.61.1': + resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@7.18.0': - resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/utils@8.61.1': + resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@typescript-eslint/visitor-keys@8.61.1': + resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitejs/plugin-vue-jsx@4.1.2': - resolution: {integrity: sha512-4Rk0GdE0QCdsIkuMmWeg11gmM4x8UmTnZR/LWPm7QJ7+BsK4tq08udrN0isrrWqz5heFy9HLV/7bOLgFS8hUjA==} - engines: {node: ^18.0.0 || >=20.0.0} + '@vitejs/plugin-vue-jsx@5.1.5': + resolution: {integrity: sha512-jIAsvHOEtWpslLOI2MeElGFxH7M8pM83BU/Tor4RLyiwH0FM4nUW3xdvbw20EeU9wc5IspQwMq225K3CMnJEpA==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^5.0.0 || ^6.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 vue: ^3.0.0 - '@vitejs/plugin-vue@5.2.3': - resolution: {integrity: sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg==} - engines: {node: ^18.0.0 || >=20.0.0} + '@vitejs/plugin-vue@6.0.7': + resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^5.0.0 || ^6.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 vue: ^3.2.25 - '@volar/language-core@2.4.12': - resolution: {integrity: sha512-RLrFdXEaQBWfSnYGVxvR2WrO6Bub0unkdHYIdC31HzIEqATIuuhRRzYu76iGPZ6OtA4Au1SnW0ZwIqPP217YhA==} + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} - '@volar/source-map@2.4.12': - resolution: {integrity: sha512-bUFIKvn2U0AWojOaqf63ER0N/iHIBYZPpNGogfLPQ68F5Eet6FnLlyho7BS0y2HJ1jFhSif7AcuTx1TqsCzRzw==} + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} - '@volar/typescript@2.4.12': - resolution: {integrity: sha512-HJB73OTJDgPc80K30wxi3if4fSsZZAOScbj2fcicMuOPoOkcf9NNAINb33o+DzhBdF9xTKC1gnPmIRDous5S0g==} + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} - '@vue/babel-helper-vue-transform-on@1.4.0': - resolution: {integrity: sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==} + '@vue/babel-helper-vue-transform-on@2.0.1': + resolution: {integrity: sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==} - '@vue/babel-plugin-jsx@1.4.0': - resolution: {integrity: sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==} + '@vue/babel-plugin-jsx@2.0.1': + resolution: {integrity: sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': 7.29.7 peerDependenciesMeta: '@babel/core': optional: true - '@vue/babel-plugin-resolve-type@1.4.0': - resolution: {integrity: sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==} + '@vue/babel-plugin-resolve-type@2.0.1': + resolution: {integrity: sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==} peerDependencies: - '@babel/core': ^7.0.0-0 - - '@vue/compiler-core@3.5.13': - resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} + '@babel/core': 7.29.7 - '@vue/compiler-dom@3.5.13': - resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} + '@vue/compiler-core@3.5.38': + resolution: {integrity: sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==} - '@vue/compiler-sfc@3.5.13': - resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} + '@vue/compiler-dom@3.5.38': + resolution: {integrity: sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==} - '@vue/compiler-ssr@3.5.13': - resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} + '@vue/compiler-sfc@3.5.38': + resolution: {integrity: sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==} - '@vue/compiler-vue2@2.7.16': - resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + '@vue/compiler-ssr@3.5.38': + resolution: {integrity: sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==} '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - '@vue/devtools-core@7.7.5': - resolution: {integrity: sha512-ElKr0NDor57gVaT+gMQ8kcVP4uFGqHcxuuQndW/rPwh6aHWvEcUL3sxL8cEk+e1Rdt28kS88erpsiIMO6hEENQ==} - peerDependencies: - vue: ^3.0.0 + '@vue/devtools-api@7.7.9': + resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} - '@vue/devtools-kit@7.7.5': - resolution: {integrity: sha512-S9VAVJYVAe4RPx2JZb9ZTEi0lqTySz2CBeF0wHT5D3dkTLnT9yMMGegKNl4b2EIELwLSkcI9bl2qp0/jW+upqA==} + '@vue/devtools-kit@7.7.9': + resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} - '@vue/devtools-shared@7.7.5': - resolution: {integrity: sha512-QBjG72RfpM0DKtpns2RZOxBltO226kOAls9e4Lri6YxS2gWTgL0H+wj1R2K76lxxIeOrqo4+2Ty6RQnzv+WSTQ==} + '@vue/devtools-shared@7.7.9': + resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} - '@vue/eslint-config-prettier@9.0.0': - resolution: {integrity: sha512-z1ZIAAUS9pKzo/ANEfd2sO+v2IUalz7cM/cTLOZ7vRFOPk5/xuRKQteOu1DErFLAh/lYGXMVZ0IfYKlyInuDVg==} + '@vue/eslint-config-prettier@10.2.0': + resolution: {integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==} peerDependencies: - eslint: '>= 8.0.0' + eslint: '>= 8.21.0' prettier: '>= 3.0.0' - '@vue/eslint-config-typescript@13.0.0': - resolution: {integrity: sha512-MHh9SncG/sfqjVqjcuFLOLD6Ed4dRAis4HNt0dXASeAuLqIAx4YMB1/m2o4pUKK1vCt8fUvYG8KKX2Ot3BVZTg==} - engines: {node: ^18.18.0 || >=20.0.0} + '@vue/eslint-config-typescript@14.8.0': + resolution: {integrity: sha512-yIquzhXH7ZsrwSSm+rYvoGCRY6wcuF4qBi76e0l7hHLq7YU0f9aC+RcR5fL+XJNfmBZxgX5cVl4sppt4x7ZCBg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 - eslint-plugin-vue: ^9.0.0 - typescript: '>=4.7.4' + eslint: ^9.10.0 || ^10.0.0 + eslint-plugin-vue: ^9.28.0 || ^10.0.0 + typescript: '>=4.8.4' peerDependenciesMeta: typescript: optional: true - '@vue/language-core@2.2.10': - resolution: {integrity: sha512-+yNoYx6XIKuAO8Mqh1vGytu8jkFEOH5C8iOv3i8Z/65A7x9iAOXA97Q+PqZ3nlm2lxf5rOJuIGI/wDtx/riNYw==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@vue/language-core@3.3.5': + resolution: {integrity: sha512-UkKu5nhX89fg4VhlG/FOeI10G3cj/7radKT/cy9BT4Q9qJmJlSTAc/dP63Xqs29aypN4f39xUV6PsLNk/dcD6g==} - '@vue/reactivity@3.5.13': - resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} + '@vue/reactivity@3.5.38': + resolution: {integrity: sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==} - '@vue/runtime-core@3.5.13': - resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} + '@vue/runtime-core@3.5.38': + resolution: {integrity: sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==} - '@vue/runtime-dom@3.5.13': - resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==} + '@vue/runtime-dom@3.5.38': + resolution: {integrity: sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==} - '@vue/server-renderer@3.5.13': - resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==} + '@vue/server-renderer@3.5.38': + resolution: {integrity: sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==} peerDependencies: - vue: 3.5.13 + vue: 3.5.38 - '@vue/shared@3.5.13': - resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} + '@vue/shared@3.5.38': + resolution: {integrity: sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==} - '@vue/tsconfig@0.5.1': - resolution: {integrity: sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==} + '@vue/tsconfig@0.7.0': + resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==} + peerDependencies: + typescript: 5.x + vue: ^3.4.0 + peerDependenciesMeta: + typescript: + optional: true + vue: + optional: true acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} engines: {node: '>=0.4.0'} hasBin: true - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - alien-signals@1.0.13: - resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} - engines: {node: '>=12'} + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} any-promise@1.3.0: @@ -852,55 +901,51 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - autoprefixer@10.4.21: - resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + autoprefixer@10.5.0: + resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: postcss: ^8.1.0 - axios@1.8.4: - resolution: {integrity: sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==} + axios@1.18.0: + resolution: {integrity: sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + baseline-browser-mapping@2.11.21: + resolution: {integrity: sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==} + engines: {node: '>=6.0.0'} + hasBin: true binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - birpc@2.3.0: - resolution: {integrity: sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==} + birpc@2.9.0: + resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.4: - resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + browserslist@4.28.9: + resolution: {integrity: sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bundle-name@4.1.0: - resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} - engines: {node: '>=18'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -913,8 +958,11 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001715: - resolution: {integrity: sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==} + caniuse-lite@1.0.30001799: + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -939,15 +987,15 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - copy-anything@3.0.5: - resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} - engines: {node: '>=12.13'} + copy-anything@4.0.5: + resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} + engines: {node: '>=18'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} @@ -958,14 +1006,11 @@ packages: engines: {node: '>=4'} hasBin: true - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - - de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -976,18 +1021,6 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - default-browser-id@5.0.0: - resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} - engines: {node: '>=18'} - - default-browser@5.2.1: - resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} - engines: {node: '>=18'} - - define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -995,40 +1028,23 @@ packages: didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} + dompurify@3.4.11: + resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==} dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - electron-to-chromium@1.5.140: - resolution: {integrity: sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q==} + electron-to-chromium@1.5.422: + resolution: {integrity: sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==} - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} - error-stack-parser-es@0.1.5: - resolution: {integrity: sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1037,17 +1053,17 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: @@ -1058,14 +1074,14 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-prettier@9.1.0: - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-plugin-prettier@5.2.6: - resolution: {integrity: sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==} + eslint-plugin-prettier@5.5.6: + resolution: {integrity: sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -1078,32 +1094,60 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-vue@9.33.0: - resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==} - engines: {node: ^14.17.0 || >=16.0.0} + eslint-plugin-vue@10.9.2: + resolution: {integrity: sha512-4g7ZP3pYcuqd7Zp0pzUKcos0W+RkjBz4EGdhJ92FcYk6v03Ti/GK5NwjgsjxHK+98eXDbHeK7VtX1az7/8doZA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + '@typescript-eslint/parser': ^7.0.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + vue-eslint-parser: ^10.3.0 + peerDependenciesMeta: + '@stylistic/eslint-plugin': + optional: true + '@typescript-eslint/parser': + optional: true - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint@8.57.1: - resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -1121,10 +1165,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - execa@9.5.2: - resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} - engines: {node: ^18.19.0 || >=20.5.0} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1141,16 +1181,21 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - figures@6.1.0: - resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} - engines: {node: '>=18'} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} file-saver@2.0.5: resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} @@ -1163,15 +1208,15 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} - follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -1179,23 +1224,12 @@ packages: debug: optional: true - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} engines: {node: '>= 6'} - fraction.js@4.3.7: - resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - - fs-extra@11.3.0: - resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} - engines: {node: '>=14.14'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -1217,10 +1251,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@9.0.1: - resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} - engines: {node: '>=18'} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1229,41 +1259,18 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - - glob@11.0.1: - resolution: {integrity: sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==} - engines: {node: 20 || >=22} - hasBin: true - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1276,25 +1283,28 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} - human-signals@8.0.1: - resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} - engines: {node: '>=18.18.0'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1303,10 +1313,6 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -1314,69 +1320,35 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} engines: {node: '>= 0.4'} - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - - is-stream@4.0.1: - resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + is-what@5.5.0: + resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} engines: {node: '>=18'} - is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} - engines: {node: '>=18'} - - is-what@4.1.16: - resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} - engines: {node: '>=12.13'} - - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} - engines: {node: '>=16'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jackspeak@4.1.0: - resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==} - engines: {node: 20 || >=22} + isexe@3.1.5: + resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} + engines: {node: '>=18'} jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} @@ -1385,8 +1357,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true jsesc@3.1.0: @@ -1397,9 +1369,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + json-parse-even-better-errors@4.0.0: + resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==} + engines: {node: ^18.17.0 || >=20.5.0} json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -1412,19 +1384,19 @@ packages: engines: {node: '>=6'} hasBin: true - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kolorist@1.8.0: - resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -1436,39 +1408,28 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.castarray@4.4.0: - resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@11.1.0: - resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} engines: {node: 20 || >=22} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-vue-next@0.445.0: - resolution: {integrity: sha512-+JWAiyLliw+hprRsxv+Og/Vba3VoCyPLK1GHyd/1nYrVH0bySlPDdobbv2DxhMfGe+3y3yOOk0aSCligFy3Vkg==} + lucide-vue-next@0.535.0: + resolution: {integrity: sha512-B79vTrPyVgp+yfnQe0xU0OcA5+xa9fz24psR8z01NZhbmn5VoxfYMwRm5E4iSk9mCRYgyFWqtx8DMp/bydiTcg==} + deprecated: Package deprecated. Please use @lucide/vue instead. peerDependencies: vue: '>=3.0.1' - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - marked@14.1.4: - resolution: {integrity: sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==} - engines: {node: '>= 18'} + marked@16.4.2: + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + engines: {node: '>= 20'} hasBin: true math-intrinsics@1.1.0: @@ -1495,28 +1456,20 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - minimatch@10.0.1: - resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} - engines: {node: 20 || >=22} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} - engines: {node: '>=10'} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1526,43 +1479,31 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.13: + resolution: {integrity: sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.1.5: - resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} - engines: {node: ^18 || >=20} - hasBin: true - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.54: + resolution: {integrity: sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==} + engines: {node: '>=18'} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - - npm-normalize-package-bin@3.0.1: - resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-normalize-package-bin@4.0.0: + resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==} + engines: {node: ^18.17.0 || >=20.5.0} - npm-run-all2@6.2.6: - resolution: {integrity: sha512-tkyb4pc0Zb0oOswCb5tORPk9MvVL6gcDq1cMItQHmsbVk1skk7YF6cH+UU2GxeNLHMuk6wFEOSmEmJ2cnAK1jg==} - engines: {node: ^14.18.0 || ^16.13.0 || >=18.0.0, npm: '>= 8'} + npm-run-all2@8.0.4: + resolution: {integrity: sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==} + engines: {node: ^20.5.0 || >=22.0.0, npm: '>= 10'} hasBin: true - npm-run-path@6.0.0: - resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} - engines: {node: '>=18'} - nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -1574,13 +1515,6 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - open@10.1.1: - resolution: {integrity: sha512-zy1wx4+P3PfhXSEPJNtZmJXfhkkIaxU1VauWIrDZw1O7uJRDRJtKr9n3Ic4NgbA16KyOxOXO2ng9gYwCdXuSXA==} - engines: {node: '>=18'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1596,14 +1530,13 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-ms@4.0.0: - resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} - engines: {node: '>=18'} - path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -1611,35 +1544,16 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-scurry@2.0.0: - resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} - engines: {node: 20 || >=22} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} @@ -1647,16 +1561,16 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} - pidtree@0.6.0: - resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + pidtree@0.6.1: + resolution: {integrity: sha512-e0F9AOF1JMrCfBsyJOwU9lNvQ0WtXTq0j/4jk0BQ5JSI9VAybPXmDpPRw/2FQ3e5d3ZFN1mLh7jW99m/jjaptw==} engines: {node: '>=0.10'} hasBin: true @@ -1664,11 +1578,11 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - pinia@2.3.1: - resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==} + pinia@3.0.4: + resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==} peerDependencies: - typescript: '>=4.4.4' - vue: ^2.7.0 || ^3.5.11 + typescript: '>=4.5.0' + vue: ^3.5.11 peerDependenciesMeta: typescript: optional: true @@ -1683,22 +1597,28 @@ packages: peerDependencies: postcss: ^8.0.0 - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} peerDependencies: + jiti: '>=1.21.0' postcss: '>=8.0.9' - ts-node: '>=9.0.0' + tsx: ^4.8.1 + yaml: 2.9.0 peerDependenciesMeta: + jiti: + optional: true postcss: optional: true - ts-node: + tsx: + optional: true + yaml: optional: true postcss-nested@6.2.0: @@ -1711,43 +1631,47 @@ packages: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} engines: {node: '>=4'} - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + postcss-selector-parser@6.1.4: + resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==} + engines: {node: '>=4'} + + postcss-selector-parser@7.1.4: + resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} engines: {node: '>=4'} postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} engines: {node: '>=6.0.0'} - prettier@3.5.3: - resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + prettier@3.8.4: + resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} engines: {node: '>=14'} hasBin: true - pretty-ms@9.2.0: - resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} - engines: {node: '>=18'} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qrcode.vue@3.6.0: - resolution: {integrity: sha512-vQcl2fyHYHMjDO1GguCldJxepq2izQjBkDEEu9NENgfVKP6mv/e2SU62WbqYHGwTgWXLhxZ1NCD1dAZKHQq1fg==} + qrcode.vue@3.10.0: + resolution: {integrity: sha512-1bjeBds9hRKMszZuBuYQZor9HdJYWtb0S44HG1JofZ9uicpJpdF+TtDvqo3+2ZlH0k80WVIkmiKB4hq0/N6/rA==} peerDependencies: vue: ^3.0.0 @@ -1757,9 +1681,12 @@ packages: read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - read-package-json-fast@3.0.2: - resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + read-package-json-fast@4.0.0: + resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==} + engines: {node: ^18.17.0 || >=20.5.0} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} @@ -1769,8 +1696,8 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true @@ -1781,37 +1708,34 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - rimraf@6.0.1: - resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} + rimraf@6.1.3: + resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} engines: {node: 20 || >=22} hasBin: true - rollup@4.40.0: - resolution: {integrity: sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==} + rollup@4.62.2: + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-applescript@7.0.0: - resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} - engines: {node: '>=18'} - run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.1: - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1820,22 +1744,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.2: - resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} + shell-quote@1.8.4: + resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==} engines: {node: '>= 0.4'} - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - sirv@3.0.1: - resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} - engines: {node: '>=18'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1847,37 +1759,20 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - - strip-final-newline@4.0.0: - resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} - engines: {node: '>=18'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true - superjson@2.2.2: - resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} + superjson@2.2.6: + resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} engines: {node: '>=16'} supports-color@7.2.0: @@ -1888,18 +1783,15 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - synckit@0.11.4: - resolution: {integrity: sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ==} + synckit@0.11.13: + resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} engines: {node: ^14.18.0 || >=16.0.0} - tailwindcss@3.4.17: - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + tailwindcss@3.4.19: + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} engines: {node: '>=14.0.0'} hasBin: true - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -1907,52 +1799,44 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - - ts-api-utils@1.4.3: - resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} - engines: {node: '>=16'} + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} peerDependencies: - typescript: '>=4.2.0' + typescript: '>=4.8.4' ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} + typescript-eslint@8.61.1: + resolution: {integrity: sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - - unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + update-browserslist-db@1.3.2: + resolution: {integrity: sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -1963,48 +1847,27 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite-hot-client@2.0.4: - resolution: {integrity: sha512-W9LOGAyGMrbGArYJN4LBCdOC5+Zwh7dHvOHC0KmGKkJhsOzaKbpo/jEjpPKVHIW0/jBWj8RZG0NUxfgA8BxgAg==} - peerDependencies: - vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 - - vite-plugin-inspect@0.8.9: - resolution: {integrity: sha512-22/8qn+LYonzibb1VeFZmISdVao5kC22jmEKm24vfFE8siEn47EpVcCLYMv6iKOYMJfjSvSJfueOwcFCkUnV3A==} - engines: {node: '>=14'} - peerDependencies: - '@nuxt/kit': '*' - vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.1 - peerDependenciesMeta: - '@nuxt/kit': - optional: true - - vite-plugin-vue-devtools@7.7.5: - resolution: {integrity: sha512-cSlQYI1E+8d0qubBg70suTBbXMFbTHLn7vLPYUPK9GjNNJ0nw+Yks0ZLOAp7/+PjmqSpN5fK1taor6HeAjKb1g==} - engines: {node: '>=v14.21.3'} - peerDependencies: - vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 - - vite-plugin-vue-inspector@5.3.1: - resolution: {integrity: sha512-cBk172kZKTdvGpJuzCCLg8lJ909wopwsu3Ve9FsL1XsnLBiRT9U3MePcqrgGHgCX2ZgkqZmAGR8taxw+TV6s7A==} - peerDependencies: - vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 - - vite@5.4.18: - resolution: {integrity: sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@7.3.5: + resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: 2.9.0 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -2019,40 +1882,40 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} - vue-demi@0.14.10: - resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} - engines: {node: '>=12'} - hasBin: true + vue-eslint-parser@10.4.1: + resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@vue/composition-api': ^1.0.0-rc.1 - vue: ^3.0.0-0 || ^2.6.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - vue-eslint-parser@9.4.3: - resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} - engines: {node: ^14.17.0 || >=16.0.0} + vue-i18n@9.14.5: + resolution: {integrity: sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==} + engines: {node: '>= 16'} + deprecated: v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html peerDependencies: - eslint: '>=6.0.0' + vue: ^3.0.0 - vue-router@4.5.0: - resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==} + vue-router@4.6.4: + resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==} peerDependencies: - vue: ^3.2.0 + vue: ^3.5.0 - vue-tsc@2.2.10: - resolution: {integrity: sha512-jWZ1xSaNbabEV3whpIDMbjVSVawjAyW+x1n3JeGQo7S0uv2n9F/JMgWW90tGWNFRKya4YwKMZgCtr0vRAM7DeQ==} + vue-tsc@3.3.5: + resolution: {integrity: sha512-Rzh/G2MmNlMSAMTiQEjDrsb4dgB/jbtEM47rVN2NtidF1dfb/q4w4QvpQBtW5+y3y5H27Hjh7deVwk+YB02fNg==} hasBin: true peerDependencies: typescript: '>=5.0.0' - vue@3.5.13: - resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} + vue@3.5.38: + resolution: {integrity: sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -2064,26 +1927,15 @@ packages: engines: {node: '>= 8'} hasBin: true - which@3.0.1: - resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} hasBin: true word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} @@ -2091,358 +1943,351 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.7.1: - resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} - engines: {node: '>= 14'} - hasBin: true - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yoctocolors@2.1.1: - resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} - engines: {node: '>=18'} - snapshots: '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - - '@antfu/utils@0.7.10': {} - - '@babel/code-frame@7.26.2': + '@babel/code-frame@7.29.7': dependencies: - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.8': {} + '@babel/compat-data@7.29.7': {} - '@babel/core@7.26.10': + '@babel/core@7.29.7': dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.27.0 - '@babel/helper-compilation-targets': 7.27.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) - '@babel/helpers': 7.27.0 - '@babel/parser': 7.27.0 - '@babel/template': 7.27.0 - '@babel/traverse': 7.27.0 - '@babel/types': 7.27.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.0 + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.27.0': + '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.25.9': + '@babel/helper-annotate-as-pure@7.29.7': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.29.7 - '@babel/helper-compilation-targets@7.27.0': + '@babel/helper-compilation-targets@7.29.7': dependencies: - '@babel/compat-data': 7.26.8 - '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.4 + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.9 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.27.0(@babel/core@7.26.10)': + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.10 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.10) - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/traverse': 7.27.0 + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/traverse': 7.29.7 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-member-expression-to-functions@7.25.9': + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-member-expression-to-functions@7.29.7': dependencies: - '@babel/traverse': 7.27.0 - '@babel/types': 7.27.0 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.25.9': + '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.27.0 - '@babel/types': 7.27.0 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.10 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.27.0 + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.25.9': + '@babel/helper-optimise-call-expression@7.29.7': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.29.7 - '@babel/helper-plugin-utils@7.26.5': {} + '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-replace-supers@7.26.5(@babel/core@7.26.10)': + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.10 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/traverse': 7.27.0 + '@babel/core': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': dependencies: - '@babel/traverse': 7.27.0 - '@babel/types': 7.27.0 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.25.9': {} - - '@babel/helper-validator-identifier@7.25.9': {} - - '@babel/helper-validator-option@7.25.9': {} - - '@babel/helpers@7.27.0': - dependencies: - '@babel/template': 7.27.0 - '@babel/types': 7.27.0 - - '@babel/parser@7.27.0': - dependencies: - '@babel/types': 7.27.0 + '@babel/helper-string-parser@7.29.7': {} - '@babel/plugin-proposal-decorators@7.25.9(@babel/core@7.26.10)': - dependencies: - '@babel/core': 7.26.10 - '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.10) - '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-decorators': 7.25.9(@babel/core@7.26.10) - transitivePeerDependencies: - - supports-color + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/plugin-syntax-decorators@7.25.9(@babel/core@7.26.10)': - dependencies: - '@babel/core': 7.26.10 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-validator-option@7.29.7': {} - '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.10)': + '@babel/helpers@7.29.7': dependencies: - '@babel/core': 7.26.10 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.10)': + '@babel/parser@7.29.7': dependencies: - '@babel/core': 7.26.10 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/types': 7.29.7 - '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.10)': + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.10 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.10)': + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.10 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-typescript@7.27.0(@babel/core@7.26.10)': + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.10 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.10) - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.10) + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) transitivePeerDependencies: - supports-color - '@babel/template@7.27.0': + '@babel/template@7.29.7': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 - '@babel/traverse@7.27.0': + '@babel/traverse@7.29.7': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.27.0 - '@babel/parser': 7.27.0 - '@babel/template': 7.27.0 - '@babel/types': 7.27.0 - debug: 4.4.0 - globals: 11.12.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.27.0': + '@babel/types@7.29.7': dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@esbuild/aix-ppc64@0.28.1': + optional: true + + '@esbuild/android-arm64@0.28.1': + optional: true + + '@esbuild/android-arm@0.28.1': + optional: true - '@esbuild/aix-ppc64@0.21.5': + '@esbuild/android-x64@0.28.1': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/darwin-arm64@0.28.1': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/darwin-x64@0.28.1': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/freebsd-arm64@0.28.1': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/freebsd-x64@0.28.1': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/linux-arm64@0.28.1': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/linux-arm@0.28.1': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/linux-ia32@0.28.1': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-loong64@0.28.1': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-mips64el@0.28.1': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ppc64@0.28.1': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-riscv64@0.28.1': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-s390x@0.28.1': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-x64@0.28.1': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/netbsd-arm64@0.28.1': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/netbsd-x64@0.28.1': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/openbsd-arm64@0.28.1': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/openbsd-x64@0.28.1': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/openharmony-arm64@0.28.1': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/sunos-x64@0.28.1': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/win32-arm64@0.28.1': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/win32-ia32@0.28.1': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.6.1(eslint@8.57.1)': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@1.21.7))': dependencies: - eslint: 8.57.1 + eslint: 9.39.4(jiti@1.21.7) eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.18.0': + '@eslint/config-array@0.21.2': dependencies: - '@eslint/object-schema': 2.1.6 - debug: 4.4.0 - minimatch: 3.1.2 + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 transitivePeerDependencies: - supports-color - '@eslint/eslintrc@2.1.4': + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': dependencies: - ajv: 6.12.6 - debug: 4.4.0 - espree: 9.6.1 - globals: 13.24.0 + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.0 - minimatch: 3.1.2 + js-yaml: 4.2.0 + minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@8.57.1': {} + '@eslint/js@9.39.4': {} - '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@2.1.7': {} - '@humanwhocodes/config-array@0.13.0': + '@eslint/plugin-kit@0.4.1': dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.0 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.3': {} + '@humanwhocodes/retry@0.4.3': {} + + '@intlify/core-base@9.14.5': + dependencies: + '@intlify/message-compiler': 9.14.5 + '@intlify/shared': 9.14.5 - '@isaacs/cliui@8.0.2': + '@intlify/message-compiler@9.14.5': dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 + '@intlify/shared': 9.14.5 + source-map-js: 1.2.1 - '@jridgewell/gen-mapping@0.3.8': + '@intlify/shared@9.14.5': {} + + '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/set-array@1.2.1': {} + '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.25': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 '@nodelib/fs.scandir@2.1.5': dependencies: @@ -2454,458 +2299,458 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + fastq: 1.20.1 - '@pkgjs/parseargs@0.11.0': - optional: true + '@pkgr/core@0.3.6': {} - '@pkgr/core@0.2.4': {} + '@rolldown/pluginutils@1.0.1': {} - '@polka/url@1.0.0-next.29': {} + '@rollup/rollup-android-arm-eabi@4.62.2': + optional: true - '@rollup/pluginutils@5.1.4(rollup@4.40.0)': - dependencies: - '@types/estree': 1.0.7 - estree-walker: 2.0.2 - picomatch: 4.0.2 - optionalDependencies: - rollup: 4.40.0 + '@rollup/rollup-android-arm64@4.62.2': + optional: true - '@rollup/rollup-android-arm-eabi@4.40.0': + '@rollup/rollup-darwin-arm64@4.62.2': optional: true - '@rollup/rollup-android-arm64@4.40.0': + '@rollup/rollup-darwin-x64@4.62.2': optional: true - '@rollup/rollup-darwin-arm64@4.40.0': + '@rollup/rollup-freebsd-arm64@4.62.2': optional: true - '@rollup/rollup-darwin-x64@4.40.0': + '@rollup/rollup-freebsd-x64@4.62.2': optional: true - '@rollup/rollup-freebsd-arm64@4.40.0': + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': optional: true - '@rollup/rollup-freebsd-x64@4.40.0': + '@rollup/rollup-linux-arm-musleabihf@4.62.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.40.0': + '@rollup/rollup-linux-arm64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.40.0': + '@rollup/rollup-linux-arm64-musl@4.62.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.40.0': + '@rollup/rollup-linux-loong64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.40.0': + '@rollup/rollup-linux-loong64-musl@4.62.2': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.40.0': + '@rollup/rollup-linux-ppc64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': + '@rollup/rollup-linux-ppc64-musl@4.62.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.40.0': + '@rollup/rollup-linux-riscv64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.40.0': + '@rollup/rollup-linux-riscv64-musl@4.62.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.40.0': + '@rollup/rollup-linux-s390x-gnu@4.62.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.40.0': + '@rollup/rollup-linux-x64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-x64-musl@4.40.0': + '@rollup/rollup-linux-x64-musl@4.62.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.40.0': + '@rollup/rollup-openbsd-x64@4.62.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.40.0': + '@rollup/rollup-openharmony-arm64@4.62.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.40.0': + '@rollup/rollup-win32-arm64-msvc@4.62.2': optional: true - '@rushstack/eslint-patch@1.11.0': {} + '@rollup/rollup-win32-ia32-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.2': + optional: true - '@sec-ant/readable-stream@0.4.1': {} + '@rollup/rollup-win32-x64-msvc@4.62.2': + optional: true - '@sindresorhus/merge-streams@4.0.0': {} + '@rushstack/eslint-patch@1.16.1': {} - '@tailwindcss/typography@0.5.16(tailwindcss@3.4.17)': + '@tailwindcss/typography@0.5.20(tailwindcss@3.4.19)': dependencies: - lodash.castarray: 4.4.0 - lodash.isplainobject: 4.0.6 - lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.17 + tailwindcss: 3.4.19 + + '@tsconfig/node20@20.1.9': {} + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.9': {} - '@tsconfig/node20@20.1.5': {} + '@types/file-saver@2.0.7': {} - '@types/estree@1.0.7': {} + '@types/json-schema@7.0.15': {} - '@types/node@20.17.30': + '@types/node@24.13.2': dependencies: - undici-types: 6.19.8 + undici-types: 7.18.2 '@types/spark-md5@3.0.5': {} - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1)(typescript@5.4.5)': + '@types/trusted-types@2.0.7': + optional: true + + '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.4.5) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.4.5) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 8.57.1 - graphemer: 1.4.0 - ignore: 5.3.2 + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.61.1 + eslint: 9.39.4(jiti@1.21.7) + ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 1.4.3(typescript@5.4.5) - optionalDependencies: - typescript: 5.4.5 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.4.5)': + '@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.0 - eslint: 8.57.1 - optionalDependencies: - typescript: 5.4.5 + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.61.1 + debug: 4.4.3 + eslint: 9.39.4(jiti@1.21.7) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.18.0': + '@typescript-eslint/project-service@8.61.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.8.3) + '@typescript-eslint/types': 8.61.1 + debug: 4.4.3 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color - '@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.4.5)': + '@typescript-eslint/scope-manager@8.61.1': dependencies: - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.4.5) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.4.5) - debug: 4.4.0 - eslint: 8.57.1 - ts-api-utils: 1.4.3(typescript@5.4.5) - optionalDependencies: - typescript: 5.4.5 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 + + '@typescript-eslint/tsconfig-utils@8.61.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) + debug: 4.4.3 + eslint: 9.39.4(jiti@1.21.7) + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@7.18.0': {} + '@typescript-eslint/types@8.61.1': {} - '@typescript-eslint/typescript-estree@7.18.0(typescript@5.4.5)': + '@typescript-eslint/typescript-estree@8.61.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.0 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.1 - ts-api-utils: 1.4.3(typescript@5.4.5) - optionalDependencies: - typescript: 5.4.5 + '@typescript-eslint/project-service': 8.61.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.8.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.4.5)': + '@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.6.1(eslint@8.57.1) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.4.5) - eslint: 8.57.1 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.8.3) + eslint: 9.39.4(jiti@1.21.7) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/visitor-keys@7.18.0': + '@typescript-eslint/visitor-keys@8.61.1': dependencies: - '@typescript-eslint/types': 7.18.0 - eslint-visitor-keys: 3.4.3 - - '@ungap/structured-clone@1.3.0': {} + '@typescript-eslint/types': 8.61.1 + eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-vue-jsx@4.1.2(vite@5.4.18(@types/node@20.17.30))(vue@3.5.13(typescript@5.4.5))': + '@vitejs/plugin-vue-jsx@5.1.5(vite@7.3.5(@types/node@24.13.2)(jiti@1.21.7))(vue@3.5.38(typescript@5.8.3))': dependencies: - '@babel/core': 7.26.10 - '@babel/plugin-transform-typescript': 7.27.0(@babel/core@7.26.10) - '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.26.10) - vite: 5.4.18(@types/node@20.17.30) - vue: 3.5.13(typescript@5.4.5) + '@babel/core': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.1 + '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7) + vite: 7.3.5(@types/node@24.13.2)(jiti@1.21.7) + vue: 3.5.38(typescript@5.8.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.2.3(vite@5.4.18(@types/node@20.17.30))(vue@3.5.13(typescript@5.4.5))': + '@vitejs/plugin-vue@6.0.7(vite@7.3.5(@types/node@24.13.2)(jiti@1.21.7))(vue@3.5.38(typescript@5.8.3))': dependencies: - vite: 5.4.18(@types/node@20.17.30) - vue: 3.5.13(typescript@5.4.5) + '@rolldown/pluginutils': 1.0.1 + vite: 7.3.5(@types/node@24.13.2)(jiti@1.21.7) + vue: 3.5.38(typescript@5.8.3) - '@volar/language-core@2.4.12': + '@volar/language-core@2.4.28': dependencies: - '@volar/source-map': 2.4.12 + '@volar/source-map': 2.4.28 - '@volar/source-map@2.4.12': {} + '@volar/source-map@2.4.28': {} - '@volar/typescript@2.4.12': + '@volar/typescript@2.4.28': dependencies: - '@volar/language-core': 2.4.12 + '@volar/language-core': 2.4.28 path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue/babel-helper-vue-transform-on@1.4.0': {} + '@vue/babel-helper-vue-transform-on@2.0.1': {} - '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.26.10)': + '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.7)': dependencies: - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10) - '@babel/template': 7.27.0 - '@babel/traverse': 7.27.0 - '@babel/types': 7.27.0 - '@vue/babel-helper-vue-transform-on': 1.4.0 - '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.26.10) - '@vue/shared': 3.5.13 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@vue/babel-helper-vue-transform-on': 2.0.1 + '@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.29.7) + '@vue/shared': 3.5.38 optionalDependencies: - '@babel/core': 7.26.10 + '@babel/core': 7.29.7 transitivePeerDependencies: - supports-color - '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.26.10)': + '@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.29.7)': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/core': 7.26.10 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/parser': 7.27.0 - '@vue/compiler-sfc': 3.5.13 + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/parser': 7.29.7 + '@vue/compiler-sfc': 3.5.38 transitivePeerDependencies: - supports-color - '@vue/compiler-core@3.5.13': + '@vue/compiler-core@3.5.38': dependencies: - '@babel/parser': 7.27.0 - '@vue/shared': 3.5.13 - entities: 4.5.0 + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.38 + entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.13': + '@vue/compiler-dom@3.5.38': dependencies: - '@vue/compiler-core': 3.5.13 - '@vue/shared': 3.5.13 + '@vue/compiler-core': 3.5.38 + '@vue/shared': 3.5.38 - '@vue/compiler-sfc@3.5.13': + '@vue/compiler-sfc@3.5.38': dependencies: - '@babel/parser': 7.27.0 - '@vue/compiler-core': 3.5.13 - '@vue/compiler-dom': 3.5.13 - '@vue/compiler-ssr': 3.5.13 - '@vue/shared': 3.5.13 + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.38 + '@vue/compiler-dom': 3.5.38 + '@vue/compiler-ssr': 3.5.38 + '@vue/shared': 3.5.38 estree-walker: 2.0.2 - magic-string: 0.30.17 - postcss: 8.5.3 + magic-string: 0.30.21 + postcss: 8.5.15 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.13': + '@vue/compiler-ssr@3.5.38': dependencies: - '@vue/compiler-dom': 3.5.13 - '@vue/shared': 3.5.13 - - '@vue/compiler-vue2@2.7.16': - dependencies: - de-indent: 1.0.2 - he: 1.2.0 + '@vue/compiler-dom': 3.5.38 + '@vue/shared': 3.5.38 '@vue/devtools-api@6.6.4': {} - '@vue/devtools-core@7.7.5(vite@5.4.18(@types/node@20.17.30))(vue@3.5.13(typescript@5.4.5))': + '@vue/devtools-api@7.7.9': dependencies: - '@vue/devtools-kit': 7.7.5 - '@vue/devtools-shared': 7.7.5 - mitt: 3.0.1 - nanoid: 5.1.5 - pathe: 2.0.3 - vite-hot-client: 2.0.4(vite@5.4.18(@types/node@20.17.30)) - vue: 3.5.13(typescript@5.4.5) - transitivePeerDependencies: - - vite + '@vue/devtools-kit': 7.7.9 - '@vue/devtools-kit@7.7.5': + '@vue/devtools-kit@7.7.9': dependencies: - '@vue/devtools-shared': 7.7.5 - birpc: 2.3.0 + '@vue/devtools-shared': 7.7.9 + birpc: 2.9.0 hookable: 5.5.3 mitt: 3.0.1 perfect-debounce: 1.0.0 speakingurl: 14.0.1 - superjson: 2.2.2 + superjson: 2.2.6 - '@vue/devtools-shared@7.7.5': + '@vue/devtools-shared@7.7.9': dependencies: rfdc: 1.4.1 - '@vue/eslint-config-prettier@9.0.0(eslint@8.57.1)(prettier@3.5.3)': + '@vue/eslint-config-prettier@10.2.0(eslint@9.39.4(jiti@1.21.7))(prettier@3.8.4)': dependencies: - eslint: 8.57.1 - eslint-config-prettier: 9.1.0(eslint@8.57.1) - eslint-plugin-prettier: 5.2.6(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.5.3) - prettier: 3.5.3 + eslint: 9.39.4(jiti@1.21.7) + eslint-config-prettier: 10.1.8(eslint@9.39.4(jiti@1.21.7)) + eslint-plugin-prettier: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@1.21.7)))(eslint@9.39.4(jiti@1.21.7))(prettier@3.8.4) + prettier: 3.8.4 transitivePeerDependencies: - '@types/eslint' - '@vue/eslint-config-typescript@13.0.0(eslint-plugin-vue@9.33.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.4.5)': + '@vue/eslint-config-typescript@14.8.0(eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.4(jiti@1.21.7))(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@1.21.7))))(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1)(typescript@5.4.5) - '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.4.5) - eslint: 8.57.1 - eslint-plugin-vue: 9.33.0(eslint@8.57.1) - vue-eslint-parser: 9.4.3(eslint@8.57.1) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) + eslint: 9.39.4(jiti@1.21.7) + eslint-plugin-vue: 10.9.2(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.4(jiti@1.21.7))(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@1.21.7))) + fast-glob: 3.3.3 + typescript-eslint: 8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) + vue-eslint-parser: 10.4.1(eslint@9.39.4(jiti@1.21.7)) optionalDependencies: - typescript: 5.4.5 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@vue/language-core@2.2.10(typescript@5.4.5)': + '@vue/language-core@3.3.5': dependencies: - '@volar/language-core': 2.4.12 - '@vue/compiler-dom': 3.5.13 - '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.13 - alien-signals: 1.0.13 - minimatch: 9.0.5 + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.38 + '@vue/shared': 3.5.38 + alien-signals: 3.2.1 muggle-string: 0.4.1 path-browserify: 1.0.1 - optionalDependencies: - typescript: 5.4.5 + picomatch: 4.0.4 - '@vue/reactivity@3.5.13': + '@vue/reactivity@3.5.38': dependencies: - '@vue/shared': 3.5.13 + '@vue/shared': 3.5.38 - '@vue/runtime-core@3.5.13': + '@vue/runtime-core@3.5.38': dependencies: - '@vue/reactivity': 3.5.13 - '@vue/shared': 3.5.13 + '@vue/reactivity': 3.5.38 + '@vue/shared': 3.5.38 - '@vue/runtime-dom@3.5.13': + '@vue/runtime-dom@3.5.38': dependencies: - '@vue/reactivity': 3.5.13 - '@vue/runtime-core': 3.5.13 - '@vue/shared': 3.5.13 - csstype: 3.1.3 + '@vue/reactivity': 3.5.38 + '@vue/runtime-core': 3.5.38 + '@vue/shared': 3.5.38 + csstype: 3.2.3 - '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.4.5))': + '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@5.8.3))': dependencies: - '@vue/compiler-ssr': 3.5.13 - '@vue/shared': 3.5.13 - vue: 3.5.13(typescript@5.4.5) + '@vue/compiler-ssr': 3.5.38 + '@vue/shared': 3.5.38 + vue: 3.5.38(typescript@5.8.3) - '@vue/shared@3.5.13': {} + '@vue/shared@3.5.38': {} - '@vue/tsconfig@0.5.1': {} + '@vue/tsconfig@0.7.0(typescript@5.8.3)(vue@3.5.38(typescript@5.8.3))': + optionalDependencies: + typescript: 5.8.3 + vue: 3.5.38(typescript@5.8.3) - acorn-jsx@5.3.2(acorn@8.14.1): + acorn-jsx@5.3.2(acorn@8.17.0): dependencies: - acorn: 8.14.1 + acorn: 8.17.0 + + acorn@8.17.0: {} - acorn@8.14.1: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color - ajv@6.12.6: + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - alien-signals@1.0.13: {} - - ansi-regex@5.0.1: {} - - ansi-regex@6.1.0: {} + alien-signals@3.2.1: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} any-promise@1.3.0: {} anymatch@3.1.3: dependencies: normalize-path: 3.0.0 - picomatch: 2.3.1 + picomatch: 2.3.2 arg@5.0.2: {} argparse@2.0.1: {} - array-union@2.1.0: {} - asynckit@0.4.0: {} - autoprefixer@10.4.21(postcss@8.5.3): + autoprefixer@10.5.0(postcss@8.5.15): dependencies: - browserslist: 4.24.4 - caniuse-lite: 1.0.30001715 - fraction.js: 4.3.7 - normalize-range: 0.1.2 + browserslist: 4.28.9 + caniuse-lite: 1.0.30001799 + fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.3 + postcss: 8.5.15 postcss-value-parser: 4.2.0 - axios@1.8.4: + axios@1.18.0: dependencies: - follow-redirects: 1.15.9 - form-data: 4.0.2 - proxy-from-env: 1.1.0 + follow-redirects: 1.16.0 + form-data: 4.0.6 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 transitivePeerDependencies: - debug + - supports-color + + balanced-match@4.0.4: {} - balanced-match@1.0.2: {} + baseline-browser-mapping@2.11.21: {} binary-extensions@2.3.0: {} - birpc@2.3.0: {} + birpc@2.9.0: {} boolbase@1.0.0: {} - brace-expansion@1.1.11: + brace-expansion@5.0.6: dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 + balanced-match: 4.0.4 braces@3.0.3: dependencies: fill-range: 7.1.1 - browserslist@4.24.4: - dependencies: - caniuse-lite: 1.0.30001715 - electron-to-chromium: 1.5.140 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.24.4) - - bundle-name@4.1.0: + browserslist@4.28.9: dependencies: - run-applescript: 7.0.0 + baseline-browser-mapping: 2.11.21 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.422 + node-releases: 2.0.54 + update-browserslist-db: 1.3.2(browserslist@4.28.9) call-bind-apply-helpers@1.0.2: dependencies: @@ -2916,7 +2761,9 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001715: {} + caniuse-lite@1.0.30001799: {} + + caniuse-lite@1.0.30001810: {} chalk@4.1.2: dependencies: @@ -2947,13 +2794,13 @@ snapshots: commander@4.1.1: {} - concat-map@0.0.1: {} - convert-source-map@2.0.0: {} - copy-anything@3.0.5: + copy-anything@4.0.5: dependencies: - is-what: 4.1.16 + is-what: 5.5.0 + + core-util-is@1.0.3: {} cross-spawn@7.0.6: dependencies: @@ -2963,38 +2810,23 @@ snapshots: cssesc@3.0.0: {} - csstype@3.1.3: {} - - de-indent@1.0.2: {} + csstype@3.2.3: {} - debug@4.4.0: + debug@4.4.3: dependencies: ms: 2.1.3 deep-is@0.1.4: {} - default-browser-id@5.0.0: {} - - default-browser@5.2.1: - dependencies: - bundle-name: 4.1.0 - default-browser-id: 5.0.0 - - define-lazy-prop@3.0.0: {} - delayed-stream@1.0.0: {} didyoumean@1.2.2: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - dlv@1.1.3: {} - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 + dompurify@3.4.11: + optionalDependencies: + '@types/trusted-types': 2.0.7 dunder-proto@1.0.1: dependencies: @@ -3002,23 +2834,15 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - eastasianwidth@0.2.0: {} - - electron-to-chromium@1.5.140: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} + electron-to-chromium@1.5.422: {} - entities@4.5.0: {} - - error-stack-parser-es@0.1.5: {} + entities@7.0.1: {} es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -3027,122 +2851,139 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 - esbuild@0.21.5: + esbuild@0.28.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-config-prettier@9.1.0(eslint@8.57.1): + eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@1.21.7)): dependencies: - eslint: 8.57.1 + eslint: 9.39.4(jiti@1.21.7) - eslint-plugin-prettier@5.2.6(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.5.3): + eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@1.21.7)))(eslint@9.39.4(jiti@1.21.7))(prettier@3.8.4): dependencies: - eslint: 8.57.1 - prettier: 3.5.3 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.4 + eslint: 9.39.4(jiti@1.21.7) + prettier: 3.8.4 + prettier-linter-helpers: 1.0.1 + synckit: 0.11.13 optionalDependencies: - eslint-config-prettier: 9.1.0(eslint@8.57.1) + eslint-config-prettier: 10.1.8(eslint@9.39.4(jiti@1.21.7)) - eslint-plugin-vue@9.33.0(eslint@8.57.1): + eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.4(jiti@1.21.7))(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@1.21.7))): dependencies: - '@eslint-community/eslint-utils': 4.6.1(eslint@8.57.1) - eslint: 8.57.1 - globals: 13.24.0 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@1.21.7)) + eslint: 9.39.4(jiti@1.21.7) natural-compare: 1.4.0 nth-check: 2.1.1 - postcss-selector-parser: 6.1.2 - semver: 7.7.1 - vue-eslint-parser: 9.4.3(eslint@8.57.1) + postcss-selector-parser: 7.1.4 + semver: 7.8.5 + vue-eslint-parser: 10.4.1(eslint@9.39.4(jiti@1.21.7)) xml-name-validator: 4.0.0 - transitivePeerDependencies: - - supports-color + optionalDependencies: + '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) - eslint-scope@7.2.2: + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-visitor-keys@3.4.3: {} - eslint@8.57.1: + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4(jiti@1.21.7): dependencies: - '@eslint-community/eslint-utils': 4.6.1(eslint@8.57.1) - '@eslint-community/regexpp': 4.12.1 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@1.21.7)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.3.0 - ajv: 6.12.6 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.0 - doctrine: 3.0.0 + debug: 4.4.3 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.6.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 esutils: 2.0.3 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.24.0 - graphemer: 1.4.0 ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 lodash.merge: 4.6.2 - minimatch: 3.1.2 + minimatch: 3.1.5 natural-compare: 1.4.0 optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 + optionalDependencies: + jiti: 1.21.7 transitivePeerDependencies: - supports-color - espree@9.6.1: + espree@10.4.0: dependencies: - acorn: 8.14.1 - acorn-jsx: 5.3.2(acorn@8.14.1) - eslint-visitor-keys: 3.4.3 + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 4.2.1 - esquery@1.6.0: + espree@11.2.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -3156,21 +2997,6 @@ snapshots: esutils@2.0.3: {} - execa@9.5.2: - dependencies: - '@sindresorhus/merge-streams': 4.0.0 - cross-spawn: 7.0.6 - figures: 6.1.0 - get-stream: 9.0.1 - human-signals: 8.0.1 - is-plain-obj: 4.1.0 - is-stream: 4.0.1 - npm-run-path: 6.0.0 - pretty-ms: 9.2.0 - signal-exit: 4.1.0 - strip-final-newline: 4.0.0 - yoctocolors: 2.1.1 - fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -3187,17 +3013,17 @@ snapshots: fast-levenshtein@2.0.6: {} - fastq@1.19.1: + fastq@1.20.1: dependencies: reusify: 1.1.0 - figures@6.1.0: - dependencies: - is-unicode-supported: 2.1.0 + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 - file-entry-cache@6.0.1: + file-entry-cache@8.0.0: dependencies: - flat-cache: 3.2.0 + flat-cache: 4.0.1 file-saver@2.0.5: {} @@ -3210,37 +3036,24 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flat-cache@3.2.0: + flat-cache@4.0.1: dependencies: - flatted: 3.3.3 + flatted: 3.4.2 keyv: 4.5.4 - rimraf: 3.0.2 - - flatted@3.3.3: {} - follow-redirects@1.15.9: {} + flatted@3.4.2: {} - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 + follow-redirects@1.16.0: {} - form-data@4.0.2: + form-data@4.0.6: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 + hasown: 2.0.4 mime-types: 2.1.35 - fraction.js@4.3.7: {} - - fs-extra@11.3.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - - fs.realpath@1.0.0: {} + fraction.js@5.3.4: {} fsevents@2.3.3: optional: true @@ -3254,23 +3067,18 @@ snapshots: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-stream@9.0.1: - dependencies: - '@sec-ant/readable-stream': 0.4.1 - is-stream: 4.0.1 + es-object-atoms: 1.1.2 glob-parent@5.1.2: dependencies: @@ -3280,54 +3088,16 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.4.5: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - - glob@11.0.1: - dependencies: - foreground-child: 3.3.1 - jackspeak: 4.1.0 - minimatch: 10.0.1 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 2.0.0 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - globals@11.12.0: {} - - globals@13.24.0: + glob@13.0.6: dependencies: - type-fest: 0.20.2 + minimatch: 10.2.5 + minipass: 7.1.3 + path-scurry: 2.0.2 - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 + globals@14.0.0: {} gopd@1.2.0: {} - graceful-fs@4.2.11: {} - - graphemer@1.4.0: {} - has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -3336,18 +3106,25 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: + hasown@2.0.4: dependencies: function-bind: 1.1.2 - he@1.2.0: {} - hookable@5.5.3: {} - human-signals@8.0.1: {} + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color ignore@5.3.2: {} + ignore@7.0.5: {} + + immediate@3.0.6: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -3355,68 +3132,37 @@ snapshots: imurmurhash@0.1.4: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - inherits@2.0.4: {} is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - is-core-module@2.16.1: + is-core-module@2.16.2: dependencies: - hasown: 2.0.2 - - is-docker@3.0.0: {} + hasown: 2.0.4 is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - is-number@7.0.0: {} - is-path-inside@3.0.3: {} - - is-plain-obj@4.1.0: {} + is-what@5.5.0: {} - is-stream@4.0.1: {} - - is-unicode-supported@2.1.0: {} - - is-what@4.1.16: {} - - is-wsl@3.1.0: - dependencies: - is-inside-container: 1.0.0 + isarray@1.0.0: {} isexe@2.0.0: {} - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jackspeak@4.1.0: - dependencies: - '@isaacs/cliui': 8.0.2 + isexe@3.1.5: {} jiti@1.21.7: {} js-tokens@4.0.0: {} - js-yaml@4.1.0: + js-yaml@4.2.0: dependencies: argparse: 2.0.1 @@ -3424,7 +3170,7 @@ snapshots: json-buffer@3.0.1: {} - json-parse-even-better-errors@3.0.2: {} + json-parse-even-better-errors@4.0.0: {} json-schema-traverse@0.4.1: {} @@ -3432,23 +3178,26 @@ snapshots: json5@2.2.3: {} - jsonfile@6.1.0: + jszip@3.10.1: dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 keyv@4.5.4: dependencies: json-buffer: 3.0.1 - kolorist@1.8.0: {} - levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -3457,31 +3206,23 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash.castarray@4.4.0: {} - - lodash.isplainobject@4.0.6: {} - lodash.merge@4.6.2: {} - lodash@4.17.21: {} - - lru-cache@10.4.3: {} - - lru-cache@11.1.0: {} + lru-cache@11.5.1: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lucide-vue-next@0.445.0(vue@3.5.13(typescript@5.4.5)): + lucide-vue-next@0.535.0(vue@3.5.38(typescript@5.8.3)): dependencies: - vue: 3.5.13(typescript@5.4.5) + vue: 3.5.38(typescript@5.8.3) - magic-string@0.30.17: + magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 - marked@14.1.4: {} + marked@16.4.2: {} math-intrinsics@1.1.0: {} @@ -3492,7 +3233,7 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 2.3.2 mime-db@1.52.0: {} @@ -3500,24 +3241,18 @@ snapshots: dependencies: mime-db: 1.52.0 - minimatch@10.0.1: + minimatch@10.2.5: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 5.0.6 - minimatch@3.1.2: + minimatch@3.1.5: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 5.0.6 - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minipass@7.1.2: {} + minipass@7.1.3: {} mitt@3.0.1: {} - mrmime@2.0.1: {} - ms@2.1.3: {} muggle-string@0.4.1: {} @@ -3528,35 +3263,26 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.11: {} - - nanoid@5.1.5: {} + nanoid@3.3.13: {} natural-compare@1.4.0: {} - node-releases@2.0.19: {} + node-releases@2.0.54: {} normalize-path@3.0.0: {} - normalize-range@0.1.2: {} + npm-normalize-package-bin@4.0.0: {} - npm-normalize-package-bin@3.0.1: {} - - npm-run-all2@6.2.6: + npm-run-all2@8.0.4: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 cross-spawn: 7.0.6 memorystream: 0.3.1 - minimatch: 9.0.5 - pidtree: 0.6.0 - read-package-json-fast: 3.0.2 - shell-quote: 1.8.2 - which: 3.0.1 - - npm-run-path@6.0.0: - dependencies: - path-key: 4.0.0 - unicorn-magic: 0.3.0 + picomatch: 4.0.4 + pidtree: 0.6.1 + read-package-json-fast: 4.0.0 + shell-quote: 1.8.4 + which: 5.0.0 nth-check@2.1.1: dependencies: @@ -3566,17 +3292,6 @@ snapshots: object-hash@3.0.0: {} - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - open@10.1.1: - dependencies: - default-browser: 5.2.1 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - is-wsl: 3.1.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -3596,123 +3311,110 @@ snapshots: package-json-from-dist@1.0.1: {} + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 - parse-ms@4.0.0: {} - path-browserify@1.0.1: {} path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} - path-key@4.0.0: {} - path-parse@1.0.7: {} - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - - path-scurry@2.0.0: + path-scurry@2.0.2: dependencies: - lru-cache: 11.1.0 - minipass: 7.1.2 - - path-type@4.0.0: {} - - pathe@2.0.3: {} + lru-cache: 11.5.1 + minipass: 7.1.3 perfect-debounce@1.0.0: {} picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@2.3.2: {} - picomatch@4.0.2: {} + picomatch@4.0.4: {} - pidtree@0.6.0: {} + pidtree@0.6.1: {} pify@2.3.0: {} - pinia@2.3.1(typescript@5.4.5)(vue@3.5.13(typescript@5.4.5)): + pinia@3.0.4(typescript@5.8.3)(vue@3.5.38(typescript@5.8.3)): dependencies: - '@vue/devtools-api': 6.6.4 - vue: 3.5.13(typescript@5.4.5) - vue-demi: 0.14.10(vue@3.5.13(typescript@5.4.5)) + '@vue/devtools-api': 7.7.9 + vue: 3.5.38(typescript@5.8.3) optionalDependencies: - typescript: 5.4.5 - transitivePeerDependencies: - - '@vue/composition-api' + typescript: 5.8.3 pirates@4.0.7: {} - postcss-import@15.1.0(postcss@8.5.3): + postcss-import@15.1.0(postcss@8.5.15): dependencies: - postcss: 8.5.3 + postcss: 8.5.15 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.10 + resolve: 1.22.12 - postcss-js@4.0.1(postcss@8.5.3): + postcss-js@4.1.0(postcss@8.5.15): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.3 + postcss: 8.5.15 - postcss-load-config@4.0.2(postcss@8.5.3): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.15): dependencies: lilconfig: 3.1.3 - yaml: 2.7.1 optionalDependencies: - postcss: 8.5.3 + jiti: 1.21.7 + postcss: 8.5.15 - postcss-nested@6.2.0(postcss@8.5.3): + postcss-nested@6.2.0(postcss@8.5.15): dependencies: - postcss: 8.5.3 - postcss-selector-parser: 6.1.2 + postcss: 8.5.15 + postcss-selector-parser: 6.1.4 postcss-selector-parser@6.0.10: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@6.1.2: + postcss-selector-parser@6.1.4: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.1.4: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 postcss-value-parser@4.2.0: {} - postcss@8.5.3: + postcss@8.5.15: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.13 picocolors: 1.1.1 source-map-js: 1.2.1 prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.0: + prettier-linter-helpers@1.0.1: dependencies: fast-diff: 1.3.0 - prettier@3.5.3: {} + prettier@3.8.4: {} - pretty-ms@9.2.0: - dependencies: - parse-ms: 4.0.0 + process-nextick-args@2.0.1: {} - proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} punycode@2.3.1: {} - qrcode.vue@3.6.0(vue@3.5.13(typescript@5.4.5)): + qrcode.vue@3.10.0(vue@3.5.38(typescript@5.8.3)): dependencies: - vue: 3.5.13(typescript@5.4.5) + vue: 3.5.38(typescript@5.8.3) queue-microtask@1.2.3: {} @@ -3720,20 +3422,31 @@ snapshots: dependencies: pify: 2.3.0 - read-package-json-fast@3.0.2: + read-package-json-fast@4.0.0: + dependencies: + json-parse-even-better-errors: 4.0.0 + npm-normalize-package-bin: 4.0.0 + + readable-stream@2.3.8: dependencies: - json-parse-even-better-errors: 3.0.2 - npm-normalize-package-bin: 3.0.1 + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 readdirp@3.6.0: dependencies: - picomatch: 2.3.1 + picomatch: 2.3.2 resolve-from@4.0.0: {} - resolve@1.22.10: + resolve@1.22.12: dependencies: - is-core-module: 2.16.1 + es-errors: 1.3.0 + is-core-module: 2.16.2 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -3741,50 +3454,53 @@ snapshots: rfdc@1.4.1: {} - rimraf@3.0.2: + rimraf@6.1.3: dependencies: - glob: 7.2.3 - - rimraf@6.0.1: - dependencies: - glob: 11.0.1 + glob: 13.0.6 package-json-from-dist: 1.0.1 - rollup@4.40.0: + rollup@4.62.2: dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.40.0 - '@rollup/rollup-android-arm64': 4.40.0 - '@rollup/rollup-darwin-arm64': 4.40.0 - '@rollup/rollup-darwin-x64': 4.40.0 - '@rollup/rollup-freebsd-arm64': 4.40.0 - '@rollup/rollup-freebsd-x64': 4.40.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.40.0 - '@rollup/rollup-linux-arm-musleabihf': 4.40.0 - '@rollup/rollup-linux-arm64-gnu': 4.40.0 - '@rollup/rollup-linux-arm64-musl': 4.40.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.40.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.40.0 - '@rollup/rollup-linux-riscv64-gnu': 4.40.0 - '@rollup/rollup-linux-riscv64-musl': 4.40.0 - '@rollup/rollup-linux-s390x-gnu': 4.40.0 - '@rollup/rollup-linux-x64-gnu': 4.40.0 - '@rollup/rollup-linux-x64-musl': 4.40.0 - '@rollup/rollup-win32-arm64-msvc': 4.40.0 - '@rollup/rollup-win32-ia32-msvc': 4.40.0 - '@rollup/rollup-win32-x64-msvc': 4.40.0 + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 fsevents: 2.3.3 - run-applescript@7.0.0: {} - run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 + safe-buffer@5.1.2: {} + semver@6.3.1: {} - semver@7.7.1: {} + semver@7.8.5: {} + + setimmediate@1.0.5: {} shebang-command@2.0.0: dependencies: @@ -3792,17 +3508,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.2: {} - - signal-exit@4.1.0: {} - - sirv@3.0.1: - dependencies: - '@polka/url': 1.0.0-next.29 - mrmime: 2.0.1 - totalist: 3.0.1 - - slash@3.0.0: {} + shell-quote@1.8.4: {} source-map-js@1.2.1: {} @@ -3810,43 +3516,25 @@ snapshots: speakingurl@14.0.1: {} - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.0: + string_decoder@1.1.1: dependencies: - ansi-regex: 6.1.0 - - strip-final-newline@4.0.0: {} + safe-buffer: 5.1.2 strip-json-comments@3.1.1: {} - sucrase@3.35.0: + sucrase@3.35.1: dependencies: - '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 - glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 + tinyglobby: 0.2.17 ts-interface-checker: 0.1.13 - superjson@2.2.2: + superjson@2.2.6: dependencies: - copy-anything: 3.0.5 + copy-anything: 4.0.5 supports-color@7.2.0: dependencies: @@ -3854,12 +3542,11 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - synckit@0.11.4: + synckit@0.11.13: dependencies: - '@pkgr/core': 0.2.4 - tslib: 2.8.1 + '@pkgr/core': 0.3.6 - tailwindcss@3.4.17: + tailwindcss@3.4.19: dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -3875,18 +3562,17 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.3 - postcss-import: 15.1.0(postcss@8.5.3) - postcss-js: 4.0.1(postcss@8.5.3) - postcss-load-config: 4.0.2(postcss@8.5.3) - postcss-nested: 6.2.0(postcss@8.5.3) - postcss-selector-parser: 6.1.2 - resolve: 1.22.10 - sucrase: 3.35.0 + postcss: 8.5.15 + postcss-import: 15.1.0(postcss@8.5.15) + postcss-js: 4.1.0(postcss@8.5.15) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.15) + postcss-nested: 6.2.0(postcss@8.5.15) + postcss-selector-parser: 6.1.4 + resolve: 1.22.12 + sucrase: 3.35.1 transitivePeerDependencies: - - ts-node - - text-table@0.2.0: {} + - tsx + - yaml thenify-all@1.6.0: dependencies: @@ -3896,37 +3582,43 @@ snapshots: dependencies: any-promise: 1.3.0 + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - totalist@3.0.1: {} - - ts-api-utils@1.4.3(typescript@5.4.5): + ts-api-utils@2.5.0(typescript@5.8.3): dependencies: - typescript: 5.4.5 + typescript: 5.8.3 ts-interface-checker@0.1.13: {} - tslib@2.8.1: {} - type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - type-fest@0.20.2: {} - - typescript@5.4.5: {} - - undici-types@6.19.8: {} + typescript-eslint@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.8.3) + eslint: 9.39.4(jiti@1.21.7) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color - unicorn-magic@0.3.0: {} + typescript@5.8.3: {} - universalify@2.0.1: {} + undici-types@7.18.2: {} - update-browserslist-db@1.1.3(browserslist@4.24.4): + update-browserslist-db@1.3.2(browserslist@4.28.9): dependencies: - browserslist: 4.24.4 + browserslist: 4.28.9 escalade: 3.2.0 picocolors: 1.1.1 @@ -3936,136 +3628,73 @@ snapshots: util-deprecate@1.0.2: {} - vite-hot-client@2.0.4(vite@5.4.18(@types/node@20.17.30)): - dependencies: - vite: 5.4.18(@types/node@20.17.30) - - vite-plugin-inspect@0.8.9(rollup@4.40.0)(vite@5.4.18(@types/node@20.17.30)): - dependencies: - '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.1.4(rollup@4.40.0) - debug: 4.4.0 - error-stack-parser-es: 0.1.5 - fs-extra: 11.3.0 - open: 10.1.1 - perfect-debounce: 1.0.0 - picocolors: 1.1.1 - sirv: 3.0.1 - vite: 5.4.18(@types/node@20.17.30) - transitivePeerDependencies: - - rollup - - supports-color - - vite-plugin-vue-devtools@7.7.5(rollup@4.40.0)(vite@5.4.18(@types/node@20.17.30))(vue@3.5.13(typescript@5.4.5)): - dependencies: - '@vue/devtools-core': 7.7.5(vite@5.4.18(@types/node@20.17.30))(vue@3.5.13(typescript@5.4.5)) - '@vue/devtools-kit': 7.7.5 - '@vue/devtools-shared': 7.7.5 - execa: 9.5.2 - sirv: 3.0.1 - vite: 5.4.18(@types/node@20.17.30) - vite-plugin-inspect: 0.8.9(rollup@4.40.0)(vite@5.4.18(@types/node@20.17.30)) - vite-plugin-vue-inspector: 5.3.1(vite@5.4.18(@types/node@20.17.30)) - transitivePeerDependencies: - - '@nuxt/kit' - - rollup - - supports-color - - vue - - vite-plugin-vue-inspector@5.3.1(vite@5.4.18(@types/node@20.17.30)): - dependencies: - '@babel/core': 7.26.10 - '@babel/plugin-proposal-decorators': 7.25.9(@babel/core@7.26.10) - '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.10) - '@babel/plugin-transform-typescript': 7.27.0(@babel/core@7.26.10) - '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.26.10) - '@vue/compiler-dom': 3.5.13 - kolorist: 1.8.0 - magic-string: 0.30.17 - vite: 5.4.18(@types/node@20.17.30) - transitivePeerDependencies: - - supports-color - - vite@5.4.18(@types/node@20.17.30): + vite@7.3.5(@types/node@24.13.2)(jiti@1.21.7): dependencies: - esbuild: 0.21.5 - postcss: 8.5.3 - rollup: 4.40.0 + esbuild: 0.28.1 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.15 + rollup: 4.62.2 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 20.17.30 + '@types/node': 24.13.2 fsevents: 2.3.3 + jiti: 1.21.7 vscode-uri@3.1.0: {} - vue-demi@0.14.10(vue@3.5.13(typescript@5.4.5)): - dependencies: - vue: 3.5.13(typescript@5.4.5) - - vue-eslint-parser@9.4.3(eslint@8.57.1): + vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@1.21.7)): dependencies: - debug: 4.4.0 - eslint: 8.57.1 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.6.0 - lodash: 4.17.21 - semver: 7.7.1 + debug: 4.4.3 + eslint: 9.39.4(jiti@1.21.7) + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + semver: 7.8.5 transitivePeerDependencies: - supports-color - vue-router@4.5.0(vue@3.5.13(typescript@5.4.5)): + vue-i18n@9.14.5(vue@3.5.38(typescript@5.8.3)): dependencies: + '@intlify/core-base': 9.14.5 + '@intlify/shared': 9.14.5 '@vue/devtools-api': 6.6.4 - vue: 3.5.13(typescript@5.4.5) + vue: 3.5.38(typescript@5.8.3) - vue-tsc@2.2.10(typescript@5.4.5): + vue-router@4.6.4(vue@3.5.38(typescript@5.8.3)): dependencies: - '@volar/typescript': 2.4.12 - '@vue/language-core': 2.2.10(typescript@5.4.5) - typescript: 5.4.5 + '@vue/devtools-api': 6.6.4 + vue: 3.5.38(typescript@5.8.3) + + vue-tsc@3.3.5(typescript@5.8.3): + dependencies: + '@volar/typescript': 2.4.28 + '@vue/language-core': 3.3.5 + typescript: 5.8.3 - vue@3.5.13(typescript@5.4.5): + vue@3.5.38(typescript@5.8.3): dependencies: - '@vue/compiler-dom': 3.5.13 - '@vue/compiler-sfc': 3.5.13 - '@vue/runtime-dom': 3.5.13 - '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.4.5)) - '@vue/shared': 3.5.13 + '@vue/compiler-dom': 3.5.38 + '@vue/compiler-sfc': 3.5.38 + '@vue/runtime-dom': 3.5.38 + '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@5.8.3)) + '@vue/shared': 3.5.38 optionalDependencies: - typescript: 5.4.5 + typescript: 5.8.3 which@2.0.2: dependencies: isexe: 2.0.0 - which@3.0.1: + which@5.0.0: dependencies: - isexe: 2.0.0 + isexe: 3.1.5 word-wrap@1.2.5: {} - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - - wrappy@1.0.2: {} - xml-name-validator@4.0.0: {} yallist@3.1.1: {} - yaml@2.7.1: {} - yocto-queue@0.1.0: {} - - yoctocolors@2.1.1: {} diff --git a/public/assets/manifest.json b/public/assets/manifest.json new file mode 100644 index 0000000..8b56335 --- /dev/null +++ b/public/assets/manifest.json @@ -0,0 +1,16 @@ +{ + "name": "FileCodeBox", + "short_name": "FileCodeBox", + "description": "FileCodeBox file transfer", + "start_url": "/", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#4f46e5", + "icons": [ + { + "src": "/assets/logo_small.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..eaa3ab7 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "packages": { + ".": { + "release-type": "node", + "package-name": "filecodebox-frontend", + "include-component-in-tag": false, + "changelog-path": "CHANGELOG.md" + } + } +} diff --git a/scripts/check-architecture.mjs b/scripts/check-architecture.mjs new file mode 100644 index 0000000..b0eb0d8 --- /dev/null +++ b/scripts/check-architecture.mjs @@ -0,0 +1,271 @@ +import { readFileSync } from 'node:fs' +import { join, relative } from 'node:path' +import { globSync } from 'glob' + +const rootDir = new URL('..', import.meta.url).pathname +const srcDir = join(rootDir, 'src') + +const rules = [ + { + name: 'views/components 不直接导入 services', + files: ['src/views/**/*.{ts,vue}', 'src/components/**/*.{ts,vue}'], + patterns: [/from ['"]@\/services(?:\/[^'"]*)?['"]/], + }, + { + name: 'views/components 不直接调用服务类', + files: ['src/views/**/*.{ts,vue}', 'src/components/**/*.{ts,vue}'], + patterns: [ + /\bFileService\./, + /\bConfigService\./, + /\bAuthService\./, + /\bStatsService\./, + /\bPresignUploadService\./, + ], + }, + { + name: 'views/components/composables 不直接导入具体 service 文件', + files: ['src/views/**/*.{ts,vue}', 'src/components/**/*.{ts,vue}', 'src/composables/**/*.ts'], + patterns: [/from ['"]@\/services\/[^'"]+['"]/], + }, + { + name: '预签名上传 composable 不直接依赖 store', + files: ['src/composables/usePresignedUpload.ts'], + patterns: [/from ['"]@\/stores\/[^'"]+['"]/], + }, + { + name: '取件流程 composable 不直接依赖路由', + files: ['src/composables/useRetrieveFlow.ts'], + patterns: [/from ['"]vue-router['"]/], + }, + { + name: '发送流程 composable 不直接依赖路由', + files: ['src/composables/useSendFlow.ts'], + patterns: [/from ['"]vue-router['"]/], + }, + { + name: '登录流程 composable 不直接依赖路由', + files: ['src/composables/useAdminLogin.ts'], + patterns: [/from ['"]vue-router['"]/], + }, + { + name: '应用壳不直接调用服务,启动任务需拆到专用 composable', + files: ['src/composables/useAppShell.ts'], + patterns: [/\b[A-Z][A-Za-z]+Service\./, /from ['"]@\/services\/[^'"]+['"]/], + }, + { + name: '公共配置启动任务独立维护', + files: ['src/composables/useAppShell.ts'], + patterns: [/\bgetUserConfig\(/, /\bapplyRemoteConfig\(/], + }, + { + name: '仪表盘数据 composable 不在初始化阶段自动加载', + files: ['src/composables/useDashboardStats.ts'], + patterns: [/\bonMounted\(/], + }, + { + name: '请求客户端统一放在 services/client', + files: ['src/**/*.{ts,vue}'], + patterns: [/from ['"]@\/utils\/api['"]/], + ignoreFiles: [/^src\/utils\/api\.ts$/], + }, + { + name: '请求客户端不直接控制浏览器跳转', + files: ['src/services/client.ts'], + patterns: [/\bwindow\.location\.(href|replace|assign)\b/, /\blocation\.(href|replace|assign)\b/], + }, + { + name: '裸 axios 只能出现在请求客户端封装内', + files: ['src/**/*.{ts,vue}'], + patterns: [/import\s+axios\b/], + ignoreFiles: [/^src\/services\/client\.ts$/, /^src\/services\/upload-client\.ts$/], + }, + { + name: '文件打包能力统一放在 file-processing 工具', + files: ['src/**/*.{ts,vue}'], + patterns: [/from ['"]jszip['"]/], + ignoreFiles: [/^src\/utils\/file-processing\.ts$/], + }, + { + name: '内容预览渲染统一放在 content-preview 工具', + files: ['src/**/*.{ts,vue}'], + patterns: [/from ['"]marked['"]/, /from ['"]dompurify['"]/i], + ignoreFiles: [/^src\/utils\/content-preview\.ts$/], + }, + { + name: '下载动作统一放在 download-action 工具', + files: ['src/**/*.{ts,vue}'], + patterns: [/from ['"]file-saver['"]/, /\bwindow\.open\(/], + ignoreFiles: [/^src\/utils\/download-action\.ts$/], + }, + { + name: 'services 统一使用 services/client 作为内部请求入口', + files: ['src/services/**/*.ts'], + patterns: [/from ['"]@\/services\/client['"]/], + }, + { + name: '非 storage helper 不直接访问 localStorage', + files: ['src/**/*.{ts,vue}'], + patterns: [/\blocalStorage\./], + ignoreFiles: [ + /^src\/utils\/auth-storage\.ts$/, + /^src\/utils\/config-storage\.ts$/, + /^src\/utils\/preference-storage\.ts$/, + /^src\/utils\/record-storage\.ts$/, + ], + }, + { + name: '非 URL helper 不直接读取 window.location.origin', + files: ['src/**/*.{ts,vue}'], + patterns: [/\bwindow\.location\.origin\b/], + ignoreFiles: [/^src\/utils\/share-url\.ts$/], + }, + { + name: '非剪贴板 helper 不直接调用剪贴板 API', + files: ['src/**/*.{ts,vue}'], + patterns: [/\bnavigator\.clipboard\b/, /\bdocument\.execCommand\(['"]copy['"]\)/], + ignoreFiles: [/^src\/utils\/clipboard\.ts$/], + }, + { + name: '页面和组件不直接导入剪贴板 helper', + files: ['src/views/**/*.{ts,vue}', 'src/components/**/*.{ts,vue}'], + patterns: [/from ['"]@\/utils\/clipboard['"]/], + }, + { + name: '工具层不直接依赖 store', + files: ['src/utils/**/*.ts'], + patterns: [/from ['"]@\/stores\/[^'"]+['"]/], + }, + { + name: '系统配置页通过 composable 动作切换二值配置', + files: ['src/views/manage/SystemSettingsView.vue'], + patterns: [/config\.(enableChunk|s3_proxy|openUpload)\s*=[^=]/], + }, + { + name: '系统配置页开关统一使用 SettingSwitch', + files: ['src/views/manage/SystemSettingsView.vue'], + patterns: [/\brole=['"]switch['"]/], + }, + { + name: '系统配置页简单数字项统一使用 SettingNumberInput', + files: ['src/views/manage/SystemSettingsView.vue'], + patterns: [/]*v-model=['"]config\.(uploadMinute|uploadCount|errorMinute|errorCount)['"]/], + }, + { + name: '系统配置页不直接处理配置表单转换', + files: ['src/views/manage/SystemSettingsView.vue'], + patterns: [/from ['"]@\/utils\/config-form['"]/], + }, + { + name: '文件管理编辑弹窗字段统一使用 FileEditField', + files: ['src/views/manage/FileManageView.vue'], + patterns: [/]*v-model=['"]editForm\.(code|prefix|suffix|expired_at|expired_count)['"]/], + }, + { + name: '发送页记录列表统一使用 SentRecordList', + files: ['src/views/SendFileView.vue'], + patterns: [/v-for=['"]record in sendRecords['"]/], + }, + { + name: '发送页记录详情统一使用 SentRecordDetailModal', + files: ['src/views/SendFileView.vue'], + patterns: [/ + globSync(pattern, { + cwd: rootDir, + absolute: true, + nodir: true, + ignore: ['node_modules/**', 'dist/**'], + }) + ) + + for (const file of files) { + const relativeFile = relative(rootDir, file) + if (rule.ignoreFiles?.some((pattern) => pattern.test(relativeFile))) { + continue + } + + const source = readFileSync(file, 'utf8') + const lines = source.split(/\r?\n/) + + lines.forEach((line, index) => { + for (const pattern of rule.patterns) { + if (pattern.test(line)) { + violations.push({ + rule: rule.name, + file: relativeFile, + line: index + 1, + text: line.trim(), + }) + } + } + }) + } +} + +if (violations.length > 0) { + console.error('架构边界检查失败:') + for (const violation of violations) { + console.error( + `- ${violation.rule}: ${violation.file}:${violation.line} ${violation.text}` + ) + } + process.exit(1) +} + +console.log(`架构边界检查通过:${srcDir}`) diff --git a/src/App.vue b/src/App.vue index cc8b709..f9fc10c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,87 +1,75 @@ \ No newline at end of file + diff --git a/src/components/common/ContentPreviewModal.vue b/src/components/common/ContentPreviewModal.vue new file mode 100644 index 0000000..e8f28ef --- /dev/null +++ b/src/components/common/ContentPreviewModal.vue @@ -0,0 +1,194 @@ + + + + + diff --git a/src/components/common/DataPagination.vue b/src/components/common/DataPagination.vue new file mode 100644 index 0000000..879aef7 --- /dev/null +++ b/src/components/common/DataPagination.vue @@ -0,0 +1,134 @@ + + + diff --git a/src/components/common/DataTable.vue b/src/components/common/DataTable.vue new file mode 100644 index 0000000..dd0461c --- /dev/null +++ b/src/components/common/DataTable.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/components/common/ExpirationSelector.vue b/src/components/common/ExpirationSelector.vue new file mode 100644 index 0000000..eae56d0 --- /dev/null +++ b/src/components/common/ExpirationSelector.vue @@ -0,0 +1,183 @@ + + + diff --git a/src/components/common/FileDetailModal.vue b/src/components/common/FileDetailModal.vue new file mode 100644 index 0000000..8b24042 --- /dev/null +++ b/src/components/common/FileDetailModal.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/src/components/common/FileEditField.vue b/src/components/common/FileEditField.vue new file mode 100644 index 0000000..6da7718 --- /dev/null +++ b/src/components/common/FileEditField.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/components/common/FileRecordList.vue b/src/components/common/FileRecordList.vue new file mode 100644 index 0000000..8fc0464 --- /dev/null +++ b/src/components/common/FileRecordList.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/src/components/common/FileUploadArea.vue b/src/components/common/FileUploadArea.vue new file mode 100644 index 0000000..2b7ee5e --- /dev/null +++ b/src/components/common/FileUploadArea.vue @@ -0,0 +1,341 @@ + + + diff --git a/src/components/common/FormInput.vue b/src/components/common/FormInput.vue new file mode 100644 index 0000000..3a2b6dc --- /dev/null +++ b/src/components/common/FormInput.vue @@ -0,0 +1,66 @@ + + + diff --git a/src/components/common/LanguageSwitcher.vue b/src/components/common/LanguageSwitcher.vue new file mode 100644 index 0000000..7b467fe --- /dev/null +++ b/src/components/common/LanguageSwitcher.vue @@ -0,0 +1,104 @@ + + + diff --git a/src/components/common/MediaPreviewModal.vue b/src/components/common/MediaPreviewModal.vue new file mode 100644 index 0000000..535ffc1 --- /dev/null +++ b/src/components/common/MediaPreviewModal.vue @@ -0,0 +1,113 @@ + + + diff --git a/src/components/common/PageFooter.vue b/src/components/common/PageFooter.vue new file mode 100644 index 0000000..7a579a9 --- /dev/null +++ b/src/components/common/PageFooter.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/components/common/PageHeader.vue b/src/components/common/PageHeader.vue new file mode 100644 index 0000000..4911f12 --- /dev/null +++ b/src/components/common/PageHeader.vue @@ -0,0 +1,59 @@ + + + diff --git a/src/components/common/RetrieveForm.vue b/src/components/common/RetrieveForm.vue new file mode 100644 index 0000000..9928497 --- /dev/null +++ b/src/components/common/RetrieveForm.vue @@ -0,0 +1,182 @@ + + + diff --git a/src/components/common/SendTypeSelector.vue b/src/components/common/SendTypeSelector.vue new file mode 100644 index 0000000..634185b --- /dev/null +++ b/src/components/common/SendTypeSelector.vue @@ -0,0 +1,62 @@ + + + diff --git a/src/components/common/SentRecordDetailModal.vue b/src/components/common/SentRecordDetailModal.vue new file mode 100644 index 0000000..459aee8 --- /dev/null +++ b/src/components/common/SentRecordDetailModal.vue @@ -0,0 +1,232 @@ + + + + + diff --git a/src/components/common/SentRecordList.vue b/src/components/common/SentRecordList.vue new file mode 100644 index 0000000..22abc4d --- /dev/null +++ b/src/components/common/SentRecordList.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/src/components/common/SettingNumberInput.vue b/src/components/common/SettingNumberInput.vue new file mode 100644 index 0000000..2e4ba13 --- /dev/null +++ b/src/components/common/SettingNumberInput.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/components/common/SettingSwitch.vue b/src/components/common/SettingSwitch.vue new file mode 100644 index 0000000..a601237 --- /dev/null +++ b/src/components/common/SettingSwitch.vue @@ -0,0 +1,56 @@ + + + diff --git a/src/components/common/SideDrawer.vue b/src/components/common/SideDrawer.vue new file mode 100644 index 0000000..92fb1c1 --- /dev/null +++ b/src/components/common/SideDrawer.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/src/components/common/StatCard.vue b/src/components/common/StatCard.vue new file mode 100644 index 0000000..10e4848 --- /dev/null +++ b/src/components/common/StatCard.vue @@ -0,0 +1,67 @@ + + + diff --git a/src/components/common/TextInputArea.vue b/src/components/common/TextInputArea.vue new file mode 100644 index 0000000..88bd5db --- /dev/null +++ b/src/components/common/TextInputArea.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/src/components/common/ThemeToggle.vue b/src/components/common/ThemeToggle.vue index 68b9e4f..d8ec75d 100644 --- a/src/components/common/ThemeToggle.vue +++ b/src/components/common/ThemeToggle.vue @@ -3,20 +3,25 @@ import { inject } from 'vue' import { SunIcon, MoonIcon } from 'lucide-vue-next' const isDarkMode = inject('isDarkMode') as { value: boolean } -const setColorMode = inject('setColorMode') as (isDark: boolean) => void +const toggleTheme = inject('toggleTheme') as () => void const toggleColorMode = () => { - setColorMode(!isDarkMode.value) + toggleTheme() } diff --git a/src/composables/index.ts b/src/composables/index.ts new file mode 100644 index 0000000..c894854 --- /dev/null +++ b/src/composables/index.ts @@ -0,0 +1,13 @@ +export { useAdminFiles } from './useAdminFiles' +export { useAdminLogin } from './useAdminLogin' +export { useAdminSession } from './useAdminSession' +export { useAppShell } from './useAppShell' +export { useDashboardStats } from './useDashboardStats' +export { useInjectedDarkMode } from './useInjectedDarkMode' +export { usePresignedUpload } from './usePresignedUpload' +export { usePublicConfigBootstrap } from './usePublicConfigBootstrap' +export { useRetrieveFlow } from './useRetrieveFlow' +export { useRouteLoading } from './useRouteLoading' +export { useSendFlow } from './useSendFlow' +export { useSystemConfig } from './useSystemConfig' +export { useTheme } from './useTheme' diff --git a/src/composables/useAdminFiles.ts b/src/composables/useAdminFiles.ts new file mode 100644 index 0000000..bb78a1f --- /dev/null +++ b/src/composables/useAdminFiles.ts @@ -0,0 +1,1720 @@ +import { computed, ref } from 'vue' +import { useI18n } from 'vue-i18n' +import { FileService } from '@/services' +import { useAlertStore } from '@/stores/alertStore' +import type { + AdminBatchEditForm, + AdminBatchDeleteFilesResponse, + AdminBatchPolicyActionRequest, + AdminBatchPolicyActionResponse, + AdminBatchUpdateFilesRequest, + AdminBatchUpdateFilesResponse, + AdminFileDetailResponse, + AdminFileDetailTimelineItem, + AdminFileDetailViewItem, + AdminFileMetadataRequest, + AdminFileInsightSeverity, + AdminFilePatchPayload, + AdminFileHealthFilter, + AdminFileListParams, + AdminFilePolicyAction, + AdminFilePolicyActionRequest, + AdminFileStatusFilter, + AdminFileSummary, + AdminFileTypeFilter, + AdminFileViewPreset, + AdminFileViewPresetParams, + AdminFileViewPresetRequest, + AdminFileViewItem, + ApiResponse, + FileEditForm, + FileListItem +} from '@/types' +import { copyToClipboard } from '@/utils/clipboard' +import { formatDuration, formatFileSize, formatTimestamp, getErrorMessage } from '@/utils/common' +import { + downloadAdminManagedFile, + exportAdminTextFile, + getSafeFilename +} from '@/utils/download-action' +import { buildRetrieveUrl } from '@/utils/share-url' + +const emptySummary = (): AdminFileSummary => ({ + totalFiles: 0, + activeCount: 0, + expiredCount: 0, + textCount: 0, + fileCount: 0, + chunkedCount: 0, + healthAttentionCount: 0, + healthDangerCount: 0, + healthWarningCount: 0, + expiringSoonCount: 0, + storageIssueCount: 0, + neverRetrievedCount: 0, + healthyCount: 0, + permanentCount: 0, + storageUsed: 0, + usedCount: 0 +}) + +const normalizeCount = (value: number | string | null | undefined) => Number(value || 0) + +const isExpiredByDate = (value: string | null | undefined) => { + if (!value) return false + const timestamp = new Date(value).getTime() + return Number.isFinite(timestamp) && timestamp < Date.now() +} + +type ErrorWithResponse = { + response?: { + status?: number + } +} + +const isLegacyEndpointUnavailable = (error: unknown) => { + const status = (error as ErrorWithResponse)?.response?.status + return status === 404 || status === 405 +} + +const legacyForeverExpiresAt = '2099-12-31T23:59' +const detailPolicyDownloadLimit = 5 +const dayInMilliseconds = 24 * 60 * 60 * 1000 + +type DetailPolicyActionOption = { + action: AdminFilePolicyAction + label: string + description: string +} + +type BatchPolicyActionOption = DetailPolicyActionOption + +type HealthFilterOption = { + value: AdminFileHealthFilter + label: string + count?: number +} + +const emptyBatchEditForm = (): AdminBatchEditForm => ({ + mode: 'expiresAt', + expired_at: '', + expired_count: null +}) + +const metadataTagSeparatorPattern = /[,,\n]+/ + +const emptyDetailMetadataForm = () => ({ + note: '', + tagsText: '' +}) + +const padDatePart = (value: number) => String(value).padStart(2, '0') + +const formatLocalDateTime = (date: Date) => { + const year = date.getFullYear() + const month = padDatePart(date.getMonth() + 1) + const day = padDatePart(date.getDate()) + const hours = padDatePart(date.getHours()) + const minutes = padDatePart(date.getMinutes()) + + return `${year}-${month}-${day}T${hours}:${minutes}` +} + +const builtInAllViewPresetId = 'built_in_all' +const adminFileStatusFilters: AdminFileStatusFilter[] = ['all', 'active', 'expired'] +const adminFileTypeFilters: AdminFileTypeFilter[] = ['all', 'file', 'text', 'chunked'] +const adminFileHealthFilters: AdminFileHealthFilter[] = [ + 'all', + 'attention', + 'danger', + 'warning', + 'healthy', + 'expired', + 'expiring_soon', + 'storage_issue', + 'never_retrieved', + 'permanent' +] +const adminFileSortFields = [ + 'created_at', + 'expired_at', + 'name', + 'size', + 'used_count', + 'code' +] as const +const adminFileSortOrders = ['asc', 'desc'] as const + +type RawViewPresetParams = Partial< + Record +> + +const normalizePresetChoice = ( + value: unknown, + allowedValues: readonly T[], + fallback: T +): T => { + const normalizedValue = String(value ?? fallback) + .replace('-', '_') + .trim() + .toLowerCase() + return allowedValues.includes(normalizedValue as T) ? (normalizedValue as T) : fallback +} + +const normalizePresetSize = (value: unknown) => { + const normalizedSize = Number(value) + if (!Number.isFinite(normalizedSize)) return 10 + return Math.min(Math.max(Math.trunc(normalizedSize), 1), 100) +} + +const buildDefaultViewPresetParams = (): AdminFileViewPresetParams => ({ + keyword: '', + status: 'all', + type: 'all', + health: 'all', + sortBy: 'created_at', + sortOrder: 'desc', + size: 10 +}) + +const normalizeViewPresetParams = (params: unknown): AdminFileViewPresetParams => { + const defaults = buildDefaultViewPresetParams() + const rawParams = + params && typeof params === 'object' + ? (params as RawViewPresetParams) + : ({} as RawViewPresetParams) + + return { + keyword: String(rawParams.keyword ?? defaults.keyword).trim(), + status: normalizePresetChoice(rawParams.status, adminFileStatusFilters, defaults.status), + type: normalizePresetChoice(rawParams.type, adminFileTypeFilters, defaults.type), + health: normalizePresetChoice(rawParams.health, adminFileHealthFilters, defaults.health), + sortBy: normalizePresetChoice( + rawParams.sortBy ?? rawParams.sort_by, + adminFileSortFields, + defaults.sortBy + ), + sortOrder: normalizePresetChoice( + rawParams.sortOrder ?? rawParams.sort_order, + adminFileSortOrders, + defaults.sortOrder + ), + size: normalizePresetSize(rawParams.size ?? defaults.size) + } +} + +export function useAdminFiles() { + const { t } = useI18n() + const alertStore = useAlertStore() + + const tableData = ref([]) + const hasLoadError = ref(false) + const isLoading = ref(false) + const isSaving = ref(false) + const summary = ref(emptySummary()) + const params = ref({ + page: 1, + size: 10, + total: 0, + keyword: '', + status: 'all', + type: 'all', + health: 'all', + sortBy: 'created_at', + sortOrder: 'desc' + }) + + const showEditModal = ref(false) + const editForm = ref({ + id: null, + code: '', + prefix: '', + suffix: '', + expired_at: '', + expired_count: null + }) + const showBatchEditModal = ref(false) + const batchEditForm = ref(emptyBatchEditForm()) + + const showTextPreview = ref(false) + const previewText = ref('') + const previewFile = ref(null) + const previewMetaText = ref('') + const isPreviewLoading = ref(false) + const showFileDetailModal = ref(false) + const selectedFileDetail = ref(null) + const isDetailLoading = ref(false) + const isDetailPolicyActionRunning = ref(false) + const isDetailMetadataSaving = ref(false) + const detailMetadataForm = ref(emptyDetailMetadataForm()) + const downloadingFileId = ref(null) + const selectedFileIds = ref>(new Set()) + const isBatchDeleting = ref(false) + const isBatchUpdating = ref(false) + const isBatchPolicyActionRunning = ref(false) + const savedViewPresets = ref([]) + const selectedViewPresetId = ref(builtInAllViewPresetId) + const isViewPresetLoading = ref(false) + const isViewPresetSaving = ref(false) + const isViewPresetDeleting = ref(false) + let detailRequestSerial = 0 + const isBatchActionRunning = computed( + () => isBatchDeleting.value || isBatchUpdating.value || isBatchPolicyActionRunning.value + ) + const totalPages = computed(() => Math.max(Math.ceil(params.value.total / params.value.size), 1)) + const storageUsedText = computed(() => formatFileSize(summary.value.storageUsed)) + const currentPageSelectedCount = computed( + () => tableData.value.filter((file) => selectedFileIds.value.has(file.id)).length + ) + const selectedCount = computed(() => selectedFileIds.value.size) + const hasSelectedFiles = computed(() => selectedCount.value > 0) + const isAllCurrentPageSelected = computed( + () => tableData.value.length > 0 && currentPageSelectedCount.value === tableData.value.length + ) + const isCurrentPagePartiallySelected = computed( + () => currentPageSelectedCount.value > 0 && !isAllCurrentPageSelected.value + ) + + const requestParams = computed(() => ({ + page: params.value.page, + size: params.value.size, + keyword: params.value.keyword, + status: params.value.status === 'all' ? undefined : params.value.status, + type: params.value.type === 'all' ? undefined : params.value.type, + health: params.value.health === 'all' ? undefined : params.value.health, + sortBy: params.value.sortBy, + sortOrder: params.value.sortOrder + })) + + const hasActiveFilters = computed( + () => + Boolean(params.value.keyword?.trim()) || + params.value.status !== 'all' || + params.value.type !== 'all' || + params.value.health !== 'all' + ) + + const buildCurrentViewPresetParams = (): AdminFileViewPresetParams => ({ + keyword: params.value.keyword || '', + status: params.value.status || 'all', + type: params.value.type || 'all', + health: params.value.health || 'all', + sortBy: params.value.sortBy || 'created_at', + sortOrder: params.value.sortOrder || 'desc', + size: params.value.size || 10 + }) + + const buildBuiltInViewPreset = ( + id: string, + nameKey: string, + filters: Partial = {} + ): AdminFileViewPreset => { + const presetFilters = normalizeViewPresetParams({ + ...buildDefaultViewPresetParams(), + ...filters + }) + + return { + id, + name: t(nameKey), + filters: presetFilters, + params: presetFilters, + isBuiltIn: true, + isDefault: id === builtInAllViewPresetId, + is_default: id === builtInAllViewPresetId + } + } + + const builtInViewPresets = computed(() => [ + buildBuiltInViewPreset(builtInAllViewPresetId, 'fileManage.viewPresetAll'), + buildBuiltInViewPreset('built_in_attention', 'fileManage.viewPresetAttention', { + health: 'attention' + }), + buildBuiltInViewPreset('built_in_expiring_soon', 'fileManage.viewPresetExpiringSoon', { + health: 'expiring_soon' + }), + buildBuiltInViewPreset('built_in_storage_issue', 'fileManage.viewPresetStorageIssue', { + health: 'storage_issue' + }), + buildBuiltInViewPreset('built_in_never_retrieved', 'fileManage.viewPresetNeverRetrieved', { + health: 'never_retrieved' + }), + buildBuiltInViewPreset('built_in_permanent', 'fileManage.viewPresetPermanent', { + health: 'permanent' + }) + ]) + + const viewPresets = computed(() => [ + ...builtInViewPresets.value, + ...savedViewPresets.value + ]) + + const healthFilterOptions = computed(() => [ + { value: 'all', label: t('fileManage.healthFilters.all'), count: summary.value.totalFiles }, + { + value: 'attention', + label: t('fileManage.healthFilters.attention'), + count: summary.value.healthAttentionCount + }, + { + value: 'danger', + label: t('fileManage.healthFilters.danger'), + count: summary.value.healthDangerCount + }, + { + value: 'warning', + label: t('fileManage.healthFilters.warning'), + count: summary.value.healthWarningCount + }, + { + value: 'expiring_soon', + label: t('fileManage.healthFilters.expiringSoon'), + count: summary.value.expiringSoonCount + }, + { + value: 'storage_issue', + label: t('fileManage.healthFilters.storageIssue'), + count: summary.value.storageIssueCount + }, + { + value: 'never_retrieved', + label: t('fileManage.healthFilters.neverRetrieved'), + count: summary.value.neverRetrievedCount + }, + { + value: 'healthy', + label: t('fileManage.healthFilters.healthy'), + count: summary.value.healthyCount + }, + { + value: 'permanent', + label: t('fileManage.healthFilters.permanent'), + count: summary.value.permanentCount + } + ]) + + const detailPolicyActionOptions = computed(() => [ + { + action: 'extend_24h', + label: t('fileManage.policyActions.extend24h'), + description: t('fileManage.policyActionDescriptions.extend24h') + }, + { + action: 'extend_7d', + label: t('fileManage.policyActions.extend7d'), + description: t('fileManage.policyActionDescriptions.extend7d') + }, + { + action: 'make_permanent', + label: t('fileManage.policyActions.makePermanent'), + description: t('fileManage.policyActionDescriptions.makePermanent') + }, + { + action: 'reset_download_limit', + label: t('fileManage.policyActions.resetDownloadLimit', { + count: detailPolicyDownloadLimit + }), + description: t('fileManage.policyActionDescriptions.resetDownloadLimit', { + count: detailPolicyDownloadLimit + }) + } + ]) + + const batchPolicyActionOptions = computed( + () => detailPolicyActionOptions.value + ) + + const inferIsText = (file: FileListItem) => { + if (typeof file.isText === 'boolean') return file.isText + if (typeof file.is_text === 'boolean') return file.is_text + if (file.type) return file.type === 'text' + return Boolean(file.text) + } + + const inferIsExpired = (file: FileListItem) => { + if (typeof file.isExpired === 'boolean') return file.isExpired + if (typeof file.is_expired === 'boolean') return file.is_expired + if ( + file.expired_count !== null && + file.expired_count !== undefined && + file.expired_count === 0 + ) { + return true + } + return isExpiredByDate(file.expired_at) + } + + const inferIsChunked = (file: FileListItem) => Boolean(file.isChunked ?? file.is_chunked) + + const getRemainingDownloads = (file: FileListItem) => { + if (file.remainingDownloads !== undefined) return file.remainingDownloads + if (file.remaining_downloads !== undefined) return file.remaining_downloads + if ( + file.expired_count !== null && + file.expired_count !== undefined && + file.expired_count >= 0 + ) { + return Math.max(file.expired_count, 0) + } + return null + } + + const buildFallbackSummary = (files: AdminFileViewItem[], total: number): AdminFileSummary => ({ + totalFiles: total, + activeCount: files.filter((file) => !file.isExpiredFile).length, + expiredCount: files.filter((file) => file.isExpiredFile).length, + textCount: files.filter((file) => file.isTextFile).length, + fileCount: files.filter((file) => !file.isTextFile).length, + chunkedCount: files.filter((file) => file.isChunkedFile).length, + healthAttentionCount: files.filter((file) => + ['danger', 'warning'].includes(file.statusInsightSeverity) + ).length, + healthDangerCount: files.filter((file) => file.statusInsightSeverity === 'danger').length, + healthWarningCount: files.filter((file) => file.statusInsightSeverity === 'warning').length, + expiringSoonCount: files.filter((file) => file.statusInsightReasons.includes('expires_soon')) + .length, + storageIssueCount: files.filter((file) => + file.statusInsightReasons.includes('storage_metadata_incomplete') + ).length, + neverRetrievedCount: files.filter((file) => + file.statusInsightReasons.includes('never_retrieved') + ).length, + healthyCount: files.filter((file) => file.statusInsightSeverity === 'success').length, + permanentCount: files.filter((file) => file.statusInsightState === 'permanent').length, + storageUsed: files.reduce((totalSize, file) => totalSize + normalizeCount(file.size), 0), + usedCount: files.reduce( + (totalUsed, file) => totalUsed + normalizeCount(file.usedCount ?? file.used_count), + 0 + ) + }) + + const normalizeSummary = ( + rawSummary: Partial | undefined, + files: AdminFileViewItem[], + total: number + ): AdminFileSummary => { + const fallback = buildFallbackSummary(files, total) + if (!rawSummary) return fallback + + return { + totalFiles: normalizeCount(rawSummary.totalFiles ?? fallback.totalFiles), + activeCount: normalizeCount(rawSummary.activeCount ?? fallback.activeCount), + expiredCount: normalizeCount(rawSummary.expiredCount ?? fallback.expiredCount), + textCount: normalizeCount(rawSummary.textCount ?? fallback.textCount), + fileCount: normalizeCount(rawSummary.fileCount ?? fallback.fileCount), + chunkedCount: normalizeCount(rawSummary.chunkedCount ?? fallback.chunkedCount), + healthAttentionCount: normalizeCount( + rawSummary.healthAttentionCount ?? fallback.healthAttentionCount + ), + healthDangerCount: normalizeCount(rawSummary.healthDangerCount ?? fallback.healthDangerCount), + healthWarningCount: normalizeCount( + rawSummary.healthWarningCount ?? fallback.healthWarningCount + ), + expiringSoonCount: normalizeCount(rawSummary.expiringSoonCount ?? fallback.expiringSoonCount), + storageIssueCount: normalizeCount(rawSummary.storageIssueCount ?? fallback.storageIssueCount), + neverRetrievedCount: normalizeCount( + rawSummary.neverRetrievedCount ?? fallback.neverRetrievedCount + ), + healthyCount: normalizeCount(rawSummary.healthyCount ?? fallback.healthyCount), + permanentCount: normalizeCount(rawSummary.permanentCount ?? fallback.permanentCount), + storageUsed: normalizeCount(rawSummary.storageUsed ?? fallback.storageUsed), + usedCount: normalizeCount(rawSummary.usedCount ?? fallback.usedCount) + } + } + + const normalizeInsightSeverity = ( + severity: AdminFileInsightSeverity | undefined + ): AdminFileInsightSeverity => { + if (severity === 'success' || severity === 'warning' || severity === 'danger') { + return severity + } + if (severity === 'info' || severity === 'neutral') { + return severity + } + return 'neutral' + } + + const createFileViewItem = (file: FileListItem): AdminFileViewItem => { + const isTextFile = inferIsText(file) + const isExpiredFile = inferIsExpired(file) + const isChunkedFile = inferIsChunked(file) + const remainingDownloadsValue = getRemainingDownloads(file) + const usedCount = normalizeCount(file.usedCount ?? file.used_count) + const statusInsights = file.statusInsights ?? file.status_insights + const isPermanentFile = + !file.expired_at && + (file.expired_count === null || file.expired_count === undefined || file.expired_count < 0) + const statusInsightState = + statusInsights?.state ?? + (isExpiredFile ? 'expired' : isPermanentFile ? 'permanent' : 'available') + const statusInsightNextAction = + statusInsights?.nextAction ?? statusInsights?.next_action ?? 'monitor' + const statusInsightSeverity = normalizeInsightSeverity(statusInsights?.severity) + + return { + ...file, + displayName: file.name || `${file.prefix}${file.suffix}` || file.code, + displaySize: formatFileSize(file.size), + displayExpiredAt: file.expired_at + ? formatTimestamp(file.expired_at) + : t('send.expiration.units.forever'), + displayUsage: `${usedCount} ${t('common.times')}`, + displayHealthState: t(`fileManage.insightStates.${statusInsightState}`), + displayHealthAction: t(`fileManage.insightActions.${statusInsightNextAction}`), + isTextFile, + isExpiredFile, + isChunkedFile, + remainingDownloadsValue, + canPreviewText: isTextFile, + statusInsightSeverity, + statusInsightState, + statusInsightNextAction, + statusInsightReasons: statusInsights?.reasons || [] + } + } + + const formatTimelineValue = (item: AdminFileDetailTimelineItem) => { + if (item.key === 'download_limit') { + if (item.status === 'unlimited') return t('fileManage.unlimited') + if (typeof item.value === 'number') return t('fileManage.remaining', { count: item.value }) + } + + if (item.key === 'retrieved' && typeof item.value === 'number') { + return `${item.value} ${t('common.times')}` + } + + if (item.key === 'expiration_policy') { + if (item.status === 'unlimited') return t('send.expiration.units.forever') + if (typeof item.value === 'number') { + const duration = formatDuration(Math.abs(item.value), (key) => t(key)) + return item.value <= 0 + ? t('fileManage.timeline.expiration_policy.overdue', { time: duration }) + : t('fileManage.timeline.expiration_policy.remaining', { time: duration }) + } + } + + if (item.detail) return item.detail + if (item.value !== null && item.value !== undefined) return String(item.value) + return '' + } + + const createTimelineViewItems = (items: AdminFileDetailTimelineItem[] = []) => + items.map((item) => { + const timestampText = item.timestamp ? formatTimestamp(item.timestamp) : '' + const valueText = formatTimelineValue(item) + const metaParts = [timestampText, valueText].filter(Boolean) + return { + ...item, + severity: normalizeInsightSeverity(item.severity), + displayTitle: t(`fileManage.timeline.${item.key}.title`), + displayDescription: t(`fileManage.timeline.${item.key}.description`, { + status: t(`fileManage.timeline.status.${item.status || 'pending'}`), + detail: item.detail || '', + value: valueText || '' + }), + displayMeta: metaParts.length > 0 ? metaParts.join(' · ') : '-' + } + }) + + const normalizeMetadataTags = (tags: string[] | undefined) => + Array.isArray(tags) ? tags.map((tag) => String(tag).trim()).filter(Boolean) : [] + + const parseMetadataTags = (value: string) => + normalizeMetadataTags(value.split(metadataTagSeparatorPattern)) + + const formatMetadataTags = (tags: string[]) => normalizeMetadataTags(tags).join(', ') + + const createFileDetailViewItem = ( + file: FileListItem | AdminFileDetailResponse + ): AdminFileDetailViewItem => { + const detail = file as AdminFileDetailResponse + const policy = detail.policy + const storage = detail.storage + const expiredAt = policy?.expiredAt ?? policy?.expired_at ?? file.expired_at ?? null + const expiredCount = policy?.expiredCount ?? policy?.expired_count ?? file.expired_count ?? null + const prefixedName = `${file.prefix || ''}${file.suffix || ''}` + const displayName = + detail.displayName ?? + detail.display_name ?? + detail.filename ?? + file.name ?? + (prefixedName || file.code) + const normalizedFile: FileListItem = { + ...file, + name: displayName, + expired_at: expiredAt, + expired_count: expiredCount + } + const viewItem = createFileViewItem(normalizedFile) + const remainingDownloadsValue = + policy?.remainingDownloads ?? policy?.remaining_downloads ?? viewItem.remainingDownloadsValue + const canPreviewText = + detail.canPreviewText ?? detail.can_preview_text ?? viewItem.canPreviewText + const canDownloadFile = detail.canDownload ?? detail.can_download ?? !viewItem.isExpiredFile + const textLengthValue = normalizeCount( + detail.textLength ?? detail.text_length ?? detail.text?.length + ) + const isPermanentFile = + detail.isPermanent ?? + detail.is_permanent ?? + policy?.isPermanent ?? + policy?.is_permanent ?? + (!expiredAt && (expiredCount === null || expiredCount === undefined || expiredCount < 0)) + const hasDownloadLimitFile = + detail.hasDownloadLimit ?? + detail.has_download_limit ?? + (expiredCount !== null && expiredCount !== undefined && expiredCount >= 0) + const hasExpirationTimeFile = + detail.hasExpirationTime ?? detail.has_expiration_time ?? Boolean(expiredAt) + const storageBackendValue = + storage?.backend ?? detail.storageBackend ?? detail.storage_backend ?? '-' + const isChunkedStorage = storage?.isChunked ?? storage?.is_chunked ?? viewItem.isChunkedFile + const statusInsights = detail.statusInsights ?? detail.status_insights + const statusInsightState = + statusInsights?.state ?? + (viewItem.isExpiredFile ? 'expired' : isPermanentFile ? 'permanent' : 'available') + const statusInsightNextAction = + statusInsights?.nextAction ?? statusInsights?.next_action ?? 'monitor' + const metadata = detail.metadata ?? detail.meta + const metadataNote = detail.note ?? metadata?.note ?? '' + const metadataTags = normalizeMetadataTags(detail.tags ?? metadata?.tags) + const metadataUpdatedAt = + detail.metadataUpdatedAt ?? + detail.metadata_updated_at ?? + metadata?.updatedAt ?? + metadata?.updated_at ?? + null + + return { + ...viewItem, + displayName, + displayExpiredAt: expiredAt ? formatTimestamp(expiredAt) : t('send.expiration.units.forever'), + displayCreatedAt: file.created_at ? formatTimestamp(file.created_at) : '-', + displayRetrieveUrl: buildRetrieveUrl(file.code), + remainingDownloadsValue, + canPreviewText, + textLengthValue, + canDownloadFile, + isPermanentFile, + hasDownloadLimitFile, + hasExpirationTimeFile, + isChunkedStorage: Boolean(isChunkedStorage), + storageBackendValue, + fileHashValue: storage?.fileHash ?? storage?.file_hash ?? detail.fileHash ?? detail.file_hash, + filePathValue: storage?.filePath ?? storage?.file_path ?? detail.filePath ?? detail.file_path, + uuidFileNameValue: + storage?.uuidFileName ?? + storage?.uuid_file_name ?? + detail.uuidFileName ?? + detail.uuid_file_name, + uploadIdValue: storage?.uploadId ?? storage?.upload_id ?? detail.uploadId ?? detail.upload_id, + metadataNote, + metadataTags, + metadataUpdatedAt, + statusInsightSeverity: normalizeInsightSeverity(statusInsights?.severity), + statusInsightState, + statusInsightNextAction, + statusInsightReasons: statusInsights?.reasons || [], + statusInsightMetrics: statusInsights?.metrics, + detailTimeline: createTimelineViewItems(detail.timeline || []) + } + } + + const syncDetailMetadataForm = (file: AdminFileDetailViewItem | null) => { + detailMetadataForm.value = { + note: file?.metadataNote || '', + tagsText: file ? formatMetadataTags(file.metadataTags) : '' + } + } + + const setSelectedFileDetail = (file: FileListItem | AdminFileDetailResponse) => { + const nextFile = createFileDetailViewItem(file) + selectedFileDetail.value = nextFile + syncDetailMetadataForm(nextFile) + } + + const syncSelectedFilesWithCurrentPage = () => { + const visibleIds = new Set(tableData.value.map((file) => file.id)) + selectedFileIds.value = new Set( + Array.from(selectedFileIds.value).filter((id) => visibleIds.has(id)) + ) + } + + const clearSelection = () => { + selectedFileIds.value = new Set() + } + + const toggleFileSelection = (id: number) => { + const nextSelectedIds = new Set(selectedFileIds.value) + if (nextSelectedIds.has(id)) { + nextSelectedIds.delete(id) + } else { + nextSelectedIds.add(id) + } + selectedFileIds.value = nextSelectedIds + } + + const toggleCurrentPageSelection = () => { + if (isAllCurrentPageSelected.value) { + clearSelection() + return + } + + selectedFileIds.value = new Set(tableData.value.map((file) => file.id)) + } + + const resetEditForm = () => { + editForm.value = { + id: null, + code: '', + prefix: '', + suffix: '', + expired_at: '', + expired_count: null + } + } + + const resetBatchEditForm = () => { + batchEditForm.value = emptyBatchEditForm() + } + + const openBatchEditModal = () => { + if (!hasSelectedFiles.value || isBatchActionRunning.value) return + + resetBatchEditForm() + showBatchEditModal.value = true + } + + const closeBatchEditModal = (force = false) => { + if (isBatchUpdating.value && !force) return + + showBatchEditModal.value = false + resetBatchEditForm() + } + + const buildBatchUpdatePayload = (ids: number[]): AdminBatchUpdateFilesRequest | null => { + const form = batchEditForm.value + + if (form.mode === 'expiresAt') { + if (!form.expired_at) return null + return { + ids, + expired_at: form.expired_at + } + } + + if (form.mode === 'downloadLimit') { + if (form.expired_count === null || !Number.isFinite(form.expired_count)) return null + return { + ids, + expired_count: form.expired_count + } + } + + return { + ids, + clearExpiredAt: true, + clear_expired_at: true + } + } + + const buildLegacyBatchPatchPayload = ( + id: number, + payload: AdminBatchUpdateFilesRequest + ): AdminFilePatchPayload => { + if (payload.clearExpiredAt || payload.clear_expired_at) { + return { + id, + expired_at: legacyForeverExpiresAt, + expired_count: -1 + } + } + + const patchPayload: AdminFilePatchPayload = { id } + if (payload.expired_at !== undefined) { + patchPayload.expired_at = payload.expired_at + } + if (payload.expired_count !== undefined) { + patchPayload.expired_count = payload.expired_count + } + return patchPayload + } + + const buildPolicyActionRequest = ( + file: AdminFileDetailViewItem, + action: AdminFilePolicyAction + ): AdminFilePolicyActionRequest => { + const request: AdminFilePolicyActionRequest = { + id: file.id, + action + } + + if (action === 'reset_download_limit') { + request.downloadLimit = detailPolicyDownloadLimit + request.download_limit = detailPolicyDownloadLimit + } + + return request + } + + const buildBatchPolicyActionRequest = ( + ids: number[], + action: AdminFilePolicyAction + ): AdminBatchPolicyActionRequest => { + const request: AdminBatchPolicyActionRequest = { + ids, + action + } + + if (action === 'reset_download_limit') { + request.downloadLimit = detailPolicyDownloadLimit + request.download_limit = detailPolicyDownloadLimit + } + + return request + } + + const getPolicyActionBaseDate = (file: Pick) => { + const now = new Date() + const expiredAt = file.expired_at ? new Date(file.expired_at) : null + if (!expiredAt || Number.isNaN(expiredAt.getTime()) || expiredAt.getTime() < now.getTime()) { + return now + } + return expiredAt + } + + const buildLegacyPolicyActionPayload = ( + file: Pick, + action: AdminFilePolicyAction + ): AdminFilePatchPayload => { + if (action === 'make_permanent') { + return { + id: file.id, + expired_at: legacyForeverExpiresAt, + expired_count: -1 + } + } + + if (action === 'reset_download_limit') { + return { + id: file.id, + expired_count: detailPolicyDownloadLimit + } + } + + const offsetDays = action === 'extend_7d' ? 7 : 1 + const nextExpiredAt = new Date( + getPolicyActionBaseDate(file).getTime() + offsetDays * dayInMilliseconds + ) + return { + id: file.id, + expired_at: formatLocalDateTime(nextExpiredAt) + } + } + + const getPresetParams = (preset: AdminFileViewPreset) => + normalizeViewPresetParams(preset.filters ?? preset.params) + + const normalizeSavedViewPreset = (preset: AdminFileViewPreset): AdminFileViewPreset | null => { + const id = String(preset.id || '').trim() + const name = String(preset.name || '').trim() + if (!id || !name) return null + + const presetParams = getPresetParams(preset) + return { + ...preset, + id, + name, + filters: presetParams, + params: presetParams, + isBuiltIn: false + } + } + + const upsertSavedViewPreset = (preset: AdminFileViewPreset) => { + const normalizedPreset = normalizeSavedViewPreset(preset) + if (!normalizedPreset) return null + + savedViewPresets.value = [ + ...savedViewPresets.value.filter((item) => item.id !== normalizedPreset.id), + normalizedPreset + ] + return normalizedPreset + } + + const buildViewPresetRequest = (name: string, id?: string): AdminFileViewPresetRequest | null => { + const normalizedName = name.trim() + if (!normalizedName) return null + + const filters = normalizeViewPresetParams(buildCurrentViewPresetParams()) + return { + id, + name: normalizedName, + filters, + params: filters + } + } + + const markViewPresetDirty = () => { + selectedViewPresetId.value = '' + } + + const loadViewPresets = async () => { + isViewPresetLoading.value = true + try { + const response = await FileService.getAdminFileViewPresets() + const rawPresets = response.detail?.presets ?? response.detail?.items ?? [] + savedViewPresets.value = rawPresets + .map((preset) => normalizeSavedViewPreset(preset)) + .filter((preset): preset is AdminFileViewPreset => Boolean(preset)) + + if ( + selectedViewPresetId.value && + !viewPresets.value.some((preset) => preset.id === selectedViewPresetId.value) + ) { + selectedViewPresetId.value = builtInAllViewPresetId + } + } catch (error: unknown) { + savedViewPresets.value = [] + alertStore.showAlert( + isLegacyEndpointUnavailable(error) + ? t('fileManage.viewPresetLoadFailed') + : getErrorMessage(error, t('fileManage.viewPresetLoadFailed')), + 'warning' + ) + } finally { + isViewPresetLoading.value = false + } + } + + const applyViewPreset = async (idOrPreset: string | AdminFileViewPreset) => { + const preset = + typeof idOrPreset === 'string' + ? viewPresets.value.find((item) => item.id === idOrPreset) + : idOrPreset + if (!preset) return + + const presetParams = getPresetParams(preset) + params.value.keyword = presetParams.keyword + params.value.status = presetParams.status + params.value.type = presetParams.type + params.value.health = presetParams.health + params.value.sortBy = presetParams.sortBy + params.value.sortOrder = presetParams.sortOrder + params.value.size = presetParams.size + params.value.page = 1 + selectedViewPresetId.value = preset.id + clearSelection() + await loadFiles() + } + + const saveCurrentViewPreset = async (name: string) => { + const request = buildViewPresetRequest(name) + if (!request || isViewPresetSaving.value) return null + + isViewPresetSaving.value = true + try { + const response = await FileService.saveAdminFileViewPreset(request) + const savedPreset = response.detail ? upsertSavedViewPreset(response.detail) : null + selectedViewPresetId.value = savedPreset?.id || selectedViewPresetId.value + alertStore.showAlert(t('fileManage.viewPresetSaveSuccess'), 'success') + return savedPreset + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('fileManage.viewPresetSaveFailed')), 'error') + return null + } finally { + isViewPresetSaving.value = false + } + } + + const updateSelectedViewPreset = async () => { + const preset = savedViewPresets.value.find((item) => item.id === selectedViewPresetId.value) + if (!preset || isViewPresetSaving.value) return null + + const request = buildViewPresetRequest(preset.name, preset.id) + if (!request) return null + + isViewPresetSaving.value = true + try { + const response = await FileService.saveAdminFileViewPreset(request) + const savedPreset = response.detail ? upsertSavedViewPreset(response.detail) : null + selectedViewPresetId.value = savedPreset?.id || preset.id + alertStore.showAlert(t('fileManage.viewPresetSaveSuccess'), 'success') + return savedPreset + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('fileManage.viewPresetSaveFailed')), 'error') + return null + } finally { + isViewPresetSaving.value = false + } + } + + const deleteSelectedViewPreset = async () => { + const preset = savedViewPresets.value.find((item) => item.id === selectedViewPresetId.value) + if (!preset || isViewPresetDeleting.value) return + if (!window.confirm(t('fileManage.viewPresetDeleteConfirm', { name: preset.name }))) return + + isViewPresetDeleting.value = true + try { + await FileService.deleteAdminFileViewPreset(preset.id) + savedViewPresets.value = savedViewPresets.value.filter((item) => item.id !== preset.id) + markViewPresetDirty() + alertStore.showAlert(t('fileManage.viewPresetDeleteSuccess'), 'success') + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('fileManage.viewPresetDeleteFailed')), 'error') + } finally { + isViewPresetDeleting.value = false + } + } + + const refreshSelectedFileDetail = async (fileId: number, detail?: AdminFileDetailResponse) => { + if (detail) { + setSelectedFileDetail(detail) + return + } + + const fallbackFile = tableData.value.find((file) => file.id === fileId) + if (fallbackFile) { + setSelectedFileDetail(fallbackFile) + } + + try { + const response = await FileService.getAdminFileDetail(fileId) + if (response.detail) { + setSelectedFileDetail(response.detail) + } + } catch (error: unknown) { + if (!isLegacyEndpointUnavailable(error)) { + throw error + } + } + } + + const loadFiles = async () => { + isLoading.value = true + try { + hasLoadError.value = false + const res = await FileService.getAdminFileList(requestParams.value) + if (!res.detail) return + + tableData.value = res.detail.data.map(createFileViewItem) + params.value.total = res.detail.total + summary.value = normalizeSummary(res.detail.summary, tableData.value, res.detail.total) + syncSelectedFilesWithCurrentPage() + } catch (error) { + hasLoadError.value = true + alertStore.showAlert( + getErrorMessage(error, t('manage.fileManage.loadFileListFailed')), + 'error' + ) + } finally { + isLoading.value = false + } + } + + const handleSearch = async () => { + markViewPresetDirty() + params.value.page = 1 + await loadFiles() + } + + const refreshFiles = async () => { + await loadFiles() + } + + const resetFilters = async () => { + params.value.page = 1 + params.value.keyword = '' + params.value.status = 'all' + params.value.type = 'all' + params.value.health = 'all' + params.value.sortBy = 'created_at' + params.value.sortOrder = 'desc' + selectedViewPresetId.value = builtInAllViewPresetId + clearSelection() + await loadFiles() + } + + const setStatusFilter = async (status: AdminFileStatusFilter) => { + markViewPresetDirty() + params.value.status = status + params.value.page = 1 + await loadFiles() + } + + const setTypeFilter = async (type: AdminFileTypeFilter) => { + markViewPresetDirty() + params.value.type = type + params.value.page = 1 + await loadFiles() + } + + const setHealthFilter = async (health: AdminFileHealthFilter) => { + markViewPresetDirty() + params.value.health = health + params.value.page = 1 + await loadFiles() + } + + const handlePageChange = async (page: number | string) => { + if (typeof page === 'string') return + if (page < 1 || page > totalPages.value) return + + params.value.page = page + await loadFiles() + } + + const openEditModal = (file: FileListItem) => { + editForm.value = { + id: file.id, + code: file.code, + prefix: file.prefix, + suffix: file.suffix, + expired_at: file.expired_at ? file.expired_at.slice(0, 16) : '', + expired_count: file.expired_count + } + showEditModal.value = true + } + + const closeEditModal = () => { + showEditModal.value = false + resetEditForm() + } + + const handleUpdate = async () => { + if (isSaving.value) return + + isSaving.value = true + try { + await FileService.updateFile(editForm.value) + await loadFiles() + closeEditModal() + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('manage.fileManage.updateFailed')), 'error') + } finally { + isSaving.value = false + } + } + + const deleteFile = async (id: number) => { + if (!window.confirm(t('manage.fileManage.deleteConfirm'))) { + return + } + + try { + await FileService.deleteAdminFile(id) + if (tableData.value.length === 1 && params.value.page > 1) { + params.value.page -= 1 + } + selectedFileIds.value.delete(id) + selectedFileIds.value = new Set(selectedFileIds.value) + await loadFiles() + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('manage.fileManage.deleteFailed')), 'error') + } + } + + const deleteAdminFilesWithFallback = async ( + ids: number[] + ): Promise> => { + try { + return await FileService.deleteAdminFiles(ids) + } catch (error: unknown) { + if (!isLegacyEndpointUnavailable(error)) { + throw error + } + + await Promise.all(ids.map((id) => FileService.deleteAdminFile(id))) + return { + code: 200, + detail: { + deletedCount: ids.length, + deleted_count: ids.length, + deleted: ids, + missing: [], + failed: [] + } + } + } + } + + const updateAdminFilesWithFallback = async ( + ids: number[], + payload: AdminBatchUpdateFilesRequest + ): Promise> => { + try { + return await FileService.updateAdminFiles(payload) + } catch (error: unknown) { + if (!isLegacyEndpointUnavailable(error)) { + throw error + } + + await Promise.all( + ids.map((id) => FileService.updateFile(buildLegacyBatchPatchPayload(id, payload))) + ) + + return { + code: 200, + detail: { + updatedCount: ids.length, + updated_count: ids.length, + updated: ids, + missing: [], + failed: [] + } + } + } + } + + const applyAdminFilesPolicyActionWithFallback = async ( + ids: number[], + action: AdminFilePolicyAction + ): Promise> => { + const request = buildBatchPolicyActionRequest(ids, action) + try { + return await FileService.applyAdminFilesPolicyAction(request) + } catch (error: unknown) { + if (!isLegacyEndpointUnavailable(error)) { + throw error + } + + const filesById = new Map(tableData.value.map((file) => [file.id, file])) + const updated: number[] = [] + const missing: number[] = [] + const failed: { id: number; reason: string }[] = [] + + for (const id of ids) { + const file = filesById.get(id) + if (!file) { + missing.push(id) + continue + } + + try { + await FileService.updateFile(buildLegacyPolicyActionPayload(file, action)) + updated.push(id) + } catch (legacyError: unknown) { + failed.push({ id, reason: getErrorMessage(legacyError, t('common.unknownError')) }) + } + } + + return { + code: 200, + detail: { + requestedCount: ids.length, + requested_count: ids.length, + uniqueCount: ids.length, + unique_count: ids.length, + updatedCount: updated.length, + updated_count: updated.length, + missingCount: missing.length, + missing_count: missing.length, + failedCount: failed.length, + failed_count: failed.length, + action, + updated, + missing, + failed + } + } + } + } + + const deleteSelectedFiles = async () => { + if (!hasSelectedFiles.value || isBatchActionRunning.value) return + + const ids = Array.from(selectedFileIds.value) + if (!window.confirm(t('fileManage.batchDeleteConfirm', { count: ids.length }))) { + return + } + + isBatchDeleting.value = true + try { + const response = await deleteAdminFilesWithFallback(ids) + const detail = response.detail + const deletedCount = detail?.deletedCount ?? detail?.deleted_count ?? ids.length + const failedCount = detail?.failedCount ?? detail?.failed_count ?? 0 + + if (tableData.value.length <= deletedCount && params.value.page > 1) { + params.value.page -= 1 + } + + clearSelection() + await loadFiles() + alertStore.showAlert( + t( + failedCount > 0 + ? 'fileManage.batchDeletePartialSuccess' + : 'fileManage.batchDeleteSuccess', + { + count: deletedCount, + failed: failedCount + } + ), + failedCount > 0 ? 'warning' : 'success' + ) + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('fileManage.batchDeleteFailed')), 'error') + } finally { + isBatchDeleting.value = false + } + } + + const handleBatchUpdate = async () => { + if (!hasSelectedFiles.value || isBatchActionRunning.value) return + + const ids = Array.from(selectedFileIds.value) + const payload = buildBatchUpdatePayload(ids) + if (!payload) { + alertStore.showAlert(t('fileManage.batchUpdateNoFields'), 'warning') + return + } + + if (!window.confirm(t('fileManage.batchUpdateConfirm', { count: ids.length }))) { + return + } + + isBatchUpdating.value = true + try { + const response = await updateAdminFilesWithFallback(ids, payload) + const detail = response.detail + const updatedCount = detail?.updatedCount ?? detail?.updated_count ?? ids.length + const failedCount = detail?.failedCount ?? detail?.failed_count ?? 0 + + clearSelection() + await loadFiles() + closeBatchEditModal(true) + alertStore.showAlert( + t( + failedCount > 0 + ? 'fileManage.batchUpdatePartialSuccess' + : 'fileManage.batchUpdateSuccess', + { + count: updatedCount, + failed: failedCount + } + ), + failedCount > 0 ? 'warning' : 'success' + ) + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('fileManage.batchUpdateFailed')), 'error') + } finally { + isBatchUpdating.value = false + } + } + + const applySelectedPolicyAction = async (action: AdminFilePolicyAction) => { + if (!hasSelectedFiles.value || isBatchActionRunning.value) return + + const ids = Array.from(selectedFileIds.value) + const actionLabel = + batchPolicyActionOptions.value.find((option) => option.action === action)?.label || action + if ( + !window.confirm( + t('fileManage.batchPolicyActionConfirm', { + count: ids.length, + action: actionLabel + }) + ) + ) { + return + } + + isBatchPolicyActionRunning.value = true + try { + const response = await applyAdminFilesPolicyActionWithFallback(ids, action) + const detail = response.detail + const updatedCount = detail?.updatedCount ?? detail?.updated_count ?? ids.length + const failedCount = + (detail?.failedCount ?? detail?.failed_count ?? 0) + + (detail?.missingCount ?? detail?.missing_count ?? 0) + + clearSelection() + await loadFiles() + alertStore.showAlert( + t( + failedCount > 0 + ? 'fileManage.batchPolicyActionPartialSuccess' + : 'fileManage.batchPolicyActionSuccess', + { + count: updatedCount, + failed: failedCount + } + ), + failedCount > 0 ? 'warning' : 'success' + ) + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('fileManage.batchPolicyActionFailed')), 'error') + } finally { + isBatchPolicyActionRunning.value = false + } + } + + const openTextPreview = async (file: AdminFileViewItem) => { + if (!file.isTextFile || isPreviewLoading.value) return + + previewFile.value = file + previewText.value = file.text || '' + previewMetaText.value = '' + showTextPreview.value = true + isPreviewLoading.value = true + + try { + const response = await FileService.previewAdminFile(file.id) + if (!response.detail) { + throw new Error(t('fileManage.previewFailed')) + } + + previewText.value = response.detail.content + previewMetaText.value = response.detail.truncated + ? t('fileManage.previewTruncated', { + shown: response.detail.previewLength ?? response.detail.preview_length ?? 0, + total: response.detail.length + }) + : t('fileManage.previewComplete', { count: response.detail.length }) + } catch (error: unknown) { + if (file.text) { + previewText.value = file.text + previewMetaText.value = t('fileManage.previewFallback') + return + } + + closeTextPreview() + alertStore.showAlert(getErrorMessage(error, t('fileManage.previewFailed')), 'error') + } finally { + isPreviewLoading.value = false + } + } + + const closeTextPreview = () => { + showTextPreview.value = false + previewText.value = '' + previewFile.value = null + previewMetaText.value = '' + } + + const openFileDetail = async (file: AdminFileViewItem) => { + const requestSerial = ++detailRequestSerial + setSelectedFileDetail(file) + showFileDetailModal.value = true + isDetailLoading.value = true + + try { + const response = await FileService.getAdminFileDetail(file.id) + if (response.detail && requestSerial === detailRequestSerial) { + setSelectedFileDetail(response.detail) + } + } catch (error: unknown) { + if (isLegacyEndpointUnavailable(error)) return + + alertStore.showAlert(getErrorMessage(error, t('fileManage.detailFailed')), 'error') + } finally { + if (requestSerial === detailRequestSerial) { + isDetailLoading.value = false + } + } + } + + const applyDetailPolicyAction = async (action: AdminFilePolicyAction) => { + const file = selectedFileDetail.value + if (!file || isDetailPolicyActionRunning.value) return + + isDetailPolicyActionRunning.value = true + try { + let nextDetail: AdminFileDetailResponse | undefined + + try { + const response = await FileService.applyAdminFilePolicyAction( + buildPolicyActionRequest(file, action) + ) + nextDetail = response.detail + } catch (error: unknown) { + if (!isLegacyEndpointUnavailable(error)) { + throw error + } + await FileService.updateFile(buildLegacyPolicyActionPayload(file, action)) + } + + await loadFiles() + await refreshSelectedFileDetail(file.id, nextDetail) + alertStore.showAlert(t('fileManage.policyActionSuccess'), 'success') + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('fileManage.policyActionFailed')), 'error') + } finally { + isDetailPolicyActionRunning.value = false + } + } + + const updateDetailMetadata = async () => { + const file = selectedFileDetail.value + if (!file || isDetailMetadataSaving.value) return + + const payload: AdminFileMetadataRequest = { + id: file.id, + note: detailMetadataForm.value.note, + tags: parseMetadataTags(detailMetadataForm.value.tagsText) + } + + isDetailMetadataSaving.value = true + try { + const response = await FileService.updateAdminFileMetadata(payload) + if (response.detail) { + setSelectedFileDetail(response.detail) + } else { + await refreshSelectedFileDetail(file.id) + } + alertStore.showAlert(t('fileManage.metadataSaveSuccess'), 'success') + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('fileManage.metadataSaveFailed')), 'error') + } finally { + isDetailMetadataSaving.value = false + } + } + + const closeFileDetail = () => { + detailRequestSerial += 1 + showFileDetailModal.value = false + selectedFileDetail.value = null + syncDetailMetadataForm(null) + isDetailLoading.value = false + } + + const copyText = async () => { + await copyToClipboard(previewText.value, { + successMsg: t('fileManage.copySuccess'), + errorMsg: t('fileManage.copyFailed'), + notify: (message, type) => alertStore.showAlert(message, type) + }) + } + + const copyDetailRetrieveCode = async () => { + if (!selectedFileDetail.value) return + + await copyToClipboard(selectedFileDetail.value.code, { + successMsg: t('fileManage.copyCodeSuccess'), + errorMsg: t('fileManage.copyFailed'), + notify: (message, type) => alertStore.showAlert(message, type) + }) + } + + const copyDetailRetrieveLink = async () => { + if (!selectedFileDetail.value) return + + await copyToClipboard(selectedFileDetail.value.displayRetrieveUrl, { + successMsg: t('fileManage.copyLinkSuccess'), + errorMsg: t('fileManage.copyFailed'), + notify: (message, type) => alertStore.showAlert(message, type) + }) + } + + const exportPreviewText = async () => { + if (!previewFile.value || !previewText.value) return + + try { + await exportAdminTextFile(previewFile.value, previewText.value) + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('fileManage.downloadFailed')), 'error') + } + } + + const downloadFile = async (file: AdminFileViewItem) => { + if (downloadingFileId.value) return + + downloadingFileId.value = file.id + try { + const response = await FileService.downloadAdminFile(file.id) + await downloadAdminManagedFile(file, response) + alertStore.showAlert( + t(file.isTextFile ? 'fileManage.exportSuccess' : 'fileManage.downloadSuccess', { + name: getSafeFilename(file.displayName || file.code) + }), + 'success' + ) + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('fileManage.downloadFailed')), 'error') + } finally { + downloadingFileId.value = null + } + } + + return { + tableData, + hasLoadError, + hasActiveFilters, + isLoading, + isAllCurrentPageSelected, + isBatchActionRunning, + isBatchDeleting, + isBatchPolicyActionRunning, + isBatchUpdating, + isCurrentPagePartiallySelected, + isDetailLoading, + isDetailMetadataSaving, + isDetailPolicyActionRunning, + isPreviewLoading, + isSaving, + isViewPresetDeleting, + isViewPresetLoading, + isViewPresetSaving, + batchEditForm, + batchPolicyActionOptions, + detailPolicyActionOptions, + detailMetadataForm, + downloadingFileId, + healthFilterOptions, + hasSelectedFiles, + params, + previewFile, + previewMetaText, + selectedFileDetail, + selectedCount, + selectedFileIds, + selectedViewPresetId, + storageUsedText, + summary, + showBatchEditModal, + showEditModal, + showFileDetailModal, + editForm, + showTextPreview, + previewText, + totalPages, + viewPresets, + applyViewPreset, + closeEditModal, + closeBatchEditModal, + closeFileDetail, + closeTextPreview, + copyDetailRetrieveCode, + copyDetailRetrieveLink, + copyText, + clearSelection, + deleteFile, + deleteSelectedFiles, + deleteSelectedViewPreset, + downloadFile, + exportPreviewText, + handlePageChange, + handleSearch, + applyDetailPolicyAction, + applySelectedPolicyAction, + handleBatchUpdate, + handleUpdate, + loadViewPresets, + loadFiles, + openBatchEditModal, + openEditModal, + openFileDetail, + openTextPreview, + refreshFiles, + resetFilters, + saveCurrentViewPreset, + setHealthFilter, + setStatusFilter, + setTypeFilter, + updateSelectedViewPreset, + updateDetailMetadata, + toggleCurrentPageSelection, + toggleFileSelection + } +} diff --git a/src/composables/useAdminLogin.ts b/src/composables/useAdminLogin.ts new file mode 100644 index 0000000..2f68aaf --- /dev/null +++ b/src/composables/useAdminLogin.ts @@ -0,0 +1,59 @@ +import { ref } from 'vue' +import { AuthService } from '@/services' +import { useAdminStore } from '@/stores/adminStore' +import { useAlertStore } from '@/stores/alertStore' +import { getErrorMessage } from '@/utils/common' + +export function useAdminLogin() { + const alertStore = useAlertStore() + const adminStore = useAdminStore() + const password = ref('') + const isLoading = ref(false) + + const validateForm = () => { + if (!password.value) { + alertStore.showAlert('无效的密码', 'error') + return false + } + + if (password.value.length < 6) { + alertStore.showAlert('密码长度至少为6位', 'error') + return false + } + + return true + } + + const handleSubmit = async () => { + if (!validateForm()) return false + + isLoading.value = true + try { + const response = await AuthService.login(password.value) + if (!response.detail?.token || !response.detail.expires_at) { + alertStore.showAlert('登录失败:未获取到有效令牌', 'error') + return false + } + + adminStore.login({ + id: response.detail.id || 'admin', + username: response.detail.username || 'admin', + token: response.detail.token, + token_type: response.detail.token_type, + expires_at: response.detail.expires_at + }) + return true + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, '登录失败'), 'error') + return false + } finally { + isLoading.value = false + } + } + + return { + password, + isLoading, + handleSubmit + } +} diff --git a/src/composables/useAdminSession.ts b/src/composables/useAdminSession.ts new file mode 100644 index 0000000..b70069b --- /dev/null +++ b/src/composables/useAdminSession.ts @@ -0,0 +1,34 @@ +import { AuthService } from '@/services' +import { useAdminStore } from '@/stores/adminStore' + +export function useAdminSession() { + const adminStore = useAdminStore() + + const verifySession = async () => { + if (!adminStore.hasToken) return false + + try { + const response = await AuthService.verifyToken() + if (response.code === 200 && response.detail?.token) { + adminStore.login(response.detail) + return true + } + } catch {} + + adminStore.logout() + return false + } + + const logout = async () => { + try { + await AuthService.logout() + } finally { + adminStore.logout() + } + } + + return { + verifySession, + logout + } +} diff --git a/src/composables/useAppShell.ts b/src/composables/useAppShell.ts new file mode 100644 index 0000000..0472ea8 --- /dev/null +++ b/src/composables/useAppShell.ts @@ -0,0 +1,86 @@ +import { computed, onMounted, onUnmounted, provide } from 'vue' +import { useRoute, useRouter } from 'vue-router' +import { AUTH_EVENTS } from '@/services' +import { ROUTES } from '@/constants' +import { useTheme } from './useTheme' +import { usePublicConfigBootstrap } from './usePublicConfigBootstrap' +import { useRouteLoading } from './useRouteLoading' +import { useAdminStore } from '@/stores/adminStore' + +export function useAppShell() { + const route = useRoute() + const router = useRouter() + const { isDarkMode, toggleTheme, initTheme } = useTheme() + const { isLoading, setupRouteLoading } = useRouteLoading(router) + const { syncPublicConfig } = usePublicConfigBootstrap() + const adminStore = useAdminStore() + const showGlobalControls = computed(() => route.meta.showGlobalControls !== false) + const routeTransitionName = computed(() => String(route.meta.routeTransition || 'fade')) + const routeTransitionMode = computed(() => (route.meta.routeTransition ? undefined : 'out-in')) + const routeViewKey = computed(() => + route.path === ROUTES.ADMIN || route.path.startsWith(`${ROUTES.ADMIN}/`) + ? ROUTES.ADMIN + : route.fullPath + ) + + let cleanupThemeListener: (() => void) | null = null + let setupRedirecting = false + + const handleUnauthorized = () => { + adminStore.logout() + if (router.currentRoute.value.path !== ROUTES.LOGIN) { + void router.push({ + path: ROUTES.LOGIN, + query: { + redirect: router.currentRoute.value.fullPath + } + }) + } + } + + const handleSetupRequired = (event: Event) => { + const setupPath = + event instanceof CustomEvent && typeof event.detail?.setupPath === 'string' + ? event.detail.setupPath + : '/setup' + + if ( + !setupPath || + setupRedirecting || + window.location.pathname.replace(/\/+$/, '') === setupPath + ) { + return + } + + setupRedirecting = true + window.location.assign(setupPath) + } + + onMounted(() => { + cleanupThemeListener = initTheme() + setupRouteLoading() + window.addEventListener(AUTH_EVENTS.UNAUTHORIZED, handleUnauthorized) + window.addEventListener(AUTH_EVENTS.SETUP_REQUIRED, handleSetupRequired) + void syncPublicConfig() + }) + + onUnmounted(() => { + cleanupThemeListener?.() + window.removeEventListener(AUTH_EVENTS.UNAUTHORIZED, handleUnauthorized) + window.removeEventListener(AUTH_EVENTS.SETUP_REQUIRED, handleSetupRequired) + }) + + provide('isDarkMode', isDarkMode) + provide('toggleTheme', toggleTheme) + provide('isLoading', isLoading) + + return { + isDarkMode, + isLoading, + route, + routeTransitionMode, + routeTransitionName, + routeViewKey, + showGlobalControls + } +} diff --git a/src/composables/useDashboardStats.ts b/src/composables/useDashboardStats.ts new file mode 100644 index 0000000..9de8ab7 --- /dev/null +++ b/src/composables/useDashboardStats.ts @@ -0,0 +1,180 @@ +import { computed, ref, reactive } from 'vue' +import { StatsService } from '@/services' +import type { DashboardData, DashboardHealthSummary, DashboardViewData } from '@/types' +import { formatFileSize, getErrorMessage } from '@/utils/common' + +type UseDashboardStatsOptions = { + loadFailedMessage?: string +} + +const emptyDashboardData = (): DashboardViewData => ({ + hasExtendedStats: false, + totalFiles: 0, + storageUsed: 0, + yesterdayCount: 0, + todayCount: 0, + yesterdaySize: 0, + todaySize: 0, + sysUptime: null, + activeCount: 0, + expiredCount: 0, + textCount: 0, + fileCount: 0, + chunkedCount: 0, + usedCount: 0, + storageBackend: '-', + uploadSizeLimit: 0, + openUpload: 0, + enableChunk: 0, + maxSaveSeconds: 0, + healthAttentionCount: 0, + healthDangerCount: 0, + healthWarningCount: 0, + expiringSoonCount: 0, + storageIssueCount: 0, + neverRetrievedCount: 0, + healthyCount: 0, + permanentCount: 0, + storageUsedText: '0 Bytes', + yesterdaySizeText: '0 Bytes', + todaySizeText: '0 Bytes', + uploadSizeLimitText: '0 Bytes', + sysUptimeText: '-', + activeRatio: 0, + textRatio: 0, + fileRatio: 0, + healthyRatio: 0, + healthAttentionRatio: 0, + todaySizeRatio: 0 +}) + +const toNumber = (value: number | string | null | undefined) => Number(value || 0) + +const clampRatio = (value: number) => Math.max(0, Math.min(100, Math.round(value))) + +const hasOwn = (target: object, key: string) => Object.prototype.hasOwnProperty.call(target, key) + +const formatDuration = (startTimestamp: number | null) => { + if (!startTimestamp) return '-' + const uptime = Date.now() - startTimestamp + const days = Math.floor(uptime / (24 * 60 * 60 * 1000)) + const hours = Math.floor((uptime % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)) + return `${days}天${hours}小时` +} + +const healthSummaryKeys: (keyof DashboardHealthSummary)[] = [ + 'healthAttentionCount', + 'healthDangerCount', + 'healthWarningCount', + 'expiringSoonCount', + 'storageIssueCount', + 'neverRetrievedCount', + 'healthyCount', + 'permanentCount' +] + +const normalizeHealthSummary = (detail: DashboardData): DashboardHealthSummary => ({ + healthAttentionCount: toNumber( + detail.healthSummary?.healthAttentionCount ?? detail.healthAttentionCount + ), + healthDangerCount: toNumber(detail.healthSummary?.healthDangerCount ?? detail.healthDangerCount), + healthWarningCount: toNumber( + detail.healthSummary?.healthWarningCount ?? detail.healthWarningCount + ), + expiringSoonCount: toNumber(detail.healthSummary?.expiringSoonCount ?? detail.expiringSoonCount), + storageIssueCount: toNumber(detail.healthSummary?.storageIssueCount ?? detail.storageIssueCount), + neverRetrievedCount: toNumber( + detail.healthSummary?.neverRetrievedCount ?? detail.neverRetrievedCount + ), + healthyCount: toNumber(detail.healthSummary?.healthyCount ?? detail.healthyCount), + permanentCount: toNumber(detail.healthSummary?.permanentCount ?? detail.permanentCount) +}) + +export function useDashboardStats(options: UseDashboardStatsOptions = {}) { + const dashboardData = reactive(emptyDashboardData()) + const isLoading = ref(false) + const errorMessage = ref('') + const lastUpdatedAt = ref(null) + const lastUpdatedText = computed(() => + lastUpdatedAt.value ? lastUpdatedAt.value.toLocaleString() : '-' + ) + + const fetchDashboardData = async () => { + isLoading.value = true + errorMessage.value = '' + + try { + const response = await StatsService.getDashboard() + if (!response.detail) { + throw new Error('No dashboard data') + } + + const detail = response.detail + dashboardData.totalFiles = toNumber(detail.totalFiles) + dashboardData.storageUsed = toNumber(detail.storageUsed) + dashboardData.yesterdayCount = toNumber(detail.yesterdayCount) + dashboardData.todayCount = toNumber(detail.todayCount) + dashboardData.yesterdaySize = toNumber(detail.yesterdaySize) + dashboardData.todaySize = toNumber(detail.todaySize) + dashboardData.sysUptime = detail.sysUptime + dashboardData.hasExtendedStats = hasOwn(detail, 'activeCount') + dashboardData.activeCount = dashboardData.hasExtendedStats + ? toNumber(detail.activeCount) + : dashboardData.totalFiles + dashboardData.expiredCount = toNumber(detail.expiredCount) + dashboardData.textCount = toNumber(detail.textCount) + dashboardData.fileCount = toNumber(detail.fileCount) + dashboardData.chunkedCount = toNumber(detail.chunkedCount) + dashboardData.usedCount = toNumber(detail.usedCount) + dashboardData.storageBackend = detail.storageBackend || '-' + dashboardData.uploadSizeLimit = toNumber(detail.uploadSizeLimit) + dashboardData.openUpload = toNumber(detail.openUpload) + dashboardData.enableChunk = toNumber(detail.enableChunk) + dashboardData.maxSaveSeconds = toNumber(detail.maxSaveSeconds) + const healthSummary = normalizeHealthSummary(detail) + healthSummaryKeys.forEach((key) => { + dashboardData[key] = healthSummary[key] + }) + + dashboardData.storageUsedText = formatFileSize(dashboardData.storageUsed) + dashboardData.yesterdaySizeText = formatFileSize(dashboardData.yesterdaySize) + dashboardData.todaySizeText = formatFileSize(dashboardData.todaySize) + dashboardData.uploadSizeLimitText = formatFileSize(dashboardData.uploadSizeLimit) + dashboardData.sysUptimeText = formatDuration(dashboardData.sysUptime) + dashboardData.activeRatio = dashboardData.totalFiles + ? clampRatio((dashboardData.activeCount / dashboardData.totalFiles) * 100) + : 0 + dashboardData.textRatio = dashboardData.totalFiles + ? clampRatio((dashboardData.textCount / dashboardData.totalFiles) * 100) + : 0 + dashboardData.fileRatio = dashboardData.totalFiles + ? clampRatio((dashboardData.fileCount / dashboardData.totalFiles) * 100) + : 0 + dashboardData.healthyRatio = dashboardData.totalFiles + ? clampRatio((dashboardData.healthyCount / dashboardData.totalFiles) * 100) + : 0 + dashboardData.healthAttentionRatio = dashboardData.totalFiles + ? clampRatio((dashboardData.healthAttentionCount / dashboardData.totalFiles) * 100) + : 0 + dashboardData.todaySizeRatio = dashboardData.uploadSizeLimit + ? clampRatio((dashboardData.todaySize / dashboardData.uploadSizeLimit) * 100) + : 0 + lastUpdatedAt.value = new Date() + } catch (error) { + errorMessage.value = getErrorMessage( + error, + options.loadFailedMessage || 'Failed to load dashboard data' + ) + } finally { + isLoading.value = false + } + } + + return { + dashboardData, + errorMessage, + fetchDashboardData, + isLoading, + lastUpdatedText + } +} diff --git a/src/composables/useInjectedDarkMode.ts b/src/composables/useInjectedDarkMode.ts new file mode 100644 index 0000000..c6497b8 --- /dev/null +++ b/src/composables/useInjectedDarkMode.ts @@ -0,0 +1,7 @@ +import { computed, inject, unref } from 'vue' +import type { Ref } from 'vue' + +export function useInjectedDarkMode() { + const injectedDarkMode = inject | boolean>('isDarkMode', false) + return computed(() => Boolean(unref(injectedDarkMode))) +} diff --git a/src/composables/usePresignedUpload.ts b/src/composables/usePresignedUpload.ts new file mode 100644 index 0000000..97630af --- /dev/null +++ b/src/composables/usePresignedUpload.ts @@ -0,0 +1,346 @@ +import { ref, computed, readonly } from 'vue' +import { PresignUploadService } from '@/services' +import type { + PresignUploadStatus, + PresignUploadMode, + PresignInitResponse, + PresignUploadOptions, + PresignStatusResponse, + UploadProgress, + ExpireStyle, + AlertType +} from '@/types' +import { getErrorMessage } from '@/utils/common' + +// 预签名上传状态常量 +export const PRESIGN_UPLOAD_STATUS = { + IDLE: 'idle', + INITIALIZING: 'initializing', + UPLOADING: 'uploading', + CONFIRMING: 'confirming', + SUCCESS: 'success', + ERROR: 'error' +} as const + +// 默认过期配置 +const DEFAULT_EXPIRE_VALUE = 1 +const DEFAULT_EXPIRE_STYLE: ExpireStyle = 'day' + +type ErrorWithResponse = { + response?: { + status?: number + } +} + +type PresignedUploadNotifier = (message: string, type: AlertType) => void + +type UsePresignedUploadOptions = { + getMaxFileSize?: () => number + notify?: PresignedUploadNotifier +} + +/** + * 预签名上传 Composable + * 支持 S3 直传模式和服务器代理模式 + */ +export function usePresignedUpload(options: UsePresignedUploadOptions = {}) { + // 状态管理 + const presignStatus = ref(PRESIGN_UPLOAD_STATUS.IDLE) + const uploadSession = ref(null) + const uploadProgress = ref({ + loaded: 0, + total: 0, + percentage: 0 + }) + const uploadedCode = ref('') + const errorMessage = ref('') + + // 计算属性 + const isInitializing = computed(() => presignStatus.value === PRESIGN_UPLOAD_STATUS.INITIALIZING) + const isUploading = computed(() => presignStatus.value === PRESIGN_UPLOAD_STATUS.UPLOADING) + const isConfirming = computed(() => presignStatus.value === PRESIGN_UPLOAD_STATUS.CONFIRMING) + const isSuccess = computed(() => presignStatus.value === PRESIGN_UPLOAD_STATUS.SUCCESS) + const isError = computed(() => presignStatus.value === PRESIGN_UPLOAD_STATUS.ERROR) + const currentMode = computed(() => uploadSession.value?.mode ?? null) + + const notify: PresignedUploadNotifier = (message, type) => { + options.notify?.(message, type) + } + + /** + * 文件大小验证 + */ + const validateFileSize = (file: File): boolean => { + const maxFileSize = options.getMaxFileSize?.() + if (!maxFileSize) { + return true + } + + if (file.size > maxFileSize) { + const maxSizeMB = Math.round(maxFileSize / 1024 / 1024) + errorMessage.value = `文件大小不能超过 ${maxSizeMB}MB` + notify(errorMessage.value, 'error') + return false + } + return true + } + + /** + * 进度追踪回调 + */ + const createProgressHandler = ( + externalHandler?: (progress: UploadProgress) => void + ): ((progress: UploadProgress) => void) => { + return (progress: UploadProgress) => { + // 确保进度值在有效范围内 + uploadProgress.value = { + loaded: Math.max(0, progress.loaded), + total: Math.max(0, progress.total), + percentage: Math.min(100, Math.max(0, progress.percentage)) + } + // 调用外部进度回调 + if (externalHandler) { + externalHandler(uploadProgress.value) + } + } + } + + /** + * 错误处理 + */ + const handleUploadError = (error: unknown): void => { + presignStatus.value = PRESIGN_UPLOAD_STATUS.ERROR + const status = (error as ErrorWithResponse)?.response?.status + const fallback = + status === 404 + ? '上传会话不存在或已过期' + : status === 500 + ? '服务器错误,请稍后重试' + : '上传失败,请重试' + + errorMessage.value = getErrorMessage(error, fallback) + notify(errorMessage.value, 'error') + } + + /** + * 初始化上传会话 + */ + const initUploadSession = async ( + file: File, + options?: PresignUploadOptions + ): Promise => { + try { + presignStatus.value = PRESIGN_UPLOAD_STATUS.INITIALIZING + + const response = await PresignUploadService.initUpload({ + file_name: file.name, + file_size: file.size, + expire_value: options?.expireValue ?? DEFAULT_EXPIRE_VALUE, + expire_style: options?.expireStyle ?? DEFAULT_EXPIRE_STYLE + }) + + if (response.code === 200 && response.detail) { + uploadSession.value = response.detail + return response.detail + } else { + throw new Error(response.message || '初始化上传失败') + } + } catch (error) { + handleUploadError(error) + return null + } + } + + /** + * 直传模式上传到 S3 + */ + const uploadDirect = async ( + file: File, + session: PresignInitResponse, + options?: PresignUploadOptions + ): Promise => { + try { + presignStatus.value = PRESIGN_UPLOAD_STATUS.UPLOADING + + // 上传到 S3 + const progressHandler = createProgressHandler(options?.onProgress) + const uploadSuccess = await PresignUploadService.directUploadToS3( + session.upload_url, + file, + progressHandler + ) + + if (!uploadSuccess) { + throw new Error('S3 上传失败') + } + + // 确认上传 + presignStatus.value = PRESIGN_UPLOAD_STATUS.CONFIRMING + const confirmResponse = await PresignUploadService.confirmUpload(session.upload_id, { + expire_value: options?.expireValue ?? DEFAULT_EXPIRE_VALUE, + expire_style: options?.expireStyle ?? DEFAULT_EXPIRE_STYLE + }) + + if (confirmResponse.code === 200 && confirmResponse.detail?.code) { + presignStatus.value = PRESIGN_UPLOAD_STATUS.SUCCESS + uploadedCode.value = String(confirmResponse.detail.code) + // 确保进度为 100% + uploadProgress.value = { + loaded: file.size, + total: file.size, + percentage: 100 + } + notify('文件上传成功!', 'success') + return uploadedCode.value + } else { + throw new Error(confirmResponse.message || '确认上传失败') + } + } catch (error) { + handleUploadError(error) + return null + } + } + + /** + * 代理模式上传 + */ + const uploadProxy = async ( + file: File, + session: PresignInitResponse, + options?: PresignUploadOptions + ): Promise => { + try { + presignStatus.value = PRESIGN_UPLOAD_STATUS.UPLOADING + + const progressHandler = createProgressHandler(options?.onProgress) + const uploadUrl = + session.proxy_upload_url || session.upload_url || session.legacy_proxy_upload_url + const response = await PresignUploadService.proxyUpload( + session.upload_id, + file, + progressHandler, + uploadUrl + ) + + if (response.code === 200 && response.detail?.code) { + presignStatus.value = PRESIGN_UPLOAD_STATUS.SUCCESS + uploadedCode.value = String(response.detail.code) + // 确保进度为 100% + uploadProgress.value = { + loaded: file.size, + total: file.size, + percentage: 100 + } + notify('文件上传成功!', 'success') + return uploadedCode.value + } else { + throw new Error(response.message || '代理上传失败') + } + } catch (error) { + handleUploadError(error) + return null + } + } + + const uploadFile = async (file: File, options?: PresignUploadOptions): Promise => { + reset() + + if (!validateFileSize(file)) { + presignStatus.value = PRESIGN_UPLOAD_STATUS.ERROR + return null + } + + const session = await initUploadSession(file, options) + if (!session) { + return null + } + + if (session.mode === 'direct') { + return await uploadDirect(file, session, options) + } else { + return await uploadProxy(file, session, options) + } + } + + /** + * 取消上传 + */ + const cancelUpload = async (): Promise => { + if (!uploadSession.value?.upload_id) { + return + } + + try { + await PresignUploadService.cancelUpload(uploadSession.value.upload_id) + notify('上传已取消', 'info') + } catch (error) { + // 取消失败时静默处理,因为会话可能已过期 + console.warn('取消上传失败:', error) + } finally { + reset() + } + } + + /** + * 获取上传状态 + */ + const getStatus = async (): Promise => { + if (!uploadSession.value?.upload_id) { + return null + } + + try { + const response = await PresignUploadService.getUploadStatus(uploadSession.value.upload_id) + if (response.code === 200 && response.detail) { + // 检查会话是否过期 + if (response.detail.is_expired) { + errorMessage.value = '上传会话已过期' + notify(errorMessage.value, 'warning') + } + return response.detail + } + return null + } catch (error) { + handleUploadError(error) + return null + } + } + + /** + * 重置状态 + */ + const reset = (): void => { + presignStatus.value = PRESIGN_UPLOAD_STATUS.IDLE + uploadSession.value = null + uploadProgress.value = { + loaded: 0, + total: 0, + percentage: 0 + } + uploadedCode.value = '' + errorMessage.value = '' + } + + return { + // 状态 (只读) + presignStatus: readonly(presignStatus), + uploadSession: readonly(uploadSession), + uploadProgress: readonly(uploadProgress), + uploadedCode: readonly(uploadedCode), + errorMessage: readonly(errorMessage), + + // 计算属性 + isInitializing, + isUploading, + isConfirming, + isSuccess, + isError, + currentMode, + + // 方法 + uploadFile, + cancelUpload, + getStatus, + reset + } +} diff --git a/src/composables/usePublicConfigBootstrap.ts b/src/composables/usePublicConfigBootstrap.ts new file mode 100644 index 0000000..d32c536 --- /dev/null +++ b/src/composables/usePublicConfigBootstrap.ts @@ -0,0 +1,26 @@ +import { ConfigService } from '@/services' +import { useAlertStore } from '@/stores/alertStore' +import { useConfigStore } from '@/stores/configStore' + +export function usePublicConfigBootstrap() { + const alertStore = useAlertStore() + const configStore = useConfigStore() + + const syncPublicConfig = async () => { + const res = await ConfigService.getUserConfig() + + if (res.code !== 200 || !res.detail) { + return + } + + configStore.applyPublicMeta(res.detail.meta) + const notifyMessage = configStore.applyRemoteConfig(res.detail.config) + if (notifyMessage) { + alertStore.showAlert(notifyMessage, 'success') + } + } + + return { + syncPublicConfig + } +} diff --git a/src/composables/useRetrieveFlow.ts b/src/composables/useRetrieveFlow.ts new file mode 100644 index 0000000..039d609 --- /dev/null +++ b/src/composables/useRetrieveFlow.ts @@ -0,0 +1,268 @@ +import { computed, ref, watch } from 'vue' +import { useI18n } from 'vue-i18n' +import { storeToRefs } from 'pinia' +import { FileService } from '@/services' +import { useAlertStore } from '@/stores/alertStore' +import { useFileDataStore } from '@/stores/fileData' +import type { ReceivedFileRecord, ShareMetadataResponse, ShareSelectResponse } from '@/types' +import { copyToClipboard } from '@/utils/clipboard' +import { getErrorMessage, getResponseMessage } from '@/utils/common' +import { renderMarkdownPreview } from '@/utils/content-preview' +import { downloadReceivedRecord } from '@/utils/download-action' + +type InputStatus = { + readonly: boolean + loading: boolean +} + +export function useRetrieveFlow() { + const { t } = useI18n() + const alertStore = useAlertStore() + const fileStore = useFileDataStore() + const { receiveData: records } = storeToRefs(fileStore) + + const code = ref('') + const inspectedFile = ref(null) + const isInspecting = ref(false) + const isRetrieving = ref(false) + const inputStatus = ref({ + readonly: false, + loading: false + }) + const error = ref('') + const selectedRecord = ref(null) + const previewMediaRecord = ref(null) + const showDrawer = ref(false) + const showPreview = ref(false) + const showMediaPreview = ref(false) + const renderedContent = ref('') + const normalizedCode = computed(() => code.value.trim()) + const isWorking = computed(() => isInspecting.value || isRetrieving.value) + const hasValidCode = computed(() => normalizedCode.value.length === 5) + + const formatFileSize = (bytes: number) => { + if (bytes === 0) return '0 ' + t('fileSize.bytes') + const k = 1024 + const sizes = [ + t('fileSize.bytes'), + t('fileSize.kb'), + t('fileSize.mb'), + t('fileSize.gb'), + t('fileSize.tb') + ] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] + } + + const createRecord = (detail: ShareSelectResponse): ReceivedFileRecord => { + const isText = + typeof detail.is_text === 'boolean' + ? detail.is_text + : detail.type + ? detail.type === 'text' + : detail.name === 'Text' && !detail.text.startsWith('/share/download') + const content = isText ? (detail.content ?? detail.text) : null + const downloadUrl = isText ? null : (detail.download_url ?? detail.text) + + return { + id: Date.now(), + code: detail.code, + filename: detail.name, + size: formatFileSize(detail.size), + downloadUrl, + content, + date: new Date().toLocaleString(), + type: isText ? 'text' : 'file', + remainingDownloads: detail.remaining_downloads + } + } + + const resetInspection = () => { + inspectedFile.value = null + error.value = '' + } + + const inspectCode = async () => { + if (!hasValidCode.value) { + alertStore.showAlert(t('retrieve.messages.invalidCode'), 'error') + return + } + + isInspecting.value = true + inputStatus.value.readonly = true + inputStatus.value.loading = true + error.value = '' + + try { + const res = await FileService.inspectFile(normalizedCode.value) + if (res.code === 200 && res.detail) { + inspectedFile.value = res.detail + } else { + inspectedFile.value = null + error.value = getResponseMessage(res, t('retrieve.messages.unknownError')) + alertStore.showAlert(t('retrieve.messages.retrieveFailure') + error.value, 'error') + } + } catch { + inspectedFile.value = null + error.value = t('retrieve.messages.previewUnavailable') + alertStore.showAlert(error.value, 'warning') + } finally { + isInspecting.value = false + inputStatus.value.readonly = false + inputStatus.value.loading = false + } + } + + const handleSubmit = async () => { + if (!hasValidCode.value) { + alertStore.showAlert(t('retrieve.messages.invalidCode'), 'error') + return + } + + isRetrieving.value = true + inputStatus.value.readonly = true + inputStatus.value.loading = true + error.value = '' + + try { + const res = await FileService.selectFile(normalizedCode.value) + if (res.code === 200 && res.detail) { + const newFileData = createRecord(res.detail) + const existingIndex = fileStore.receiveData.findIndex( + (file) => file.code === newFileData.code + ) + if (existingIndex !== -1) { + fileStore.deleteReceiveData(existingIndex) + } + fileStore.addReceiveData(newFileData) + selectedRecord.value = newFileData + if (newFileData.content) { + showPreview.value = true + } + inspectedFile.value = null + alertStore.showAlert(t('retrieve.messages.retrieveSuccess'), 'success') + } else { + error.value = getResponseMessage(res, t('retrieve.messages.unknownError')) + alertStore.showAlert(t('retrieve.messages.retrieveFailure') + error.value, 'error') + } + } catch (err: unknown) { + const errorMessage = getErrorMessage(err, t('retrieve.messages.unknownError')) + error.value = errorMessage + alertStore.showAlert(t('retrieve.messages.networkError') + errorMessage, 'error') + } finally { + isRetrieving.value = false + inputStatus.value.readonly = false + inputStatus.value.loading = false + code.value = '' + } + } + + const copyContent = async () => { + if (selectedRecord.value?.content) { + await copyToClipboard(selectedRecord.value.content, { + successMsg: t('fileRecord.contentCopied'), + errorMsg: t('fileRecord.copyFailed'), + notify: (message, type) => alertStore.showAlert(message, type) + }) + } + } + + const viewDetails = (record: ReceivedFileRecord) => { + selectedRecord.value = record + } + + const closeDetails = () => { + selectedRecord.value = null + } + + const deleteRecord = (id: number) => { + const index = records.value.findIndex((record) => record.id === id) + if (index !== -1) { + fileStore.deleteReceiveData(index) + } + } + + const toggleDrawer = () => { + showDrawer.value = !showDrawer.value + } + + const downloadRecord = async (record: ReceivedFileRecord) => { + try { + await downloadReceivedRecord(record) + } catch (err: unknown) { + alertStore.showAlert(getErrorMessage(err, t('common.downloadFailed')), 'error') + } + } + + const showContentPreview = () => { + showPreview.value = true + } + + const closeContentPreview = () => { + showPreview.value = false + } + + const showFilePreview = () => { + if (!selectedRecord.value) return + previewMediaRecord.value = selectedRecord.value + showMediaPreview.value = true + } + + const closeFilePreview = () => { + showMediaPreview.value = false + previewMediaRecord.value = null + } + + watch( + () => selectedRecord.value?.content, + async (content) => { + if (content) { + renderedContent.value = await renderMarkdownPreview(content) + } else { + renderedContent.value = '' + } + }, + { immediate: true } + ) + + watch(normalizedCode, (nextCode) => { + if (inspectedFile.value && inspectedFile.value.code !== nextCode) { + resetInspection() + } + + if (nextCode.length < 5 && error.value) { + error.value = '' + } + }) + + return { + code, + inspectedFile, + isInspecting, + isRetrieving, + isWorking, + hasValidCode, + inputStatus, + error, + records, + selectedRecord, + previewMediaRecord, + showDrawer, + showPreview, + showMediaPreview, + renderedContent, + closeFilePreview, + closeContentPreview, + closeDetails, + copyContent, + deleteRecord, + downloadRecord, + handleSubmit, + inspectCode, + resetInspection, + showFilePreview, + showContentPreview, + toggleDrawer, + viewDetails + } +} diff --git a/src/composables/useRouteLoading.ts b/src/composables/useRouteLoading.ts new file mode 100644 index 0000000..6c3592f --- /dev/null +++ b/src/composables/useRouteLoading.ts @@ -0,0 +1,44 @@ +import { onUnmounted, ref } from 'vue' +import type { Router } from 'vue-router' + +const ROUTE_LOADING_DELAY = 200 + +export function useRouteLoading(router: Router) { + const isLoading = ref(false) + let loadingTimer: number | null = null + let cleanupBeforeGuard: (() => void) | null = null + let cleanupAfterHook: (() => void) | null = null + + const clearLoadingTimer = () => { + if (loadingTimer !== null) { + window.clearTimeout(loadingTimer) + loadingTimer = null + } + } + + const setupRouteLoading = () => { + cleanupBeforeGuard = router.beforeEach((to) => { + clearLoadingTimer() + isLoading.value = to.meta.showRouteLoading !== false + }) + + cleanupAfterHook = router.afterEach(() => { + clearLoadingTimer() + loadingTimer = window.setTimeout(() => { + isLoading.value = false + loadingTimer = null + }, ROUTE_LOADING_DELAY) + }) + } + + onUnmounted(() => { + clearLoadingTimer() + cleanupBeforeGuard?.() + cleanupAfterHook?.() + }) + + return { + isLoading, + setupRouteLoading + } +} diff --git a/src/composables/useSendFlow.ts b/src/composables/useSendFlow.ts new file mode 100644 index 0000000..f675f95 --- /dev/null +++ b/src/composables/useSendFlow.ts @@ -0,0 +1,420 @@ +import { computed, ref, watch } from 'vue' +import { useI18n } from 'vue-i18n' +import { useAlertStore } from '@/stores/alertStore' +import { useAdminStore } from '@/stores/adminStore' +import { useConfigStore } from '@/stores/configStore' +import { useFileDataStore } from '@/stores/fileData' +import type { SendType, SentFileRecord, UploadProgress } from '@/types' +import { getClipboardFile, insertTextAtSelection } from '@/utils/clipboard-paste' +import { getErrorMessage } from '@/utils/common' +import { getStorageUnit } from '@/utils/convert' +import { calculateFileHash } from '@/utils/file-processing' +import { buildSentRecord, isExpirationWithinLimit } from '@/utils/send-record' +import { createSentRecordActions } from '@/utils/sent-record-actions' +import { useSendSubmit } from './useSendSubmit' + +export function useSendFlow() { + const { t } = useI18n() + const alertStore = useAlertStore() + const adminStore = useAdminStore() + const configStore = useConfigStore() + const fileDataStore = useFileDataStore() + const config = computed(() => configStore.config) + const sendType = ref('file') + const selectedFile = ref(null) + const selectedFiles = ref([]) + const textContent = ref('') + const expirationMethod = ref(config.value.expireStyle[0] || 'day') + const expirationValue = ref('1') + const uploadProgress = ref(0) + const uploadedBytes = ref(0) + const totalBytes = ref(0) + const uploadSpeed = ref(0) + const lastProgressSnapshot = ref({ loaded: 0, time: 0 }) + const showDrawer = ref(false) + const selectedRecord = ref(null) + const isSubmitting = ref(false) + const fileHash = ref('') + const sendRecords = computed(() => fileDataStore.shareData) + const uploadDescription = computed(() => + t('send.uploadArea.descriptionWithLimit', { + size: getStorageUnit(config.value.uploadSize) + }) + ) + const allowedFileTypes = computed(() => { + const types = config.value.allowedFileTypes || config.value.allowed_file_types || ['*'] + const normalized = types.map((type) => String(type).trim()).filter(Boolean) + return normalized.length > 0 ? normalized : ['*'] + }) + const acceptedTypes = computed(() => { + if (allowedFileTypes.value.some((type) => type === '*' || type === '*/*')) return '*' + + return allowedFileTypes.value + .map((type) => { + const normalizedType = type.toLowerCase() + if (normalizedType.includes('/')) return normalizedType + return normalizedType.startsWith('.') ? normalizedType : `.${normalizedType}` + }) + .join(',') + }) + const expirationOptions = computed(() => + config.value.expireStyle.map((value) => ({ + value, + label: getUnit(value) + })) + ) + watch( + () => config.value.expireStyle, + (expireStyle) => { + if (expireStyle.length > 0 && !expireStyle.includes(expirationMethod.value)) { + expirationMethod.value = expireStyle[0] + } + }, + { immediate: true } + ) + const notifyCopyResult = (message: string, type: 'success' | 'error') => { + alertStore.showAlert(message, type) + } + const sentRecordActions = createSentRecordActions(notifyCopyResult) + const { resetPresignUpload, submitFile, submitText } = useSendSubmit({ + getMaxFileSize: () => configStore.uploadSizeLimit, + notify: (message, type) => alertStore.showAlert(message, type), + translate: t, + onProgress: (progress: UploadProgress) => { + const now = performance.now() + if (lastProgressSnapshot.value.time > 0 && now > lastProgressSnapshot.value.time) { + const deltaBytes = Math.max(0, progress.loaded - lastProgressSnapshot.value.loaded) + const deltaSeconds = (now - lastProgressSnapshot.value.time) / 1000 + uploadSpeed.value = deltaSeconds > 0 ? deltaBytes / deltaSeconds : 0 + } + + lastProgressSnapshot.value = { + loaded: progress.loaded, + time: now + } + uploadedBytes.value = progress.loaded + totalBytes.value = progress.total + uploadProgress.value = progress.percentage + }, + onHashCalculated: (hash) => { + fileHash.value = hash + } + }) + + const checkOpenUpload = () => { + if (config.value.openUpload === 0 && !adminStore.hasToken) { + alertStore.showAlert(t('send.messages.guestUploadDisabled'), 'error') + return false + } + return true + } + + const resetUploadProgress = () => { + uploadProgress.value = 0 + uploadedBytes.value = 0 + totalBytes.value = 0 + uploadSpeed.value = 0 + lastProgressSnapshot.value = { loaded: 0, time: 0 } + } + + const checkFileSize = (file: File) => { + if (file.size > config.value.uploadSize) { + alertStore.showAlert( + t('send.messages.fileSizeExceeded', { size: getStorageUnit(config.value.uploadSize) }), + 'error' + ) + selectedFile.value = null + return false + } + return true + } + + const checkFileType = (file: File) => { + if (allowedFileTypes.value.some((type) => type === '*' || type === '*/*')) { + return true + } + + const fileName = file.name.toLowerCase() + const mimeType = file.type.toLowerCase() + const isAllowed = allowedFileTypes.value.some((type) => { + const rule = type.toLowerCase() + if (rule.includes('/')) { + if (rule.endsWith('/*')) { + return mimeType.startsWith(rule.slice(0, -1)) + } + return mimeType === rule + } + + const extension = rule.startsWith('.') ? rule : `.${rule}` + return fileName.endsWith(extension) + }) + + if (!isAllowed) { + alertStore.showAlert( + t('send.messages.fileTypeNotAllowed', { types: allowedFileTypes.value.join(', ') }), + 'error' + ) + } + + return isAllowed + } + + const checkExpirationTime = (method: string, value: string): boolean => + isExpirationWithinLimit(method, value, config.value.max_save_seconds || 0) + + const checkUpload = () => { + if (!selectedFile.value) return false + if (!checkOpenUpload()) return false + if (!checkFileSize(selectedFile.value)) return false + if (!checkFileType(selectedFile.value)) return false + if (!checkExpirationTime(expirationMethod.value, expirationValue.value)) return false + return true + } + + const handleFileSelected = async (file: File) => { + selectedFile.value = file + selectedFiles.value = [] + if (!checkOpenUpload()) return + if (!checkFileSize(file)) return + if (!checkFileType(file)) return + fileHash.value = await calculateFileHash(file) + } + + const handleFilesSelected = async (files: File[]) => { + if (!checkOpenUpload()) return + const invalidFile = files.find((file) => !checkFileSize(file) || !checkFileType(file)) + if (invalidFile) return + selectedFiles.value = files + selectedFile.value = null + fileHash.value = '' + } + + const handleFileDrop = async (event: DragEvent) => { + if (!event.dataTransfer?.files || event.dataTransfer.files.length === 0) return + const files = Array.from(event.dataTransfer.files) + if (files.length === 1) { + const file = files[0] + selectedFile.value = file + selectedFiles.value = [] + if (!checkUpload()) return + fileHash.value = await calculateFileHash(file) + } else { + if (!checkOpenUpload()) return + const invalidFile = files.find((file) => !checkFileSize(file) || !checkFileType(file)) + if (invalidFile) return + selectedFiles.value = files + selectedFile.value = null + fileHash.value = '' + } + } + + const handlePaste = async (event: ClipboardEvent) => { + const items = event.clipboardData?.items + if (!items) return + + const file = getClipboardFile(items) + if (file) { + if (file.size === 0) { + alertStore.showAlert(t('send.messages.emptyFileError'), 'error') + return + } + + selectedFile.value = file + if (!checkUpload()) return + + try { + fileHash.value = await calculateFileHash(file) + alertStore.showAlert( + t('send.messages.fileAddedFromClipboard', { filename: file.name }), + 'success' + ) + } catch (err) { + alertStore.showAlert(t('send.messages.fileProcessingFailed'), 'error') + console.error('File hash calculation failed:', err) + } + return + } + + const textItem = items[0] + if (!textItem) return + + sendType.value = 'text' + textItem.getAsString((str: string) => { + const trimmedStr = str.trim() + if (!trimmedStr) return + + const textareaElement = document.getElementById('text-content') as HTMLTextAreaElement + if (!textareaElement) { + textContent.value += trimmedStr + return + } + + const insertion = insertTextAtSelection({ + text: textContent.value, + insertText: trimmedStr, + selectionStart: textareaElement.selectionStart, + selectionEnd: textareaElement.selectionEnd + }) + textContent.value = insertion.value + + setTimeout(() => { + textareaElement.setSelectionRange(insertion.cursor, insertion.cursor) + textareaElement.focus() + }, 0) + }) + } + + const getUnit = (value: string = expirationMethod.value) => { + switch (value) { + case 'day': + return t('send.expiration.units.days') + case 'hour': + return t('send.expiration.units.hours') + case 'minute': + return t('send.expiration.units.minutes') + case 'count': + return t('send.expiration.units.times') + case 'forever': + return t('send.expiration.units.forever') + default: + return '' + } + } + + const handleSubmit = async () => { + if (isSubmitting.value) return + isSubmitting.value = true + + try { + if (sendType.value === 'file' && !selectedFile.value && selectedFiles.value.length === 0) { + alertStore.showAlert(t('send.messages.selectFile'), 'error') + return + } + if (sendType.value === 'text' && !textContent.value.trim()) { + alertStore.showAlert(t('send.messages.enterText'), 'error') + return + } + if (!checkOpenUpload()) { + return + } + if (expirationMethod.value !== 'forever' && !expirationValue.value) { + alertStore.showAlert(t('send.messages.enterExpirationValue'), 'error') + return + } + + if (!checkExpirationTime(expirationMethod.value, expirationValue.value)) { + const maxDays = Math.floor(config.value.max_save_seconds / 86400) + alertStore.showAlert(t('send.messages.expirationTooLong', { days: maxDays }), 'error') + return + } + + const expireValue = expirationValue.value ? parseInt(expirationValue.value) : 1 + let response + if (sendType.value === 'file') { + response = await submitFile({ + selectedFile: selectedFile.value, + selectedFiles: selectedFiles.value, + expireValue, + expireStyle: expirationMethod.value, + enableChunk: Boolean(config.value.enableChunk), + validateFileSize: checkFileSize + }) + } else { + response = await submitText({ + text: textContent.value, + expireValue, + expireStyle: expirationMethod.value + }) + } + + if (!response) return + + if (response?.code === 200) { + const newRecord = buildSentRecord({ + response, + sendType: sendType.value, + textContent: textContent.value, + selectedFile: selectedFile.value, + selectedFiles: selectedFiles.value, + expirationMethod: expirationMethod.value, + expirationValue: expirationValue.value, + translate: t, + getUnit + }) + fileDataStore.addShareDataRecord(newRecord) + alertStore.showAlert( + t('send.messages.sendSuccess', { code: newRecord.retrieveCode }), + 'success' + ) + selectedFile.value = null + selectedFiles.value = [] + textContent.value = '' + resetUploadProgress() + resetPresignUpload() + selectedRecord.value = newRecord + await sentRecordActions.copyLink(newRecord) + } else { + throw new Error(t('send.messages.serverError')) + } + } catch (error: unknown) { + alertStore.showAlert(getErrorMessage(error, t('send.messages.sendFailed')), 'error') + } finally { + resetUploadProgress() + isSubmitting.value = false + } + } + + const toggleDrawer = () => { + showDrawer.value = !showDrawer.value + } + + const viewDetails = (record: SentFileRecord) => { + selectedRecord.value = record + } + + const closeDetails = () => { + selectedRecord.value = null + } + + const deleteRecord = (id: number) => { + const index = fileDataStore.shareData.findIndex((record) => record.id === id) + if (index !== -1) { + fileDataStore.deleteShareData(index) + } + } + + return { + config, + sendType, + selectedFile, + selectedFiles, + textContent, + expirationMethod, + expirationValue, + uploadProgress, + uploadedBytes, + totalBytes, + uploadSpeed, + acceptedTypes, + showDrawer, + selectedRecord, + isSubmitting, + sendRecords, + uploadDescription, + expirationOptions, + closeDetails, + deleteRecord, + copySentRecordCode: sentRecordActions.copyCode, + copySentRecordLink: sentRecordActions.copyLink, + copySentRecordWgetCommand: sentRecordActions.copyWgetCommand, + getQRCodeValue: sentRecordActions.getQRCodeValue, + getUnit, + handleFileDrop, + handleFileSelected, + handleFilesSelected, + handlePaste, + handleSubmit, + toggleDrawer, + viewDetails + } +} diff --git a/src/composables/useSendSubmit.ts b/src/composables/useSendSubmit.ts new file mode 100644 index 0000000..645fd58 --- /dev/null +++ b/src/composables/useSendSubmit.ts @@ -0,0 +1,122 @@ +import { FileService, uploadChunkedFile } from '@/services' +import type { AlertType, ApiResponse, ExpireStyle, UploadProgress } from '@/types' +import { calculateFileHash, packFilesAsZip } from '@/utils/file-processing' +import { usePresignedUpload } from './usePresignedUpload' + +type Translate = ( + key: string, + params?: Record +) => string + +type UseSendSubmitOptions = { + getMaxFileSize: () => number + notify: (message: string, type: AlertType) => void + translate: Translate + onProgress: (progress: UploadProgress) => void + onHashCalculated: (hash: string) => void +} + +type SubmitFileOptions = { + selectedFile: File | null + selectedFiles: File[] + expireValue: number + expireStyle: string + enableChunk: boolean + validateFileSize: (file: File) => boolean +} + +type SubmitTextOptions = { + text: string + expireValue: number + expireStyle: string +} + +export function useSendSubmit(options: UseSendSubmitOptions) { + const { uploadFile: presignUploadFile, reset: resetPresignUpload } = usePresignedUpload({ + getMaxFileSize: options.getMaxFileSize, + notify: options.notify + }) + + const handleChunkUpload = async ( + file: File, + expireValue: number, + expireStyle: string + ): Promise => { + return uploadChunkedFile(file, { + expireValue, + expireStyle, + onHashCalculated: options.onHashCalculated, + onProgress: (progress: UploadProgress) => { + options.onProgress(progress) + }, + messages: { + initFailed: options.translate('send.messages.initChunkUploadFailed'), + chunkFailed: (index) => options.translate('send.messages.chunkUploadFailed', { index }), + completeFailed: options.translate('send.messages.completeUploadFailed') + } + }) + } + + const handlePresignedUpload = async ( + file: File, + expireValue: number, + expireStyle: string + ): Promise> => { + const code = await presignUploadFile(file, { + expireValue, + expireStyle: expireStyle as ExpireStyle, + onProgress: (progress) => { + options.onProgress(progress) + } + }) + + if (!code) { + throw new Error(options.translate('send.messages.uploadFailed')) + } + + return { + code: 200, + detail: { + code, + name: file.name + } + } + } + + const submitFile = async ({ + selectedFile, + selectedFiles, + expireValue, + expireStyle, + enableChunk, + validateFileSize + }: SubmitFileOptions): Promise => { + let fileToUpload = selectedFile + + if (selectedFiles.length > 0) { + options.notify('正在打包文件...', 'success') + fileToUpload = await packFilesAsZip(selectedFiles) + if (!validateFileSize(fileToUpload)) { + return null + } + options.onHashCalculated(await calculateFileHash(fileToUpload)) + } + + if (!fileToUpload) { + throw new Error(options.translate('send.messages.selectFile')) + } + + return enableChunk + ? handleChunkUpload(fileToUpload, expireValue, expireStyle) + : handlePresignedUpload(fileToUpload, expireValue, expireStyle) + } + + const submitText = ({ text, expireValue, expireStyle }: SubmitTextOptions) => + FileService.uploadText(text, expireValue, expireStyle) + + return { + resetPresignUpload, + submitFile, + submitText + } +} diff --git a/src/composables/useSystemConfig.ts b/src/composables/useSystemConfig.ts index e69de29..af02458 100644 --- a/src/composables/useSystemConfig.ts +++ b/src/composables/useSystemConfig.ts @@ -0,0 +1,258 @@ +import { ref, computed } from 'vue' +import { ConfigService } from '@/services' +import { useAlertStore } from '@/stores/alertStore' +import { useConfigStore } from '@/stores/configStore' +import type { ConfigState } from '@/types' +import { DEFAULT_CONFIG_STATE, readStoredConfig } from '@/utils/config-storage' +import { getErrorMessage } from '@/utils/common' +import { + buildConfigSubmitPayload, + bytesToFileSizeForm, + fileSizeFormToBytes, + secondsToSaveTimeForm, + type FileSizeUnit, + type SaveTimeUnit +} from '@/utils/config-form' + +type ConfigFlagKey = 'enableChunk' | 's3_proxy' | 'openUpload' | 'showAdminAddr' + +const SECONDS_PER_DAY = 24 * 60 * 60 + +export function useSystemConfig() { + const alertStore = useAlertStore() + const configStore = useConfigStore() + + // 状态管理 + const config = ref({ ...DEFAULT_CONFIG_STATE }) + const isRefreshing = ref(false) + const isSaving = ref(false) + const savedPayloadSnapshot = ref('') + const fileSize = ref(1) + const sizeUnit = ref('MB') + const storageLimit = ref(0) + const storageLimitUnit = ref('GB') + const saveTime = ref(1) + const saveTimeUnit = ref('天') + const adminSessionDays = ref(30) + + const isLoading = computed(() => isRefreshing.value || isSaving.value) + + const buildSubmitPayload = (): Partial => { + const payload: Partial = buildConfigSubmitPayload( + config.value, + { value: fileSize.value, unit: sizeUnit.value }, + { value: saveTime.value, unit: saveTimeUnit.value } + ) + payload.adminSessionExpire = Math.round(adminSessionDays.value * SECONDS_PER_DAY) + payload.storageLimit = + storageLimit.value === 0 ? 0 : fileSizeFormToBytes(storageLimit.value, storageLimitUnit.value) + + if (!payload.admin_token) { + delete payload.admin_token + } + + return payload + } + + const snapshotPayload = (payload: Partial) => JSON.stringify(payload) + + const normalizeAllowedFileTypes = (value: unknown): string[] => { + const rawTypes = + typeof value === 'string' + ? value.split(',') + : Array.isArray(value) + ? value + : DEFAULT_CONFIG_STATE.allowed_file_types + + const normalized = rawTypes.map((item) => String(item).trim()).filter(Boolean) + return normalized.length > 0 ? normalized : ['*'] + } + + const normalizeEditableConfig = (nextConfig: Partial): ConfigState => { + const allowedFileTypes = normalizeAllowedFileTypes( + nextConfig.allowed_file_types ?? nextConfig.allowedFileTypes + ) + const codeGenerateType = nextConfig.code_generate_type === 'secret' ? 'secret' : 'number' + + return { + ...DEFAULT_CONFIG_STATE, + ...nextConfig, + allowed_file_types: allowedFileTypes, + allowedFileTypes, + code_generate_type: codeGenerateType, + admin_token: '' + } + } + + const markConfigSaved = () => { + savedPayloadSnapshot.value = snapshotPayload(buildSubmitPayload()) + } + + const isDirty = computed(() => { + if (!savedPayloadSnapshot.value) { + return false + } + + return snapshotPayload(buildSubmitPayload()) !== savedPayloadSnapshot.value + }) + + // 从本地存储获取配置 + const getStoredConfig = (): ConfigState | null => { + const storedConfig = readStoredConfig>() + return storedConfig ? normalizeEditableConfig(storedConfig) : null + } + + // 保存配置到本地存储 + const saveConfigToStorage = (configData: ConfigState) => { + configStore.updateConfig(configData) + } + + // 获取系统配置 + const fetchConfig = async (): Promise => { + try { + isRefreshing.value = true + + const response = await ConfigService.getConfig() + + if (response.code === 200 && response.detail) { + config.value = normalizeEditableConfig(response.detail) + const notifyMessage = configStore.applyRemoteConfig(config.value) + if (notifyMessage) { + alertStore.showAlert(notifyMessage, 'success') + } + + return config.value + } else { + throw new Error(response.message || '获取配置失败') + } + } catch (error) { + // 如果网络请求失败,尝试使用本地存储的配置 + const storedConfig = getStoredConfig() + if (storedConfig) { + config.value = storedConfig + return config.value + } + + alertStore.showAlert(getErrorMessage(error, '获取配置失败'), 'error') + return null + } finally { + isRefreshing.value = false + } + } + + // 更新系统配置 + const updateConfig = async (newConfig: Partial): Promise => { + try { + isSaving.value = true + + const response = await ConfigService.updateConfig(newConfig) + + if (response.code === 200) { + config.value = normalizeEditableConfig({ ...config.value, ...newConfig }) + syncConfigForm(config.value) + markConfigSaved() + saveConfigToStorage(config.value) + alertStore.showAlert('配置更新成功!', 'success') + return true + } else { + throw new Error(response.message || '更新配置失败') + } + } catch (error) { + alertStore.showAlert(getErrorMessage(error, '更新配置失败'), 'error') + return false + } finally { + isSaving.value = false + } + } + + const toggleConfigFlag = (key: ConfigFlagKey) => { + config.value[key] = config.value[key] === 1 ? 0 : 1 + } + + const syncConfigForm = (nextConfig: ConfigState) => { + const sizeForm = bytesToFileSizeForm(nextConfig.uploadSize) + fileSize.value = sizeForm.value + sizeUnit.value = sizeForm.unit + + const storageLimitForm = bytesToFileSizeForm(nextConfig.storageLimit) + storageLimit.value = storageLimitForm.value + storageLimitUnit.value = storageLimitForm.unit + + const saveTimeForm = secondsToSaveTimeForm(nextConfig.max_save_seconds) + saveTime.value = saveTimeForm.value + saveTimeUnit.value = saveTimeForm.unit + adminSessionDays.value = Math.max( + 1, + Math.round(nextConfig.adminSessionExpire / SECONDS_PER_DAY) + ) + } + + const refreshConfig = async () => { + const latestConfig = await fetchConfig() + if (latestConfig) { + syncConfigForm(latestConfig) + markConfigSaved() + } + } + + const submitConfig = () => { + if (!isDirty.value || isSaving.value) { + return Promise.resolve(false) + } + + return updateConfig(buildSubmitPayload()) + } + + // 初始化配置 + const initConfig = async () => { + // 先尝试从本地存储加载 + const storedConfig = getStoredConfig() + if (storedConfig) { + config.value = storedConfig + syncConfigForm(storedConfig) + markConfigSaved() + } + + // 然后从服务器获取最新配置 + await refreshConfig() + } + + // 计算属性 + const maxFileSizeMB = computed(() => { + return Math.round(config.value.uploadSize / 1024 / 1024) + }) + + const isConfigLoaded = computed(() => { + return config.value.name !== DEFAULT_CONFIG_STATE.name || !isLoading.value + }) + + return { + // 状态 + config, + isLoading, + isRefreshing, + isSaving, + isDirty, + fileSize, + sizeUnit, + storageLimit, + storageLimitUnit, + saveTime, + saveTimeUnit, + adminSessionDays, + + // 计算属性 + maxFileSizeMB, + isConfigLoaded, + + // 方法 + fetchConfig, + updateConfig, + refreshConfig, + submitConfig, + toggleConfigFlag, + initConfig, + getStoredConfig, + saveConfigToStorage + } +} diff --git a/src/composables/useTheme.ts b/src/composables/useTheme.ts new file mode 100644 index 0000000..63fe823 --- /dev/null +++ b/src/composables/useTheme.ts @@ -0,0 +1,139 @@ +import { ref, computed } from 'vue' +import { THEME_MODES } from '@/constants' +import type { ThemeMode } from '@/types' +import { readStoredThemeMode, writeStoredThemeMode } from '@/utils/preference-storage' + +export function useTheme() { + // 状态管理 + const isDarkMode = ref(false) + const themeMode = ref(THEME_MODES.SYSTEM) + + // 检查系统颜色模式 + const checkSystemColorScheme = (): boolean => { + return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches + } + + // 从本地存储获取用户之前的选择 + const getUserPreference = (): ThemeMode | null => { + const storedPreference = readStoredThemeMode() + if (storedPreference && Object.values(THEME_MODES).includes(storedPreference as ThemeMode)) { + return storedPreference as ThemeMode + } + return null + } + + // 设置颜色模式 + const setThemeMode = (mode: ThemeMode) => { + themeMode.value = mode + writeStoredThemeMode(mode) + + // 根据模式设置实际的暗色模式状态 + if (mode === THEME_MODES.SYSTEM) { + isDarkMode.value = checkSystemColorScheme() + } else { + isDarkMode.value = mode === THEME_MODES.DARK + } + + // 更新 HTML 类名 + updateHtmlClass() + } + + // 更新 HTML 元素的类名 + const updateHtmlClass = () => { + const html = document.documentElement + if (isDarkMode.value) { + html.classList.add('dark') + } else { + html.classList.remove('dark') + } + } + + // 切换主题 + const toggleTheme = () => { + if (themeMode.value === THEME_MODES.LIGHT) { + setThemeMode(THEME_MODES.DARK) + } else if (themeMode.value === THEME_MODES.DARK) { + setThemeMode(THEME_MODES.SYSTEM) + } else { + setThemeMode(THEME_MODES.LIGHT) + } + } + + // 监听系统主题变化 + const setupSystemThemeListener = () => { + if (window.matchMedia) { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') + + const handleChange = (e: MediaQueryListEvent) => { + if (themeMode.value === THEME_MODES.SYSTEM) { + isDarkMode.value = e.matches + updateHtmlClass() + } + } + + mediaQuery.addEventListener('change', handleChange) + + // 返回清理函数 + return () => { + mediaQuery.removeEventListener('change', handleChange) + } + } + return () => {} + } + + // 初始化主题 + const initTheme = () => { + const userPreference = getUserPreference() + if (userPreference) { + setThemeMode(userPreference) + } else { + setThemeMode(THEME_MODES.SYSTEM) + } + + // 设置系统主题监听 + return setupSystemThemeListener() + } + + // 计算属性 + const themeIcon = computed(() => { + switch (themeMode.value) { + case THEME_MODES.LIGHT: + return 'sun' + case THEME_MODES.DARK: + return 'moon' + case THEME_MODES.SYSTEM: + return 'monitor' + default: + return 'monitor' + } + }) + + const themeLabel = computed(() => { + switch (themeMode.value) { + case THEME_MODES.LIGHT: + return '浅色模式' + case THEME_MODES.DARK: + return '深色模式' + case THEME_MODES.SYSTEM: + return '跟随系统' + default: + return '跟随系统' + } + }) + + return { + // 状态 + isDarkMode, + themeMode, + + // 计算属性 + themeIcon, + themeLabel, + + // 方法 + setThemeMode, + toggleTheme, + initTheme, + checkSystemColorScheme + } +} diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 0000000..ae2ab41 --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1,100 @@ +// 应用常量配置 + +// 存储键名 +export const STORAGE_KEYS = { + COLOR_MODE: 'colorMode', + ADMIN_PASSWORD: 'adminPassword', + TOKEN: 'token', + TOKEN_EXPIRES_AT: 'tokenExpiresAt', + CONFIG: 'config', + NOTIFY: 'notify' +} as const + +// 主题模式 +export const THEME_MODES = { + LIGHT: 'light', + DARK: 'dark', + SYSTEM: 'system' +} as const + +// 发送类型 +export const SEND_TYPES = { + FILE: 'file', + TEXT: 'text' +} as const + +// 警告类型 +export const ALERT_TYPES = { + SUCCESS: 'success', + ERROR: 'error', + WARNING: 'warning', + INFO: 'info' +} as const + +// 上传状态 +export const UPLOAD_STATUS = { + IDLE: 'idle', + UPLOADING: 'uploading', + SUCCESS: 'success', + ERROR: 'error' +} as const + +// API 响应状态码 +export const API_STATUS_CODES = { + SUCCESS: 200, + UNAUTHORIZED: 401, + SETUP_REQUIRED: 428, + FORBIDDEN: 403, + NOT_FOUND: 404, + SERVER_ERROR: 500 +} as const + +// 文件大小限制 (字节) +export const FILE_SIZE_LIMITS = { + MAX_FILE_SIZE: 100 * 1024 * 1024, // 100MB + CHUNK_SIZE: 1024 * 1024 // 1MB +} as const + +// 时间相关常量 +export const TIME_CONSTANTS = { + ALERT_DURATION: 5000, // 5秒 + REQUEST_TIMEOUT: 300000000, + PROGRESS_UPDATE_INTERVAL: 100 // 100毫秒 +} as const + +// 路由路径 +export const ROUTES = { + HOME: '/', + SEND: '/send', + ADMIN: '/admin', + LOGIN: '/login', + DASHBOARD: '/admin/dashboard', + FILE_MANAGE: '/admin/files', + SETTINGS: '/admin/settings' +} as const + +export const ROUTE_NAMES = { + RETRIEVE: 'Retrieve', + SEND: 'Send', + ADMIN: 'Manage', + LOGIN: 'Login', + DASHBOARD: 'Dashboard', + FILE_MANAGE: 'FileManage', + SETTINGS: 'Settings' +} as const + +// 正则表达式 +export const REGEX_PATTERNS = { + EMAIL: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, + PHONE: /^1[3-9]\d{9}$/, + CODE: /^[A-Za-z0-9]{4,8}$/ // 取件码格式 +} as const + +// 默认配置 +export const DEFAULT_CONFIG = { + name: 'FileCodeBox', + description: '文件传输工具', + maxFileSize: FILE_SIZE_LIMITS.MAX_FILE_SIZE, + allowedFileTypes: ['*'] as string[], + expireDays: 7 +} diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..0207fdd --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1,14 @@ +// 自定义 Vue hooks 导出文件 +// 这里可以导出所有自定义的 Vue hooks + +// 示例:导出常用的自定义 hooks +// export { useLocalStorage } from './useLocalStorage' +// export { useDebounce } from './useDebounce' +// export { useThrottle } from './useThrottle' +// export { useEventListener } from './useEventListener' +// export { useClickOutside } from './useClickOutside' + +// 目前项目中的业务逻辑已经通过 composables 进行了封装 +// hooks 文件夹主要用于存放更通用的、可复用的 Vue hooks + +export {} \ No newline at end of file diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..7072171 --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,67 @@ +import { createI18n } from 'vue-i18n' +import zhCN from './locales/zh-CN' +import enUS from './locales/en-US' +import { readStoredLocale, writeStoredLocale } from '@/utils/preference-storage' + +type SupportedLocale = 'zh-CN' | 'en-US' + +const normalizeLocale = (locale: string | null | undefined): SupportedLocale | null => { + if (!locale) return null + + const normalized = locale.trim().toLowerCase() + if (normalized === 'zh' || normalized.startsWith('zh-')) return 'zh-CN' + if (normalized === 'en' || normalized.startsWith('en-')) return 'en-US' + + return null +} + +// 获取浏览器语言设置 +const getDefaultLocale = (): SupportedLocale => { + const savedLocale = normalizeLocale(readStoredLocale()) + if (savedLocale) { + writeStoredLocale(savedLocale) + return savedLocale + } + + return normalizeLocale(navigator.language) ?? 'en-US' +} + +const messages = { + 'zh-CN': zhCN, + 'en-US': enUS +} + +const defaultLocale = getDefaultLocale() + +const i18n = createI18n({ + legacy: false, // 使用 Composition API 模式 + locale: defaultLocale, + fallbackLocale: 'zh-CN', + messages, + globalInjection: true, // 全局注入 $t 函数 + missingWarn: import.meta.env.DEV, + fallbackWarn: import.meta.env.DEV +}) + +document.documentElement.lang = defaultLocale + +export default i18n + +// 导出切换语言的函数 +export const setLocale = (locale: string) => { + const nextLocale = normalizeLocale(locale) ?? 'zh-CN' + i18n.global.locale.value = nextLocale + writeStoredLocale(nextLocale) + document.documentElement.lang = nextLocale +} + +// 导出获取当前语言的函数 +export const getCurrentLocale = () => { + return i18n.global.locale.value +} + +// 导出可用语言列表 +export const availableLocales = [ + { code: 'zh-CN', name: '中文' }, + { code: 'en-US', name: 'English' } +] diff --git a/src/i18n/locales/en-US.ts b/src/i18n/locales/en-US.ts new file mode 100644 index 0000000..ddca646 --- /dev/null +++ b/src/i18n/locales/en-US.ts @@ -0,0 +1,999 @@ +export default { + // Common + common: { + confirm: 'Confirm', + cancel: 'Cancel', + close: 'Close', + delete: 'Delete', + edit: 'Edit', + save: 'Save', + add: 'Add', + back: 'Back', + next: 'Next', + previous: 'Previous', + loading: 'Loading...', + noData: 'No Data', + success: 'Success', + error: 'Error', + warning: 'Warning', + info: 'Information', + search: 'Search', + copy: 'Copy', + uploadSuccess: 'Upload successful', + uploadFailed: 'Upload failed', + copySuccess: 'Copy successful', + copyFailed: 'Copy failed', + deleteSuccess: 'Delete successful', + deleteFailed: 'Delete failed', + downloadSuccess: 'Download successful', + downloadFailed: 'Download failed', + shareSuccess: 'Share successful', + shareFailed: 'Share failed', + expiredFile: 'File has expired', + fileNotFound: 'File not found', + networkError: 'Network error', + unknownError: 'Unknown error', + invalidFileType: 'Invalid file type', + fileTooLarge: 'File too large', + uploadCanceled: 'Upload canceled', + processing: 'Processing', + pleaseWait: 'Please wait', + fileDetails: 'File Details', + enabled: 'Enabled', + disabled: 'Disabled', + minute: 'minute', + files: 'files', + second: 'second', + hour: 'hour', + day: 'day', + times: 'times', + appName: 'FileCodeBox - File Transfer', + appDescription: 'Ready-to-use file transfer system' + }, + + // Admin module + admin: { + logout: 'Logout', + session: { + loggedIn: 'Signed in, open admin console', + loggedOut: 'Sign in to admin console' + }, + dashboard: { + title: 'Dashboard', + totalFiles: 'Total Files', + storageSpace: 'Storage Space', + todayShares: 'Today Shares', + totalRetrievals: 'Total Retrievals', + activeFiles: 'Active files: {count}', + todayIncrease: 'Today added: {count}', + yesterdayShares: 'Yesterday: {count}', + serverUptime: 'Uptime', + refresh: 'Refresh', + refreshing: 'Refreshing', + lastUpdated: 'Last updated: {time}', + loadFailed: 'Failed to load dashboard data', + footerProduct: 'FileCodeBox Admin Console', + backendVersion: 'Server Version', + frontendVersion: 'Frontend Version', + versionPending: 'Pending sync', + fileHealth: 'File Health', + fileHealthDesc: 'Status insights for available, risky, and pending file queues.', + activeFileRatio: 'Active Ratio', + fileShareRatio: 'File Ratio', + textShareRatio: 'Text Ratio', + binaryFiles: '{count} file shares', + textShares: '{count} text shares', + expiredFiles: 'Expired Files', + needCleanup: 'Clean up in file management', + chunkedFiles: 'Chunked Files', + storagePolicy: 'Storage & Upload Policy', + storagePolicyDesc: 'Current settings that affect upload behavior.', + storageBackend: 'Storage Backend', + singleFileLimit: 'Single File Limit', + guestUpload: 'Guest Upload', + maxSaveTime: 'Max Retention', + noSaveLimit: 'Unlimited', + todayCapacityReference: 'Today Size / Single File Limit', + healthActions: { + attention: { + title: 'Needs Attention', + description: 'Handle issue and warning files together' + }, + storageIssue: { + title: 'Storage Issues', + description: 'Review files missing download metadata' + }, + expiringSoon: { + title: 'Expiring Soon', + description: 'Extend shares that should stay available' + }, + neverRetrieved: { + title: 'Never Retrieved', + description: 'Find shares that have not been retrieved yet' + }, + permanent: { + title: 'Permanent', + description: 'View long-retention share records' + } + } + }, + fileManage: { + title: 'File Management' + }, + settings: { + title: 'System Settings', + basicSettings: 'General', + websiteInfo: 'Site Information', + websiteName: 'Site Name', + websiteDescription: 'Site Description', + siteName: 'Site Name', + adminPassword: 'Admin Password', + sessionDuration: 'Login Session Duration', + sessionDurationHelp: 'Applies to new logins only. Allowed range: 1 to 365 days.', + showAdminAddress: 'Show Admin Entry on Home Pages', + passwordPlaceholder: 'Enter new password to change', + passwordNote: 'Leave empty to keep current', + keywords: 'SEO Keywords', + themeSelection: 'Theme', + robotsFile: 'Robots.txt Configuration', + notificationSettings: 'Notifications', + notificationTitle: 'Announcement Title', + notificationContent: 'Announcement Content', + storageSettings: 'Storage', + storageLimit: 'Total Storage Capacity', + storageLimitHelp: + 'Active shares and pending uploads share this quota. Set to 0 for unlimited.', + storagePath: 'Storage Path', + storagePathPlaceholder: 'Leave empty for default path', + storageMethod: 'Storage Type', + localStorage: 'Local Storage', + s3Storage: 'S3 Object Storage', + webdavStorage: 'WebDAV Storage', + chunkUploadNote: 'Chunked Upload (experimental, better for large files)', + s3AccessKeyId: 'Access Key ID', + s3SecretAccessKey: 'Secret Access Key', + s3BucketName: 'Bucket Name', + s3EndpointUrl: 'Endpoint URL', + s3RegionName: 'Region Name', + s3SignatureVersion: 'Signature Version', + s3AddressingStyle: 'S3 Addressing Style', + s3AddressingAuto: 'Auto', + s3AddressingPath: 'Path-style', + s3AddressingVirtual: 'Virtual-host', + s3Hostname: 'Custom Domain', + s3v2: 'S3v2', + s3v4: 'S3v4', + autoPlaceholder: 'Auto-detect', + enabled: 'Enabled', + disabled: 'Disabled', + webdavUrl: 'Enter WebDAV server URL', + webdavUsername: 'Enter WebDAV username', + webdavPassword: 'Enter WebDAV password', + webdavUrlPlaceholder: 'Enter WebDAV server URL', + webdavUsernamePlaceholder: 'Enter WebDAV username', + webdavPasswordPlaceholder: 'Enter WebDAV password', + enableProxy: 'Enable Download Proxy', + uploadLimits: 'Upload Settings', + uploadRateLimit: 'Time Window (limit uploads within this period)', + uploadPerMinute: 'Time Window (limit uploads within this period)', + minute: 'min', + uploadCountLimit: 'Max Files Allowed (within the time window)', + files: 'files', + fileSizeLimit: 'Max File Size', + allowedFileTypes: 'Allowed File Types', + allowedFileTypesPlaceholder: '* or .rar, .zip, image/*', + allowedFileTypesHelp: + 'Separate multiple types with commas. * allows all; extensions and MIME wildcards are supported.', + codeGenerateType: 'Passcode Rule', + codeGenerateNumber: 'Numeric Passcode', + codeGenerateSecret: 'Secret Passcode', + codeGenerateTypeHelp: + 'Numeric keeps the existing 5-digit code. Secret uses a random alphanumeric code.', + expirationMethod: 'Expiration Options', + expirationType: 'Expiration Options', + expiration: { + day: 'Days', + hour: 'Hours', + minute: 'Minutes', + forever: 'Never Expire', + count: 'Download Count' + }, + maxSaveTime: 'Max Retention Period', + timeUnits: { + second: 'sec', + minute: 'min', + hour: 'hr', + day: 'day' + }, + guestUpload: 'Allow Guest Uploads', + errorLimits: 'Access Protection', + errorRateLimit: 'Detection Window (count errors within this period)', + errorPerMinute: 'Detection Window (count errors within this period)', + errorCountLimit: 'Max Errors Allowed (block after exceeding)', + times: 'times', + saveChanges: 'Save Settings', + fileSizeUnits: { + kb: 'KB', + mb: 'MB', + gb: 'GB' + } + } + }, + + // Navigation + nav: { + sendFile: 'Send File', + retrieveFile: 'Retrieve', + fileRecords: 'File Records' + }, + + // Retrieve page + retrieve: { + title: 'Retrieve File', + codeInput: { + placeholder: 'Enter your 5-character retrieval code', + label: 'Retrieval Code' + }, + submit: 'Retrieve Now', + needSendFile: 'Need to send a file?', + recordsDrawer: 'Retrieval Records', + messages: { + invalidCode: 'Please enter a 5-character retrieval code', + retrieveSuccess: 'File retrieved successfully', + invalidCodeError: 'Invalid retrieval code', + retrieveFailure: 'Failed to retrieve file: ', + networkError: 'Retrieval failed, please try again later: ', + unknownError: 'Unknown error', + previewUnavailable: 'Preview is unavailable, you can still retrieve directly' + }, + workspace: { + sendFile: 'Send File', + inspect: 'Preview', + inspecting: 'Previewing', + retrieving: 'Retrieving', + ready: 'Ready', + noPreview: 'Waiting for a code', + noPreviewDesc: + 'Enter a 5-character retrieval code to preview file details before consuming a retrieval.', + latestRecord: 'Latest Retrieval', + noRecord: 'No retrieval records yet', + currentCode: 'Code', + fileSize: 'File Size', + viewDetail: 'View Detail', + historyCount: '{count} records', + openRecords: 'Open retrieval records', + security: 'Safe Retrieval', + securityState: + 'Previewing does not consume retrieval count. Access is recorded only after confirmation.', + noExpiry: 'No limit', + unlimited: 'Unlimited', + remainingCount: '{count} left', + textType: 'Text Content', + fileType: 'File Download', + expiresAt: 'Expires At', + remainingDownloads: 'Remaining', + usedCount: 'Retrieved', + emptyCode: 'Empty', + previewState: 'Preview State', + waiting: 'Waiting' + } + }, + + // Send page + send: { + title: 'File Send', + sendText: 'Send Text', + uploadArea: { + dragText: 'Drag files here or', + clickText: 'click to select files', + textInput: 'Enter text to send here...', + placeholder: 'Click or drag files here to upload', + description: 'Supports various common formats', + descriptionWithLimit: 'Supports common formats, up to {size}', + retry: 'Retry', + selectedFiles: '{count} files selected', + status: { + initializing: 'Preparing upload...', + uploading: 'Uploading files...', + confirming: 'Confirming upload...', + success: 'Upload complete!' + } + }, + submit: 'Secure Send', + submitting: 'Sending...', + needRetrieveFile: 'Need to retrieve? Click here', + sendRecords: 'Send Records', + secureEncryption: 'Secure Encryption', + workspace: { + uploadLimit: 'Single File Limit', + uploadMode: 'Upload Mode', + standardMode: 'Standard', + chunkMode: 'Chunked', + guestPolicy: 'Guest Upload', + guestOpen: 'Allowed', + guestClosed: 'Disabled', + currentTask: 'Current Task', + awaitingFile: 'Waiting for files', + fileReady: '{count} files ready', + textDraft: 'Text draft, {count} chars', + payload: 'Payload', + expirationPreview: 'Expiration', + security: 'Security', + latestRecord: 'Latest Send', + noRecord: 'No records yet', + copyLink: 'Copy Link', + viewDetail: 'View Detail', + historyTitle: 'Send History', + historyCount: '{count} records', + openRecords: 'Open send records' + }, + fileDetails: 'File Details', + expirationMethod: 'Expiration Method', + expiration: { + day: 'By Day', + hour: 'By Hour', + minute: 'By Minute', + forever: 'Forever', + count: 'By Count', + label: 'Expiration Time', + placeholders: { + days: 'Enter days', + hours: 'Enter hours', + minutes: 'Enter minutes', + count: 'Enter view count', + forever: 'Forever', + default: 'Enter value' + }, + units: { + days: 'days', + hours: 'hours', + minutes: 'minutes', + times: 'times', + forever: 'Forever' + } + }, + time: { + day: 'By Day', + hour: 'By Hour', + minute: 'By Minute', + forever: 'Forever', + count: 'By Count' + }, + messages: { + selectFile: 'Please select a file to upload', + enterText: 'Please enter text to send', + enterExpirationValue: 'Please enter expiration value', + expirationTooLong: 'Expiration time cannot exceed {days} days', + sendSuccess: 'File sent successfully! Retrieve code: {code}', + initChunkUploadFailed: 'Failed to initialize chunk upload', + chunkUploadFailed: 'Chunk {index} upload failed', + completeUploadFailed: 'Failed to complete upload', + uploadFailed: 'Upload failed, please try again later', + guestUploadDisabled: 'Guest upload feature is disabled', + fileSizeExceeded: 'File size exceeds limit ({size})', + fileTypeNotAllowed: 'This file type is not allowed. Allowed types: {types}', + serverError: 'Server response error', + sendFailed: 'Send failed, please try again later', + expiresAfterCount: 'Expires after {count} times', + expiresAt: 'Expires at {date}', + emptyFileError: 'Cannot read empty file', + fileAddedFromClipboard: 'File added from clipboard: {filename}', + fileProcessingFailed: 'File processing failed', + expiresAfter: 'Expires after {value} {unit}' + } + }, + + // File records + fileRecord: { + filename: 'Filename', + size: 'Size', + date: 'Date', + code: 'Code', + actions: 'Actions', + download: 'Download', + viewDetails: 'View Details', + deleteRecord: 'Delete Record', + preview: 'Preview', + copyContent: 'Copy Content', + contentCopied: 'Content copied to clipboard', + copyFailed: 'Copy failed, please try again' + }, + + // File Detail Modal + fileDetail: { + title: 'File Details', + content: 'File Content', + previewContent: 'Preview Content', + download: 'Click to Download', + qrCode: 'Retrieve QR Code', + scanQrCode: 'Scan QR code for quick retrieval' + }, + + // Content Preview Modal + contentPreview: { + title: 'Content Preview' + }, + + // File Management + fileManage: { + title: 'File Management', + subtitle: '{count} share records. Filter by status, type, and usage.', + searchPlaceholder: 'Search code, name, or text...', + allFiles: 'All Files', + detail: 'Details', + detailTitle: 'File Details', + detailLoading: 'Loading details...', + detailFailed: 'Failed to load details', + editFileInfo: 'Edit File Information', + saveChanges: 'Save Changes', + refresh: 'Refresh List', + resetFilters: 'Reset Filters', + totalFiles: 'All Records', + activeFiles: 'Available', + expiredFiles: 'Expired', + storageUsed: 'Storage Used', + statusLabel: 'Status', + typeLabel: 'Type', + healthLabel: 'Health', + viewPreset: 'View', + viewPresetCustom: 'Custom Filters', + viewPresetAll: 'All Files', + viewPresetAttention: 'Needs Attention', + viewPresetExpiringSoon: 'Expiring Soon', + viewPresetStorageIssue: 'Storage Issues', + viewPresetNeverRetrieved: 'Never Retrieved', + viewPresetPermanent: 'Permanent', + saveViewPreset: 'Save View', + updateViewPreset: 'Update View', + deleteViewPreset: 'Delete View', + viewPresetNamePrompt: 'Enter a view name', + viewPresetSaveSuccess: 'View saved', + viewPresetSaveFailed: 'Failed to save view', + viewPresetDeleteConfirm: 'Delete view "{name}"?', + viewPresetDeleteSuccess: 'View deleted', + viewPresetDeleteFailed: 'Failed to delete view', + viewPresetLoadFailed: 'Failed to load saved views; using built-in views', + all: 'All', + active: 'Available', + expired: 'Expired', + fileType: 'File', + textType: 'Text', + chunkedType: 'Chunked', + healthFilters: { + all: 'All', + attention: 'Needs Attention', + danger: 'Issues', + warning: 'Warnings', + expiringSoon: 'Expiring Soon', + storageIssue: 'Storage Issues', + neverRetrieved: 'Never Retrieved', + healthy: 'Healthy', + permanent: 'Permanent' + }, + sortBy: 'Sort', + sortOrder: 'Direction', + unlimited: 'Unlimited', + remaining: '{count} left', + loadError: 'Failed to load file list', + noMatches: 'No matching files', + viewText: 'View', + textPreview: 'Text Preview', + copyText: 'Copy Text', + copyCode: 'Copy Code', + copyLink: 'Copy Link', + copySuccess: 'Text copied to clipboard', + copyCodeSuccess: 'Retrieve code copied to clipboard', + copyLinkSuccess: 'Retrieve link copied to clipboard', + copyFailed: 'Copy failed, please try again', + charCount: '{count} characters', + downloadFile: 'Download File', + exportText: 'Export Text', + downloadSuccess: 'Downloaded: {name}', + exportSuccess: 'Exported: {name}', + downloadFailed: 'Download failed', + previewFailed: 'Failed to load preview', + loadingPreview: 'Loading preview...', + previewComplete: 'Loaded full content, {count} characters', + previewTruncated: 'Showing {shown} / {total} characters', + previewFallback: 'Preview endpoint unavailable; using text from the list', + selectCurrentPage: 'Select current page', + selectedCount: '{count} selected', + selectFile: 'Select {name}', + clearSelection: 'Clear Selection', + batchDelete: 'Batch Delete', + batchDeleteConfirm: 'Delete the selected {count} files? This action cannot be undone.', + batchDeleteSuccess: 'Deleted {count} files', + batchDeletePartialSuccess: 'Deleted {count} files, {failed} failed', + batchDeleteFailed: 'Batch delete failed', + batchEdit: 'Batch Edit', + batchEditTitle: 'Batch Edit File Policy', + batchEditSelected: 'Update the selected {count} files', + batchEditMode: 'Update Mode', + batchEditExpiresAt: 'Set Expiration Time', + batchEditDownloadLimit: 'Set Retrieval Limit', + batchEditForever: 'Make Permanent', + batchEditForeverHint: 'Clear the expiration time and make retrievals unlimited.', + batchUpdateConfirm: 'Update the selected {count} files?', + batchUpdateSuccess: 'Updated {count} files', + batchUpdatePartialSuccess: 'Updated {count} files, {failed} failed', + batchUpdateFailed: 'Batch update failed', + batchUpdateNoFields: 'Choose a policy to update', + batchPolicyActionConfirm: 'Apply "{action}" to the selected {count} files?', + batchPolicyActionSuccess: 'Processed {count} files', + batchPolicyActionPartialSuccess: 'Processed {count} files, {failed} failed', + batchPolicyActionFailed: 'Batch policy action failed', + policyActionSuccess: 'File policy updated', + policyActionFailed: 'Failed to apply policy action', + metadataInfo: 'Operations Notes', + metadataHint: + 'Visible to admins only. Use it for handling notes, archive tags, and follow-up actions.', + metadataNote: 'Note', + metadataNotePlaceholder: 'Record context, handling history, or ownership...', + metadataTags: 'Tags', + metadataTagsPlaceholder: 'Comma-separated, e.g. Client A, Renew, Important', + metadataUpdatedAt: 'Last saved: {time}', + metadataNeverUpdated: 'No notes saved yet', + saveMetadata: 'Save Notes', + metadataSaveSuccess: 'Notes and tags saved', + metadataSaveFailed: 'Failed to save notes', + policyActions: { + extend24h: 'Extend 24h', + extend7d: 'Extend 7d', + makePermanent: 'Make Permanent', + resetDownloadLimit: 'Reset to {count}' + }, + policyActionDescriptions: { + extend24h: 'Add one day from the active expiration', + extend7d: 'Good for a one-week temporary extension', + makePermanent: 'Clear expiration time and retrieval limits', + resetDownloadLimit: 'Restore retrievals to {count}' + }, + applyBatchEdit: 'Apply Changes', + overview: 'Overview', + policyInfo: 'Policy', + storageInfo: 'Storage', + statusInsight: 'Status Insight', + nextAction: 'Next Action', + lifecycle: 'Lifecycle', + permanent: 'Permanent', + createdAt: 'Created Time', + remainingDownloads: 'Remaining Downloads', + textLength: 'Text Length', + hasDownloadLimit: 'Has Retrieval Limit', + hasExpirationTime: 'Has Expiration', + canPreviewText: 'Can Preview Text', + canDownload: 'Can Download', + storageBackend: 'Storage Backend', + fileHash: 'File Hash', + isChunked: 'Chunked Upload', + filePath: 'Storage Path', + uuidFileName: 'Stored File Name', + uploadId: 'Upload Session', + yes: 'Yes', + no: 'No', + insightSeverity: { + success: 'Healthy', + warning: 'Needs Attention', + danger: 'Issue', + info: 'Info', + neutral: 'Watch' + }, + insightStates: { + available: 'Available for retrieval', + expired: 'Unavailable for retrieval', + storage_incomplete: 'Storage metadata incomplete', + expiring_soon: 'Expiring soon', + permanent: 'Permanent' + }, + insightActions: { + monitor: 'Keep monitoring', + extend_or_delete: 'Extend retention or clean up', + inspect_storage: 'Inspect storage path and file name', + extend_expiration: 'Extend expiration if needed' + }, + insightReasons: { + expired: 'Past expiration time', + download_limit_exhausted: 'Retrieval limit exhausted', + expires_soon: 'Expires within 24 hours', + never_retrieved: 'Never retrieved', + storage_metadata_incomplete: 'Missing downloadable storage metadata', + chunked_upload: 'Created by chunked upload' + }, + timeline: { + status: { + done: 'Done', + pending: 'Pending', + active: 'Active', + expired: 'Expired', + exhausted: 'Exhausted', + unlimited: 'Unlimited' + }, + created: { + title: 'Record Created', + description: 'The share record has been created and is currently {status}.' + }, + content_ready: { + title: 'Content Ready', + description: 'Content type is {detail}; current status is {status}.' + }, + upload_session: { + title: 'Upload Session', + description: 'Upload session recorded: {detail}.' + }, + expiration_policy: { + title: 'Expiration Policy', + description: 'Expiration policy is currently {status}.', + remaining: '{time} left', + overdue: '{time} overdue' + }, + download_limit: { + title: 'Retrieval Limit', + description: 'Retrieval limit is currently {status}; {value}.' + }, + retrieved: { + title: 'Retrieval History', + description: 'Retrieved {value}; current status is {status}.' + } + }, + headers: { + select: 'Select', + code: 'Retrieve Code', + name: 'Name', + type: 'Type', + size: 'Size', + usage: 'Retrievals', + status: 'Status', + description: 'Description', + expiration: 'Expiration', + actions: 'Actions' + }, + sort: { + createdAt: 'Created Time', + expiredAt: 'Expiration', + name: 'Name', + size: 'Size', + usedCount: 'Retrievals', + code: 'Retrieve Code', + desc: 'Descending', + asc: 'Ascending' + }, + form: { + code: 'Retrieve Code', + codePlaceholder: 'Enter retrieve code', + filename: 'File Name', + filenamePlaceholder: 'Enter file name', + suffix: 'File Extension', + suffixPlaceholder: 'Enter file extension', + downloadLimit: 'Download Limit', + downloadLimitPlaceholder: 'Enter download limit' + } + }, + + // Side drawer + drawer: { + noRecords: 'No records' + }, + + // Admin pages + manage: { + settings: { + title: 'System Settings', + basicSettings: 'General', + websiteInfo: 'Site Information', + websiteName: 'Site Name', + websiteDescription: 'Site Description', + siteName: 'Site Name', + adminPassword: 'Admin Password', + passwordPlaceholder: 'Enter new password to change', + passwordNote: 'Leave empty to keep current', + keywords: 'SEO Keywords', + themeSelection: 'Theme', + notificationSettings: 'Notifications', + notificationTitle: 'Announcement Title', + notificationContent: 'Announcement Content', + storageSettings: 'Storage', + storagePath: 'Storage Path', + storagePathPlaceholder: 'Leave empty for default path', + storageMethod: 'Storage Type', + localStorage: 'Local Storage', + s3Storage: 'S3 Object Storage', + webdavStorage: 'WebDAV Storage', + chunkUploadNote: 'Chunked Upload (experimental, better for large files)', + enabled: 'Enabled', + disabled: 'Disabled', + webdavUrl: 'Enter WebDAV server URL', + webdavUsername: 'Enter WebDAV username', + webdavPassword: 'Enter WebDAV password', + webdavUrlPlaceholder: 'Enter WebDAV server URL', + webdavUsernamePlaceholder: 'Enter WebDAV username', + webdavPasswordPlaceholder: 'Enter WebDAV password', + enableProxy: 'Enable Download Proxy', + uploadLimits: 'Upload Settings', + uploadRateLimit: 'Time Window (limit uploads within this period)', + uploadPerMinute: 'Time Window (limit uploads within this period)', + minute: 'min', + uploadCountLimit: 'Max Files Allowed (within the time window)', + files: 'files', + fileSizeLimit: 'Max File Size', + allowedFileTypes: 'Allowed File Types', + allowedFileTypesPlaceholder: '* or .rar, .zip, image/*', + allowedFileTypesHelp: + 'Separate multiple types with commas. * allows all; extensions and MIME wildcards are supported.', + codeGenerateType: 'Passcode Rule', + codeGenerateNumber: 'Numeric Passcode', + codeGenerateSecret: 'Secret Passcode', + codeGenerateTypeHelp: + 'Numeric keeps the existing 5-digit code. Secret uses a random alphanumeric code.', + expirationMethod: 'Expiration Options', + expirationType: 'Expiration Options', + expiration: { + day: 'Days', + hour: 'Hours', + minute: 'Minutes', + forever: 'Never Expire', + count: 'Download Count' + }, + maxSaveTime: 'Max Retention Period', + timeUnits: { + second: 'sec', + minute: 'min', + hour: 'hr', + day: 'day' + }, + guestUpload: 'Allow Guest Uploads', + errorLimits: 'Access Protection', + errorRateLimit: 'Detection Window (count errors within this period)', + errorPerMinute: 'Detection Window (count errors within this period)', + errorCountLimit: 'Max Errors Allowed (block after exceeding)', + times: 'times', + fileSizeUnits: { + kb: 'KB', + mb: 'MB', + gb: 'GB' + }, + saveChanges: 'Save Settings', + refreshConfig: 'Refresh Config', + refreshing: 'Refreshing', + saving: 'Saving', + unsavedChanges: 'Unsaved configuration changes', + allChangesSaved: 'All configuration changes are saved', + refreshBlocked: 'Save current changes before refreshing' + }, + systemSettings: { + title: 'System Settings', + basicSettings: 'General', + websiteInfo: 'Site Information', + websiteName: 'Site Name', + websiteDescription: 'Site Description', + adminPassword: 'Admin Password', + passwordPlaceholder: 'Enter new password to change', + passwordNote: 'Leave empty to keep current', + keywords: 'SEO Keywords', + themeSelection: 'Theme', + notificationSettings: 'Notifications', + notificationTitle: 'Announcement Title', + notificationContent: 'Announcement Content', + storageSettings: 'Storage', + storagePath: 'Storage Path', + storagePathPlaceholder: 'Leave empty for default path', + storageMethod: 'Storage Type', + localStorage: 'Local Storage', + s3Storage: 'S3 Object Storage', + webdavStorage: 'WebDAV Storage', + chunkUploadNote: 'Chunked Upload (experimental, better for large files)', + enabled: 'Enabled', + disabled: 'Disabled', + webdavUrl: 'Enter WebDAV server URL', + webdavUsername: 'Enter WebDAV username', + webdavPassword: 'Enter WebDAV password', + enableProxy: 'Enable Download Proxy', + uploadLimits: 'Upload Settings', + uploadRateLimit: 'Time Window (limit uploads within this period)', + minute: 'min', + uploadCountLimit: 'Max Files Allowed (within the time window)', + files: 'files', + fileSizeLimit: 'Max File Size', + codeGenerateType: 'Passcode Rule', + codeGenerateNumber: 'Numeric Passcode', + codeGenerateSecret: 'Secret Passcode', + codeGenerateTypeHelp: + 'Numeric keeps the existing 5-digit code. Secret uses a random alphanumeric code.', + expirationMethod: 'Expiration Options', + expirationMethods: { + day: 'Days', + hour: 'Hours', + minute: 'Minutes', + forever: 'Never Expire', + count: 'Download Count' + }, + expiration: { + day: 'Days', + hour: 'Hours', + minute: 'Minutes', + forever: 'Never Expire', + count: 'Download Count' + }, + maxSaveTime: 'Max Retention Period', + timeUnits: { + second: 'sec', + minute: 'min', + hour: 'hr', + day: 'day' + }, + guestUpload: 'Allow Guest Uploads', + errorLimits: 'Access Protection', + errorRateLimit: 'Detection Window (count errors within this period)', + errorCountLimit: 'Max Errors Allowed (block after exceeding)', + times: 'times', + saveSettings: 'Save Settings', + saveSuccess: 'Settings saved successfully', + saveFailed: 'Failed to save, please try again', + getConfigFailed: 'Failed to load settings, please refresh' + }, + dashboard: { + title: 'Dashboard', + totalFiles: 'Total Files', + storageSpace: 'Storage Space', + todayShares: 'Today Shares', + totalRetrievals: 'Total Retrievals', + activeFiles: 'Active files: {count}', + todayIncrease: 'Today added: {count}', + yesterdayShares: 'Yesterday: {count}', + serverUptime: 'Uptime', + refresh: 'Refresh', + fileHealth: 'File Health', + fileHealthDesc: 'Status insights for available, risky, and pending file queues.', + activeFileRatio: 'Active Ratio', + fileShareRatio: 'File Ratio', + textShareRatio: 'Text Ratio', + binaryFiles: '{count} file shares', + textShares: '{count} text shares', + expiredFiles: 'Expired Files', + needCleanup: 'Clean up in file management', + chunkedFiles: 'Chunked Files', + storagePolicy: 'Storage & Upload Policy', + storagePolicyDesc: 'Current settings that affect upload behavior.', + storageBackend: 'Storage Backend', + singleFileLimit: 'Single File Limit', + guestUpload: 'Guest Upload', + maxSaveTime: 'Max Retention', + noSaveLimit: 'Unlimited', + todayCapacityReference: 'Today Size / Single File Limit' + }, + fileManage: { + title: 'File Management', + searchPlaceholder: 'Search file name, description...', + allFiles: 'All Files', + editFileInfo: 'Edit File Info', + saveChanges: 'Save Changes', + headers: { + code: 'Code', + name: 'Name', + size: 'Size', + description: 'Description', + expiration: 'Expiration', + actions: 'Actions' + }, + form: { + code: 'Code', + codePlaceholder: 'Enter code', + filename: 'File Name', + filenamePlaceholder: 'Enter file name', + suffix: 'File Suffix', + suffixPlaceholder: 'Enter file suffix', + downloadLimit: 'Download Limit', + downloadLimitPlaceholder: 'Enter download limit' + }, + updateFailed: 'Update failed', + deleteFailed: 'Delete failed', + deleteConfirm: 'Delete this file? This action cannot be undone.', + loadFileListFailed: 'Failed to load file list' + }, + login: { + title: 'Login', + password: 'Password', + passwordPlaceholder: 'Password', + loginButton: 'Login', + loggingIn: 'Logging in...', + invalidPassword: 'Invalid password', + passwordTooShort: 'Password must be at least 6 characters', + loginFailed: 'Login failed', + noValidToken: 'Login failed: No valid token received' + } + }, + + // File size units + fileSize: { + bytes: 'Bytes', + kb: 'KB', + mb: 'MB', + gb: 'GB', + tb: 'TB' + }, + + // Utility functions + utils: { + // Time formatting + formatTime: 'Format timestamp to readable format', + formatFileSize: 'Format file size', + formatDuration: 'Format duration', + time: { + forever: 'Forever', + day: 'day', + hour: 'hour', + minute: 'minute', + second: 'second' + }, + copyToClipboard: 'Copy text to clipboard', + copyFailed: 'Copy failed:', + debounce: 'Debounce function', + throttle: 'Throttle function', + validateEmail: 'Validate email format', + validateUrl: 'Validate URL format', + generateRandomString: 'Generate random string', + deepClone: 'Deep clone object', + getFileExtension: 'Get file extension', + isMobileDevice: 'Check if mobile device', + formatNumber: 'Format number with thousand separators', + clipboard: { + title: 'Clipboard utility functions', + copyText: 'Copy text to clipboard', + copySuccess: 'Copy successful', + copyFailed: 'Copy failed, please copy manually' + } + }, + + // Components + components: { + pagination: { + showing: 'Showing', + to: 'to', + of: 'of', + total: 'total', + previous: 'Previous', + next: 'Next' + }, + languageSwitcher: { + clickOutsideToClose: 'Click outside to close dropdown menu' + }, + borderProgressBar: { + borderWidth: 'Border width', + cornerRadius: 'Corner radius', + createGradient: 'Create gradient', + drawBackground: 'Draw background', + calculateProgress: 'Calculate progress', + drawProgress: 'Draw progress' + } + }, + + // Miscellaneous + misc: { + emptyFileError: 'Cannot read empty file', + fileAddedFromClipboard: 'File added from clipboard: ', + fileProcessFailed: 'File processing failed', + chunkSize: 'Keep 2MB chunk size for hash calculation', + secureContext: 'If not in secure context (HTTP), return an alternative hash based on file info', + cryptoFallback: 'If crypto.subtle.digest fails, use fallback method', + generateAlternativeHash: 'Function to generate alternative hash', + fileInfoHash: 'Generate simple hash using filename, size and last modified time', + convertToHex: 'Convert to hex string and pad to 64 bits', + defaultChunkSize: 'Default chunk size is 5MB', + initChunkUpload: '1. Initialize chunk upload', + uploadChunk: '2. Upload chunk', + completeUpload: '3. Complete upload', + chunkUploadFailed: 'Chunk upload failed: ', + uploadProgressListener: 'Add upload progress listener', + noLimitCheck: 'If no limit, return true directly', + expirationValidation: 'Add expiration time validation', + chunkUploadReplacement: 'Use chunk upload to replace direct upload', + textUploadUnchanged: 'Text upload remains unchanged', + addSendRecord: 'Add new send record', + permanent: 'Permanent', + sendSuccessMessage: 'Show send success message', + resetForm: 'Reset form - only reset file and text content, keep expiration info', + showDetails: 'Show details', + autoCopyLink: 'Auto copy retrieve code link', + delayedLoading: 'Use onMounted hook to delay load non-critical resources or initialization', + nonCriticalInit: 'Place non-immediately needed initialization code here' + } +} diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts new file mode 100644 index 0000000..c4da53c --- /dev/null +++ b/src/i18n/locales/zh-CN.ts @@ -0,0 +1,1017 @@ +export default { + // 通用 + common: { + confirm: '确认', + cancel: '取消', + close: '关闭', + delete: '删除', + edit: '编辑', + save: '保存', + add: '添加', + back: '返回', + next: '下一页', + previous: '上一页', + loading: '加载中...', + noData: '暂无数据', + success: '成功', + error: '错误', + warning: '警告', + info: '信息', + search: '搜索', + copy: '复制', + uploadSuccess: '上传成功', + uploadFailed: '上传失败', + copySuccess: '复制成功', + copyFailed: '复制失败', + deleteSuccess: '删除成功', + deleteFailed: '删除失败', + downloadSuccess: '下载成功', + downloadFailed: '下载失败', + shareSuccess: '分享成功', + shareFailed: '分享失败', + expiredFile: '文件已过期', + fileNotFound: '文件不存在', + networkError: '网络错误', + unknownError: '未知错误', + invalidFileType: '不支持的文件类型', + fileTooLarge: '文件过大', + uploadCanceled: '上传已取消', + processing: '处理中...', + pleaseWait: '请稍候...', + fileDetails: '文件详情', + enabled: '已开启', + disabled: '已关闭', + minute: '分钟', + files: '个文件', + second: '秒', + hour: '小时', + day: '天', + times: '次', + appName: '文件快递柜 - FileCodeBox', + appDescription: '开箱即用的文件快传系统' + }, + + // 管理员模块 + admin: { + logout: '退出登录', + session: { + loggedIn: '已登录,进入管理后台', + loggedOut: '登录管理后台' + }, + dashboard: { + title: '仪表盘', + totalFiles: '总文件数', + storageSpace: '存储空间', + todayShares: '今日分享', + totalRetrievals: '累计取件', + activeFiles: '有效文件:{count}', + todayIncrease: '今日新增容量:{count}', + yesterdayShares: '昨日分享:{count}', + serverUptime: '运行时间', + refresh: '刷新数据', + refreshing: '刷新中', + lastUpdated: '最近更新:{time}', + loadFailed: '仪表盘数据加载失败', + footerProduct: 'FileCodeBox 管理后台', + backendVersion: '服务端版本', + frontendVersion: '前端版本', + versionPending: '待同步', + fileHealth: '文件健康', + fileHealthDesc: '基于状态洞察统计可取件、风险与待处理队列。', + activeFileRatio: '有效占比', + fileShareRatio: '文件占比', + textShareRatio: '文本占比', + binaryFiles: '文件分享 {count} 个', + textShares: '文本分享 {count} 条', + expiredFiles: '已过期文件', + needCleanup: '可在文件管理中清理', + chunkedFiles: '分片上传文件', + storagePolicy: '存储与上传策略', + storagePolicyDesc: '当前后台配置对上传链路的影响。', + storageBackend: '存储后端', + singleFileLimit: '单文件上限', + guestUpload: '游客上传', + maxSaveTime: '最长保存', + noSaveLimit: '不限制', + todayCapacityReference: '今日容量 / 单文件上限', + healthActions: { + attention: { + title: '需关注', + description: '集中处理异常和预警文件' + }, + storageIssue: { + title: '存储异常', + description: '检查缺少下载信息的文件' + }, + expiringSoon: { + title: '即将过期', + description: '优先续期仍需保留的分享' + }, + neverRetrieved: { + title: '未取件', + description: '识别创建后尚未被取件的记录' + }, + permanent: { + title: '永久有效', + description: '查看长期保留的分享记录' + } + } + }, + fileManage: { + title: '文件管理' + }, + settings: { + title: '系统设置', + basicSettings: '基本设置', + websiteInfo: '网站基本信息', + websiteName: '网站名称', + websiteDescription: '网站描述', + siteName: '网站名称', + adminPassword: '管理员密码', + sessionDuration: '登录会话时长', + sessionDurationHelp: '仅影响新登录,允许设置 1 至 365 天。', + showAdminAddress: '在主页显示管理入口', + passwordPlaceholder: '如需修改请输入新密码', + passwordNote: '不修改请留空', + keywords: 'SEO 关键词', + themeSelection: '界面主题', + robotsFile: 'Robots.txt 配置', + notificationSettings: '通知设置', + notificationTitle: '公告标题', + notificationContent: '公告内容', + storageSettings: '存储配置', + storageLimit: '总存储容量上限', + storageLimitHelp: '所有有效分享与未完成上传共同占用容量,设置为 0 表示不限制。', + storagePath: '文件存储路径', + storagePathPlaceholder: '使用默认路径可留空', + storageMethod: '存储方式', + localStorage: '本地存储', + s3Storage: 'S3 对象存储', + webdavStorage: 'WebDAV 存储', + chunkUploadNote: '分片上传(实验性功能,大文件上传更稳定)', + s3AccessKeyId: 'Access Key ID', + s3SecretAccessKey: 'Secret Access Key', + s3BucketName: '存储桶名称', + s3EndpointUrl: '服务端点 URL', + s3RegionName: '区域名称', + s3SignatureVersion: '签名版本', + s3AddressingStyle: 'S3 地址风格', + s3AddressingAuto: '自动', + s3AddressingPath: 'Path-style', + s3AddressingVirtual: 'Virtual-host', + s3Hostname: '自定义域名', + s3v2: 'S3v2', + s3v4: 'S3v4', + autoPlaceholder: '自动识别', + enabled: '已启用', + disabled: '已禁用', + webdavUrl: '输入 WebDAV 服务地址', + webdavUsername: '输入 WebDAV 用户名', + webdavPassword: '输入 WebDAV 密码', + webdavUrlPlaceholder: '输入 WebDAV 服务地址', + webdavUsernamePlaceholder: '输入 WebDAV 用户名', + webdavPasswordPlaceholder: '输入 WebDAV 密码', + enableProxy: '启用下载代理', + uploadLimits: '上传设置', + uploadRateLimit: '限流时间窗口(在此时间内限制上传次数)', + uploadPerMinute: '限流时间窗口(在此时间内限制上传次数)', + minute: '分钟', + uploadCountLimit: '允许上传文件数(限流窗口内最多上传几个文件)', + files: '个文件', + fileSizeLimit: '单文件大小上限', + allowedFileTypes: '允许上传类型', + allowedFileTypesPlaceholder: '* 或 .rar, .zip, image/*', + allowedFileTypesHelp: '多个类型用英文逗号分隔,* 表示不限制;支持扩展名和 MIME 通配。', + codeGenerateType: '取件码规则', + codeGenerateNumber: '数字取件码', + codeGenerateSecret: 'Secret 取件码', + codeGenerateTypeHelp: '数字模式保持原有 5 位数字;Secret 模式使用字母数字混合随机码。', + expirationMethod: '过期方式', + expirationType: '过期方式', + expiration: { + day: '按天数', + hour: '按小时', + minute: '按分钟', + forever: '永久有效', + count: '按下载次数' + }, + maxSaveTime: '文件最长保存时间', + timeUnits: { + second: '秒', + minute: '分钟', + hour: '小时', + day: '天' + }, + guestUpload: '允许游客上传', + errorLimits: '访问保护', + errorRateLimit: '检测时间窗口(在此时间内统计错误次数)', + errorPerMinute: '检测时间窗口(在此时间内统计错误次数)', + errorCountLimit: '允许错误次数(超过后临时封禁)', + times: '次', + saveChanges: '保存设置', + fileSizeUnits: { + kb: 'KB', + mb: 'MB', + gb: 'GB' + } + } + }, + + // 页面标题和导航 + nav: { + sendFile: '发送文件', + retrieveFile: '取件', + fileRecords: '取件记录' + }, + + // 取件页面 + retrieve: { + title: '提取文件', + codeInput: { + placeholder: '请输入您的 5 位取件码', + label: '取件码' + }, + submit: '立即提取', + needSendFile: '需要发送文件?', + recordsDrawer: '取件记录', + messages: { + invalidCode: '请输入5位取件码', + retrieveSuccess: '文件获取成功', + invalidCodeError: '无效的取件码', + retrieveFailure: '获取文件失败:', + networkError: '取件失败,请稍后重试:', + unknownError: '未知错误', + previewUnavailable: '暂时无法预览文件信息,可直接取件' + }, + workspace: { + sendFile: '发送文件', + inspect: '预览', + inspecting: '预览中', + retrieving: '取件中', + ready: '可取件', + noPreview: '等待取件码', + noPreviewDesc: '输入 5 位取件码后会先读取文件信息,确认后再消耗取件次数。', + latestRecord: '最近取件', + noRecord: '暂无取件记录', + currentCode: '取件码', + fileSize: '文件大小', + viewDetail: '查看详情', + historyCount: '{count} 条记录', + openRecords: '打开取件记录', + security: '安全取件', + securityState: '预览不会消耗取件次数,确认取件后才会记录访问。', + noExpiry: '不限制', + unlimited: '不限次数', + remainingCount: '剩余 {count} 次', + textType: '文本内容', + fileType: '文件下载', + expiresAt: '过期时间', + remainingDownloads: '剩余次数', + usedCount: '已取件', + emptyCode: '未输入', + previewState: '预览状态', + waiting: '等待输入' + } + }, + + // 发送页面 + send: { + title: '文件发送', + sendText: '发送文本', + fileDetails: '文件详情', + expirationMethod: '过期方式', + uploadArea: { + dragText: '拖拽文件到此处或', + clickText: '点击选择文件', + textInput: '在此输入要发送的文本...', + placeholder: '点击或拖放文件到此处上传', + description: '支持各种常见格式', + descriptionWithLimit: '支持各种常见格式,最大{size}', + retry: '重试', + selectedFiles: '已选择 {count} 个文件', + status: { + initializing: '正在初始化上传...', + uploading: '正在上传文件...', + confirming: '正在确认上传...', + success: '上传成功!' + } + }, + submit: '安全寄送', + submitting: '发送中...', + needRetrieveFile: '需要取件?', + sendRecords: '发件记录', + secureEncryption: '安全加密', + workspace: { + uploadLimit: '单文件上限', + uploadMode: '上传模式', + standardMode: '标准上传', + chunkMode: '分片上传', + guestPolicy: '游客上传', + guestOpen: '允许', + guestClosed: '已关闭', + currentTask: '当前任务', + awaitingFile: '等待选择文件', + fileReady: '已选择 {count} 个文件', + textDraft: '文本草稿 {count} 字', + payload: '发送内容', + expirationPreview: '有效期', + security: '安全状态', + latestRecord: '最近一次发送', + noRecord: '暂无记录', + copyLink: '复制链接', + viewDetail: '查看详情', + historyTitle: '发送历史', + historyCount: '{count} 条记录', + openRecords: '打开发送记录' + }, + fileDetail: { + title: '文件详情', + content: '文件内容', + previewContent: '预览内容', + download: '点击下载', + qrCode: '取件二维码', + scanQrCode: '扫描二维码快速取件' + }, + contentPreview: { + title: '内容预览' + }, + fileManage: { + title: '文件管理', + searchPlaceholder: '搜索文件名称、描述...', + allFiles: '所有文件', + editFileInfo: '编辑文件信息', + saveChanges: '保存更改', + headers: { + code: '取件码', + name: '名称', + size: '大小', + description: '描述', + expiration: '过期时间', + actions: '操作' + }, + form: { + code: '取件码', + codePlaceholder: '输入取件码', + filename: '文件名称', + filenamePlaceholder: '输入文件名称', + suffix: '文件后缀', + suffixPlaceholder: '输入文件后缀', + downloadLimit: '下载次数限制', + downloadLimitPlaceholder: '输入下载次数限制' + } + }, + expiration: { + label: '过期时间', + placeholders: { + days: '输入天数', + hours: '输入小时数', + minutes: '输入分钟数', + count: '输入查看次数', + forever: '永久', + default: '输入值' + }, + units: { + days: '天', + hours: '小时', + minutes: '分钟', + times: '次', + forever: '永久' + } + }, + time: { + day: '按天', + hour: '按小时', + minute: '按分钟', + forever: '永久', + count: '按次数' + }, + messages: { + selectFile: '请选择要上传的文件', + enterText: '请输入要发送的文本', + enterExpirationValue: '请输入过期值', + expirationTooLong: '过期时间不能超过{days}天', + sendSuccess: '文件发送成功!取件码:{code}', + initChunkUploadFailed: '初始化切片上传失败', + chunkUploadFailed: '切片 {index} 上传失败', + completeUploadFailed: '完成上传失败', + uploadFailed: '上传失败,请稍后重试', + guestUploadDisabled: '游客上传功能已关闭', + fileSizeExceeded: '文件大小超过限制 ({size})', + fileTypeNotAllowed: '不允许上传该类型文件,允许类型:{types}', + serverError: '服务器响应异常', + sendFailed: '发送失败,请稍后重试', + expiresAfterCount: '{count}次后过期', + expiresAt: '{date}过期', + emptyFileError: '无法读取空文件', + fileAddedFromClipboard: '已从剪贴板添加文件:{filename}', + fileProcessingFailed: '文件处理失败', + expiresAfter: '{value}{unit}后过期' + } + }, + + // 文件管理 + fileManage: { + title: '文件管理', + subtitle: '共 {count} 条分享记录,可按状态、类型和使用情况快速筛选。', + searchPlaceholder: '搜索取件码、名称或文本...', + allFiles: '所有文件', + detail: '详情', + detailTitle: '文件详情', + detailLoading: '正在加载详情...', + detailFailed: '详情加载失败', + editFileInfo: '编辑文件信息', + saveChanges: '保存更改', + refresh: '刷新列表', + resetFilters: '重置筛选', + totalFiles: '全部记录', + activeFiles: '可取件', + expiredFiles: '已过期', + storageUsed: '占用空间', + statusLabel: '状态', + typeLabel: '类型', + healthLabel: '健康', + viewPreset: '视图', + viewPresetCustom: '自定义筛选', + viewPresetAll: '全部文件', + viewPresetAttention: '需关注', + viewPresetExpiringSoon: '即将过期', + viewPresetStorageIssue: '存储异常', + viewPresetNeverRetrieved: '未取件', + viewPresetPermanent: '永久有效', + saveViewPreset: '保存视图', + updateViewPreset: '覆盖视图', + deleteViewPreset: '删除视图', + viewPresetNamePrompt: '输入视图名称', + viewPresetSaveSuccess: '视图已保存', + viewPresetSaveFailed: '视图保存失败', + viewPresetDeleteConfirm: '确认删除视图“{name}”?', + viewPresetDeleteSuccess: '视图已删除', + viewPresetDeleteFailed: '视图删除失败', + viewPresetLoadFailed: '视图预设加载失败,已使用内置视图', + all: '全部', + active: '可取件', + expired: '已过期', + fileType: '文件', + textType: '文本', + chunkedType: '分片', + healthFilters: { + all: '全部', + attention: '需关注', + danger: '异常', + warning: '预警', + expiringSoon: '即将过期', + storageIssue: '存储异常', + neverRetrieved: '未取件', + healthy: '健康', + permanent: '永久有效' + }, + sortBy: '排序', + sortOrder: '方向', + unlimited: '不限次数', + remaining: '剩余 {count} 次', + loadError: '文件列表加载失败', + noMatches: '没有匹配的文件', + viewText: '查看', + textPreview: '文本预览', + copyText: '复制文本', + copyCode: '复制取件码', + copyLink: '复制取件链接', + copySuccess: '文本已复制到剪贴板', + copyCodeSuccess: '取件码已复制到剪贴板', + copyLinkSuccess: '取件链接已复制到剪贴板', + copyFailed: '复制失败,请重试', + charCount: '共 {count} 个字符', + downloadFile: '下载文件', + exportText: '导出文本', + downloadSuccess: '已下载:{name}', + exportSuccess: '已导出:{name}', + downloadFailed: '下载失败', + previewFailed: '预览加载失败', + loadingPreview: '正在加载预览...', + previewComplete: '已加载完整内容,共 {count} 个字符', + previewTruncated: '已显示 {shown} / {total} 个字符', + previewFallback: '预览接口不可用,已使用列表中的文本内容', + selectCurrentPage: '选择当前页', + selectedCount: '已选择 {count} 项', + selectFile: '选择 {name}', + clearSelection: '清空选择', + batchDelete: '批量删除', + batchDeleteConfirm: '确认删除选中的 {count} 个文件?此操作不可撤销。', + batchDeleteSuccess: '已删除 {count} 个文件', + batchDeletePartialSuccess: '已删除 {count} 个文件,{failed} 个失败', + batchDeleteFailed: '批量删除失败', + batchEdit: '批量编辑', + batchEditTitle: '批量编辑文件策略', + batchEditSelected: '将更新选中的 {count} 个文件', + batchEditMode: '更新方式', + batchEditExpiresAt: '设置过期时间', + batchEditDownloadLimit: '设置取件次数', + batchEditForever: '设为永久有效', + batchEditForeverHint: '清空过期时间,并将取件次数设为不限。', + batchUpdateConfirm: '确认更新选中的 {count} 个文件?', + batchUpdateSuccess: '已更新 {count} 个文件', + batchUpdatePartialSuccess: '已更新 {count} 个文件,{failed} 个失败', + batchUpdateFailed: '批量更新失败', + batchUpdateNoFields: '请选择要更新的策略', + batchPolicyActionConfirm: '确认对选中的 {count} 个文件执行“{action}”?', + batchPolicyActionSuccess: '已处理 {count} 个文件', + batchPolicyActionPartialSuccess: '已处理 {count} 个文件,{failed} 个失败', + batchPolicyActionFailed: '批量策略动作执行失败', + policyActionSuccess: '文件策略已更新', + policyActionFailed: '策略动作执行失败', + metadataInfo: '运营备注', + metadataHint: '仅管理员可见,用于记录处理线索、归档标签和后续动作。', + metadataNote: '备注', + metadataNotePlaceholder: '记录这个分享的背景、处理记录或负责人...', + metadataTags: '标签', + metadataTagsPlaceholder: '逗号分隔,例如:客户A, 待续期, 重要', + metadataUpdatedAt: '最近保存:{time}', + metadataNeverUpdated: '尚未保存备注', + saveMetadata: '保存备注', + metadataSaveSuccess: '备注和标签已保存', + metadataSaveFailed: '备注保存失败', + policyActions: { + extend24h: '延长 24 小时', + extend7d: '延长 7 天', + makePermanent: '设为永久', + resetDownloadLimit: '重置为 {count} 次' + }, + policyActionDescriptions: { + extend24h: '从当前有效期继续追加一天', + extend7d: '适合临时续期一周', + makePermanent: '清空过期时间和次数限制', + resetDownloadLimit: '恢复可取件次数到 {count} 次' + }, + applyBatchEdit: '应用更改', + overview: '概览', + policyInfo: '策略信息', + storageInfo: '存储信息', + statusInsight: '状态洞察', + nextAction: '建议动作', + lifecycle: '生命周期', + permanent: '永久有效', + createdAt: '创建时间', + remainingDownloads: '剩余次数', + textLength: '文本长度', + hasDownloadLimit: '限制取件次数', + hasExpirationTime: '限制过期时间', + canPreviewText: '可预览文本', + canDownload: '可下载', + storageBackend: '存储后端', + fileHash: '文件哈希', + isChunked: '分片上传', + filePath: '存储路径', + uuidFileName: '存储文件名', + uploadId: '上传会话', + yes: '是', + no: '否', + insightSeverity: { + success: '正常', + warning: '需关注', + danger: '异常', + info: '提示', + neutral: '待观察' + }, + insightStates: { + available: '当前可正常取件', + expired: '当前已不可取件', + storage_incomplete: '存储信息不完整', + expiring_soon: '即将过期', + permanent: '永久有效' + }, + insightActions: { + monitor: '保持观察', + extend_or_delete: '延长有效期或清理记录', + inspect_storage: '检查存储路径与文件名', + extend_expiration: '按需延长过期时间' + }, + insightReasons: { + expired: '已超过过期时间', + download_limit_exhausted: '取件次数已用尽', + expires_soon: '24 小时内过期', + never_retrieved: '尚未被取件', + storage_metadata_incomplete: '缺少可下载存储信息', + chunked_upload: '由分片上传生成' + }, + timeline: { + status: { + done: '已完成', + pending: '等待中', + active: '生效中', + expired: '已过期', + exhausted: '已用尽', + unlimited: '不限' + }, + created: { + title: '记录创建', + description: '分享记录已创建,当前状态为 {status}。' + }, + content_ready: { + title: '内容就绪', + description: '内容类型为 {detail},当前状态为 {status}。' + }, + upload_session: { + title: '上传会话', + description: '上传会话已记录:{detail}。' + }, + expiration_policy: { + title: '过期策略', + description: '过期策略当前为 {status}。', + remaining: '还剩 {time}', + overdue: '已超时 {time}' + }, + download_limit: { + title: '取件次数', + description: '取件次数策略当前为 {status},{value}。' + }, + retrieved: { + title: '取件记录', + description: '累计取件 {value},当前状态为 {status}。' + } + }, + headers: { + select: '选择', + code: '取件码', + name: '名称', + type: '类型', + size: '大小', + usage: '取件', + status: '状态', + description: '描述', + expiration: '过期时间', + actions: '操作' + }, + sort: { + createdAt: '创建时间', + expiredAt: '过期时间', + name: '名称', + size: '大小', + usedCount: '取件次数', + code: '取件码', + desc: '降序', + asc: '升序' + }, + form: { + code: '取件码', + codePlaceholder: '输入取件码', + filename: '文件名称', + filenamePlaceholder: '输入文件名称', + suffix: '文件后缀', + suffixPlaceholder: '输入文件后缀', + downloadLimit: '下载次数限制', + downloadLimitPlaceholder: '输入下载次数限制' + } + }, + + // 文件记录 + fileRecord: { + filename: '文件名', + size: '文件大小', + date: '取件日期', + code: '取件码', + actions: '操作', + download: '下载', + viewDetails: '查看详情', + deleteRecord: '删除记录', + preview: '预览', + copyContent: '复制内容', + contentCopied: '内容已复制到剪贴板', + copyFailed: '复制失败,请重试' + }, + + // 文件详情模态框 + fileDetail: { + title: '文件详情', + content: '文件内容', + previewContent: '预览内容', + download: '点击下载', + qrCode: '取件二维码', + scanQrCode: '扫描二维码快速取件' + }, + + // 内容预览 + contentPreview: { + title: '内容预览' + }, + + // 侧边抽屉 + drawer: { + noRecords: '暂无记录' + }, + + // 文件大小单位 + fileSize: { + bytes: 'Bytes', + kb: 'KB', + mb: 'MB', + gb: 'GB', + tb: 'TB' + }, + + // 管理页面 + manage: { + settings: { + title: '系统设置', + basicSettings: '基本设置', + siteName: '网站名称', + adminPassword: '管理员密码', + passwordPlaceholder: '如需修改请输入新密码', + passwordNote: '不修改请留空', + keywords: 'SEO 关键词', + themeSelection: '界面主题', + robotsFile: 'Robots.txt 配置', + notificationSettings: '通知设置', + notificationTitle: '公告标题', + notificationContent: '公告内容', + storageSettings: '存储配置', + storagePath: '文件存储路径', + storagePathPlaceholder: '使用默认路径可留空', + storageMethod: '存储方式', + localStorage: '本地存储', + s3Storage: 'S3 对象存储', + webdavStorage: 'WebDAV 存储', + chunkUploadNote: '分片上传(实验性功能,大文件上传更稳定)', + uploadLimits: '上传设置', + uploadPerMinute: '限流时间窗口(在此时间内限制上传次数)', + uploadCountLimit: '允许上传文件数(限流窗口内最多上传几个文件)', + fileSizeLimit: '单文件大小上限', + allowedFileTypes: '允许上传类型', + allowedFileTypesPlaceholder: '* 或 .rar, .zip, image/*', + allowedFileTypesHelp: '多个类型用英文逗号分隔,* 表示不限制;支持扩展名和 MIME 通配。', + codeGenerateType: '取件码规则', + codeGenerateNumber: '数字取件码', + codeGenerateSecret: 'Secret 取件码', + codeGenerateTypeHelp: '数字模式保持原有 5 位数字;Secret 模式使用字母数字混合随机码。', + expiration: { + day: '按天数', + hour: '按小时', + minute: '按分钟', + forever: '永久有效', + count: '按下载次数' + }, + maxSaveTime: '文件最长保存时间', + s3AccessKeyId: 'Access Key ID', + s3SecretAccessKey: 'Secret Access Key', + s3BucketName: '存储桶名称', + s3EndpointUrl: '服务端点 URL', + s3RegionName: '区域名称', + s3SignatureVersion: '签名版本', + s3AddressingStyle: 'S3 地址风格', + s3AddressingAuto: '自动', + s3AddressingPath: 'Path-style', + s3AddressingVirtual: 'Virtual-host', + s3Hostname: '自定义域名', + s3v2: 'S3v2', + s3v4: 'S3v4', + autoPlaceholder: '自动识别', + enableProxy: '启用下载代理', + webdavUrlPlaceholder: '输入 WebDAV 服务地址', + webdavUsernamePlaceholder: '输入 WebDAV 用户名', + webdavPasswordPlaceholder: '输入 WebDAV 密码', + fileSizeUnits: { + kb: 'KB', + mb: 'MB', + gb: 'GB' + }, + expirationType: '过期方式', + guestUpload: '允许游客上传', + errorLimits: '访问保护', + errorPerMinute: '检测时间窗口(在此时间内统计错误次数)', + errorCountLimit: '允许错误次数(超过后临时封禁)', + saveChanges: '保存设置', + refreshConfig: '刷新配置', + refreshing: '刷新中', + saving: '保存中', + unsavedChanges: '有未保存的配置变更', + allChangesSaved: '所有配置已保存', + refreshBlocked: '请先保存当前变更再刷新' + }, + dashboard: { + title: '仪表盘', + totalFiles: '总文件数', + storageSpace: '存储空间', + todayShares: '今日分享', + totalRetrievals: '累计取件', + activeFiles: '有效文件:{count}', + todayIncrease: '今日新增容量:{count}', + yesterdayShares: '昨日分享:{count}', + serverUptime: '运行时间', + refresh: '刷新数据', + fileHealth: '文件健康', + fileHealthDesc: '基于状态洞察统计可取件、风险与待处理队列。', + activeFileRatio: '有效占比', + fileShareRatio: '文件占比', + textShareRatio: '文本占比', + binaryFiles: '文件分享 {count} 个', + textShares: '文本分享 {count} 条', + expiredFiles: '已过期文件', + needCleanup: '可在文件管理中清理', + chunkedFiles: '分片上传文件', + storagePolicy: '存储与上传策略', + storagePolicyDesc: '当前后台配置对上传链路的影响。', + storageBackend: '存储后端', + singleFileLimit: '单文件上限', + guestUpload: '游客上传', + maxSaveTime: '最长保存', + noSaveLimit: '不限制', + todayCapacityReference: '今日容量 / 单文件上限' + }, + fileManage: { + title: '文件管理', + searchPlaceholder: '搜索文件名称、描述...', + allFiles: '所有文件', + editFileInfo: '编辑文件信息', + saveChanges: '保存更改', + headers: { + code: '取件码', + name: '名称', + size: '大小', + description: '描述', + expiration: '过期时间', + actions: '操作' + }, + form: { + code: '取件码', + codePlaceholder: '输入取件码', + filename: '文件名称', + filenamePlaceholder: '输入文件名称', + suffix: '文件后缀', + suffixPlaceholder: '输入文件后缀', + downloadLimit: '下载次数限制', + downloadLimitPlaceholder: '输入下载次数限制' + }, + updateFailed: '更新失败', + deleteFailed: '删除失败', + deleteConfirm: '确认删除这个文件?此操作不可撤销。', + loadFileListFailed: '加载文件列表失败' + }, + systemSettings: { + title: '系统设置', + basicSettings: '基本设置', + websiteInfo: '网站基本信息', + websiteName: '网站名称', + websiteDescription: '网站描述', + adminPassword: '管理员密码', + passwordPlaceholder: '如需修改请输入新密码', + passwordNote: '不修改请留空', + keywords: 'SEO 关键词', + themeSelection: '界面主题', + notificationSettings: '通知设置', + notificationTitle: '公告标题', + notificationContent: '公告内容', + storageSettings: '存储配置', + storagePath: '文件存储路径', + storagePathPlaceholder: '使用默认路径可留空', + storageMethod: '存储方式', + localStorage: '本地存储', + s3Storage: 'S3 对象存储', + webdavStorage: 'WebDAV 存储', + chunkUploadNote: '分片上传(实验性功能,大文件上传更稳定)', + enabled: '已启用', + disabled: '已禁用', + webdavUrl: '输入 WebDAV 服务地址', + webdavUsername: '输入 WebDAV 用户名', + webdavPassword: '输入 WebDAV 密码', + enableProxy: '启用下载代理', + uploadLimits: '上传设置', + uploadRateLimit: '限流时间窗口(在此时间内限制上传次数)', + minute: '分钟', + uploadCountLimit: '允许上传文件数(限流窗口内最多上传几个文件)', + files: '个文件', + fileSizeLimit: '单文件大小上限', + codeGenerateType: '取件码规则', + codeGenerateNumber: '数字取件码', + codeGenerateSecret: 'Secret 取件码', + codeGenerateTypeHelp: '数字模式保持原有 5 位数字;Secret 模式使用字母数字混合随机码。', + expirationMethod: '过期方式', + expirationMethods: { + day: '按天数', + hour: '按小时', + minute: '按分钟', + forever: '永久有效', + count: '按下载次数' + }, + expiration: { + day: '按天数', + hour: '按小时', + minute: '按分钟', + forever: '永久有效', + count: '按下载次数' + }, + maxSaveTime: '文件最长保存时间', + timeUnits: { + second: '秒', + minute: '分钟', + hour: '小时', + day: '天' + }, + guestUpload: '允许游客上传', + errorLimits: '访问保护', + errorRateLimit: '检测时间窗口(在此时间内统计错误次数)', + errorCountLimit: '允许错误次数(超过后临时封禁)', + times: '次', + saveSettings: '保存设置', + saveSuccess: '设置已保存', + saveFailed: '保存失败,请重试', + getConfigFailed: '获取配置失败,请刷新页面' + }, + login: { + title: '登录', + password: '密码', + passwordPlaceholder: '密码', + loginButton: '登录', + loggingIn: '登录中...', + invalidPassword: '无效的密码', + passwordTooShort: '密码长度至少为6位', + loginFailed: '登录失败', + noValidToken: '登录失败:未获取到有效令牌' + } + }, + + // 工具函数相关 + utils: { + common: { + formatTime: '格式化时间戳为可读格式', + formatFileSize: '格式化文件大小', + formatDuration: '格式化持续时间', + time: { + forever: '永久', + day: '天', + hour: '小时', + minute: '分钟', + second: '秒' + }, + copyToClipboard: '复制文本到剪贴板', + copyFailed: '复制失败:', + debounce: '防抖函数', + throttle: '节流函数', + validateEmail: '验证邮箱格式', + validateUrl: '验证URL格式', + generateRandomString: '生成随机字符串', + deepClone: '深度克隆对象', + getFileExtension: '获取文件扩展名', + isMobileDevice: '检查是否为移动设备', + formatNumber: '格式化数字,添加千分位分隔符' + }, + clipboard: { + title: '剪贴板工具函数', + copyText: '复制文本到剪贴板', + copySuccess: '复制成功', + copyFailed: '复制失败,请手动复制' + } + }, + + // 组件相关 + components: { + pagination: { + showing: '显示第', + to: '到', + of: '条,共', + total: '条', + previous: '上一页', + next: '下一页' + }, + languageSwitcher: { + clickOutsideToClose: '点击外部关闭下拉菜单' + }, + borderProgressBar: { + borderWidth: '边框宽度', + cornerRadius: '拐角半径', + createGradient: '创建渐变', + drawBackground: '绘制背景', + calculateProgress: '计算进度', + drawProgress: '绘制进度' + } + }, + + // 其他通用文本 + misc: { + emptyFileError: '无法读取空文件', + fileAddedFromClipboard: '已从剪贴板添加文件:', + fileProcessFailed: '文件处理失败', + chunkSize: '保持 2MB 的切片大小用于计算哈希', + secureContext: '如果不是安全上下文(HTTP),则返回一个基于文件信息的替代哈希', + cryptoFallback: '如果 crypto.subtle.digest 失败,使用替代方案', + generateAlternativeHash: '生成替代哈希的函数', + fileInfoHash: '使用文件名、大小和最后修改时间生成一个简单的哈希', + convertToHex: '转换为16进制字符串并填充到64位', + defaultChunkSize: '默认切片大小为5MB', + initChunkUpload: '1. 初始化切片上传', + uploadChunk: '2. 上传切片', + completeUpload: '3. 完成上传', + chunkUploadFailed: '切片上传失败:', + uploadProgressListener: '添加上传进度监听', + noLimitCheck: '如果没有限制,直接返回true', + expirationValidation: '添加过期时间验证', + chunkUploadReplacement: '使用切片上传替代原来的直接上传', + textUploadUnchanged: '文本上传保持不变', + addSendRecord: '添加新的发送记录', + permanent: '永久', + sendSuccessMessage: '显示发送成功消息', + resetForm: '重置表单 - 只重置文件和文本内容,保留过期信息', + showDetails: '显示详情', + autoCopyLink: '自动复制取件码链接', + delayedLoading: '使用 onMounted 钩子延迟加载一些非关键资源或初始化', + nonCriticalInit: '这里可以放置一些非立即需要的初始化代码' + } +} diff --git a/src/layout/AdminLayout/AdminLayout.vue b/src/layout/AdminLayout/AdminLayout.vue index 2b12828..85df4ee 100644 --- a/src/layout/AdminLayout/AdminLayout.vue +++ b/src/layout/AdminLayout/AdminLayout.vue @@ -1,89 +1,81 @@ diff --git a/src/main.ts b/src/main.ts index 170dfde..bad6438 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,10 +5,25 @@ import { createPinia } from 'pinia' import App from './App.vue' import router from './router' +import i18n from './i18n' + +const OPEN_SOURCE_BANNER = String.raw` + ______ _ _ _____ _ ____ + | ____(_) | / ____| | | | _ \ + | |__ _| | ___| | ___ __| | ___| |_) | _____ __ + | __| | | |/ _ \ | / _ \ / _\` |/ _ \ _ < / _ \ \/ / + | | | | | __/ |___| (_) | (_| | __/ |_) | (_) > < + |_| |_|_|\___|\_____\___/ \__,_|\___|____/ \___/_/\_\ + + Open Source: https://github.com/vastsa/FileCodeBox +` + +console.info(OPEN_SOURCE_BANNER) const app = createApp(App) app.use(createPinia()) app.use(router) +app.use(i18n) app.mount('#app') diff --git a/src/router/index.ts b/src/router/index.ts index 2891e0a..e51a3bf 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,50 +1,105 @@ import { createRouter, createWebHashHistory } from 'vue-router' +import type { RouteRecordRaw } from 'vue-router' +import { ROUTE_NAMES, ROUTES } from '@/constants' +import { hasValidStoredAdminSession } from '@/utils/auth-storage' -// 预加载 SendFileView 组件 -const SendFileView = () => import('../views/SendFileView.vue') -const router = createRouter({ - history: createWebHashHistory(import.meta.env.BASE_URL), - routes: [ - { - path: '/', - name: 'Retrieve', - component: () => import('@/views/RetrievewFileView.vue') - }, - { - path: '/send', - name: 'Send', - component: SendFileView - }, - { - path: '/admin', - name: 'Manage', - component: () => import('@/layout/AdminLayout/AdminLayout.vue'), - redirect: '/admin/dashboard', - children: [ - { - path: '/admin/dashboard', - name: 'Dashboard', - component: () => import('@/views/manage/DashboardView.vue') - }, - { - path: '/admin/files', - name: 'FileManage', - component: () => import('@/views/manage/FileManageView.vue') - }, - { - path: '/admin/settings', - name: 'Settings', - component: () => import('@/views/manage/SystemSettingsView.vue') +const publicPageMeta = { + showGlobalControls: true, + showRouteLoading: false, + routeTransition: 'transfer-fade' +} + +const adminPageMeta = { + requiresAuth: true, + showGlobalControls: false, + showRouteLoading: false +} + +const routes: RouteRecordRaw[] = [ + { + path: '/', + name: ROUTE_NAMES.RETRIEVE, + component: () => import('@/views/RetrievewFileView.vue'), + meta: { + ...publicPageMeta, + title: 'retrieve' + } + }, + { + path: ROUTES.SEND, + name: ROUTE_NAMES.SEND, + component: () => import('@/views/SendFileView.vue'), + meta: { + ...publicPageMeta, + title: 'send' + } + }, + { + path: ROUTES.ADMIN, + name: ROUTE_NAMES.ADMIN, + component: () => import('@/layout/AdminLayout/AdminLayout.vue'), + redirect: ROUTES.DASHBOARD, + meta: adminPageMeta, + children: [ + { + path: 'dashboard', + name: ROUTE_NAMES.DASHBOARD, + component: () => import('@/views/manage/DashboardView.vue'), + meta: { + ...adminPageMeta, + title: 'dashboard' + } + }, + { + path: 'files', + name: ROUTE_NAMES.FILE_MANAGE, + component: () => import('@/views/manage/FileManageView.vue'), + meta: { + ...adminPageMeta, + title: 'files' } - ] - }, - { - path: '/login', - name: 'Login', - component: () => import('@/views/manage/LoginView.vue') + }, + { + path: 'settings', + name: ROUTE_NAMES.SETTINGS, + component: () => import('@/views/manage/SystemSettingsView.vue'), + meta: { + ...adminPageMeta, + title: 'settings' + } + } + ] + }, + { + path: ROUTES.LOGIN, + name: ROUTE_NAMES.LOGIN, + component: () => import('@/views/manage/LoginView.vue'), + meta: { + showGlobalControls: true, + showRouteLoading: true, + title: 'login' } - ] + }, + { + path: '/:pathMatch(.*)*', + redirect: ROUTES.HOME + } +] + +const router = createRouter({ + history: createWebHashHistory(import.meta.env.BASE_URL), + routes }) +router.beforeEach((to) => { + if (to.meta.requiresAuth && !hasValidStoredAdminSession()) { + return { + path: ROUTES.LOGIN, + query: { + redirect: to.fullPath + } + } + } +}) export default router diff --git a/src/services/auth.ts b/src/services/auth.ts new file mode 100644 index 0000000..47fb448 --- /dev/null +++ b/src/services/auth.ts @@ -0,0 +1,16 @@ +import api from './client' +import type { AdminUser, ApiResponse } from '@/types' + +export class AuthService { + static async login(password: string): Promise> { + return api.post('/admin/login', { password }) + } + + static async logout(): Promise { + return api.post('/admin/logout') + } + + static async verifyToken(): Promise> { + return api.get('/admin/verify') + } +} diff --git a/src/services/client.ts b/src/services/client.ts new file mode 100644 index 0000000..15a26ea --- /dev/null +++ b/src/services/client.ts @@ -0,0 +1,71 @@ +import axios, { type AxiosError, type InternalAxiosRequestConfig } from 'axios' +import { API_STATUS_CODES, TIME_CONSTANTS } from '@/constants' +import type { ApiErrorPayload } from '@/types' +import { clearStoredToken, hasValidStoredAdminSession, readStoredToken } from '@/utils/auth-storage' + +export const AUTH_EVENTS = { + UNAUTHORIZED: 'filecodebox:auth:unauthorized', + SETUP_REQUIRED: 'filecodebox:setup:required' +} as const + +const rawBaseURL = + import.meta.env.MODE === 'production' + ? import.meta.env.VITE_API_BASE_URL_PROD + : import.meta.env.VITE_API_BASE_URL_DEV + +export const apiBaseURL = typeof rawBaseURL === 'string' ? rawBaseURL.replace(/\/+$/, '') : '' + +const clientOptions = { + baseURL: apiBaseURL, + timeout: TIME_CONSTANTS.REQUEST_TIMEOUT, + headers: { + 'Content-Type': 'application/json' + } +} + +const apiClient = axios.create(clientOptions) +export const rawApiClient = axios.create(clientOptions) + +const attachAuthToken = (config: InternalAxiosRequestConfig) => { + if (hasValidStoredAdminSession()) { + const token = readStoredToken() + config.headers.Authorization = `Bearer ${token}` + } else { + clearStoredToken() + } + return config +} + +const getSetupPath = (payload?: ApiErrorPayload) => { + const detail = payload?.detail + if (detail && typeof detail === 'object' && typeof detail.setup === 'string') { + return detail.setup + } + return '' +} + +const handleAuthError = (error: AxiosError) => { + if (error.response?.status === API_STATUS_CODES.SETUP_REQUIRED) { + window.dispatchEvent( + new CustomEvent(AUTH_EVENTS.SETUP_REQUIRED, { + detail: { setupPath: getSetupPath(error.response.data) } + }) + ) + } + + if (error.response?.status === API_STATUS_CODES.UNAUTHORIZED) { + clearStoredToken() + window.dispatchEvent(new CustomEvent(AUTH_EVENTS.UNAUTHORIZED)) + } + return Promise.reject(error) +} + +apiClient.interceptors.request.use(attachAuthToken, (error) => Promise.reject(error)) + +rawApiClient.interceptors.request.use(attachAuthToken, (error) => Promise.reject(error)) + +apiClient.interceptors.response.use((response) => response.data, handleAuthError) + +rawApiClient.interceptors.response.use((response) => response, handleAuthError) + +export default apiClient diff --git a/src/services/config.ts b/src/services/config.ts new file mode 100644 index 0000000..4a48b6a --- /dev/null +++ b/src/services/config.ts @@ -0,0 +1,52 @@ +import api from './client' +import type { ApiResponse, ConfigState, PublicConfigPayload } from '@/types' + +const isPublicConfigEnvelope = ( + detail: ConfigState | PublicConfigPayload | null | undefined +): detail is PublicConfigPayload => { + return !!detail && typeof detail === 'object' && 'config' in detail +} + +const normalizeUserConfigResponse = ( + response: ApiResponse +): ApiResponse => { + if (isPublicConfigEnvelope(response.detail)) { + return { + ...response, + detail: { + config: response.detail.config, + meta: response.detail.meta + } + } + } + + return { + ...response, + detail: { + config: response.detail ?? {} + } + } +} + +export class ConfigService { + static async getConfig(): Promise> { + return api.get('/admin/config/get') + } + + static async getUserConfig(): Promise> { + try { + const response = (await api.get('/api/v1/config')) as ApiResponse< + ConfigState | PublicConfigPayload + > + + return normalizeUserConfigResponse(response) + } catch { + const response = (await api.post('/')) as ApiResponse + return normalizeUserConfigResponse(response) + } + } + + static async updateConfig(config: Partial): Promise { + return api.patch('/admin/config/update', config) + } +} diff --git a/src/services/file.ts b/src/services/file.ts new file mode 100644 index 0000000..b1eef53 --- /dev/null +++ b/src/services/file.ts @@ -0,0 +1,272 @@ +import api, { rawApiClient } from './client' +import { multipartUploadConfig } from './shared' +import type { + AdminBatchDeleteFilesResponse, + AdminBatchPolicyActionRequest, + AdminBatchPolicyActionResponse, + AdminBatchUpdateFilesRequest, + AdminBatchUpdateFilesResponse, + AdminFilePatchPayload, + AdminFileDetailResponse, + AdminFileListParams, + AdminFileMetadataRequest, + AdminFilePolicyActionRequest, + AdminFilePreviewResponse, + AdminFileViewPreset, + AdminFileViewPresetRequest, + AdminFileViewPresetsResponse, + ApiResponse, + ChunkUploadCompleteRequest, + ChunkUploadInitRequest, + ChunkUploadInitResponse, + ChunkUploadResponse, + FileEditForm, + FileInfo, + FileListResponse, + FileUploadResponse, + ShareMetadataResponse, + ShareSelectResponse, + TextSendResponse, + UploadProgress +} from '@/types' + +const urlEncodedConfig = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } +} + +const toUrlEncodedForm = (data: Record) => { + const form = new URLSearchParams() + Object.entries(data).forEach(([key, value]) => { + form.append(key, String(value)) + }) + return form +} + +const isMethodFallbackError = (error: unknown) => { + const status = (error as { response?: { status?: number } })?.response?.status + return status === 404 || status === 405 +} + +export class FileService { + static async uploadFile( + file: File, + onProgress?: (progress: UploadProgress) => void + ): Promise> { + const formData = new FormData() + formData.append('file', file) + + return api.post('/share/file/', formData, multipartUploadConfig(onProgress)) + } + + static async uploadText( + text: string, + expireValue = 1, + expireStyle = 'day' + ): Promise> { + const formData = new FormData() + formData.append('text', text) + formData.append('expire_value', String(expireValue)) + formData.append('expire_style', expireStyle) + return api.post('/share/text/', formData, multipartUploadConfig()) + } + + static async initChunkUpload( + request: ChunkUploadInitRequest + ): Promise> { + return api.post( + '/chunk/upload/init/', + toUrlEncodedForm({ + file_name: request.file_name, + file_size: request.file_size, + chunk_size: request.chunk_size, + file_hash: request.file_hash + }), + urlEncodedConfig + ) + } + + static async uploadChunk( + uploadId: string, + chunkIndex: number, + chunk: Blob, + onProgress?: (progress: UploadProgress) => void + ): Promise> { + const formData = new FormData() + formData.append('chunk', chunk) + return api.post( + `/chunk/upload/chunk/${uploadId}/${chunkIndex}`, + formData, + multipartUploadConfig(onProgress) + ) + } + + static async completeChunkUpload( + uploadId: string, + request: ChunkUploadCompleteRequest + ): Promise> { + return api.post( + `/chunk/upload/complete/${uploadId}`, + toUrlEncodedForm({ + expire_value: request.expire_value, + expire_style: request.expire_style + }), + urlEncodedConfig + ) + } + + static async selectFile(code: string): Promise> { + return api.post('/share/select/', { code }) + } + + static async inspectFile(code: string): Promise> { + return api.post('/share/metadata/', { code }) + } + + static async getFile(code: string): Promise> { + return api.get(`/file/${code}`) + } + + static async downloadFile(code: string): Promise { + const response = await rawApiClient.get(`/download/${code}`, { + responseType: 'blob' + }) + return response.data + } + + static async getAdminFileList( + params: AdminFileListParams + ): Promise> { + return api.get('/admin/file/list', { params }) + } + + static async getAdminFileDetail(id: number): Promise> { + return api.get('/admin/file/detail', { + params: { id } + }) + } + + static async updateFile(data: FileEditForm | AdminFilePatchPayload): Promise { + return api.patch('/admin/file/update', data) + } + + static async applyAdminFilePolicyAction( + data: AdminFilePolicyActionRequest + ): Promise> { + try { + return await api.patch('/admin/file/policy-action', data) + } catch (error: unknown) { + if (!isMethodFallbackError(error)) { + throw error + } + + return api.post('/admin/file/policy-action', data) + } + } + + static async applyAdminFilesPolicyAction( + data: AdminBatchPolicyActionRequest + ): Promise> { + try { + return await api.patch('/admin/file/batch-policy-action', data) + } catch (error: unknown) { + if (!isMethodFallbackError(error)) { + throw error + } + + return api.post('/admin/file/batch-policy-action', data) + } + } + + static async updateAdminFileMetadata( + data: AdminFileMetadataRequest + ): Promise> { + try { + return await api.patch('/admin/file/metadata', data) + } catch (error: unknown) { + if (!isMethodFallbackError(error)) { + throw error + } + + return api.post('/admin/file/metadata', data) + } + } + + static async getAdminFileViewPresets(): Promise> { + return api.get('/admin/file/view-presets') + } + + static async saveAdminFileViewPreset( + data: AdminFileViewPresetRequest + ): Promise> { + try { + return await api.patch('/admin/file/view-presets', data) + } catch (error: unknown) { + if (!isMethodFallbackError(error)) { + throw error + } + + return api.post('/admin/file/view-presets', data) + } + } + + static async updateAdminFiles( + data: AdminBatchUpdateFilesRequest + ): Promise> { + return api.patch('/admin/file/batch-update', data) + } + + static async deleteAdminFile(id: number): Promise { + return api.delete('/admin/file/delete', { + data: { id } + }) + } + + static async deleteAdminFiles( + ids: number[] + ): Promise> { + return api.delete('/admin/file/batch-delete', { + data: { ids } + }) + } + + static async deleteAdminFileViewPreset(id: string): Promise { + try { + return await api.delete('/admin/file/view-presets', { + data: { id } + }) + } catch (error: unknown) { + if (!isMethodFallbackError(error)) { + throw error + } + + return api.post('/admin/file/view-presets/delete', { id }) + } + } + + static async downloadAdminFile( + id: number + ): Promise<{ data: Blob; headers: Record }> { + const response = await rawApiClient.get('/admin/file/download', { + params: { id }, + responseType: 'blob' + }) + return { + data: response.data, + headers: response.headers as Record + } + } + + static async previewAdminFile( + id: number, + maxChars = 20000 + ): Promise> { + return api.get('/admin/file/preview', { + params: { + id, + maxChars + } + }) + } +} diff --git a/src/services/index.ts b/src/services/index.ts new file mode 100644 index 0000000..38e6d24 --- /dev/null +++ b/src/services/index.ts @@ -0,0 +1,7 @@ +export { AUTH_EVENTS } from './client' +export { AuthService } from './auth' +export { ConfigService } from './config' +export { FileService } from './file' +export { PresignUploadService } from './presign-upload' +export { StatsService } from './stats' +export { uploadChunkedFile } from './upload-strategy' diff --git a/src/services/presign-upload.ts b/src/services/presign-upload.ts new file mode 100644 index 0000000..47c6684 --- /dev/null +++ b/src/services/presign-upload.ts @@ -0,0 +1,58 @@ +import api from './client' +import { multipartUploadConfig } from './shared' +import { uploadToExternalUrl } from './upload-client' +import type { + ApiResponse, + PresignConfirmRequest, + PresignInitRequest, + PresignInitResponse, + PresignStatusResponse, + PresignUploadResult, + UploadProgress +} from '@/types' + +export class PresignUploadService { + static async initUpload(request: PresignInitRequest): Promise> { + return api.post('/presign/upload/init', request) + } + + static async proxyUpload( + uploadId: string, + file: File, + onProgress?: (progress: UploadProgress) => void, + uploadUrl?: string + ): Promise> { + const formData = new FormData() + formData.append('file', file) + + return api.put( + uploadUrl || `/presign/upload/proxy/${uploadId}`, + formData, + multipartUploadConfig(onProgress) + ) + } + + static async confirmUpload( + uploadId: string, + request?: PresignConfirmRequest + ): Promise> { + return api.post(`/presign/upload/confirm/${uploadId}`, request || {}) + } + + static async getUploadStatus(uploadId: string): Promise> { + return api.get(`/presign/upload/status/${uploadId}`) + } + + static async cancelUpload(uploadId: string): Promise> { + return api.delete(`/presign/upload/${uploadId}`) + } + + static async directUploadToS3( + uploadUrl: string, + file: File, + onProgress?: (progress: UploadProgress) => void + ): Promise { + await uploadToExternalUrl(uploadUrl, file, onProgress) + return true + } +} diff --git a/src/services/shared.ts b/src/services/shared.ts new file mode 100644 index 0000000..58c5535 --- /dev/null +++ b/src/services/shared.ts @@ -0,0 +1,20 @@ +import type { AxiosRequestConfig } from 'axios' +import type { UploadProgress } from '@/types' + +export const multipartUploadConfig = ( + onProgress?: (progress: UploadProgress) => void +): AxiosRequestConfig => ({ + headers: { + 'Content-Type': 'multipart/form-data' + }, + timeout: 0, + onUploadProgress: (progressEvent) => { + if (onProgress && progressEvent.total) { + onProgress({ + loaded: progressEvent.loaded, + total: progressEvent.total, + percentage: Math.round((progressEvent.loaded * 100) / progressEvent.total) + }) + } + } +}) diff --git a/src/services/stats.ts b/src/services/stats.ts new file mode 100644 index 0000000..c275a12 --- /dev/null +++ b/src/services/stats.ts @@ -0,0 +1,8 @@ +import api from './client' +import type { ApiResponse, DashboardData } from '@/types' + +export class StatsService { + static async getDashboard(): Promise> { + return api.get('/admin/dashboard') + } +} diff --git a/src/services/upload-client.ts b/src/services/upload-client.ts new file mode 100644 index 0000000..062fe77 --- /dev/null +++ b/src/services/upload-client.ts @@ -0,0 +1,26 @@ +import axios from 'axios' +import type { UploadProgress } from '@/types' + +export async function uploadToExternalUrl( + uploadUrl: string, + file: File, + onProgress?: (progress: UploadProgress) => void +): Promise { + await axios.put(uploadUrl, file, { + headers: { + 'Content-Type': 'application/octet-stream' + }, + timeout: 0, + onUploadProgress: (progressEvent) => { + if (!onProgress || !progressEvent.total) { + return + } + + onProgress({ + loaded: progressEvent.loaded, + total: progressEvent.total, + percentage: Math.round((progressEvent.loaded * 100) / progressEvent.total) + }) + } + }) +} diff --git a/src/services/upload-strategy.ts b/src/services/upload-strategy.ts new file mode 100644 index 0000000..933a483 --- /dev/null +++ b/src/services/upload-strategy.ts @@ -0,0 +1,97 @@ +import { FileService } from './file' +import type { ApiResponse, ChunkUploadInitResponse, FileUploadResponse, UploadProgress } from '@/types' +import { calculateFileHash } from '@/utils/file-processing' + +const CHUNK_SIZE = 5 * 1024 * 1024 + +type ChunkedUploadOptions = { + expireValue: number + expireStyle: string + onHashCalculated?: (hash: string) => void + onProgress?: (progress: UploadProgress) => void + messages?: { + initFailed?: string + chunkFailed?: (index: number) => string + completeFailed?: string + } +} + +type ChunkedUploadResult = ChunkUploadInitResponse | FileUploadResponse + +const calculateCompletedBytes = (uploadedChunks: Set, chunkSize: number, fileSize: number) => + Array.from(uploadedChunks).reduce((total, index) => { + const chunkStart = index * chunkSize + const chunkEnd = Math.min((index + 1) * chunkSize, fileSize) + return total + Math.max(0, chunkEnd - chunkStart) + }, 0) + +export const uploadChunkedFile = async ( + file: File, + options: ChunkedUploadOptions +): Promise> => { + const fileHash = await calculateFileHash(file) + options.onHashCalculated?.(fileHash) + + const chunks = Math.ceil(file.size / CHUNK_SIZE) + const initResponse = await FileService.initChunkUpload({ + file_name: file.name, + file_size: file.size, + chunk_size: CHUNK_SIZE, + file_hash: fileHash + }) + + if (initResponse.code !== 200) { + throw new Error(options.messages?.initFailed || 'Init chunk upload failed') + } + + if (initResponse.detail?.existed) { + return initResponse + } + + const initDetail = initResponse.detail + const uploadId = initDetail?.upload_id + if (!uploadId) { + throw new Error(options.messages?.initFailed || 'Init chunk upload failed') + } + + const uploadedChunks = new Set(initDetail.uploaded_chunks || []) + for (let index = 0; index < chunks; index++) { + if (uploadedChunks.has(index)) { + continue + } + + const start = index * CHUNK_SIZE + const end = Math.min(start + CHUNK_SIZE, file.size) + const chunk = file.slice(start, end) + const chunkResponse = await FileService.uploadChunk( + uploadId, + index, + new Blob([chunk], { type: file.type }), + (progress) => { + const completedBytes = calculateCompletedBytes(uploadedChunks, CHUNK_SIZE, file.size) + const percentage = Math.round(((completedBytes + progress.loaded) * 100) / file.size) + options.onProgress?.({ + loaded: completedBytes + progress.loaded, + total: file.size, + percentage: Math.min(percentage, 99) + }) + } + ) + + if (chunkResponse.code !== 200) { + throw new Error(options.messages?.chunkFailed?.(index) || `Chunk upload failed: ${index}`) + } + uploadedChunks.add(index) + } + + const completeResponse = await FileService.completeChunkUpload(uploadId, { + expire_value: options.expireValue, + expire_style: options.expireStyle + }) + + if (completeResponse.code !== 200) { + throw new Error(options.messages?.completeFailed || 'Complete chunk upload failed') + } + + return completeResponse +} diff --git a/src/stores/adminStore.ts b/src/stores/adminStore.ts index 9986d22..f855f6e 100644 --- a/src/stores/adminStore.ts +++ b/src/stores/adminStore.ts @@ -1,11 +1,122 @@ import { defineStore } from 'pinia' -import { ref } from 'vue' +import { ref, computed } from 'vue' +import type { AdminUser } from '@/types' +import { + clearStoredAuth, + clearStoredToken, + hasValidStoredAdminSession, + readStoredAdminPassword, + readStoredToken, + readStoredTokenExpiresAt, + writeStoredAdminPassword, + writeStoredToken +} from '@/utils/auth-storage' -export const useAdminData = defineStore('adminData', () => { - const adminPassword = ref(localStorage.getItem('adminPassword') || '') - function updateAdminPwd(pwd: string) { +export const useAdminStore = defineStore('admin', () => { + const MAX_TIMER_DELAY = 2_147_483_647 + let expirationTimer: ReturnType | null = null + + // 状态 + const storedSessionIsValid = hasValidStoredAdminSession() + if (!storedSessionIsValid) clearStoredToken() + const adminPassword = ref(readStoredAdminPassword()) + const token = ref(storedSessionIsValid ? readStoredToken() : '') + const expiresAt = ref(storedSessionIsValid ? readStoredTokenExpiresAt() : null) + const isLoggedIn = ref(!!token.value) + const userInfo = ref(null) + + // 计算属性 + const isAuthenticated = computed(() => { + return isLoggedIn.value && !!token.value + }) + const hasToken = computed(() => !!token.value) + + const clearExpirationTimer = () => { + if (expirationTimer) clearTimeout(expirationTimer) + expirationTimer = null + } + + const scheduleExpiration = (sessionExpiresAt: number | null) => { + clearExpirationTimer() + if (!sessionExpiresAt) return + + const remaining = sessionExpiresAt * 1000 - Date.now() + if (remaining <= 0) { + logout() + return + } + expirationTimer = setTimeout( + () => scheduleExpiration(sessionExpiresAt), + Math.min(remaining, MAX_TIMER_DELAY) + ) + } + + // 方法 + const updateAdminPassword = (pwd: string) => { adminPassword.value = pwd - localStorage.setItem('token', pwd) + writeStoredAdminPassword(pwd) + } + + const setToken = (newToken: string, newExpiresAt?: number) => { + token.value = newToken + expiresAt.value = newExpiresAt ?? null + writeStoredToken(newToken, newExpiresAt) + scheduleExpiration(expiresAt.value) + } + + const setUserInfo = (user: AdminUser) => { + userInfo.value = user + isLoggedIn.value = true + setToken(user.token, user.expires_at) + } + + const login = (user: AdminUser) => { + setUserInfo(user) + } + + const logout = () => { + clearExpirationTimer() + adminPassword.value = '' + token.value = '' + expiresAt.value = null + isLoggedIn.value = false + userInfo.value = null + + clearStoredAuth() + } + + const initAuth = () => { + if (hasValidStoredAdminSession()) { + const storedToken = readStoredToken() + token.value = storedToken + expiresAt.value = readStoredTokenExpiresAt() + isLoggedIn.value = true + scheduleExpiration(expiresAt.value) + } else { + logout() + } + } + + scheduleExpiration(expiresAt.value) + + return { + // 状态 + adminPassword, + token, + expiresAt, + isLoggedIn, + userInfo, + + // 计算属性 + isAuthenticated, + hasToken, + + // 方法 + updateAdminPassword, + setToken, + setUserInfo, + login, + logout, + initAuth } - return { adminPassword, updateAdminPwd } }) diff --git a/src/stores/alertStore.ts b/src/stores/alertStore.ts index a25d102..21b7e2d 100644 --- a/src/stores/alertStore.ts +++ b/src/stores/alertStore.ts @@ -1,13 +1,10 @@ import { defineStore } from 'pinia' +import type { Alert, AlertType } from '@/types' +import { TIME_CONSTANTS } from '@/constants' -interface Alert { - id: number - message: string - type: 'success' | 'error' | 'warning' | 'info' - progress: number - duration: number - startTime: number -} +let progressTimer: ReturnType | null = null +let alertIdSeed = 0 +const alertRemoveTimers = new Map>() export const useAlertStore = defineStore('alert', { state: () => ({ @@ -16,19 +13,31 @@ export const useAlertStore = defineStore('alert', { actions: { showAlert( message: string, - type: 'success' | 'error' | 'warning' | 'info' = 'info', - duration = 5000 + type: AlertType = 'info', + duration = TIME_CONSTANTS.ALERT_DURATION ) { - const id = Date.now() + const id = Date.now() + alertIdSeed + alertIdSeed = (alertIdSeed + 1) % 1000 const startTime = Date.now() this.alerts.push({ id, message, type, progress: 100, duration, startTime }) - setTimeout(() => this.removeAlert(id), duration) + alertRemoveTimers.set(id, setTimeout(() => this.removeAlert(id), duration)) + this.startProgressTimer() }, removeAlert(id: number) { + const removeTimer = alertRemoveTimers.get(id) + if (removeTimer) { + clearTimeout(removeTimer) + alertRemoveTimers.delete(id) + } + const index = this.alerts.findIndex((alert) => alert.id === id) if (index > -1) { this.alerts.splice(index, 1) } + + if (this.alerts.length === 0) { + this.stopProgressTimer() + } }, updateAlertProgress(id: number) { const alert = this.alerts.find((a) => a.id === id) @@ -40,6 +49,25 @@ export const useAlertStore = defineStore('alert', { this.removeAlert(id) } } + }, + startProgressTimer() { + if (progressTimer || this.alerts.length === 0) { + return + } + + progressTimer = setInterval(() => { + this.alerts.forEach((alert) => { + this.updateAlertProgress(alert.id) + }) + }, TIME_CONSTANTS.PROGRESS_UPDATE_INTERVAL) + }, + stopProgressTimer() { + if (!progressTimer) { + return + } + + clearInterval(progressTimer) + progressTimer = null } } }) diff --git a/src/stores/configStore.ts b/src/stores/configStore.ts new file mode 100644 index 0000000..6400f4b --- /dev/null +++ b/src/stores/configStore.ts @@ -0,0 +1,76 @@ +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' +import type { ConfigState, PublicConfigMeta } from '@/types' +import { + DEFAULT_PUBLIC_CONFIG, + readNotifyKey, + readStoredConfig, + toPublicConfig, + writeNotifyKey, + writeStoredConfig, + type PublicConfig +} from '@/utils/config-storage' + +export const useConfigStore = defineStore('config', () => { + const config = ref({ + ...DEFAULT_PUBLIC_CONFIG, + ...toPublicConfig(readStoredConfig>()) + }) + const publicMeta = ref({}) + + const uploadSizeLimit = computed(() => config.value.uploadSize) + const appVersion = computed(() => publicMeta.value.version || '') + + const updateConfig = (nextConfig: Partial) => { + config.value = { + ...DEFAULT_PUBLIC_CONFIG, + ...config.value, + ...toPublicConfig(nextConfig) + } + writeStoredConfig(config.value) + } + + const applyRemoteConfig = (nextConfig: Partial): string | null => { + updateConfig(nextConfig) + + const { notify_title: notifyTitle, notify_content: notifyContent } = nextConfig + if (!notifyTitle || !notifyContent) { + return null + } + + const notifyKey = notifyTitle + notifyContent + if (readNotifyKey() === notifyKey) { + return null + } + + writeNotifyKey(notifyKey) + return `${notifyTitle}: ${notifyContent}` + } + + const applyPublicMeta = (nextMeta: PublicConfigMeta | undefined) => { + if (!nextMeta) return + + publicMeta.value = { + ...publicMeta.value, + ...nextMeta + } + } + + const reloadStoredConfig = () => { + config.value = { + ...DEFAULT_PUBLIC_CONFIG, + ...toPublicConfig(readStoredConfig>()) + } + } + + return { + appVersion, + config, + publicMeta, + uploadSizeLimit, + applyPublicMeta, + applyRemoteConfig, + updateConfig, + reloadStoredConfig + } +}) diff --git a/src/stores/fileData.ts b/src/stores/fileData.ts index dbeaa9f..a4836fe 100644 --- a/src/stores/fileData.ts +++ b/src/stores/fileData.ts @@ -1,39 +1,55 @@ import { defineStore } from 'pinia' -import { reactive } from 'vue' +import { ref } from 'vue' +import type { ReceivedFileRecord, SentFileRecord } from '@/types' export const useFileDataStore = defineStore('fileData', () => { - const receiveData = reactive(JSON.parse(localStorage.getItem('receiveData') || '[]') || []) // 接收的数据 - const shareData = reactive(JSON.parse(localStorage.getItem('shareData') || '[]') || []) // 接收的数据 - function save() { - localStorage.setItem('receiveData', JSON.stringify(receiveData)) - localStorage.setItem('shareData', JSON.stringify(shareData)) + const receiveData = ref([]) + const shareData = ref([]) + + const addReceiveData = (record: ReceivedFileRecord) => { + receiveData.value.push(record) + } + + const removeReceiveData = (id: number) => { + const index = receiveData.value.findIndex((record) => record.id === id) + if (index !== -1) { + receiveData.value.splice(index, 1) + } } - function addReceiveData(data: any) { - receiveData.unshift(data) - save() + + const deleteReceiveData = (index: number) => { + if (index >= 0 && index < receiveData.value.length) { + receiveData.value.splice(index, 1) + } } - function addShareData(data: any) { - shareData.unshift(data) - save() + const clearReceiveData = () => { + receiveData.value = [] } - function deleteReceiveData(index: number) { - receiveData.splice(index, 1) - save() + const addShareDataRecord = (record: SentFileRecord) => { + shareData.value.push(record) } - function deleteShareData(index: number) { - shareData.splice(index, 1) - save() + const deleteShareData = (index: number) => { + if (index >= 0 && index < shareData.value.length) { + shareData.value.splice(index, 1) + } } + + const clearShareData = () => { + shareData.value = [] + } + return { receiveData, shareData, - save, - addShareData, addReceiveData, + removeReceiveData, deleteReceiveData, - deleteShareData + clearReceiveData, + addShareDataRecord, + deleteShareData, + clearShareData } }) diff --git a/src/types/api.ts b/src/types/api.ts new file mode 100644 index 0000000..37e873a --- /dev/null +++ b/src/types/api.ts @@ -0,0 +1,12 @@ +export interface ApiResponse { + code: number + message?: string + msg?: string + detail?: T +} + +export interface ApiErrorPayload { + detail?: string | { setup?: string } + message?: string + msg?: string +} diff --git a/src/types/auth.ts b/src/types/auth.ts new file mode 100644 index 0000000..0f0bb18 --- /dev/null +++ b/src/types/auth.ts @@ -0,0 +1,8 @@ +export interface AdminUser { + id: string + username: string + token: string + token_type?: string + expires_at: number + expires_in?: number +} diff --git a/src/types/config.ts b/src/types/config.ts new file mode 100644 index 0000000..f09ac24 --- /dev/null +++ b/src/types/config.ts @@ -0,0 +1,73 @@ +export interface SystemConfig { + name: string + description?: string + maxFileSize: number + allowedFileTypes: string[] + expireDays: number + notify_title?: string + notify_content?: string +} + +export interface ThemeChoice { + key: string + name: string + author: string + version: string +} + +export interface PublicConfigMeta { + version?: string + api?: Record + features?: Record + limits?: Record +} + +export interface PublicConfigPayload { + config: Partial + meta?: PublicConfigMeta +} + +export interface ConfigState { + name: string + description: string + file_storage: string + themesChoices: ThemeChoice[] + expireStyle: string[] + code_generate_type: 'number' | 'secret' + adminSessionExpire: number + admin_token: string + robotsText: string + keywords: string + notify_title: string + notify_content: string + openUpload: number + uploadSize: number + allowed_file_types: string[] + allowedFileTypes?: string[] + storage_path: string + storageLimit: number + uploadMinute: number + max_save_seconds: number + opacity: number + enableChunk: number + s3_access_key_id: string + background: string + showAdminAddr: number + page_explain: string + s3_secret_access_key: string + aws_session_token: string + s3_signature_version: string + s3_addressing_style: string + s3_region_name: string + s3_bucket_name: string + s3_endpoint_url: string + s3_hostname: string + uploadCount: number + errorMinute: number + errorCount: number + s3_proxy: number + themesSelect: string + webdav_url: string + webdav_username: string + webdav_password: string +} diff --git a/src/types/dashboard.ts b/src/types/dashboard.ts new file mode 100644 index 0000000..6c10d0a --- /dev/null +++ b/src/types/dashboard.ts @@ -0,0 +1,98 @@ +import type { AdminFileHealthFilter } from './file' + +export interface DashboardHealthSummary { + healthAttentionCount: number + healthDangerCount: number + healthWarningCount: number + expiringSoonCount: number + storageIssueCount: number + neverRetrievedCount: number + healthyCount: number + permanentCount: number +} + +export interface DashboardData { + totalFiles: number + storageUsed: number | string + yesterdayCount: number + todayCount: number + yesterdaySize: number | string + todaySize: number | string + sysUptime: number | null + activeCount?: number + expiredCount?: number + textCount?: number + fileCount?: number + chunkedCount?: number + usedCount?: number + storageBackend?: string + uploadSizeLimit?: number + openUpload?: number + enableChunk?: number + maxSaveSeconds?: number + healthAttentionCount?: number + healthDangerCount?: number + healthWarningCount?: number + expiringSoonCount?: number + storageIssueCount?: number + neverRetrievedCount?: number + healthyCount?: number + permanentCount?: number + healthSummary?: Partial +} + +export type DashboardViewData = Omit< + DashboardData, + | keyof DashboardHealthSummary + | 'activeCount' + | 'expiredCount' + | 'textCount' + | 'fileCount' + | 'chunkedCount' + | 'usedCount' + | 'storageBackend' + | 'uploadSizeLimit' + | 'openUpload' + | 'enableChunk' + | 'maxSaveSeconds' + | 'storageUsed' + | 'yesterdaySize' + | 'todaySize' +> & + DashboardHealthSummary & { + hasExtendedStats: boolean + activeCount: number + expiredCount: number + textCount: number + fileCount: number + chunkedCount: number + usedCount: number + storageBackend: string + uploadSizeLimit: number + openUpload: number + enableChunk: number + maxSaveSeconds: number + storageUsed: number + yesterdaySize: number + todaySize: number + storageUsedText: string + yesterdaySizeText: string + todaySizeText: string + uploadSizeLimitText: string + sysUptimeText: string + activeRatio: number + textRatio: number + fileRatio: number + healthyRatio: number + healthAttentionRatio: number + todaySizeRatio: number + } + +export interface DashboardHealthAction { + key: string + label: string + description: string + count: number + health: AdminFileHealthFilter + tone: 'danger' | 'warning' | 'success' | 'neutral' +} diff --git a/src/types/file.ts b/src/types/file.ts new file mode 100644 index 0000000..50fc5a4 --- /dev/null +++ b/src/types/file.ts @@ -0,0 +1,491 @@ +export interface FileInfo { + id: string + name: string + size: number + type: string + uploadTime: string + downloadCount: number + expireTime?: string +} + +export interface FileListItem { + id: number + code: string + prefix: string + suffix: string + size: number + text?: string + description?: string + expired_at: string | null + expired_count: number | null + created_at: string + name?: string + type?: 'text' | 'file' + status?: 'active' | 'expired' + isText?: boolean + is_text?: boolean + isExpired?: boolean + is_expired?: boolean + isChunked?: boolean + is_chunked?: boolean + statusInsights?: AdminFileDetailStatusInsights + status_insights?: AdminFileDetailStatusInsights + remainingDownloads?: number | null + remaining_downloads?: number | null + usedCount?: number + used_count?: number + fileHash?: string | null + file_hash?: string | null +} + +export interface AdminFileViewItem extends FileListItem { + displayName: string + displaySize: string + displayExpiredAt: string + displayUsage: string + displayHealthState: string + displayHealthAction: string + isTextFile: boolean + isExpiredFile: boolean + isChunkedFile: boolean + remainingDownloadsValue: number | null + canPreviewText: boolean + statusInsightSeverity: AdminFileInsightSeverity + statusInsightState: string + statusInsightNextAction: string + statusInsightReasons: string[] +} + +export interface AdminFileSummary { + totalFiles: number + activeCount: number + expiredCount: number + textCount: number + fileCount: number + chunkedCount: number + healthAttentionCount: number + healthDangerCount: number + healthWarningCount: number + expiringSoonCount: number + storageIssueCount: number + neverRetrievedCount: number + healthyCount: number + permanentCount: number + storageUsed: number + usedCount: number +} + +export type AdminFileStatusFilter = 'all' | 'active' | 'expired' +export type AdminFileTypeFilter = 'all' | 'file' | 'text' | 'chunked' +export type AdminFileHealthFilter = + | 'all' + | 'attention' + | 'danger' + | 'warning' + | 'healthy' + | 'expired' + | 'expiring_soon' + | 'storage_issue' + | 'never_retrieved' + | 'permanent' +export type AdminFileSortBy = 'created_at' | 'expired_at' | 'name' | 'size' | 'used_count' | 'code' +export type AdminFileSortOrder = 'asc' | 'desc' + +export interface AdminFileListParams { + page: number + size: number + keyword?: string + status?: AdminFileStatusFilter + type?: AdminFileTypeFilter + health?: AdminFileHealthFilter + sortBy?: AdminFileSortBy + sortOrder?: AdminFileSortOrder +} + +export interface AdminFileViewPresetParams { + keyword: string + status: AdminFileStatusFilter + type: AdminFileTypeFilter + health: AdminFileHealthFilter + sortBy: AdminFileSortBy + sortOrder: AdminFileSortOrder + size: number +} + +export interface AdminFileViewPreset { + id: string + name: string + filters?: AdminFileViewPresetParams + params?: AdminFileViewPresetParams + isBuiltIn?: boolean + isDefault?: boolean + is_default?: boolean + createdAt?: string | null + created_at?: string | null + updatedAt?: string | null + updated_at?: string | null +} + +export interface AdminFileViewPresetRequest { + id?: string + name: string + filters: AdminFileViewPresetParams + params?: AdminFileViewPresetParams +} + +export interface AdminFileViewPresetsResponse { + presets?: AdminFileViewPreset[] + items?: AdminFileViewPreset[] + total?: number +} + +export interface FileEditForm { + id: number | null + code: string + prefix: string + suffix: string + expired_at: string + expired_count: number | null +} + +export interface AdminFilePatchPayload { + id: number + code?: string + prefix?: string + suffix?: string + expired_at?: string | null + expired_count?: number | null +} + +export type AdminFilePolicyAction = + | 'extend_24h' + | 'extend_7d' + | 'make_permanent' + | 'reset_download_limit' + +export interface AdminFilePolicyActionRequest { + id: number + action: AdminFilePolicyAction + downloadLimit?: number + download_limit?: number +} + +export interface AdminFileMetadata { + note: string + tags: string[] + updatedAt?: string | null + updated_at?: string | null +} + +export interface AdminFileMetadataRequest { + id: number + note?: string + tags?: string[] +} + +export interface FileListResponse { + data: FileListItem[] + total: number + page: number + size: number + summary?: AdminFileSummary +} + +export interface AdminFilePreviewResponse { + id: number + code: string + name: string + type: 'text' + content: string + length: number + previewLength?: number + preview_length?: number + truncated: boolean + maxChars?: number + max_chars?: number + createdAt?: string | null + created_at?: string | null + expiredAt?: string | null + expired_at?: string | null +} + +export interface AdminFileDetailPolicy { + expiredAt?: string | null + expired_at?: string | null + expiredCount?: number | null + expired_count?: number | null + remainingDownloads?: number | null + remaining_downloads?: number | null + isExpired?: boolean + is_expired?: boolean + isPermanent?: boolean + is_permanent?: boolean +} + +export interface AdminFileDetailStorage { + backend?: string + filePath?: string | null + file_path?: string | null + uuidFileName?: string | null + uuid_file_name?: string | null + fileHash?: string | null + file_hash?: string | null + isChunked?: boolean + is_chunked?: boolean + uploadId?: string | null + upload_id?: string | null +} + +export type AdminFileInsightSeverity = 'success' | 'warning' | 'danger' | 'info' | 'neutral' + +export interface AdminFileDetailInsightMetrics { + ageSeconds?: number + age_seconds?: number + secondsUntilExpiration?: number | null + seconds_until_expiration?: number | null + remainingDownloads?: number | null + remaining_downloads?: number | null + usedCount?: number + used_count?: number +} + +export interface AdminFileDetailStatusInsights { + severity?: AdminFileInsightSeverity + state?: string + nextAction?: string + next_action?: string + reasons?: string[] + metrics?: AdminFileDetailInsightMetrics +} + +export interface AdminFileDetailTimelineItem { + key: string + status?: string + severity?: AdminFileInsightSeverity + timestamp?: string | null + value?: number | string | null + detail?: string | null +} + +export interface AdminFileDetailTimelineViewItem extends AdminFileDetailTimelineItem { + severity: AdminFileInsightSeverity + displayTitle: string + displayDescription: string + displayMeta: string +} + +export interface AdminFileDetailResponse extends FileListItem { + filename?: string + displayName?: string + display_name?: string + isPermanent?: boolean + is_permanent?: boolean + hasDownloadLimit?: boolean + has_download_limit?: boolean + hasExpirationTime?: boolean + has_expiration_time?: boolean + textLength?: number + text_length?: number + canPreviewText?: boolean + can_preview_text?: boolean + canDownload?: boolean + can_download?: boolean + storageBackend?: string + storage_backend?: string + filePath?: string | null + file_path?: string | null + uuidFileName?: string | null + uuid_file_name?: string | null + uploadId?: string | null + upload_id?: string | null + policy?: AdminFileDetailPolicy + storage?: AdminFileDetailStorage + metadata?: AdminFileMetadata + meta?: AdminFileMetadata + note?: string + tags?: string[] + metadataUpdatedAt?: string | null + metadata_updated_at?: string | null + statusInsights?: AdminFileDetailStatusInsights + status_insights?: AdminFileDetailStatusInsights + timeline?: AdminFileDetailTimelineItem[] +} + +export interface AdminFileDetailViewItem extends AdminFileViewItem { + displayCreatedAt: string + displayRetrieveUrl: string + textLengthValue: number + canDownloadFile: boolean + isPermanentFile: boolean + hasDownloadLimitFile: boolean + hasExpirationTimeFile: boolean + isChunkedStorage: boolean + storageBackendValue: string + fileHashValue?: string | null + filePathValue?: string | null + uuidFileNameValue?: string | null + uploadIdValue?: string | null + metadataNote: string + metadataTags: string[] + metadataUpdatedAt: string | null + statusInsightMetrics?: AdminFileDetailInsightMetrics + detailTimeline: AdminFileDetailTimelineViewItem[] +} + +export interface AdminBatchDeleteFileFailure { + id: number + reason: string +} + +export interface AdminBatchDeleteFilesResponse { + requestedCount?: number + requested_count?: number + uniqueCount?: number + unique_count?: number + deletedCount?: number + deleted_count?: number + missingCount?: number + missing_count?: number + failedCount?: number + failed_count?: number + deleted?: number[] + missing?: number[] + failed?: AdminBatchDeleteFileFailure[] +} + +export interface AdminBatchUpdateFileFailure { + id: number + reason: string +} + +export interface AdminBatchUpdateFilesRequest { + ids: number[] + expired_at?: string | null + expired_count?: number | null + clearExpiredAt?: boolean + clear_expired_at?: boolean +} + +export interface AdminBatchUpdateFilesResponse { + requestedCount?: number + requested_count?: number + uniqueCount?: number + unique_count?: number + updatedCount?: number + updated_count?: number + missingCount?: number + missing_count?: number + failedCount?: number + failed_count?: number + updated?: number[] + missing?: number[] + failed?: AdminBatchUpdateFileFailure[] +} + +export interface AdminBatchPolicyActionRequest { + ids: number[] + action: AdminFilePolicyAction + downloadLimit?: number + download_limit?: number +} + +export interface AdminBatchPolicyActionResponse extends AdminBatchUpdateFilesResponse { + action?: AdminFilePolicyAction | string +} + +export type AdminBatchEditMode = 'expiresAt' | 'downloadLimit' | 'forever' + +export interface AdminBatchEditForm { + mode: AdminBatchEditMode + expired_at: string + expired_count: number | null +} + +export interface FileUploadResponse { + code: string + name: string +} + +export interface TextSendResponse { + code: string +} + +export interface ShareSelectResponse { + code: string + name: string + text: string + size: number + type?: 'file' | 'text' + is_text?: boolean + content?: string | null + download_url?: string | null + created_at?: string | null + expired_at?: string | null + expires_at?: string | null + expired_count?: number | null + used_count?: number + remaining_downloads?: number | null +} + +export interface ShareMetadataResponse { + code: string + name: string + size: number + type: 'file' | 'text' + is_text: boolean + created_at?: string | null + expired_at?: string | null + expires_at?: string | null + expired_count?: number | null + used_count?: number + remaining_downloads?: number | null +} + +export interface ReceivedFileRecord { + id: number + code: string + filename: string + size: string + downloadUrl: string | null + content: string | null + date: string + type?: 'file' | 'text' + remainingDownloads?: number | null +} + +export interface SentFileRecord { + id: number + filename: string + date: string + size: string + expiration: string + retrieveCode: string +} + +export interface UploadProgress { + loaded: number + total: number + percentage: number +} + +export interface ChunkUploadInitRequest { + file_name: string + file_size: number + chunk_size: number + file_hash: string +} + +export interface ChunkUploadInitResponse { + code?: string + name?: string + upload_id?: string + existed?: boolean + uploaded_chunks?: number[] +} + +export interface ChunkUploadCompleteRequest { + expire_value: number + expire_style: string +} + +export type ChunkUploadResponse = null diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..9553f6d --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,7 @@ +export * from './api' +export * from './auth' +export * from './config' +export * from './dashboard' +export * from './file' +export * from './presign-upload' +export * from './ui' diff --git a/src/types/presign-upload.ts b/src/types/presign-upload.ts new file mode 100644 index 0000000..abb09ea --- /dev/null +++ b/src/types/presign-upload.ts @@ -0,0 +1,55 @@ +import type { UploadProgress } from './file' + +export type PresignUploadMode = 'direct' | 'proxy' + +export type PresignUploadStatus = + | 'idle' + | 'initializing' + | 'uploading' + | 'confirming' + | 'success' + | 'error' + +export type ExpireStyle = 'day' | 'hour' | 'minute' | 'forever' | 'count' + +export interface PresignInitRequest { + file_name: string + file_size: number + expire_value?: number + expire_style?: ExpireStyle +} + +export interface PresignInitResponse { + upload_id: string + upload_url: string + proxy_upload_url?: string + legacy_proxy_upload_url?: string + mode: PresignUploadMode + expires_in: number +} + +export interface PresignConfirmRequest { + expire_value?: number + expire_style?: ExpireStyle +} + +export interface PresignUploadResult { + code: string + name: string +} + +export interface PresignStatusResponse { + upload_id: string + file_name: string + file_size: number + mode: PresignUploadMode + created_at: string + expires_at: string + is_expired: boolean +} + +export interface PresignUploadOptions { + expireValue?: number + expireStyle?: ExpireStyle + onProgress?: (progress: UploadProgress) => void +} diff --git a/src/types/ui.ts b/src/types/ui.ts new file mode 100644 index 0000000..8a8e21f --- /dev/null +++ b/src/types/ui.ts @@ -0,0 +1,26 @@ +export type ThemeMode = 'light' | 'dark' | 'system' + +export type SendType = 'file' | 'text' + +export type AlertType = 'success' | 'error' | 'warning' | 'info' + +export interface Alert { + id: number + message: string + type: AlertType + progress: number + duration: number + startTime: number +} + +export type UploadStatus = 'idle' | 'uploading' | 'success' | 'error' + +export interface RouteConfig { + path: string + name: string + component: () => Promise<{ default: object }> + meta?: { + requiresAuth?: boolean + title?: string + } +} diff --git a/src/utils/api.ts b/src/utils/api.ts index 8469250..f4e76cb 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -1,75 +1,2 @@ -import axios from 'axios' - -// 从环境变量中获取 API 基础 URL -const baseURL = - import.meta.env.MODE === 'production' - ? import.meta.env.VITE_API_BASE_URL_PROD - : import.meta.env.VITE_API_BASE_URL_DEV - -// 确保 baseURL 是一个有效的字符串 -const sanitizedBaseURL = typeof baseURL === 'string' ? baseURL : '' - -// 创建 axios 实例 -const api = axios.create({ - baseURL: sanitizedBaseURL, - timeout: 1000000000000000, // 请求超时时间 - headers: { - 'Content-Type': 'application/json' - } -}) - -// 请求拦截器 -api.interceptors.request.use( - (config) => { - // 从 localStorage 获取 token - const token = localStorage.getItem('token') - if (token) { - config.headers['Authorization'] = `Bearer ${token}` - } - - // 确保 URL 是有效的 - if (config.url && !config.url.startsWith('http')) { - config.url = `${sanitizedBaseURL}/${config.url.replace(/^\//, '')}` - } - - return config - }, - (error) => { - return Promise.reject(error) - } -) -// 响应拦截器 -api.interceptors.response.use( - (response) => { - return response.data - }, - (error) => { - // 处理错误响应 - if (error.response) { - switch (error.response.status) { - case 401: - console.error('未授权,请重新登录') - localStorage.clear() - window.location.href = '/#/login' - break - case 403: - // 禁止访问 - console.error('禁止访问') - break - case 404: - // 未找到 - console.error('请求的资源不存在') - break - default: - console.error('发生错误:', error.response.data) - } - } else if (error.request) { - console.error('未收到响应:', error.request) - } else { - console.error('请求配置错误:', error.message) - } - return Promise.reject(error) - } -) - -export default api +export { apiBaseURL, rawApiClient } from '@/services/client' +export { default } from '@/services/client' diff --git a/src/utils/auth-storage.ts b/src/utils/auth-storage.ts new file mode 100644 index 0000000..36a7fd4 --- /dev/null +++ b/src/utils/auth-storage.ts @@ -0,0 +1,56 @@ +import { STORAGE_KEYS } from '@/constants' + +export function readStoredAdminPassword(): string { + return localStorage.getItem(STORAGE_KEYS.ADMIN_PASSWORD) || '' +} + +export function writeStoredAdminPassword(password: string) { + localStorage.setItem(STORAGE_KEYS.ADMIN_PASSWORD, password) +} + +export function readStoredToken(): string { + return localStorage.getItem(STORAGE_KEYS.TOKEN) || '' +} + +const readTokenPayloadExpiration = (token: string): number | null => { + try { + const payload = JSON.parse(atob(token.split('.')[1] || '')) as { exp?: unknown } + const expiresAt = Number(payload.exp) + return Number.isFinite(expiresAt) ? expiresAt : null + } catch { + return null + } +} + +export function readStoredTokenExpiresAt(): number | null { + const storedValue = Number(localStorage.getItem(STORAGE_KEYS.TOKEN_EXPIRES_AT)) + if (Number.isFinite(storedValue) && storedValue > 0) return storedValue + return readTokenPayloadExpiration(readStoredToken()) +} + +export function hasValidStoredAdminSession(): boolean { + const token = readStoredToken() + const expiresAt = readStoredTokenExpiresAt() + return !!token && expiresAt !== null && expiresAt > Date.now() / 1000 +} + +export function writeStoredToken(token: string, expiresAt?: number) { + localStorage.setItem(STORAGE_KEYS.TOKEN, token) + const normalizedExpiresAt = Number(expiresAt) || readTokenPayloadExpiration(token) + if (normalizedExpiresAt) { + localStorage.setItem(STORAGE_KEYS.TOKEN_EXPIRES_AT, String(normalizedExpiresAt)) + } else { + localStorage.removeItem(STORAGE_KEYS.TOKEN_EXPIRES_AT) + } +} + +export function clearStoredAuth() { + localStorage.removeItem(STORAGE_KEYS.ADMIN_PASSWORD) + localStorage.removeItem(STORAGE_KEYS.TOKEN) + localStorage.removeItem(STORAGE_KEYS.TOKEN_EXPIRES_AT) +} + +export function clearStoredToken() { + localStorage.removeItem(STORAGE_KEYS.TOKEN) + localStorage.removeItem(STORAGE_KEYS.TOKEN_EXPIRES_AT) +} diff --git a/src/utils/build-info.ts b/src/utils/build-info.ts new file mode 100644 index 0000000..6c7d08b --- /dev/null +++ b/src/utils/build-info.ts @@ -0,0 +1,10 @@ +const abbreviatedCommit = __GIT_COMMIT__.slice(0, 12) + +export const buildInfo = Object.freeze({ + version: __APP_VERSION__, + commit: abbreviatedCommit, + displayVersion: + abbreviatedCommit && abbreviatedCommit !== 'unknown' + ? `${__APP_VERSION__} (${abbreviatedCommit})` + : __APP_VERSION__ +}) diff --git a/src/utils/clipboard-paste.ts b/src/utils/clipboard-paste.ts new file mode 100644 index 0000000..4a44e0a --- /dev/null +++ b/src/utils/clipboard-paste.ts @@ -0,0 +1,40 @@ +export const getClipboardFile = (items: DataTransferItemList): File | null => { + for (let index = 0; index < items.length; index++) { + const item = items[index] + if (item.kind !== 'file') { + continue + } + + const file = item.getAsFile() + if (file) { + return file + } + } + return null +} + +export type TextInsertionInput = { + text: string + insertText: string + selectionStart: number + selectionEnd: number +} + +export type TextInsertionResult = { + value: string + cursor: number +} + +export const insertTextAtSelection = ({ + text, + insertText, + selectionStart, + selectionEnd +}: TextInsertionInput): TextInsertionResult => { + const beforeSelection = text.substring(0, selectionStart) + const afterSelection = text.substring(selectionEnd) + return { + value: beforeSelection + insertText + afterSelection, + cursor: selectionStart + insertText.length + } +} diff --git a/src/utils/clipboard.ts b/src/utils/clipboard.ts index fdc9ca2..1f38d21 100644 --- a/src/utils/clipboard.ts +++ b/src/utils/clipboard.ts @@ -2,11 +2,15 @@ * 剪贴板工具函数 */ -import { useAlertStore } from '@/stores/alertStore' +import { buildRetrieveUrl, buildWgetCommand } from '@/utils/share-url' + +type CopyNotifyType = 'success' | 'error' + interface CopyOptions { successMsg?: string errorMsg?: string showMsg?: boolean + notify?: (message: string, type: CopyNotifyType) => void } /** @@ -19,13 +23,24 @@ export const copyToClipboard = async ( text: string, options: CopyOptions = {} ): Promise => { - const { successMsg = '复制成功', errorMsg = '复制失败,请手动复制', showMsg = true } = options - const alertStore = useAlertStore() + const { + successMsg = '复制成功', + errorMsg = '复制失败,请手动复制', + showMsg = true, + notify + } = options + + const showCopyMessage = (message: string, type: CopyNotifyType) => { + if (showMsg) { + notify?.(message, type) + } + } + try { // 优先使用 Clipboard API if (document.hasFocus() && navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(text) - if (showMsg) alertStore.showAlert(successMsg, 'success') + showCopyMessage(successMsg, 'success') return true } // 后备方案:使用传统的复制方法 @@ -38,14 +53,14 @@ export const copyToClipboard = async ( const success = document.execCommand('copy') document.body.removeChild(textarea) if (success) { - if (showMsg) alertStore.showAlert(successMsg, 'success') + showCopyMessage(successMsg, 'success') return true } else { throw new Error('execCommand copy failed') } } catch (err) { console.error('复制失败:', err) - if (showMsg) alertStore.showAlert(errorMsg, 'error') + showCopyMessage(errorMsg, 'error') return false } } @@ -55,11 +70,15 @@ export const copyToClipboard = async ( * @param code 取件码 * @returns Promise 是否复制成功 */ -export const copyRetrieveLink = async (code: string): Promise => { - const link = `${window.location.origin}/#/?code=${code}` +export const copyRetrieveLink = async ( + code: string, + options: Pick = {} +): Promise => { + const link = buildRetrieveUrl(code) return copyToClipboard(link, { successMsg: '取件链接已复制到剪贴板', - errorMsg: '复制失败,请手动复制取件链接' + errorMsg: '复制失败,请手动复制取件链接', + ...options }) } @@ -68,50 +87,26 @@ export const copyRetrieveLink = async (code: string): Promise => { * @param code 取件码 * @returns Promise 是否复制成功 */ -export const copyRetrieveCode = async (code: string): Promise => { +export const copyRetrieveCode = async ( + code: string, + options: Pick = {} +): Promise => { return copyToClipboard(code, { successMsg: '取件码已复制到剪贴板', - errorMsg: '复制失败,请手动复制取件码' + errorMsg: '复制失败,请手动复制取件码', + ...options }) } -const baseUrl = window.location.origin + '/'; - -export const copyWgetCommand = (retrieveCode: string, fileName: string) => { - const command = `wget ${baseUrl}share/select?code=${retrieveCode} -O "${fileName}"`; - - if (navigator.clipboard && navigator.clipboard.writeText) { - navigator.clipboard.writeText(command) - .then(() => { - console.log("命令已复制到剪贴板!"); - }) - .catch((err) => { - console.error("复制失败,使用回退方法:", err); - fallbackCopyTextToClipboard(command); - }); - } else { - console.warn("Clipboard API 不可用,使用回退方法。"); - fallbackCopyTextToClipboard(command); - } -}; -function fallbackCopyTextToClipboard(text:string) { - const textArea = document.createElement("textarea"); - textArea.value = text; - textArea.style.position = "fixed"; // 避免滚动 - document.body.appendChild(textArea); - textArea.focus(); - textArea.select(); - try { - const successful = document.execCommand("copy"); - console.log("回退复制操作成功:", successful); - } catch (err) { - console.error("回退复制操作失败:", err); - } - document.body.removeChild(textArea); +export const copyWgetCommand = ( + retrieveCode: string, + fileName: string, + options: Pick = {} +) => { + const command = buildWgetCommand(retrieveCode, fileName) + void copyToClipboard(command, { + successMsg: '命令已复制到剪贴板', + errorMsg: '复制失败,请手动复制命令', + ...options + }) } - -if (document.hasFocus() && navigator.clipboard && navigator.clipboard.writeText) { - navigator.clipboard.writeText("要复制的文本"); -} else { - fallbackCopyTextToClipboard("要复制的文本"); -} \ No newline at end of file diff --git a/src/utils/common.ts b/src/utils/common.ts new file mode 100644 index 0000000..85d2001 --- /dev/null +++ b/src/utils/common.ts @@ -0,0 +1,267 @@ +/** + * 通用工具函数 + */ +import type { ApiErrorPayload, ApiResponse } from '@/types' + +/** + * 格式化时间戳为可读格式 + * @param timestamp 时间戳字符串 + * @param format 格式类型 + * @returns 格式化后的时间字符串 + */ +export function formatTimestamp(timestamp: string, format: 'datetime' | 'date' | 'time' = 'datetime'): string { + const date = new Date(timestamp) + const year = date.getFullYear() + const month = (date.getMonth() + 1).toString().padStart(2, '0') + const day = date.getDate().toString().padStart(2, '0') + const hours = date.getHours().toString().padStart(2, '0') + const minutes = date.getMinutes().toString().padStart(2, '0') + const seconds = date.getSeconds().toString().padStart(2, '0') + + switch (format) { + case 'date': + return `${year}-${month}-${day}` + case 'time': + return `${hours}:${minutes}:${seconds}` + case 'datetime': + default: + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` + } +} + +/** + * 格式化文件大小 + * @param bytes 字节数 + * @param decimals 小数位数 + * @returns 格式化后的文件大小字符串 + */ +export function formatFileSize(bytes: number, decimals: number = 2): string { + if (bytes === 0) return '0 Bytes' + + const k = 1024 + const dm = decimals < 0 ? 0 : decimals + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + + const i = Math.floor(Math.log(bytes) / Math.log(k)) + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i] +} + +/** + * 格式化持续时间 + * @param seconds 秒数 + * @param t 翻译函数 + * @returns 格式化后的持续时间字符串 + */ +export function formatDuration(seconds: number, t?: (key: string) => string): string { + if (seconds === 0) return t ? t('utils.time.forever') : 'Forever' + + const units = [ + { key: 'utils.time.day', value: 86400 }, + { key: 'utils.time.hour', value: 3600 }, + { key: 'utils.time.minute', value: 60 }, + { key: 'utils.time.second', value: 1 } + ] + + for (const unit of units) { + if (seconds >= unit.value) { + const value = Math.floor(seconds / unit.value) + const unitName = t ? t(unit.key) : unit.key.split('.').pop() + return `${value}${unitName}` + } + } + + const secondName = t ? t('utils.time.second') : 'second' + return `${seconds}${secondName}` +} + +/** + * 防抖函数 + * @param func 要防抖的函数 + * @param wait 等待时间(毫秒) + * @returns 防抖后的函数 + */ +export function debounce unknown>( + func: T, + wait: number +): (...args: Parameters) => void { + let timeout: ReturnType | null = null + + return (...args: Parameters) => { + if (timeout) { + clearTimeout(timeout) + } + timeout = setTimeout(() => func(...args), wait) + } +} + +/** + * 节流函数 + * @param func 要节流的函数 + * @param limit 限制时间(毫秒) + * @returns 节流后的函数 + */ +export function throttle unknown>( + func: T, + limit: number +): (...args: Parameters) => void { + let inThrottle: boolean = false + + return (...args: Parameters) => { + if (!inThrottle) { + func(...args) + inThrottle = true + setTimeout(() => inThrottle = false, limit) + } + } +} + +/** + * 验证邮箱格式 + * @param email 邮箱地址 + * @returns 是否为有效邮箱 + */ +export function isValidEmail(email: string): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ + return emailRegex.test(email) +} + +/** + * 验证URL格式 + * @param url URL地址 + * @returns 是否为有效URL + */ +export function isValidUrl(url: string): boolean { + try { + new URL(url) + return true + } catch { + return false + } +} + +/** + * 生成随机字符串 + * @param length 字符串长度 + * @param chars 可选字符集 + * @returns 随机字符串 + */ +export function generateRandomString( + length: number = 8, + chars: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' +): string { + let result = '' + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)) + } + return result +} + +/** + * 深度克隆对象 + * @param obj 要克隆的对象 + * @returns 克隆后的对象 + */ +export function deepClone(obj: T): T { + if (obj === null || typeof obj !== 'object') { + return obj + } + + if (obj instanceof Date) { + return new Date(obj.getTime()) as T + } + + if (obj instanceof Array) { + return obj.map(item => deepClone(item)) as T + } + + if (typeof obj === 'object') { + const clonedObj = {} as T + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + clonedObj[key] = deepClone(obj[key]) + } + } + return clonedObj + } + + return obj +} + +/** + * 获取文件扩展名 + * @param filename 文件名 + * @returns 文件扩展名(不包含点) + */ +export function getFileExtension(filename: string): string { + return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2) +} + +/** + * 检查是否为移动设备 + * @returns 是否为移动设备 + */ +export function isMobile(): boolean { + return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) +} + +/** + * 格式化数字,添加千分位分隔符 + * @param num 数字 + * @returns 格式化后的数字字符串 + */ +export function formatNumber(num: number): string { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') +} + +type ErrorWithResponse = { + response?: { + data?: ApiErrorPayload + } + message?: string +} + +const getReadableMessage = (value: unknown): string => { + if (typeof value === 'string') { + return value + } + + if (!value || typeof value !== 'object') { + return '' + } + + const payload = value as Record + for (const key of ['message', 'msg', 'detail', 'error']) { + const fieldValue = payload[key] + if (typeof fieldValue === 'string' && fieldValue.trim()) { + return fieldValue + } + } + + return '' +} + +export function getResponseMessage(response: ApiResponse, fallback: string): string { + return ( + getReadableMessage(response.detail) || + getReadableMessage(response.message) || + getReadableMessage(response.msg) || + fallback + ) +} + +export function getErrorMessage(error: unknown, fallback: string): string { + if (!error || typeof error !== 'object') { + return fallback + } + + const errorWithResponse = error as ErrorWithResponse + const responseData = errorWithResponse.response?.data + return ( + getReadableMessage(responseData?.detail) || + getReadableMessage(responseData?.message) || + getReadableMessage(responseData?.msg) || + errorWithResponse.message || + fallback + ) +} diff --git a/src/utils/config-form.ts b/src/utils/config-form.ts new file mode 100644 index 0000000..4f14636 --- /dev/null +++ b/src/utils/config-form.ts @@ -0,0 +1,107 @@ +import type { ConfigState } from '@/types' + +export type FileSizeUnit = 'KB' | 'MB' | 'GB' +export type SaveTimeUnit = '秒' | '分' | '时' | '天' + +export type FileSizeForm = { + value: number + unit: FileSizeUnit +} + +export type SaveTimeForm = { + value: number + unit: SaveTimeUnit +} + +const FILE_SIZE_UNITS: Record = { + KB: 1024, + MB: 1024 * 1024, + GB: 1024 * 1024 * 1024 +} + +const SAVE_TIME_UNITS: Record = { + 秒: 1, + 分: 60, + 时: 3600, + 天: 86400 +} + +export function bytesToFileSizeForm(bytes: number): FileSizeForm { + if (bytes >= FILE_SIZE_UNITS.GB) { + return { + value: Math.round(bytes / FILE_SIZE_UNITS.GB), + unit: 'GB' + } + } + + if (bytes >= FILE_SIZE_UNITS.MB) { + return { + value: Math.round(bytes / FILE_SIZE_UNITS.MB), + unit: 'MB' + } + } + + return { + value: Math.round(bytes / FILE_SIZE_UNITS.KB), + unit: 'KB' + } +} + +export function fileSizeFormToBytes(value: number, unit: FileSizeUnit): number { + return value * FILE_SIZE_UNITS[unit] +} + +export function secondsToSaveTimeForm(seconds: number): SaveTimeForm { + if (seconds === 0) { + return { + value: 7, + unit: '天' + } + } + + if (seconds % SAVE_TIME_UNITS.天 === 0 && seconds >= SAVE_TIME_UNITS.天) { + return { + value: seconds / SAVE_TIME_UNITS.天, + unit: '天' + } + } + + if (seconds % SAVE_TIME_UNITS.时 === 0 && seconds >= SAVE_TIME_UNITS.时) { + return { + value: seconds / SAVE_TIME_UNITS.时, + unit: '时' + } + } + + if (seconds % SAVE_TIME_UNITS.分 === 0 && seconds >= SAVE_TIME_UNITS.分) { + return { + value: seconds / SAVE_TIME_UNITS.分, + unit: '分' + } + } + + return { + value: seconds, + unit: '秒' + } +} + +export function saveTimeFormToSeconds(value: number, unit: SaveTimeUnit): number { + if (value === 0) { + return 7 * SAVE_TIME_UNITS.天 + } + + return value * SAVE_TIME_UNITS[unit] +} + +export function buildConfigSubmitPayload( + config: ConfigState, + fileSize: FileSizeForm, + saveTime: SaveTimeForm +): ConfigState { + return { + ...config, + uploadSize: fileSizeFormToBytes(fileSize.value, fileSize.unit), + max_save_seconds: saveTimeFormToSeconds(saveTime.value, saveTime.unit) + } +} diff --git a/src/utils/config-storage.ts b/src/utils/config-storage.ts new file mode 100644 index 0000000..077168a --- /dev/null +++ b/src/utils/config-storage.ts @@ -0,0 +1,150 @@ +import { DEFAULT_CONFIG, FILE_SIZE_LIMITS, STORAGE_KEYS } from '@/constants' +import type { ConfigState, SystemConfig } from '@/types' + +export type PublicConfig = SystemConfig & { + uploadSize: number + allowed_file_types?: string[] + expireStyle: string[] + code_generate_type?: 'number' | 'secret' + openUpload: number + max_save_seconds: number + enableChunk: number + notify_title?: string + notify_content?: string + page_explain?: string + showAdminAddr?: number + themesSelect?: string + background?: string + opacity?: number +} + +type PublicConfigInput = Omit, 'showAdminAddr'> & { + showAdminAddr?: number | string + show_admin_address?: number | string +} + +export const DEFAULT_PUBLIC_CONFIG: PublicConfig = { + ...DEFAULT_CONFIG, + uploadSize: FILE_SIZE_LIMITS.MAX_FILE_SIZE, + allowedFileTypes: ['*'], + allowed_file_types: ['*'], + expireStyle: ['day'], + code_generate_type: 'secret', + openUpload: 1, + max_save_seconds: 0, + enableChunk: 0, + showAdminAddr: 0 +} + +export const DEFAULT_CONFIG_STATE: ConfigState = { + name: DEFAULT_PUBLIC_CONFIG.name, + description: DEFAULT_PUBLIC_CONFIG.description || '', + file_storage: '', + themesChoices: [], + expireStyle: DEFAULT_PUBLIC_CONFIG.expireStyle, + code_generate_type: DEFAULT_PUBLIC_CONFIG.code_generate_type || 'secret', + adminSessionExpire: 30 * 24 * 60 * 60, + admin_token: '', + robotsText: '', + keywords: '', + notify_title: '', + notify_content: '', + openUpload: DEFAULT_PUBLIC_CONFIG.openUpload, + uploadSize: DEFAULT_PUBLIC_CONFIG.uploadSize, + allowed_file_types: DEFAULT_PUBLIC_CONFIG.allowedFileTypes, + allowedFileTypes: DEFAULT_PUBLIC_CONFIG.allowedFileTypes, + storage_path: '', + storageLimit: 0, + uploadMinute: 1, + max_save_seconds: DEFAULT_PUBLIC_CONFIG.max_save_seconds, + opacity: 0.9, + enableChunk: DEFAULT_PUBLIC_CONFIG.enableChunk, + s3_access_key_id: '', + background: '', + showAdminAddr: 0, + page_explain: '', + s3_secret_access_key: '', + aws_session_token: '', + s3_signature_version: '', + s3_addressing_style: 'auto', + s3_region_name: '', + s3_bucket_name: '', + s3_endpoint_url: '', + s3_hostname: '', + uploadCount: 1, + errorMinute: 1, + errorCount: 1, + s3_proxy: 0, + themesSelect: '', + webdav_url: '', + webdav_username: '', + webdav_password: '' +} + +function normalizeFileTypes(value: unknown): string[] { + const rawTypes = + typeof value === 'string' + ? value.split(',') + : Array.isArray(value) + ? value + : DEFAULT_PUBLIC_CONFIG.allowedFileTypes + const normalized = rawTypes.map((item) => String(item).trim()).filter(Boolean) + return normalized.length > 0 ? normalized : ['*'] +} + +function normalizeAdminAddress(value: number | string | undefined): number | undefined { + if (value === undefined) return undefined + return Number(value) === 1 ? 1 : 0 +} + +export function readStoredConfig>(): T | null { + try { + const rawConfig = localStorage.getItem(STORAGE_KEYS.CONFIG) + return rawConfig ? (JSON.parse(rawConfig) as T) : null + } catch { + return null + } +} + +export function toPublicConfig( + config: PublicConfigInput | null | undefined +): Partial { + if (!config) return {} + + const allowedFileTypes = normalizeFileTypes(config.allowedFileTypes ?? config.allowed_file_types) + + return { + name: config.name, + description: config.description, + uploadSize: config.uploadSize, + allowedFileTypes, + allowed_file_types: allowedFileTypes, + expireStyle: config.expireStyle, + code_generate_type: config.code_generate_type, + openUpload: config.openUpload, + max_save_seconds: config.max_save_seconds, + enableChunk: config.enableChunk, + notify_title: config.notify_title, + notify_content: config.notify_content, + page_explain: config.page_explain, + showAdminAddr: normalizeAdminAddress(config.showAdminAddr ?? config.show_admin_address), + themesSelect: config.themesSelect, + background: config.background, + opacity: config.opacity + } +} + +export function writeStoredConfig(config: object) { + localStorage.setItem( + STORAGE_KEYS.CONFIG, + JSON.stringify(toPublicConfig(config as PublicConfigInput)) + ) +} + +export function readNotifyKey(): string | null { + return localStorage.getItem(STORAGE_KEYS.NOTIFY) +} + +export function writeNotifyKey(notifyKey: string) { + localStorage.setItem(STORAGE_KEYS.NOTIFY, notifyKey) +} diff --git a/src/utils/content-preview.ts b/src/utils/content-preview.ts new file mode 100644 index 0000000..6f0bb9a --- /dev/null +++ b/src/utils/content-preview.ts @@ -0,0 +1,57 @@ +const MARKDOWN_ALLOWED_TAGS = [ + 'p', + 'br', + 'strong', + 'em', + 'u', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'ul', + 'ol', + 'li', + 'blockquote', + 'code', + 'pre', + 'a', + 'img' +] + +const MARKDOWN_ALLOWED_ATTR = ['href', 'src', 'alt', 'title', 'class'] + +type MarkedRenderer = typeof import('marked')['marked'] +type DOMPurifyModule = typeof import('dompurify')['default'] + +let markdownRendererLoader: Promise<{ + marked: MarkedRenderer + DOMPurify: DOMPurifyModule +}> | null = null + +const loadMarkdownRenderer = async () => { + markdownRendererLoader ??= Promise.all([import('marked'), import('dompurify')]).then( + ([markedModule, domPurifyModule]) => ({ + marked: markedModule.marked, + DOMPurify: domPurifyModule.default + }) + ) + return markdownRendererLoader +} + +export async function renderMarkdownPreview(content: string): Promise { + try { + const { marked, DOMPurify } = await loadMarkdownRenderer() + const rawHtml = await marked(content) + return DOMPurify.sanitize(rawHtml, { + ALLOWED_TAGS: MARKDOWN_ALLOWED_TAGS, + ALLOWED_ATTR: MARKDOWN_ALLOWED_ATTR, + ALLOWED_URI_REGEXP: + /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|xxx):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i + }) + } catch (error) { + console.error('Markdown 渲染失败:', error) + return content + } +} diff --git a/src/utils/download-action.ts b/src/utils/download-action.ts new file mode 100644 index 0000000..e5c2722 --- /dev/null +++ b/src/utils/download-action.ts @@ -0,0 +1,100 @@ +import type { AdminFileViewItem, ApiResponse, ReceivedFileRecord } from '@/types' +import { buildDownloadUrl } from '@/utils/share-url' + +const unsafeFilenamePattern = new RegExp( + `[\\\\/:*?"<>|${String.fromCharCode(0)}-${String.fromCharCode(31)}]`, + 'g' +) + +type SaveAs = typeof import('file-saver') +type FileSaverModule = { + default: SaveAs + saveAs: SaveAs +} + +let fileSaverLoader: Promise | null = null + +const saveBlobAsFile = async (blob: Blob, filename: string) => { + fileSaverLoader ??= import('file-saver') + const { saveAs } = await fileSaverLoader + saveAs(blob, filename) +} + +export async function downloadReceivedRecord(record: ReceivedFileRecord): Promise { + if (record.downloadUrl) { + window.open(buildDownloadUrl(record.downloadUrl), '_blank') + return + } + + if (record.content) { + const blob = new Blob([record.content], { type: 'text/plain;charset=utf-8' }) + await saveBlobAsFile(blob, `${record.filename}.txt`) + } +} + +export const getSafeFilename = (name: string) => + name + .replace(unsafeFilenamePattern, '_') + .replace(/\s+/g, ' ') + .trim() + .slice(0, 180) || 'download' + +const getFilenameFromDisposition = (disposition?: string) => { + if (!disposition) return '' + + const utf8Match = disposition.match(/filename\*=UTF-8''([^;]+)/i) + if (utf8Match?.[1]) { + try { + return decodeURIComponent(utf8Match[1]) + } catch { + return utf8Match[1] + } + } + + const asciiMatch = disposition.match(/filename="?([^"]+)"?/i) + return asciiMatch?.[1] || '' +} + +const readBlobAsText = (blob: Blob): Promise => + new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = () => resolve(String(reader.result || '')) + reader.onerror = () => reject(reader.error) + reader.readAsText(blob) + }) + +export async function exportAdminTextFile( + file: AdminFileViewItem, + content: string +): Promise { + const filename = getSafeFilename(file.displayName || file.code) + const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }) + await saveBlobAsFile(blob, `${filename}.txt`) +} + +export async function downloadAdminManagedFile( + file: AdminFileViewItem, + response: { data: Blob; headers: Record } +): Promise { + if (file.isTextFile) { + const text = await readBlobAsText(response.data) + let content = text + + try { + const payload = JSON.parse(text) as ApiResponse + content = typeof payload.detail === 'string' ? payload.detail : text + } catch { + content = text + } + + await exportAdminTextFile(file, content) + return + } + + const disposition = + response.headers['content-disposition'] || response.headers['Content-Disposition'] + const filename = getSafeFilename( + getFilenameFromDisposition(disposition) || file.displayName || file.code + ) + await saveBlobAsFile(response.data, filename) +} diff --git a/src/utils/file-processing.ts b/src/utils/file-processing.ts new file mode 100644 index 0000000..b22d9fe --- /dev/null +++ b/src/utils/file-processing.ts @@ -0,0 +1,74 @@ +const SMALL_FILE_HASH_LIMIT = 10 * 1024 * 1024 +const LARGE_FILE_HASH_CHUNK_SIZE = 5 * 1024 * 1024 + +type JSZipConstructor = typeof import('jszip') +type JSZipModule = JSZipConstructor & { + default?: JSZipConstructor +} + +let jsZipLoader: Promise | null = null + +const loadJSZip = async () => { + jsZipLoader ??= import('jszip').then((module) => { + const normalizedModule = module as unknown as JSZipModule + return normalizedModule.default ?? normalizedModule + }) + return jsZipLoader +} + +const generateFallbackHash = (file: File): string => { + const fileInfo = `${file.name}-${file.size}-${file.lastModified}` + let hash = 0 + for (let i = 0; i < fileInfo.length; i++) { + const char = fileInfo.charCodeAt(i) + hash = (hash << 5) - hash + char + hash = hash & hash + } + return Math.abs(hash).toString(16).padStart(64, '0') +} + +const createSha256Hash = async (data: BufferSource): Promise => { + const hashBuffer = await crypto.subtle.digest('SHA-256', data) + const hashArray = Array.from(new Uint8Array(hashBuffer)) + return hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join('') +} + +export const calculateFileHash = async (file: File): Promise => { + try { + if (file.size <= SMALL_FILE_HASH_LIMIT) { + const buffer = await file.arrayBuffer() + return window.isSecureContext ? createSha256Hash(buffer) : generateFallbackHash(file) + } + + const firstChunk = file.slice(0, LARGE_FILE_HASH_CHUNK_SIZE) + const lastChunk = file.slice(-LARGE_FILE_HASH_CHUNK_SIZE) + const [firstBuffer, lastBuffer] = await Promise.all([ + firstChunk.arrayBuffer(), + lastChunk.arrayBuffer() + ]) + const combined = new Uint8Array(firstBuffer.byteLength + lastBuffer.byteLength + 16) + combined.set(new Uint8Array(firstBuffer), 0) + combined.set(new Uint8Array(lastBuffer), firstBuffer.byteLength) + const sizeBytes = new TextEncoder().encode(file.size.toString()) + combined.set(sizeBytes, firstBuffer.byteLength + lastBuffer.byteLength) + + return window.isSecureContext ? createSha256Hash(combined) : generateFallbackHash(file) + } catch (error) { + console.error('File hash calculation failed:', error) + return generateFallbackHash(file) + } +} + +export const packFilesAsZip = async (files: File[]): Promise => { + const JSZip = await loadJSZip() + const zip = new JSZip() + for (const file of files) { + zip.file(file.name, file) + } + const blob = await zip.generateAsync({ + type: 'blob', + compression: 'DEFLATE', + compressionOptions: { level: 6 } + }) + return new File([blob], `files_${Date.now()}.zip`, { type: 'application/zip' }) +} diff --git a/src/utils/preference-storage.ts b/src/utils/preference-storage.ts new file mode 100644 index 0000000..6582123 --- /dev/null +++ b/src/utils/preference-storage.ts @@ -0,0 +1,20 @@ +import { STORAGE_KEYS } from '@/constants' +import type { ThemeMode } from '@/types' + +const LOCALE_STORAGE_KEY = 'locale' + +export function readStoredThemeMode(): string | null { + return localStorage.getItem(STORAGE_KEYS.COLOR_MODE) +} + +export function writeStoredThemeMode(mode: ThemeMode) { + localStorage.setItem(STORAGE_KEYS.COLOR_MODE, mode) +} + +export function readStoredLocale(): string | null { + return localStorage.getItem(LOCALE_STORAGE_KEY) +} + +export function writeStoredLocale(locale: string) { + localStorage.setItem(LOCALE_STORAGE_KEY, locale) +} diff --git a/src/utils/record-storage.ts b/src/utils/record-storage.ts new file mode 100644 index 0000000..6aba34b --- /dev/null +++ b/src/utils/record-storage.ts @@ -0,0 +1,23 @@ +export function readStoredRecords(key: string): T[] { + if (typeof window === 'undefined') return [] + + try { + const raw = localStorage.getItem(key) + if (!raw) return [] + + const parsed = JSON.parse(raw) + return Array.isArray(parsed) ? (parsed as T[]) : [] + } catch { + return [] + } +} + +export function writeStoredRecords(key: string, records: T[]) { + if (typeof window === 'undefined') return + + try { + localStorage.setItem(key, JSON.stringify(records)) + } catch { + // 本地存储不可用时保持内存记录,不影响当前会话。 + } +} diff --git a/src/utils/send-record.ts b/src/utils/send-record.ts new file mode 100644 index 0000000..00fe046 --- /dev/null +++ b/src/utils/send-record.ts @@ -0,0 +1,99 @@ +import type { ApiResponse, SendType, SentFileRecord } from '@/types' + +type Translate = (key: string, params?: Record) => string + +type BuildSentRecordInput = { + response: ApiResponse + sendType: SendType + textContent: string + selectedFile: File | null + selectedFiles: File[] + expirationMethod: string + expirationValue: string + translate: Translate + getUnit: (method: string) => string +} + +const expirationSecondsByMethod: Record = { + minute: 60, + hour: 3600, + day: 86400 +} + +export function isExpirationWithinLimit( + method: string, + value: string, + maxSaveSeconds: number +): boolean { + if (method === 'forever' || method === 'count') return true + if (maxSaveSeconds === 0) return true + + const multiplier = expirationSecondsByMethod[method] + if (!multiplier) return false + + return parseInt(value) * multiplier <= maxSaveSeconds +} + +export function formatExpirationTime( + method: string, + value: string, + translate: Translate, + getUnit: (method: string) => string +): string { + if (method === 'forever') return translate('send.expiration.units.forever') + if (method === 'count') return translate('send.messages.expiresAfterCount', { count: value }) + + const now = new Date() + const expireValue = parseInt(value) + + switch (method) { + case 'minute': + now.setMinutes(now.getMinutes() + expireValue) + break + case 'hour': + now.setHours(now.getHours() + expireValue) + break + case 'day': + now.setDate(now.getDate() + expireValue) + break + default: + return translate('send.messages.expiresAfter', { value, unit: getUnit(method) }) + } + + const year = now.getFullYear() + const month = (now.getMonth() + 1).toString().padStart(2, '0') + const day = now.getDate().toString().padStart(2, '0') + const hours = now.getHours().toString().padStart(2, '0') + const minutes = now.getMinutes().toString().padStart(2, '0') + return translate('send.messages.expiresAt', { date: `${year}-${month}-${day} ${hours}:${minutes}` }) +} + +export function buildSentRecord(input: BuildSentRecordInput): SentFileRecord { + const retrieveCode = (input.response.detail as { code?: string } | undefined)?.code || '' + const fileName = (input.response.detail as { name?: string } | undefined)?.name || '' + + const totalSelectedSize = input.selectedFiles.reduce((total, file) => total + file.size, 0) + const displaySize = + input.sendType === 'text' + ? `${(input.textContent.length / 1024).toFixed(2)} KB` + : input.selectedFiles.length > 0 + ? `${(totalSelectedSize / (1024 * 1024)).toFixed(1)} MB` + : `${((input.selectedFile?.size || 0) / (1024 * 1024)).toFixed(1)} MB` + + return { + id: Date.now(), + filename: fileName, + date: new Date().toISOString().split('T')[0], + size: displaySize, + expiration: + input.expirationMethod === 'forever' + ? input.translate('send.expiration.forever') + : formatExpirationTime( + input.expirationMethod, + input.expirationValue, + input.translate, + input.getUnit + ), + retrieveCode + } +} diff --git a/src/utils/sent-record-actions.ts b/src/utils/sent-record-actions.ts new file mode 100644 index 0000000..944cbb0 --- /dev/null +++ b/src/utils/sent-record-actions.ts @@ -0,0 +1,17 @@ +import type { SentFileRecord } from '@/types' +import { copyRetrieveCode, copyRetrieveLink, copyWgetCommand } from '@/utils/clipboard' +import { buildSentRecordQrValue } from '@/utils/share-url' + +type CopyNotify = (message: string, type: 'success' | 'error') => void + +export function createSentRecordActions(notify: CopyNotify) { + return { + copyLink: (record: SentFileRecord) => + copyRetrieveLink(record.retrieveCode, { notify }), + copyCode: (record: SentFileRecord) => + copyRetrieveCode(record.retrieveCode, { notify }), + copyWgetCommand: (record: SentFileRecord) => + copyWgetCommand(record.retrieveCode, record.filename, { notify }), + getQRCodeValue: (record: SentFileRecord) => buildSentRecordQrValue(record) + } +} diff --git a/src/utils/share-url.ts b/src/utils/share-url.ts new file mode 100644 index 0000000..b361ab5 --- /dev/null +++ b/src/utils/share-url.ts @@ -0,0 +1,38 @@ +import { apiBaseURL } from '@/services/client' + +const getApiOrigin = () => { + if (!apiBaseURL) return window.location.origin + return new URL(apiBaseURL, window.location.origin).origin +} + +export function buildAbsoluteUrl(path: string): string { + if (/^https?:\/\//i.test(path)) { + return path + } + + const normalizedPath = path.startsWith('/') ? path : `/${path}` + return `${getApiOrigin()}${normalizedPath}` +} + +export function buildRetrieveUrl(code: string): string { + return `${window.location.origin}/#/?code=${code}` +} + +export function buildDownloadUrl(downloadUrl: string | null): string { + return downloadUrl ? buildAbsoluteUrl(downloadUrl) : '' +} + +export function buildReceivedRecordQrValue(record: { + code: string + downloadUrl: string | null +}): string { + return record.downloadUrl ? buildDownloadUrl(record.downloadUrl) : buildRetrieveUrl(record.code) +} + +export function buildSentRecordQrValue(record: { retrieveCode: string }): string { + return buildRetrieveUrl(record.retrieveCode) +} + +export function buildWgetCommand(retrieveCode: string, fileName: string): string { + return `wget ${buildAbsoluteUrl(`/share/select?code=${retrieveCode}`)} -O "${fileName}"` +} diff --git a/src/views/RetrievewFileView.vue b/src/views/RetrievewFileView.vue index 5c2d8b5..01e10b6 100644 --- a/src/views/RetrievewFileView.vue +++ b/src/views/RetrievewFileView.vue @@ -1,578 +1,121 @@ - - - diff --git a/src/views/SendFileView.vue b/src/views/SendFileView.vue index 7e0a635..88a4619 100644 --- a/src/views/SendFileView.vue +++ b/src/views/SendFileView.vue @@ -1,1030 +1,200 @@ diff --git a/src/views/manage/DashboardView.vue b/src/views/manage/DashboardView.vue index f97c3a1..e87ed8f 100644 --- a/src/views/manage/DashboardView.vue +++ b/src/views/manage/DashboardView.vue @@ -1,198 +1,441 @@ diff --git a/src/views/manage/FileManageView.vue b/src/views/manage/FileManageView.vue index b9c5f39..1c9e71a 100644 --- a/src/views/manage/FileManageView.vue +++ b/src/views/manage/FileManageView.vue @@ -1,629 +1,1741 @@ - + +watch( + () => route.query.health, + async () => { + const health = getRouteHealthFilter() + if (params.value.health === health) return + await setHealthFilter(health) + } +) + +onMounted(() => { + params.value.health = getRouteHealthFilter() + void loadViewPresets() + void loadFiles() +}) + diff --git a/src/views/manage/LoginView.vue b/src/views/manage/LoginView.vue index c3422d2..aa73c01 100644 --- a/src/views/manage/LoginView.vue +++ b/src/views/manage/LoginView.vue @@ -1,58 +1,91 @@ @@ -137,7 +149,7 @@ const handleSubmit = async () => { } input:focus { - box-shadow: 0 0 15px rgba(99, 102, 241, 0.3); + box-shadow: 0 0 15px rgba(39, 39, 42, 0.18); } button:active:not(:disabled) { @@ -145,8 +157,9 @@ button:active:not(:disabled) { } .cyber-grid { - background-image: linear-gradient(transparent 95%, rgba(99, 102, 241, 0.1) 50%), - linear-gradient(90deg, transparent 95%, rgba(99, 102, 241, 0.1) 50%); + background-image: + linear-gradient(transparent 95%, rgba(39, 39, 42, 0.08) 50%), + linear-gradient(90deg, transparent 95%, rgba(39, 39, 42, 0.08) 50%); background-size: 30px 30px; width: 100%; height: 100%; @@ -168,7 +181,7 @@ button:active:not(:disabled) { position: absolute; width: 100%; height: 100%; - background-image: radial-gradient(circle at center, rgba(99, 102, 241, 0.1) 0%, transparent 50%); + background-image: radial-gradient(circle at center, rgba(39, 39, 42, 0.08) 0%, transparent 50%); animation: float 20s infinite linear; } @@ -192,7 +205,7 @@ button:active:not(:disabled) { } button:hover:not(:disabled) { - box-shadow: 0 0 25px rgba(99, 102, 241, 0.5); + box-shadow: 0 0 25px rgba(39, 39, 42, 0.22); } .fade-enter-active, diff --git a/src/views/manage/SystemSettingsView.vue b/src/views/manage/SystemSettingsView.vue index 99bdcf6..7c9d676 100644 --- a/src/views/manage/SystemSettingsView.vue +++ b/src/views/manage/SystemSettingsView.vue @@ -1,417 +1,424 @@ - + diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 933bf66..7eed4f7 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -3,3 +3,14 @@ declare module '*.vue' { const componentOptions: ComponentOptions export default componentOptions } + +declare module 'vue-router' { + interface RouteMeta { + requiresAuth?: boolean + showGlobalControls?: boolean + showRouteLoading?: boolean + title?: string + } +} + +export {} diff --git a/tsconfig.app.json b/tsconfig.app.json index e14c754..bfe4103 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -5,6 +5,7 @@ "compilerOptions": { "composite": true, "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "moduleResolution": "bundler", "baseUrl": ".", "paths": { diff --git a/tsconfig.json b/tsconfig.json index 38b270a..5a81d7e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,12 @@ "paths": { "@/*": ["src/*"] }, - "noImplicitAny": false, - "strict":false + "noImplicitAny": true, + "strict": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true } } diff --git a/vite.config.ts b/vite.config.ts index d3d07e7..1bbc295 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,13 +1,43 @@ import { fileURLToPath, URL } from 'node:url' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' -import vueDevTools from 'vite-plugin-vue-devtools' import tailwindcss from 'tailwindcss' import autoprefixer from 'autoprefixer' + +interface PackageMetadata { + version: string +} + +const packageMetadata = JSON.parse( + readFileSync(fileURLToPath(new URL('./package.json', import.meta.url)), 'utf8') +) as PackageMetadata + +const resolveGitCommit = () => { + if (process.env.VITE_GIT_COMMIT) return process.env.VITE_GIT_COMMIT + + try { + return execFileSync('git', ['rev-parse', '--short=12', 'HEAD'], { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'] + }).trim() + } catch { + return 'unknown' + } +} + +const buildVersion = process.env.VITE_APP_VERSION || packageMetadata.version +const buildCommit = resolveGitCommit() + export default defineConfig({ - plugins: [vue(), vueJsx(), vueDevTools()], + define: { + __APP_VERSION__: JSON.stringify(buildVersion), + __GIT_COMMIT__: JSON.stringify(buildCommit) + }, + plugins: [vue(), vueJsx()], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url))