From 4c115cf3258ddc4ad8e3fc96e7dafabe867391dc Mon Sep 17 00:00:00 2001
From: CaasianVale <1544257291@qq.com>
Date: Fri, 7 Mar 2025 03:33:18 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E5=89=8D=E7=AB=AF?=
=?UTF-8?q?=E6=98=BE=E7=A4=BA&=E4=BF=AE=E5=A4=8D=E8=8B=A5=E5=B9=B2bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +
frontend/index.html | 4 +-
frontend/package-lock.json | 24 +-
frontend/package.json | 4 +-
frontend/src/App.vue | 3 +-
frontend/src/components/ApiConfigPanel.vue | 365 +++-
frontend/src/components/LoginPage.vue | 536 ++++++
frontend/src/components/MarketTimeDisplay.vue | 97 +-
frontend/src/components/StockAnalysisApp.vue | 209 ++-
frontend/src/components/StockCard.vue | 385 ++++-
frontend/src/components/StockSearch.vue | 8 +-
frontend/src/main.ts | 2 +
frontend/src/router/index.ts | 89 +
frontend/src/services/api.ts | 97 +-
frontend/src/types/index.ts | 12 +
frontend/src/utils/stockValidator.ts | 172 ++
frontend/vite.config.ts | 25 +
frontend/yarn.lock | 1485 ++++++++++++++++-
fund_service.py | 51 -
requirements.txt | 2 +
services/ai_analyzer.py | 104 +-
services/fund_service_async.py | 5 +-
services/stock_analyzer_service.py | 24 +
services/stock_data_provider.py | 132 +-
services/us_stock_service_async.py | 5 +-
stock_analyzer.py | 758 ---------
us_stock_service.py | 53 -
utils/api_control.py | 43 -
web_server.py | 239 ++-
29 files changed, 3726 insertions(+), 1209 deletions(-)
create mode 100644 frontend/src/components/LoginPage.vue
create mode 100644 frontend/src/router/index.ts
create mode 100644 frontend/src/utils/stockValidator.ts
delete mode 100644 fund_service.py
delete mode 100644 stock_analyzer.py
delete mode 100644 us_stock_service.py
delete mode 100644 utils/api_control.py
diff --git a/README.md b/README.md
index d72310e..d510ace 100644
--- a/README.md
+++ b/README.md
@@ -19,9 +19,11 @@ docker run -d \
-e API_URL=替换为你的api地址 \
-e API_MODEL=替换为你的模型 \
-e API_TIMEOUT=60 \
+ -e LOGIN_PASSWORD=替换为你的密码 \
lanzhihong/stock-scanner:latest
API_TIMEOUT=60 202503040712版本开始 (AI分析发生错误,查看日志是否有timed out类似错误,需要增加你的API超时时间)
+LOGIN_PASSWORD 为空时,表示不需要登录,否则需要经过登录接口验证
注意⚠️: 环境变量名变更,更新版本后需要调整!!!
diff --git a/frontend/index.html b/frontend/index.html
index cbb7c0b..ad351f4 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -3,9 +3,9 @@
- 股票分析系统
+ 股票AI分析系统
-
+
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 96ad038..e9038b3 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -14,7 +14,8 @@
"axios": "^1.8.1",
"marked": "^15.0.7",
"naive-ui": "^2.41.0",
- "vue": "^3.5.13"
+ "vue": "^3.5.13",
+ "vue-router": "^4.5.0"
},
"devDependencies": {
"@types/marked": "^5.0.2",
@@ -298,6 +299,12 @@
"he": "^1.2.0"
}
},
+ "node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+ "license": "MIT"
+ },
"node_modules/@vue/language-core": {
"version": "2.2.8",
"resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.2.8.tgz",
@@ -1217,6 +1224,21 @@
}
}
},
+ "node_modules/vue-router": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz",
+ "integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^6.6.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+ },
"node_modules/vue-tsc": {
"version": "2.2.8",
"resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-2.2.8.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index cbe1418..0a9d388 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -15,7 +15,9 @@
"axios": "^1.8.1",
"marked": "^15.0.7",
"naive-ui": "^2.41.0",
- "vue": "^3.5.13"
+ "npm": "^11.2.0",
+ "vue": "^3.5.13",
+ "vue-router": "4"
},
"devDependencies": {
"@types/marked": "^5.0.2",
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 84095a0..07a97be 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -4,7 +4,7 @@
-
+
@@ -21,7 +21,6 @@ import {
NDialogProvider,
NNotificationProvider,
} from 'naive-ui'
-import StockAnalysisApp from './components/StockAnalysisApp.vue'
// 主题设置 (默认使用亮色主题)
const theme = ref(null) // 可以切换为 darkTheme 以启用暗色模式
diff --git a/frontend/src/components/ApiConfigPanel.vue b/frontend/src/components/ApiConfigPanel.vue
index f840b70..dde2b99 100644
--- a/frontend/src/components/ApiConfigPanel.vue
+++ b/frontend/src/components/ApiConfigPanel.vue
@@ -5,16 +5,17 @@
size="small"
@click="toggleConfig"
:quaternary="true"
+ :type="expanded ? 'primary' : 'default'"
>
- API配置 {{ expanded ? '收起' : '展开' }}
+ API配置 {{ expanded ? '收起' : '展开' }}
-
-
+
+
@@ -28,21 +29,32 @@
-
-
+
+
+ round
+ >
+
+
+
+
- {{ formattedUrl }}
+
+
实际请求地址: {{ formattedUrl }}
+
+
提示: URL以/结尾将忽略v1路径
+
URL以#结尾将使用原始地址
+
+
-
+
+ round
+ >
+
+
+
+
-
+
-
+ round
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
您可以直接输入模型名称,或点击右侧按钮从下拉菜单选择
+
常用模型:
+
+
+ {{ model.label }}
+
+
+
+
-
+
+ :show-button="false"
+ class="timeout-input"
+ >
+
+
+
+
+
+
+
+
+
+
+
@@ -92,22 +159,36 @@
:loading="testingConnection"
:disabled="!isConfigValid"
@click="testConnection"
+ round
>
+
+
+
测试连接
-
+
+
+
+
重置
+
+
+
+
+
+ {{ connectionStatus.message }}
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/MarketTimeDisplay.vue b/frontend/src/components/MarketTimeDisplay.vue
index 2b9e48a..3acda4a 100644
--- a/frontend/src/components/MarketTimeDisplay.vue
+++ b/frontend/src/components/MarketTimeDisplay.vue
@@ -13,9 +13,16 @@
A股市场
-
- {{ marketInfo.cnMarket.isOpen ? '交易中' : '已休市' }}
-
+
+
+
+ 交易中
+
+
+
+ 已休市
+
+
{{ marketInfo.cnMarket.nextTime }}
@@ -24,9 +31,16 @@
港股市场
-
- {{ marketInfo.hkMarket.isOpen ? '交易中' : '已休市' }}
-
+
+
+
+ 交易中
+
+
+
+ 已休市
+
+
{{ marketInfo.hkMarket.nextTime }}
@@ -35,9 +49,16 @@
美股市场
-
- {{ marketInfo.usMarket.isOpen ? '交易中' : '已休市' }}
-
+
+
+
+ 交易中
+
+
+
+ 已休市
+
+
{{ marketInfo.usMarket.nextTime }}
@@ -47,7 +68,11 @@
diff --git a/frontend/src/components/StockSearch.vue b/frontend/src/components/StockSearch.vue
index ed7eed7..44abb46 100644
--- a/frontend/src/components/StockSearch.vue
+++ b/frontend/src/components/StockSearch.vue
@@ -68,7 +68,7 @@ const searchKeyword = ref('');
const results = ref([]);
const loading = ref(false);
const showResults = ref(false);
-const searchInputRef = ref(null);
+const searchInputRef = ref(null);
// 创建防抖搜索函数
const debouncedSearch = debounce(async (keyword: string) => {
@@ -83,7 +83,9 @@ const debouncedSearch = debounce(async (keyword: string) => {
try {
if (props.marketType === 'US') {
// 美股搜索
- results.value = await apiService.searchUsStocks(keyword);
+ const searchResults = await apiService.searchUsStocks(keyword);
+ // 限制只显示前10个结果
+ results.value = searchResults.slice(0, 10);
} else {
// 其他市场搜索 (后端需要实现对应的接口)
results.value = [];
@@ -128,7 +130,7 @@ function formatMarketValue(value: number): string {
function handleClickOutside(event: MouseEvent) {
if (
searchInputRef.value &&
- !searchInputRef.value.contains(event.target as Node)
+ !searchInputRef.value.$el.contains(event.target as Node)
) {
showResults.value = false;
}
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
index e03fc18..e3a485c 100644
--- a/frontend/src/main.ts
+++ b/frontend/src/main.ts
@@ -1,6 +1,8 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
+import router from './router'
const app = createApp(App)
+app.use(router)
app.mount('#app')
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
new file mode 100644
index 0000000..e2bd7f0
--- /dev/null
+++ b/frontend/src/router/index.ts
@@ -0,0 +1,89 @@
+import { createRouter, createWebHistory } from 'vue-router';
+import type { RouteRecordRaw } from 'vue-router';
+import { apiService } from '@/services/api';
+import StockAnalysisApp from '@/components/StockAnalysisApp.vue';
+import LoginPage from '@/components/LoginPage.vue';
+
+const routes: Array = [
+ {
+ path: '/',
+ name: 'Home',
+ component: StockAnalysisApp,
+ meta: { requiresAuth: true }
+ },
+ {
+ path: '/login',
+ name: 'Login',
+ component: LoginPage,
+ meta: { requiresAuth: false }
+ },
+ {
+ path: '/:pathMatch(.*)*',
+ redirect: '/'
+ }
+];
+
+const router = createRouter({
+ history: createWebHistory(),
+ routes
+});
+
+// 全局前置守卫
+router.beforeEach(async (to, from, next) => {
+ console.log(`路由跳转: 从 ${from.path} 到 ${to.path}`);
+
+ // 如果已经在登录页面,直接通过
+ if (to.path === '/login') {
+ next();
+ return;
+ }
+
+ // 检查路由是否需要认证
+ if (to.matched.some(record => record.meta.requiresAuth)) {
+ console.log('当前路由需要认证');
+
+ try {
+ // 先检查系统是否需要登录
+ const requireLogin = await apiService.checkNeedLogin();
+ console.log('系统是否需要登录:', requireLogin);
+
+ if (!requireLogin) {
+ // 系统不需要登录,直接通过
+ console.log('系统不需要登录,允许访问');
+ next();
+ return;
+ }
+
+ // 系统需要登录,检查本地是否有token
+ const token = localStorage.getItem('token');
+ if (!token) {
+ console.log('本地没有token,跳转到登录页');
+ next({ name: 'Login' });
+ return;
+ }
+
+ const isAuthenticated = await apiService.checkAuth();
+ console.log('认证检查结果:', isAuthenticated);
+
+ if (!isAuthenticated) {
+ // 未登录,重定向到登录页
+ console.log('认证失败,跳转到登录页');
+ next({ name: 'Login' });
+ } else {
+ // 已登录,允许访问
+ console.log('认证成功,允许访问');
+ next();
+ }
+ } catch (error) {
+ console.error('认证检查失败:', error);
+ // 认证检查失败,重定向到登录页
+ next({ name: 'Login' });
+ }
+ } else {
+ // 不需要认证的路由,直接访问
+ console.log('当前路由不需要认证,直接访问');
+ next();
+ }
+});
+
+export default router;
\ No newline at end of file
diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts
index 6c38f9a..082f02f 100644
--- a/frontend/src/services/api.ts
+++ b/frontend/src/services/api.ts
@@ -1,13 +1,88 @@
import axios from 'axios';
-import type { AnalyzeRequest, TestApiRequest, TestApiResponse, SearchResult } from '@/types';
+import type { AnalyzeRequest, TestApiRequest, TestApiResponse, SearchResult, LoginRequest, LoginResponse } from '@/types';
-// 在开发环境中前缀为空,因为已经在vite.config.ts中配置了代理
+// 在开发环境中使用完整URL
const API_PREFIX = '';
+// 创建axios实例
+const axiosInstance = axios.create({
+ baseURL: API_PREFIX
+});
+
+// 请求拦截器,添加token
+axiosInstance.interceptors.request.use(
+ (config) => {
+ const token = localStorage.getItem('token');
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+ return config;
+ },
+ (error) => {
+ return Promise.reject(error);
+ }
+);
+
+// 响应拦截器,处理401错误
+axiosInstance.interceptors.response.use(
+ (response) => {
+ return response;
+ },
+ (error) => {
+ if (error.response && error.response.status === 401) {
+ // 清除token
+ localStorage.removeItem('token');
+ // 不要在这里跳转,避免循环重定向
+ }
+ return Promise.reject(error);
+ }
+);
+
export const apiService = {
+ // 用户登录
+ login: async (request: LoginRequest): Promise => {
+ try {
+ const response = await axios.post(`${API_PREFIX}/login`, request);
+ if (response.data.access_token) {
+ localStorage.setItem('token', response.data.access_token);
+ }
+ return response.data;
+ } catch (error: any) {
+ if (error.response) {
+ return {
+ success: false,
+ message: error.response.data.detail || '登录失败',
+ };
+ }
+ return {
+ success: false,
+ message: error.message || '登录失败'
+ };
+ }
+ },
+
+ // 检查认证状态
+ checkAuth: async (): Promise => {
+ try {
+ const response = await axiosInstance.get(`${API_PREFIX}/check_auth`);
+ return response.data.authenticated === true;
+ } catch (error) {
+ // 认证失败,清除token
+ localStorage.removeItem('token');
+ return false;
+ }
+ },
+
+ // 登出
+ logout: () => {
+ localStorage.removeItem('token');
+ // 简化登出逻辑
+ window.location.href = '/login';
+ },
+
// 分析股票
analyzeStocks: async (request: AnalyzeRequest) => {
- return axios.post(`${API_PREFIX}/analyze`, request, {
+ return axiosInstance.post(`${API_PREFIX}/analyze`, request, {
responseType: 'stream'
});
},
@@ -15,7 +90,7 @@ export const apiService = {
// 测试API连接
testApiConnection: async (request: TestApiRequest): Promise => {
try {
- const response = await axios.post(`${API_PREFIX}/test_api_connection`, request);
+ const response = await axiosInstance.post(`${API_PREFIX}/test_api_connection`, request);
return response.data;
} catch (error: any) {
if (error.response) {
@@ -31,7 +106,7 @@ export const apiService = {
// 搜索美股
searchUsStocks: async (keyword: string): Promise => {
try {
- const response = await axios.get(`${API_PREFIX}/search_us_stocks`, {
+ const response = await axiosInstance.get(`${API_PREFIX}/search_us_stocks`, {
params: { keyword }
});
return response.data.results || [];
@@ -55,5 +130,17 @@ export const apiService = {
default_api_timeout: '60'
};
}
+ },
+
+ // 检查是否需要登录
+ checkNeedLogin: async (): Promise => {
+ try {
+ const response = await axios.get(`${API_PREFIX}/need_login`);
+ return response.data.require_login;
+ } catch (error) {
+ console.error('检查是否需要登录时出错:', error);
+ // 默认为需要登录,确保安全
+ return true;
+ }
}
};
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
index d73a9f9..a6f75ff 100644
--- a/frontend/src/types/index.ts
+++ b/frontend/src/types/index.ts
@@ -7,6 +7,18 @@ export interface ApiConfig {
saveApiConfig: boolean;
}
+// 登录相关类型
+export interface LoginRequest {
+ password: string;
+}
+
+export interface LoginResponse {
+ access_token?: string;
+ token_type?: string;
+ success?: boolean;
+ message?: string;
+}
+
export interface StockInfo {
code: string;
name: string;
diff --git a/frontend/src/utils/stockValidator.ts b/frontend/src/utils/stockValidator.ts
new file mode 100644
index 0000000..edbbc58
--- /dev/null
+++ b/frontend/src/utils/stockValidator.ts
@@ -0,0 +1,172 @@
+ /**
+ * 股票代码验证工具
+ * 用于验证不同市场类型的股票代码格式
+ */
+
+/**
+ * 市场类型枚举
+ */
+export enum MarketType {
+ A = 'A', // A股
+ HK = 'HK', // 港股
+ US = 'US', // 美股
+ ETF = 'ETF', // ETF基金
+ LOF = 'LOF' // LOF基金
+}
+
+/**
+ * 验证A股股票代码
+ * @param code 股票代码
+ * @returns 是否为有效的A股代码
+ */
+export const validateAStock = (code: string): boolean => {
+ // 上海证券交易所股票代码以6开头,6位数字
+ // 深圳证券交易所股票代码以0或3开头,6位数字
+ // 科创板股票代码以688开头,6位数字
+ // 北京证券交易所股票代码以8开头,一般为5位数字(如80XXX)
+ // 注意:中小板、创业板代码格式已合并处理
+
+ // 验证上海证券交易所(以6开头的6位数字)
+ if (code.startsWith('6') && /^\d{6}$/.test(code)) {
+ return true;
+ }
+
+ // 验证深圳证券交易所(以0或3开头的6位数字)
+ if ((code.startsWith('0') || code.startsWith('3')) && /^\d{6}$/.test(code)) {
+ return true;
+ }
+
+ // 验证科创板(以688开头的6位数字)
+ if (code.startsWith('688') && /^\d{6}$/.test(code)) {
+ return true;
+ }
+
+ // 验证北京证券交易所(以8开头的股票)
+ // 北交所股票一般是5位数字,格式为8xxxx
+ if (code.startsWith('8') && /^\d{5}$/.test(code)) {
+ return true;
+ }
+
+ return false;
+};
+
+/**
+ * 验证港股股票代码
+ * @param code 股票代码
+ * @returns 是否为有效的港股代码
+ */
+export const validateHKStock = (code: string): boolean => {
+ // 港股通常是5位数字
+ return /^\d{5}$/.test(code);
+};
+
+/**
+ * 验证美股股票代码
+ * @param code 股票代码
+ * @returns 是否为有效的美股代码
+ */
+export const validateUSStock = (code: string): boolean => {
+ // 美股代码通常由字母组成,长度在1-5之间
+ return /^[A-Za-z]{1,5}$/.test(code);
+};
+
+/**
+ * 验证ETF/LOF基金代码
+ * @param code 基金代码
+ * @returns 是否为有效的基金代码
+ */
+export const validateFund = (code: string): boolean => {
+ // 基金代码通常为6位数字
+ return /^\d{6}$/.test(code);
+};
+
+/**
+ * 根据市场类型验证股票代码
+ * @param code 股票代码
+ * @param marketType 市场类型
+ * @returns 包含验证结果和错误信息的对象
+ */
+export const validateStockCode = (
+ code: string,
+ marketType: MarketType
+): { valid: boolean; errorMessage?: string } => {
+
+ if (!code || code.trim() === '') {
+ return {
+ valid: false,
+ errorMessage: '股票代码不能为空'
+ };
+ }
+
+ switch (marketType) {
+ case MarketType.A:
+ if (!validateAStock(code)) {
+ return {
+ valid: false,
+ errorMessage: `无效的A股股票代码格式: ${code}。A股代码应以0、3、6、688或8开头,且为6位数字或5位数字`
+ };
+ }
+ break;
+
+ case MarketType.HK:
+ if (!validateHKStock(code)) {
+ return {
+ valid: false,
+ errorMessage: `无效的港股代码格式: ${code}。港股代码应为5位数字`
+ };
+ }
+ break;
+
+ case MarketType.US:
+ if (!validateUSStock(code)) {
+ return {
+ valid: false,
+ errorMessage: `无效的美股代码格式: ${code}。美股代码应为1-5位字母`
+ };
+ }
+ break;
+
+ case MarketType.ETF:
+ case MarketType.LOF:
+ if (!validateFund(code)) {
+ return {
+ valid: false,
+ errorMessage: `无效的${marketType}基金代码格式: ${code}。基金代码应为6位数字`
+ };
+ }
+ break;
+
+ default:
+ return {
+ valid: false,
+ errorMessage: `不支持的市场类型: ${marketType}`
+ };
+ }
+
+ return { valid: true };
+};
+
+/**
+ * 批量验证多个股票代码
+ * @param codes 股票代码数组
+ * @param marketType 市场类型
+ * @returns 包含所有无效代码及其错误信息的数组
+ */
+export const validateMultipleStockCodes = (
+ codes: string[],
+ marketType: MarketType
+): { code: string; errorMessage: string }[] => {
+ const invalidCodes: { code: string; errorMessage: string }[] = [];
+
+ for (const code of codes) {
+ const result = validateStockCode(code, marketType);
+ if (!result.valid && result.errorMessage) {
+ invalidCodes.push({
+ code,
+ errorMessage: result.errorMessage
+ });
+ }
+ }
+
+ return invalidCodes;
+};
\ No newline at end of file
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 181d624..8009f22 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -16,6 +16,7 @@ export default defineConfig({
},
},
server: {
+ cors: true,
proxy: {
'/api': {
target: 'http://127.0.0.1:8888',
@@ -38,6 +39,30 @@ export default defineConfig({
target: 'http://127.0.0.1:8888',
changeOrigin: true,
},
+ '/login': {
+ target: 'http://127.0.0.1:8888',
+ changeOrigin: true,
+ },
+ '/check_auth': {
+ target: 'http://127.0.0.1:8888',
+ changeOrigin: true,
+ },
+ '/need_login': {
+ target: 'http://127.0.0.1:8888',
+ changeOrigin: true,
+ },
+ '/us_stock_detail': {
+ target: 'http://127.0.0.1:8888',
+ changeOrigin: true,
+ },
+ '/fund_detail': {
+ target: 'http://127.0.0.1:8888',
+ changeOrigin: true,
+ },
+ '/search_funds': {
+ target: 'http://127.0.0.1:8888',
+ changeOrigin: true,
+ },
},
},
})
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 5584045..2adc86a 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -167,6 +167,30 @@
resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz"
integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==
+"@isaacs/cliui@^8.0.2":
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
+ integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==
+ dependencies:
+ string-width "^5.1.2"
+ string-width-cjs "npm:string-width@^4.2.0"
+ strip-ansi "^7.0.1"
+ strip-ansi-cjs "npm:strip-ansi@^6.0.1"
+ wrap-ansi "^8.1.0"
+ wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
+
+"@isaacs/fs-minipass@^4.0.0":
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32"
+ integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==
+ dependencies:
+ minipass "^7.0.4"
+
+"@isaacs/string-locale-compare@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b"
+ integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==
+
"@jridgewell/sourcemap-codec@^1.5.0":
version "1.5.0"
resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
@@ -177,6 +201,180 @@
resolved "https://registry.npmmirror.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz"
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
+"@npmcli/agent@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-3.0.0.tgz#1685b1fbd4a1b7bb4f930cbb68ce801edfe7aa44"
+ integrity sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==
+ dependencies:
+ agent-base "^7.1.0"
+ http-proxy-agent "^7.0.0"
+ https-proxy-agent "^7.0.1"
+ lru-cache "^10.0.1"
+ socks-proxy-agent "^8.0.3"
+
+"@npmcli/arborist@^9.0.1":
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-9.0.1.tgz#5574221af060d6192e1d73f4c3af5f9e3f7b502e"
+ integrity sha512-m00iV8hgbmli0IMf4Os+UmEq5JRTgqOHR+x5h07O7mO/60q5hLaYwZUMamJ73wlMG68c3WB8ZloOxON/knF5vg==
+ dependencies:
+ "@isaacs/string-locale-compare" "^1.1.0"
+ "@npmcli/fs" "^4.0.0"
+ "@npmcli/installed-package-contents" "^3.0.0"
+ "@npmcli/map-workspaces" "^4.0.1"
+ "@npmcli/metavuln-calculator" "^9.0.0"
+ "@npmcli/name-from-folder" "^3.0.0"
+ "@npmcli/node-gyp" "^4.0.0"
+ "@npmcli/package-json" "^6.0.1"
+ "@npmcli/query" "^4.0.0"
+ "@npmcli/redact" "^3.0.0"
+ "@npmcli/run-script" "^9.0.1"
+ bin-links "^5.0.0"
+ cacache "^19.0.1"
+ common-ancestor-path "^1.0.1"
+ hosted-git-info "^8.0.0"
+ json-stringify-nice "^1.1.4"
+ lru-cache "^10.2.2"
+ minimatch "^9.0.4"
+ nopt "^8.0.0"
+ npm-install-checks "^7.1.0"
+ npm-package-arg "^12.0.0"
+ npm-pick-manifest "^10.0.0"
+ npm-registry-fetch "^18.0.1"
+ pacote "^21.0.0"
+ parse-conflict-json "^4.0.0"
+ proc-log "^5.0.0"
+ proggy "^3.0.0"
+ promise-all-reject-late "^1.0.0"
+ promise-call-limit "^3.0.1"
+ read-package-json-fast "^4.0.0"
+ semver "^7.3.7"
+ ssri "^12.0.0"
+ treeverse "^3.0.0"
+ walk-up-path "^4.0.0"
+
+"@npmcli/config@^10.1.0":
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/@npmcli/config/-/config-10.1.0.tgz#fe6a0bd26da87ddc92c85e0443b8ef3855c359c0"
+ integrity sha512-ygyCJATTr+xmuQHiX28adNT3tbDcWIfHQggNtLL2ykSyH4VCF5YeG4SilZaYIxf+72GZA6CJpESaDyhq9Boozg==
+ dependencies:
+ "@npmcli/map-workspaces" "^4.0.1"
+ "@npmcli/package-json" "^6.0.1"
+ ci-info "^4.0.0"
+ ini "^5.0.0"
+ nopt "^8.1.0"
+ proc-log "^5.0.0"
+ semver "^7.3.5"
+ walk-up-path "^4.0.0"
+
+"@npmcli/fs@^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-4.0.0.tgz#a1eb1aeddefd2a4a347eca0fab30bc62c0e1c0f2"
+ integrity sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==
+ dependencies:
+ semver "^7.3.5"
+
+"@npmcli/git@^6.0.0", "@npmcli/git@^6.0.1":
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-6.0.3.tgz#966cbb228514372877de5244db285b199836f3aa"
+ integrity sha512-GUYESQlxZRAdhs3UhbB6pVRNUELQOHXwK9ruDkwmCv2aZ5y0SApQzUJCg02p3A7Ue2J5hxvlk1YI53c00NmRyQ==
+ dependencies:
+ "@npmcli/promise-spawn" "^8.0.0"
+ ini "^5.0.0"
+ lru-cache "^10.0.1"
+ npm-pick-manifest "^10.0.0"
+ proc-log "^5.0.0"
+ promise-retry "^2.0.1"
+ semver "^7.3.5"
+ which "^5.0.0"
+
+"@npmcli/installed-package-contents@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-3.0.0.tgz#2c1170ff4f70f68af125e2842e1853a93223e4d1"
+ integrity sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==
+ dependencies:
+ npm-bundled "^4.0.0"
+ npm-normalize-package-bin "^4.0.0"
+
+"@npmcli/map-workspaces@^4.0.1", "@npmcli/map-workspaces@^4.0.2":
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-4.0.2.tgz#d02c5508bf55624f60aaa58fe413748a5c773802"
+ integrity sha512-mnuMuibEbkaBTYj9HQ3dMe6L0ylYW+s/gfz7tBDMFY/la0w9Kf44P9aLn4/+/t3aTR3YUHKoT6XQL9rlicIe3Q==
+ dependencies:
+ "@npmcli/name-from-folder" "^3.0.0"
+ "@npmcli/package-json" "^6.0.0"
+ glob "^10.2.2"
+ minimatch "^9.0.0"
+
+"@npmcli/metavuln-calculator@^9.0.0":
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-9.0.0.tgz#7e54d7c9f33999fde0ad2998904e0edd1627e26d"
+ integrity sha512-znLKqdy1ZEGNK3VB9j/RzGyb/P0BJb3fGpvEbHIAyBAXsps2l1ce8SVHfsGAFLl9s8072PxafqTn7RC8wSnQPg==
+ dependencies:
+ cacache "^19.0.0"
+ json-parse-even-better-errors "^4.0.0"
+ pacote "^21.0.0"
+ proc-log "^5.0.0"
+ semver "^7.3.5"
+
+"@npmcli/name-from-folder@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-3.0.0.tgz#ed49b18d16b954149f31240e16630cfec511cd57"
+ integrity sha512-61cDL8LUc9y80fXn+lir+iVt8IS0xHqEKwPu/5jCjxQTVoSCmkXvw4vbMrzAMtmghz3/AkiBjhHkDKUH+kf7kA==
+
+"@npmcli/node-gyp@^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-4.0.0.tgz#01f900bae62f0f27f9a5a127b40d443ddfb9d4c6"
+ integrity sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA==
+
+"@npmcli/package-json@^6.0.0", "@npmcli/package-json@^6.0.1", "@npmcli/package-json@^6.1.0", "@npmcli/package-json@^6.1.1":
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-6.1.1.tgz#78ff92d138fdcb85f31cab907455d5db96d017cb"
+ integrity sha512-d5qimadRAUCO4A/Txw71VM7UrRZzV+NPclxz/dc+M6B2oYwjWTjqh8HA/sGQgs9VZuJ6I/P7XIAlJvgrl27ZOw==
+ dependencies:
+ "@npmcli/git" "^6.0.0"
+ glob "^10.2.2"
+ hosted-git-info "^8.0.0"
+ json-parse-even-better-errors "^4.0.0"
+ proc-log "^5.0.0"
+ semver "^7.5.3"
+ validate-npm-package-license "^3.0.4"
+
+"@npmcli/promise-spawn@^8.0.0", "@npmcli/promise-spawn@^8.0.2":
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-8.0.2.tgz#053688f8bc2b4ecc036d2d52c691fd82af58ea5e"
+ integrity sha512-/bNJhjc+o6qL+Dwz/bqfTQClkEO5nTQ1ZEcdCkAQjhkZMHIh22LPG7fNh1enJP1NKWDqYiiABnjFCY7E0zHYtQ==
+ dependencies:
+ which "^5.0.0"
+
+"@npmcli/query@^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@npmcli/query/-/query-4.0.0.tgz#7a2470254f5a12a1499d2296a7343043c7847568"
+ integrity sha512-3pPbese0fbCiFJ/7/X1GBgxAKYFE8sxBddA7GtuRmOgNseH4YbGsXJ807Ig3AEwNITjDUISHglvy89cyDJnAwA==
+ dependencies:
+ postcss-selector-parser "^6.1.2"
+
+"@npmcli/redact@^3.0.0", "@npmcli/redact@^3.1.1":
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/@npmcli/redact/-/redact-3.1.1.tgz#ac295c148d01c70a5a006d2e162388b3cef15195"
+ integrity sha512-3Hc2KGIkrvJWJqTbvueXzBeZlmvoOxc2jyX00yzr3+sNFquJg0N8hH4SAPLPVrkWIRQICVpVgjrss971awXVnA==
+
+"@npmcli/run-script@^9.0.0", "@npmcli/run-script@^9.0.1":
+ version "9.0.2"
+ resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-9.0.2.tgz#621f993d59bae770104a5b655a38c6579d5ce6be"
+ integrity sha512-cJXiUlycdizQwvqE1iaAb4VRUM3RX09/8q46zjvy+ct9GhfZRWd7jXYVc1tn/CfRlGPVkX/u4sstRlepsm7hfw==
+ dependencies:
+ "@npmcli/node-gyp" "^4.0.0"
+ "@npmcli/package-json" "^6.0.0"
+ "@npmcli/promise-spawn" "^8.0.0"
+ node-gyp "^11.0.0"
+ proc-log "^5.0.0"
+ which "^5.0.0"
+
+"@pkgjs/parseargs@^0.11.0":
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
+ integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
+
"@rollup/rollup-android-arm-eabi@4.34.9":
version "4.34.9"
resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz#661a45a4709c70e59e596ec78daa9cb8b8d27604"
@@ -272,6 +470,65 @@
resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz"
integrity sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==
+"@sigstore/bundle@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-3.1.0.tgz#74f8f3787148400ddd364be8a9a9212174c66646"
+ integrity sha512-Mm1E3/CmDDCz3nDhFKTuYdB47EdRFRQMOE/EAbiG1MJW77/w1b3P7Qx7JSrVJs8PfwOLOVcKQCHErIwCTyPbag==
+ dependencies:
+ "@sigstore/protobuf-specs" "^0.4.0"
+
+"@sigstore/core@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@sigstore/core/-/core-2.0.0.tgz#f888a8e4c8fdaa27848514a281920b6fd8eca955"
+ integrity sha512-nYxaSb/MtlSI+JWcwTHQxyNmWeWrUXJJ/G4liLrGG7+tS4vAz6LF3xRXqLH6wPIVUoZQel2Fs4ddLx4NCpiIYg==
+
+"@sigstore/protobuf-specs@^0.4.0":
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.4.0.tgz#7524509d93efcb14e77d0bc34c43a1ae85f851c5"
+ integrity sha512-o09cLSIq9EKyRXwryWDOJagkml9XgQCoCSRjHOnHLnvsivaW7Qznzz6yjfV7PHJHhIvyp8OH7OX8w0Dc5bQK7A==
+
+"@sigstore/sign@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@sigstore/sign/-/sign-3.1.0.tgz#5d098d4d2b59a279e9ac9b51c794104cda0c649e"
+ integrity sha512-knzjmaOHOov1Ur7N/z4B1oPqZ0QX5geUfhrVaqVlu+hl0EAoL4o+l0MSULINcD5GCWe3Z0+YJO8ues6vFlW0Yw==
+ dependencies:
+ "@sigstore/bundle" "^3.1.0"
+ "@sigstore/core" "^2.0.0"
+ "@sigstore/protobuf-specs" "^0.4.0"
+ make-fetch-happen "^14.0.2"
+ proc-log "^5.0.0"
+ promise-retry "^2.0.1"
+
+"@sigstore/tuf@^3.0.0", "@sigstore/tuf@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@sigstore/tuf/-/tuf-3.1.0.tgz#f533ac8ac572c9f7e36f5e08f1effa6b2244f55a"
+ integrity sha512-suVMQEA+sKdOz5hwP9qNcEjX6B45R+hFFr4LAWzbRc5O+U2IInwvay/bpG5a4s+qR35P/JK/PiKiRGjfuLy1IA==
+ dependencies:
+ "@sigstore/protobuf-specs" "^0.4.0"
+ tuf-js "^3.0.1"
+
+"@sigstore/verify@^2.1.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@sigstore/verify/-/verify-2.1.0.tgz#63e31dd69b678ed6d98cbfdc6d6c104b82d0905c"
+ integrity sha512-kAAM06ca4CzhvjIZdONAL9+MLppW3K48wOFy1TbuaWFW/OMfl8JuTgW0Bm02JB1WJGT/ET2eqav0KTEKmxqkIA==
+ dependencies:
+ "@sigstore/bundle" "^3.1.0"
+ "@sigstore/core" "^2.0.0"
+ "@sigstore/protobuf-specs" "^0.4.0"
+
+"@tufjs/canonical-json@2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz#a52f61a3d7374833fca945b2549bc30a2dd40d0a"
+ integrity sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==
+
+"@tufjs/models@3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@tufjs/models/-/models-3.0.1.tgz#5aebb782ebb9e06f071ae7831c1f35b462b0319c"
+ integrity sha512-UUYHISyhCU3ZgN8yaear3cGATHb3SMuKHsQ/nVbHXcmnBf+LzQ/cQfhNG+rfaSHgqGKNEm2cOCLVLELStUQ1JA==
+ dependencies:
+ "@tufjs/canonical-json" "2.0.0"
+ minimatch "^9.0.5"
+
"@types/estree@1.0.6":
version "1.0.6"
resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz"
@@ -392,6 +649,11 @@
de-indent "^1.0.2"
he "^1.2.0"
+"@vue/devtools-api@^6.6.4":
+ version "6.6.4"
+ resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343"
+ integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==
+
"@vue/language-core@2.2.8":
version "2.2.8"
resolved "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.2.8.tgz"
@@ -471,11 +733,53 @@
dependencies:
vue "^3.5.13"
+abbrev@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-3.0.0.tgz#c29a6337e167ac61a84b41b80461b29c5c271a27"
+ integrity sha512-+/kfrslGQ7TNV2ecmQwMJj/B65g5KVq1/L3SGVZ3tCYGqlzFuFCGBZJtMP99wH3NpEUyAjn0zPdPUg0D+DwrOA==
+
+agent-base@^7.1.0, agent-base@^7.1.2:
+ version "7.1.3"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1"
+ integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==
+
alien-signals@^1.0.3:
version "1.0.4"
resolved "https://registry.npmmirror.com/alien-signals/-/alien-signals-1.0.4.tgz"
integrity sha512-DJqqQD3XcsaQcQ1s+iE2jDUZmmQpXwHiR6fCAim/w87luaW+vmLY8fMlrdkmRwzaFXhkxf3rqPCR59tKVv1MDw==
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-regex@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654"
+ integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
+
+ansi-styles@^4.0.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+ansi-styles@^6.1.0:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
+ integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
+
+aproba@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
+ integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
+
+archy@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40"
+ integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==
+
async-validator@^4.2.5:
version "4.2.5"
resolved "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz"
@@ -500,6 +804,22 @@ balanced-match@^1.0.0:
resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+bin-links@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-5.0.0.tgz#2b0605b62dd5e1ddab3b92a3c4e24221cae06cca"
+ integrity sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA==
+ dependencies:
+ cmd-shim "^7.0.0"
+ npm-normalize-package-bin "^4.0.0"
+ proc-log "^5.0.0"
+ read-cmd-shim "^5.0.0"
+ write-file-atomic "^6.0.0"
+
+binary-extensions@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-3.0.0.tgz#14ce687f80e3ebab2a2fb78bb8611584c29f12c3"
+ integrity sha512-X0RfwMgXPEesg6PCXzytQZt9Unh9gtc4SfeTNJvKifUL//Oegcc/Yf31z6hThNZ8dnD3Ir3wkHVN0eWrTvP5ww==
+
brace-expansion@^2.0.1:
version "2.0.1"
resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz"
@@ -507,6 +827,24 @@ brace-expansion@^2.0.1:
dependencies:
balanced-match "^1.0.0"
+cacache@^19.0.0, cacache@^19.0.1:
+ version "19.0.1"
+ resolved "https://registry.yarnpkg.com/cacache/-/cacache-19.0.1.tgz#3370cc28a758434c85c2585008bd5bdcff17d6cd"
+ integrity sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==
+ dependencies:
+ "@npmcli/fs" "^4.0.0"
+ fs-minipass "^3.0.0"
+ glob "^10.2.2"
+ lru-cache "^10.0.1"
+ minipass "^7.0.3"
+ minipass-collect "^2.0.1"
+ minipass-flush "^1.0.5"
+ minipass-pipeline "^1.2.4"
+ p-map "^7.0.2"
+ ssri "^12.0.0"
+ tar "^7.4.3"
+ unique-filename "^4.0.0"
+
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz"
@@ -515,6 +853,58 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
es-errors "^1.3.0"
function-bind "^1.1.2"
+chalk@^5.4.1:
+ version "5.4.1"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8"
+ integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==
+
+chownr@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
+ integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
+
+chownr@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4"
+ integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==
+
+ci-info@^4.0.0, ci-info@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.1.0.tgz#92319d2fa29d2620180ea5afed31f589bc98cf83"
+ integrity sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==
+
+cidr-regex@^4.1.1:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-4.1.3.tgz#df94af8ac16fc2e0791e2824693b957ff1ac4d3e"
+ integrity sha512-86M1y3ZeQvpZkZejQCcS+IaSWjlDUC+ORP0peScQ4uEUFCZ8bEQVz7NlJHqysoUb6w3zCjx4Mq/8/2RHhMwHYw==
+ dependencies:
+ ip-regex "^5.0.0"
+
+cli-columns@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/cli-columns/-/cli-columns-4.0.0.tgz#9fe4d65975238d55218c41bd2ed296a7fa555646"
+ integrity sha512-XW2Vg+w+L9on9wtwKpyzluIPCWXjaBahI7mTcYjx+BVIYD9c3yqcv/yKC7CmdCZat4rq2yiE1UMSJC5ivKfMtQ==
+ dependencies:
+ string-width "^4.2.3"
+ strip-ansi "^6.0.1"
+
+cmd-shim@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-7.0.0.tgz#23bcbf69fff52172f7e7c02374e18fb215826d95"
+ integrity sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz"
@@ -522,6 +912,20 @@ combined-stream@^1.0.8:
dependencies:
delayed-stream "~1.0.0"
+common-ancestor-path@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7"
+ integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==
+
+cross-spawn@^7.0.6:
+ version "7.0.6"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
+ integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
css-render@^0.15.10, css-render@^0.15.14:
version "0.15.14"
resolved "https://registry.npmmirror.com/css-render/-/css-render-0.15.14.tgz"
@@ -530,6 +934,11 @@ css-render@^0.15.10, css-render@^0.15.14:
"@emotion/hash" "~0.8.0"
csstype "~3.0.5"
+cssesc@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+ integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
csstype@^3.1.3:
version "3.1.3"
resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz"
@@ -555,11 +964,23 @@ de-indent@^1.0.2:
resolved "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz"
integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
+debug@4, debug@^4.3.4, debug@^4.3.6:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
+ integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
+ dependencies:
+ ms "^2.1.3"
+
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+diff@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-7.0.0.tgz#3fb34d387cd76d803f6eebea67b921dab0182a9a"
+ integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==
+
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz"
@@ -569,11 +990,43 @@ dunder-proto@^1.0.1:
es-errors "^1.3.0"
gopd "^1.2.0"
+eastasianwidth@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
+ integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+emoji-regex@^9.2.2:
+ version "9.2.2"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
+ integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
+
+encoding@^0.1.13:
+ version "0.1.13"
+ resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
+ integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
+ dependencies:
+ iconv-lite "^0.6.2"
+
entities@^4.5.0:
version "4.5.0"
resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+env-paths@^2.2.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
+ integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
+
+err-code@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
+ integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
+
es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz"
@@ -642,11 +1095,29 @@ evtd@^0.2.2, evtd@^0.2.4:
resolved "https://registry.npmmirror.com/evtd/-/evtd-0.2.4.tgz"
integrity sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==
+exponential-backoff@^3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.2.tgz#a8f26adb96bf78e8cd8ad1037928d5e5c0679d91"
+ integrity sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==
+
+fastest-levenshtein@^1.0.16:
+ version "1.0.16"
+ resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
+ integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
+
follow-redirects@^1.15.6:
version "1.15.9"
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz"
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
+foreground-child@^3.1.0:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
+ integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==
+ dependencies:
+ cross-spawn "^7.0.6"
+ signal-exit "^4.0.1"
+
form-data@^4.0.0:
version "4.0.2"
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.2.tgz"
@@ -657,6 +1128,20 @@ form-data@^4.0.0:
es-set-tostringtag "^2.1.0"
mime-types "^2.1.12"
+fs-minipass@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
+ integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
+ dependencies:
+ minipass "^3.0.0"
+
+fs-minipass@^3.0.0, fs-minipass@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54"
+ integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==
+ dependencies:
+ minipass "^7.0.3"
+
fsevents@~2.3.2, fsevents@~2.3.3:
version "2.3.3"
resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
@@ -691,11 +1176,28 @@ get-proto@^1.0.1:
dunder-proto "^1.0.1"
es-object-atoms "^1.0.0"
+glob@^10.2.2, glob@^10.3.10, glob@^10.3.7, glob@^10.4.5:
+ version "10.4.5"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
+ integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
+ dependencies:
+ foreground-child "^3.1.0"
+ jackspeak "^3.1.2"
+ minimatch "^9.0.4"
+ minipass "^7.1.2"
+ package-json-from-dist "^1.0.0"
+ path-scurry "^1.11.1"
+
gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
+graceful-fs@^4.2.11, graceful-fs@^4.2.6:
+ version "4.2.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
has-symbols@^1.0.3, has-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz"
@@ -725,6 +1227,249 @@ highlight.js@^11.8.0:
resolved "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz"
integrity sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==
+hosted-git-info@^8.0.0, hosted-git-info@^8.0.2:
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-8.0.2.tgz#5bd7d8b5395616e41cc0d6578381a32f669b14b2"
+ integrity sha512-sYKnA7eGln5ov8T8gnYlkSOxFJvywzEx9BueN6xo/GKO8PGiI6uK6xx+DIGe45T3bdVjLAQDQW1aicT8z8JwQg==
+ dependencies:
+ lru-cache "^10.0.1"
+
+http-cache-semantics@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
+ integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
+
+http-proxy-agent@^7.0.0:
+ version "7.0.2"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
+ integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==
+ dependencies:
+ agent-base "^7.1.0"
+ debug "^4.3.4"
+
+https-proxy-agent@^7.0.1:
+ version "7.0.6"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9"
+ integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==
+ dependencies:
+ agent-base "^7.1.2"
+ debug "4"
+
+iconv-lite@^0.6.2:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
+ integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3.0.0"
+
+ignore-walk@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-7.0.0.tgz#8350e475cf4375969c12eb49618b3fd9cca6704f"
+ integrity sha512-T4gbf83A4NH95zvhVYZc+qWocBBGlpzUXLPGurJggw/WIOwicfXJChLDP/iBZnN5WqROSu5Bm3hhle4z8a8YGQ==
+ dependencies:
+ minimatch "^9.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+ini@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-5.0.0.tgz#a7a4615339843d9a8ccc2d85c9d81cf93ffbc638"
+ integrity sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==
+
+init-package-json@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-8.0.0.tgz#feaa2e5e949c68bec3bcfb25c1b7075ffe6fc88b"
+ integrity sha512-zKgxfaGt6Zzi8VBSInOK0CYDigA9gzDCWPnSzGIoUlTU/5w7qIyi+6MyJYX96mMlxDGrIR85FhQszVyodYfB9g==
+ dependencies:
+ "@npmcli/package-json" "^6.1.0"
+ npm-package-arg "^12.0.0"
+ promzard "^2.0.0"
+ read "^4.0.0"
+ semver "^7.3.5"
+ validate-npm-package-license "^3.0.4"
+ validate-npm-package-name "^6.0.0"
+
+ip-address@^9.0.5:
+ version "9.0.5"
+ resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a"
+ integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==
+ dependencies:
+ jsbn "1.1.0"
+ sprintf-js "^1.1.3"
+
+ip-regex@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-5.0.0.tgz#cd313b2ae9c80c07bd3851e12bf4fa4dc5480632"
+ integrity sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==
+
+is-cidr@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-5.1.1.tgz#83ec462922c2b9209bc64794c4e3b2a890d23994"
+ integrity sha512-AwzRMjtJNTPOgm7xuYZ71715z99t+4yRnSnSzgK5err5+heYi4zMuvmpUadaJ28+KCXCQo8CjUrKQZRWSPmqTQ==
+ dependencies:
+ cidr-regex "^4.1.1"
+
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+isexe@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d"
+ integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==
+
+jackspeak@^3.1.2:
+ version "3.4.3"
+ resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
+ integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==
+ dependencies:
+ "@isaacs/cliui" "^8.0.2"
+ optionalDependencies:
+ "@pkgjs/parseargs" "^0.11.0"
+
+jsbn@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
+ integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
+
+json-parse-even-better-errors@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz#d3f67bd5925e81d3e31aa466acc821c8375cec43"
+ integrity sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==
+
+json-stringify-nice@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67"
+ integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==
+
+jsonparse@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
+ integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
+
+just-diff-apply@^5.2.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.5.0.tgz#771c2ca9fa69f3d2b54e7c3f5c1dfcbcc47f9f0f"
+ integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==
+
+just-diff@^6.0.0:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-6.0.2.tgz#03b65908543ac0521caf6d8eb85035f7d27ea285"
+ integrity sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==
+
+libnpmaccess@^10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-10.0.0.tgz#a5279a683af982fa971599d37ed471e59339bd01"
+ integrity sha512-Nz9Lolajvh6nPA5ixdKNfN2BJS0N7LvqTXPqy3+F37i3T4mcped24JCjwnp5KCPCB0ewX3ccopwUnhaTS1/yXg==
+ dependencies:
+ npm-package-arg "^12.0.0"
+ npm-registry-fetch "^18.0.1"
+
+libnpmdiff@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/libnpmdiff/-/libnpmdiff-8.0.1.tgz#b8348a759306dff56330ba6228eaa58af8abf33e"
+ integrity sha512-3HoZq96FtqpEq1miPKQVj49T+KAKF4bP1UflWBBQ1YZDwm77tgNnYttuSRj6N41R5B2bxL5wK8a0zFbFGIN7tw==
+ dependencies:
+ "@npmcli/arborist" "^9.0.1"
+ "@npmcli/installed-package-contents" "^3.0.0"
+ binary-extensions "^3.0.0"
+ diff "^7.0.0"
+ minimatch "^9.0.4"
+ npm-package-arg "^12.0.0"
+ pacote "^21.0.0"
+ tar "^6.2.1"
+
+libnpmexec@^10.1.0:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/libnpmexec/-/libnpmexec-10.1.0.tgz#451435c04d0feae8e874851aab9bfdfa1d58ec34"
+ integrity sha512-ojQgfhwlC4PCzHUSVRaTUg3aKxrJbArtc/9KwC3mED1Wc1FSW11pHo0Ufs5DJLDbRK5LhjjEQ8AxzwRIUQVY+A==
+ dependencies:
+ "@npmcli/arborist" "^9.0.1"
+ "@npmcli/package-json" "^6.1.1"
+ "@npmcli/run-script" "^9.0.1"
+ ci-info "^4.0.0"
+ npm-package-arg "^12.0.0"
+ pacote "^21.0.0"
+ proc-log "^5.0.0"
+ read "^4.0.0"
+ read-package-json-fast "^4.0.0"
+ semver "^7.3.7"
+ walk-up-path "^4.0.0"
+
+libnpmfund@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/libnpmfund/-/libnpmfund-7.0.1.tgz#36d4393706b7f8a9094868adb2f0a3ec9fbd6fc5"
+ integrity sha512-bkam0l6uKTTwBZ5LhG05tdTnMt75g/jrL5tzPaWIpSMDWlcqFqLy+aqT+FkQCi4fCp8XDEOCZ2POqCyAFUiJuA==
+ dependencies:
+ "@npmcli/arborist" "^9.0.1"
+
+libnpmorg@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-8.0.0.tgz#e133658149f7e70f5536511a8501ad9348559c2e"
+ integrity sha512-VO/mxds3Qu67S7/3TsFbykN+7kzpes14P/RiO3ECtLtUYQdlE5ddXGArRgU2tP4hUHZRvyBhc4sSiAXEzTA4eQ==
+ dependencies:
+ aproba "^2.0.0"
+ npm-registry-fetch "^18.0.1"
+
+libnpmpack@^9.0.1:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/libnpmpack/-/libnpmpack-9.0.1.tgz#21f4977a84734ec3a5d98dd5507442289a30fe1f"
+ integrity sha512-0b9x8h0xAiu99ZewqZqZInf82dCxVx2AWB6jqwooIHzey4i2XYcIi0Tcik9GhkayU5nJ5WLD/W9PVyZcIWcm2w==
+ dependencies:
+ "@npmcli/arborist" "^9.0.1"
+ "@npmcli/run-script" "^9.0.1"
+ npm-package-arg "^12.0.0"
+ pacote "^21.0.0"
+
+libnpmpublish@^11.0.0:
+ version "11.0.0"
+ resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-11.0.0.tgz#7b1bd0d4e2b388add88e53cc8d689307adbad355"
+ integrity sha512-c+cBWLWXafHzmSEQwRVKjHP6KkWntvqvAAT83agwmWrOwRpEXWDtiIlkopwzPcLRau6BcS6BwOttTlAWboH3BQ==
+ dependencies:
+ ci-info "^4.0.0"
+ normalize-package-data "^7.0.0"
+ npm-package-arg "^12.0.0"
+ npm-registry-fetch "^18.0.1"
+ proc-log "^5.0.0"
+ semver "^7.3.7"
+ sigstore "^3.0.0"
+ ssri "^12.0.0"
+
+libnpmsearch@^9.0.0:
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-9.0.0.tgz#dfe2e86ef28a24d8687f79480a045f002b38542d"
+ integrity sha512-uMUbX5ynU/imuXlijCPathemyi1EZVtka9PEbaIqghdrjdHmMJITbyTsmSB+muzBWm1NUUFwRRKdpwktEmvipg==
+ dependencies:
+ npm-registry-fetch "^18.0.1"
+
+libnpmteam@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-8.0.0.tgz#d537352ce727aa57fb1acef318d69571b011d04c"
+ integrity sha512-GfbxITlY4rVe3PKUU6wBjfNNc4Xho9Jv03N0sdzqho9H+9hynFjiwJpfWGwfVBdtimH+kPQW58qRUMott/Bkyg==
+ dependencies:
+ aproba "^2.0.0"
+ npm-registry-fetch "^18.0.1"
+
+libnpmversion@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/libnpmversion/-/libnpmversion-8.0.0.tgz#68998a1eb2c9c068e721e9221073d78edb4efc73"
+ integrity sha512-nqHD/YQtC/xLRquvFj2W2hvTNAIWSssJdz5ULCV0jAGBxjlQaPS9s8FNIiJ3w+iina+pCJo5AmlBjA7oWew0JQ==
+ dependencies:
+ "@npmcli/git" "^6.0.1"
+ "@npmcli/run-script" "^9.0.1"
+ json-parse-even-better-errors "^4.0.0"
+ proc-log "^5.0.0"
+ semver "^7.3.7"
+
lodash-es@^4.17.21:
version "4.17.21"
resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz"
@@ -735,6 +1480,11 @@ lodash@^4.17.21:
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+lru-cache@^10.0.1, lru-cache@^10.2.0, lru-cache@^10.2.2:
+ version "10.4.3"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
+ integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
+
magic-string@^0.30.11:
version "0.30.17"
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz"
@@ -742,6 +1492,23 @@ magic-string@^0.30.11:
dependencies:
"@jridgewell/sourcemap-codec" "^1.5.0"
+make-fetch-happen@^14.0.0, make-fetch-happen@^14.0.1, make-fetch-happen@^14.0.2, make-fetch-happen@^14.0.3:
+ version "14.0.3"
+ resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz#d74c3ecb0028f08ab604011e0bc6baed483fcdcd"
+ integrity sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==
+ dependencies:
+ "@npmcli/agent" "^3.0.0"
+ cacache "^19.0.1"
+ http-cache-semantics "^4.1.1"
+ minipass "^7.0.2"
+ minipass-fetch "^4.0.0"
+ minipass-flush "^1.0.5"
+ minipass-pipeline "^1.2.4"
+ negotiator "^1.0.0"
+ proc-log "^5.0.0"
+ promise-retry "^2.0.1"
+ ssri "^12.0.0"
+
marked@^15.0.7:
version "15.0.7"
resolved "https://registry.npmmirror.com/marked/-/marked-15.0.7.tgz"
@@ -764,18 +1531,110 @@ mime-types@^2.1.12:
dependencies:
mime-db "1.52.0"
-minimatch@^9.0.3:
+minimatch@^9.0.0, minimatch@^9.0.3, minimatch@^9.0.4, minimatch@^9.0.5:
version "9.0.5"
resolved "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz"
integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
dependencies:
brace-expansion "^2.0.1"
+minipass-collect@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-2.0.1.tgz#1621bc77e12258a12c60d34e2276ec5c20680863"
+ integrity sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==
+ dependencies:
+ minipass "^7.0.3"
+
+minipass-fetch@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-4.0.1.tgz#f2d717d5a418ad0b1a7274f9b913515d3e78f9e5"
+ integrity sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==
+ dependencies:
+ minipass "^7.0.3"
+ minipass-sized "^1.0.3"
+ minizlib "^3.0.1"
+ optionalDependencies:
+ encoding "^0.1.13"
+
+minipass-flush@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373"
+ integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==
+ dependencies:
+ minipass "^3.0.0"
+
+minipass-pipeline@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c"
+ integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==
+ dependencies:
+ minipass "^3.0.0"
+
+minipass-sized@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70"
+ integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==
+ dependencies:
+ minipass "^3.0.0"
+
+minipass@^3.0.0:
+ version "3.3.6"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
+ integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
+ dependencies:
+ yallist "^4.0.0"
+
+minipass@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d"
+ integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
+
+"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.0.4, minipass@^7.1.1, minipass@^7.1.2:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
+ integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
+
+minizlib@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
+ integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
+ dependencies:
+ minipass "^3.0.0"
+ yallist "^4.0.0"
+
+minizlib@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.0.1.tgz#46d5329d1eb3c83924eff1d3b858ca0a31581012"
+ integrity sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==
+ dependencies:
+ minipass "^7.0.4"
+ rimraf "^5.0.5"
+
+mkdirp@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
+ integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
+
+mkdirp@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50"
+ integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==
+
+ms@^2.1.2, ms@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
muggle-string@^0.4.1:
version "0.4.1"
resolved "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz"
integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==
+mute-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-2.0.0.tgz#a5446fc0c512b71c83c44d908d5c7b7b4c493b2b"
+ integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==
+
naive-ui@^2.41.0:
version "2.41.0"
resolved "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.41.0.tgz"
@@ -806,16 +1665,266 @@ nanoid@^3.3.8:
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.8.tgz"
integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
+negotiator@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a"
+ integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==
+
+node-gyp@^11.0.0, node-gyp@^11.1.0:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-11.1.0.tgz#212a1d9c167c50d727d42659410780b40e07bbd3"
+ integrity sha512-/+7TuHKnBpnMvUQnsYEb0JOozDZqarQbfNuSGLXIjhStMT0fbw7IdSqWgopOP5xhRZE+lsbIvAHcekddruPZgQ==
+ dependencies:
+ env-paths "^2.2.0"
+ exponential-backoff "^3.1.1"
+ glob "^10.3.10"
+ graceful-fs "^4.2.6"
+ make-fetch-happen "^14.0.3"
+ nopt "^8.0.0"
+ proc-log "^5.0.0"
+ semver "^7.3.5"
+ tar "^7.4.3"
+ which "^5.0.0"
+
+nopt@^8.0.0, nopt@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-8.1.0.tgz#b11d38caf0f8643ce885818518064127f602eae3"
+ integrity sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==
+ dependencies:
+ abbrev "^3.0.0"
+
+normalize-package-data@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-7.0.0.tgz#ab4f49d02f2e25108d3f4326f3c13f0de6fa6a0a"
+ integrity sha512-k6U0gKRIuNCTkwHGZqblCfLfBRh+w1vI6tBo+IeJwq2M8FUiOqhX7GH+GArQGScA7azd1WfyRCvxoXDO3hQDIA==
+ dependencies:
+ hosted-git-info "^8.0.0"
+ semver "^7.3.5"
+ validate-npm-package-license "^3.0.4"
+
+npm-audit-report@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/npm-audit-report/-/npm-audit-report-6.0.0.tgz#0262e5e2b674fabf0ea47e900fc7384b83de0fbb"
+ integrity sha512-Ag6Y1irw/+CdSLqEEAn69T8JBgBThj5mw0vuFIKeP7hATYuQuS5jkMjK6xmVB8pr7U4g5Audbun0lHhBDMIBRA==
+
+npm-bundled@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-4.0.0.tgz#f5b983f053fe7c61566cf07241fab2d4e9d513d3"
+ integrity sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==
+ dependencies:
+ npm-normalize-package-bin "^4.0.0"
+
+npm-install-checks@^7.1.0, npm-install-checks@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-7.1.1.tgz#e9d679fc8a1944c75cdcc96478a22f9d0f763632"
+ integrity sha512-u6DCwbow5ynAX5BdiHQ9qvexme4U3qHW3MWe5NqH+NeBm0LbiH6zvGjNNew1fY+AZZUtVHbOPF3j7mJxbUzpXg==
+ dependencies:
+ semver "^7.1.1"
+
+npm-normalize-package-bin@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz#df79e70cd0a113b77c02d1fe243c96b8e618acb1"
+ integrity sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==
+
+npm-package-arg@^12.0.0, npm-package-arg@^12.0.2:
+ version "12.0.2"
+ resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-12.0.2.tgz#3b1e04ebe651cc45028e298664e8c15ce9c0ca40"
+ integrity sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==
+ dependencies:
+ hosted-git-info "^8.0.0"
+ proc-log "^5.0.0"
+ semver "^7.3.5"
+ validate-npm-package-name "^6.0.0"
+
+npm-packlist@^10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-10.0.0.tgz#35634f0a90f84a811ebdf565eb78d2b36252888c"
+ integrity sha512-rht9U6nS8WOBDc53eipZNPo5qkAV4X2rhKE2Oj1DYUQ3DieXfj0mKkVmjnf3iuNdtMd8WfLdi2L6ASkD/8a+Kg==
+ dependencies:
+ ignore-walk "^7.0.0"
+
+npm-pick-manifest@^10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz#6cc120c6473ceea56dfead500f00735b2b892851"
+ integrity sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==
+ dependencies:
+ npm-install-checks "^7.1.0"
+ npm-normalize-package-bin "^4.0.0"
+ npm-package-arg "^12.0.0"
+ semver "^7.3.5"
+
+npm-profile@^11.0.1:
+ version "11.0.1"
+ resolved "https://registry.yarnpkg.com/npm-profile/-/npm-profile-11.0.1.tgz#6ffac43f3d186316d37e80986d84aef2470269a2"
+ integrity sha512-HP5Cw9WHwFS9vb4fxVlkNAQBUhVL5BmW6rAR+/JWkpwqcFJid7TihKUdYDWqHl0NDfLd0mpucheGySqo8ysyfw==
+ dependencies:
+ npm-registry-fetch "^18.0.0"
+ proc-log "^5.0.0"
+
+npm-registry-fetch@^18.0.0, npm-registry-fetch@^18.0.1, npm-registry-fetch@^18.0.2:
+ version "18.0.2"
+ resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-18.0.2.tgz#340432f56b5a8b1af068df91aae0435d2de646b5"
+ integrity sha512-LeVMZBBVy+oQb5R6FDV9OlJCcWDU+al10oKpe+nsvcHnG24Z3uM3SvJYKfGJlfGjVU8v9liejCrUR/M5HO5NEQ==
+ dependencies:
+ "@npmcli/redact" "^3.0.0"
+ jsonparse "^1.3.1"
+ make-fetch-happen "^14.0.0"
+ minipass "^7.0.2"
+ minipass-fetch "^4.0.0"
+ minizlib "^3.0.1"
+ npm-package-arg "^12.0.0"
+ proc-log "^5.0.0"
+
+npm-user-validate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-3.0.0.tgz#9b1410796bf1f1d78297a8096328c55d3083f233"
+ integrity sha512-9xi0RdSmJ4mPYTC393VJPz1Sp8LyCx9cUnm/L9Qcb3cFO8gjT4mN20P9FAsea8qDHdQ7LtcN8VLh2UT47SdKCw==
+
+npm@^11.2.0:
+ version "11.2.0"
+ resolved "https://registry.yarnpkg.com/npm/-/npm-11.2.0.tgz#fcf483cf8719864a3ab9749cbc5aed691c1c9120"
+ integrity sha512-PcnFC6gTo9VDkxVaQ1/mZAS3JoWrDjAI+a6e2NgfYQSGDwftJlbdV0jBMi2V8xQPqbGcWaa7p3UP0SKF+Bhm2g==
+ dependencies:
+ "@isaacs/string-locale-compare" "^1.1.0"
+ "@npmcli/arborist" "^9.0.1"
+ "@npmcli/config" "^10.1.0"
+ "@npmcli/fs" "^4.0.0"
+ "@npmcli/map-workspaces" "^4.0.2"
+ "@npmcli/package-json" "^6.1.1"
+ "@npmcli/promise-spawn" "^8.0.2"
+ "@npmcli/redact" "^3.1.1"
+ "@npmcli/run-script" "^9.0.1"
+ "@sigstore/tuf" "^3.0.0"
+ abbrev "^3.0.0"
+ archy "~1.0.0"
+ cacache "^19.0.1"
+ chalk "^5.4.1"
+ ci-info "^4.1.0"
+ cli-columns "^4.0.0"
+ fastest-levenshtein "^1.0.16"
+ fs-minipass "^3.0.3"
+ glob "^10.4.5"
+ graceful-fs "^4.2.11"
+ hosted-git-info "^8.0.2"
+ ini "^5.0.0"
+ init-package-json "^8.0.0"
+ is-cidr "^5.1.1"
+ json-parse-even-better-errors "^4.0.0"
+ libnpmaccess "^10.0.0"
+ libnpmdiff "^8.0.1"
+ libnpmexec "^10.1.0"
+ libnpmfund "^7.0.1"
+ libnpmorg "^8.0.0"
+ libnpmpack "^9.0.1"
+ libnpmpublish "^11.0.0"
+ libnpmsearch "^9.0.0"
+ libnpmteam "^8.0.0"
+ libnpmversion "^8.0.0"
+ make-fetch-happen "^14.0.3"
+ minimatch "^9.0.5"
+ minipass "^7.1.1"
+ minipass-pipeline "^1.2.4"
+ ms "^2.1.2"
+ node-gyp "^11.1.0"
+ nopt "^8.1.0"
+ normalize-package-data "^7.0.0"
+ npm-audit-report "^6.0.0"
+ npm-install-checks "^7.1.1"
+ npm-package-arg "^12.0.2"
+ npm-pick-manifest "^10.0.0"
+ npm-profile "^11.0.1"
+ npm-registry-fetch "^18.0.2"
+ npm-user-validate "^3.0.0"
+ p-map "^7.0.3"
+ pacote "^21.0.0"
+ parse-conflict-json "^4.0.0"
+ proc-log "^5.0.0"
+ qrcode-terminal "^0.12.0"
+ read "^4.1.0"
+ semver "^7.7.1"
+ spdx-expression-parse "^4.0.0"
+ ssri "^12.0.0"
+ supports-color "^10.0.0"
+ tar "^6.2.1"
+ text-table "~0.2.0"
+ tiny-relative-date "^1.3.0"
+ treeverse "^3.0.0"
+ validate-npm-package-name "^6.0.0"
+ which "^5.0.0"
+
+p-map@^7.0.2, p-map@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-7.0.3.tgz#7ac210a2d36f81ec28b736134810f7ba4418cdb6"
+ integrity sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==
+
+package-json-from-dist@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
+ integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
+
+pacote@^21.0.0:
+ version "21.0.0"
+ resolved "https://registry.yarnpkg.com/pacote/-/pacote-21.0.0.tgz#5fe3878a9f808ca5c455c4c1d8ca46eb13351f7b"
+ integrity sha512-lcqexq73AMv6QNLo7SOpz0JJoaGdS3rBFgF122NZVl1bApo2mfu+XzUBU/X/XsiJu+iUmKpekRayqQYAs+PhkA==
+ dependencies:
+ "@npmcli/git" "^6.0.0"
+ "@npmcli/installed-package-contents" "^3.0.0"
+ "@npmcli/package-json" "^6.0.0"
+ "@npmcli/promise-spawn" "^8.0.0"
+ "@npmcli/run-script" "^9.0.0"
+ cacache "^19.0.0"
+ fs-minipass "^3.0.0"
+ minipass "^7.0.2"
+ npm-package-arg "^12.0.0"
+ npm-packlist "^10.0.0"
+ npm-pick-manifest "^10.0.0"
+ npm-registry-fetch "^18.0.0"
+ proc-log "^5.0.0"
+ promise-retry "^2.0.1"
+ sigstore "^3.0.0"
+ ssri "^12.0.0"
+ tar "^6.1.11"
+
+parse-conflict-json@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-4.0.0.tgz#996b1edfc0c727583b56c7644dbb3258fc9e9e4b"
+ integrity sha512-37CN2VtcuvKgHUs8+0b1uJeEsbGn61GRHz469C94P5xiOoqpDYJYwjg4RY9Vmz39WyZAVkR5++nbJwLMIgOCnQ==
+ dependencies:
+ json-parse-even-better-errors "^4.0.0"
+ just-diff "^6.0.0"
+ just-diff-apply "^5.2.0"
+
path-browserify@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz"
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
+path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-scurry@^1.11.1:
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2"
+ integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
+ dependencies:
+ lru-cache "^10.2.0"
+ minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
+
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+postcss-selector-parser@^6.1.2:
+ version "6.1.2"
+ resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de"
+ integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
+ dependencies:
+ cssesc "^3.0.0"
+ util-deprecate "^1.0.2"
+
postcss@^8.4.48, postcss@^8.5.3:
version "8.5.3"
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.5.3.tgz"
@@ -825,11 +1934,83 @@ postcss@^8.4.48, postcss@^8.5.3:
picocolors "^1.1.1"
source-map-js "^1.2.1"
+proc-log@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-5.0.0.tgz#e6c93cf37aef33f835c53485f314f50ea906a9d8"
+ integrity sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==
+
+proggy@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/proggy/-/proggy-3.0.0.tgz#874e91fed27fe00a511758e83216a6b65148bd6c"
+ integrity sha512-QE8RApCM3IaRRxVzxrjbgNMpQEX6Wu0p0KBeoSiSEw5/bsGwZHsshF4LCxH2jp/r6BU+bqA3LrMDEYNfJnpD8Q==
+
+promise-all-reject-late@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2"
+ integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==
+
+promise-call-limit@^3.0.1:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-3.0.2.tgz#524b7f4b97729ff70417d93d24f46f0265efa4f9"
+ integrity sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==
+
+promise-retry@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22"
+ integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==
+ dependencies:
+ err-code "^2.0.2"
+ retry "^0.12.0"
+
+promzard@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/promzard/-/promzard-2.0.0.tgz#03ad0e4db706544dfdd4f459281f13484fc10c49"
+ integrity sha512-Ncd0vyS2eXGOjchIRg6PVCYKetJYrW1BSbbIo+bKdig61TB6nH2RQNF2uP+qMpsI73L/jURLWojcw8JNIKZ3gg==
+ dependencies:
+ read "^4.0.0"
+
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+qrcode-terminal@^0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819"
+ integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==
+
+read-cmd-shim@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-5.0.0.tgz#6e5450492187a0749f6c80dcbef0debc1117acca"
+ integrity sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw==
+
+read-package-json-fast@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz#8ccbc05740bb9f58264f400acc0b4b4eee8d1b39"
+ integrity sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==
+ dependencies:
+ json-parse-even-better-errors "^4.0.0"
+ npm-normalize-package-bin "^4.0.0"
+
+read@^4.0.0, read@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/read/-/read-4.1.0.tgz#d97c2556b009b47b16b5bb82311d477cc7503548"
+ integrity sha512-uRfX6K+f+R8OOrYScaM3ixPY4erg69f8DN6pgTvMcA9iRc8iDhwrA4m3Yu8YYKsXJgVvum+m8PkRboZwwuLzYA==
+ dependencies:
+ mute-stream "^2.0.0"
+
+retry@^0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
+ integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==
+
+rimraf@^5.0.5:
+ version "5.0.10"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c"
+ integrity sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==
+ dependencies:
+ glob "^10.3.7"
+
rollup@^4.30.1:
version "4.34.9"
resolved "https://registry.npmmirror.com/rollup/-/rollup-4.34.9.tgz"
@@ -858,21 +2039,229 @@ rollup@^4.30.1:
"@rollup/rollup-win32-x64-msvc" "4.34.9"
fsevents "~2.3.2"
+"safer-buffer@>= 2.1.2 < 3.0.0":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
seemly@^0.3.6, seemly@^0.3.8:
version "0.3.10"
resolved "https://registry.npmmirror.com/seemly/-/seemly-0.3.10.tgz"
integrity sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==
+semver@^7.1.1, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.7.1:
+ version "7.7.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
+ integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+signal-exit@^4.0.1:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
+ integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+
+sigstore@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-3.1.0.tgz#08dc6c0c425263e9fdab85ffdb6477550e2c511d"
+ integrity sha512-ZpzWAFHIFqyFE56dXqgX/DkDRZdz+rRcjoIk/RQU4IX0wiCv1l8S7ZrXDHcCc+uaf+6o7w3h2l3g6GYG5TKN9Q==
+ dependencies:
+ "@sigstore/bundle" "^3.1.0"
+ "@sigstore/core" "^2.0.0"
+ "@sigstore/protobuf-specs" "^0.4.0"
+ "@sigstore/sign" "^3.1.0"
+ "@sigstore/tuf" "^3.1.0"
+ "@sigstore/verify" "^2.1.0"
+
+smart-buffer@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
+ integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
+
+socks-proxy-agent@^8.0.3:
+ version "8.0.5"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz#b9cdb4e7e998509d7659d689ce7697ac21645bee"
+ integrity sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==
+ dependencies:
+ agent-base "^7.1.2"
+ debug "^4.3.4"
+ socks "^2.8.3"
+
+socks@^2.8.3:
+ version "2.8.4"
+ resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.4.tgz#07109755cdd4da03269bda4725baa061ab56d5cc"
+ integrity sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==
+ dependencies:
+ ip-address "^9.0.5"
+ smart-buffer "^4.2.0"
+
source-map-js@^1.2.0, source-map-js@^1.2.1:
version "1.2.1"
resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+spdx-correct@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c"
+ integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==
+ dependencies:
+ spdx-expression-parse "^3.0.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-exceptions@^2.1.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66"
+ integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==
+
+spdx-expression-parse@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
+ integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
+ dependencies:
+ spdx-exceptions "^2.1.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-expression-parse@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz#a23af9f3132115465dac215c099303e4ceac5794"
+ integrity sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==
+ dependencies:
+ spdx-exceptions "^2.1.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-license-ids@^3.0.0:
+ version "3.0.21"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz#6d6e980c9df2b6fc905343a3b2d702a6239536c3"
+ integrity sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==
+
+sprintf-js@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
+ integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==
+
+ssri@^12.0.0:
+ version "12.0.0"
+ resolved "https://registry.yarnpkg.com/ssri/-/ssri-12.0.0.tgz#bcb4258417c702472f8191981d3c8a771fee6832"
+ integrity sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==
+ dependencies:
+ minipass "^7.0.3"
+
+"string-width-cjs@npm:string-width@^4.2.0":
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string-width@^4.1.0, string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string-width@^5.0.1, string-width@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
+ integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
+ dependencies:
+ eastasianwidth "^0.2.0"
+ emoji-regex "^9.2.2"
+ strip-ansi "^7.0.1"
+
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-ansi@^7.0.1:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
+ integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+ dependencies:
+ ansi-regex "^6.0.1"
+
+supports-color@^10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-10.0.0.tgz#32000d5e49f1ae70b2645d47701004644a1d7b90"
+ integrity sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==
+
+tar@^6.1.11, tar@^6.2.1:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
+ integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
+ dependencies:
+ chownr "^2.0.0"
+ fs-minipass "^2.0.0"
+ minipass "^5.0.0"
+ minizlib "^2.1.1"
+ mkdirp "^1.0.3"
+ yallist "^4.0.0"
+
+tar@^7.4.3:
+ version "7.4.3"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-7.4.3.tgz#88bbe9286a3fcd900e94592cda7a22b192e80571"
+ integrity sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==
+ dependencies:
+ "@isaacs/fs-minipass" "^4.0.0"
+ chownr "^3.0.0"
+ minipass "^7.1.2"
+ minizlib "^3.0.1"
+ mkdirp "^3.0.1"
+ yallist "^5.0.0"
+
+text-table@~0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+
+tiny-relative-date@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07"
+ integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==
+
treemate@^0.3.11:
version "0.3.11"
resolved "https://registry.npmmirror.com/treemate/-/treemate-0.3.11.tgz"
integrity sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==
+treeverse@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-3.0.0.tgz#dd82de9eb602115c6ebd77a574aae67003cb48c8"
+ integrity sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==
+
+tuf-js@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-3.0.1.tgz#e3f07ed3d8e87afaa70607bd1ef801d5c1f57177"
+ integrity sha512-+68OP1ZzSF84rTckf3FA95vJ1Zlx/uaXyiiKyPd1pA4rZNkpEvDAKmsu1xUSmbF/chCRYgZ6UZkDwC7PmzmAyA==
+ dependencies:
+ "@tufjs/models" "3.0.1"
+ debug "^4.3.6"
+ make-fetch-happen "^14.0.1"
+
typescript@~5.7.2:
version "5.7.3"
resolved "https://registry.npmmirror.com/typescript/-/typescript-5.7.3.tgz"
@@ -883,6 +2272,38 @@ undici-types@~6.20.0:
resolved "https://registry.npmmirror.com/undici-types/-/undici-types-6.20.0.tgz"
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
+unique-filename@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-4.0.0.tgz#a06534d370e7c977a939cd1d11f7f0ab8f1fed13"
+ integrity sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==
+ dependencies:
+ unique-slug "^5.0.0"
+
+unique-slug@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-5.0.0.tgz#ca72af03ad0dbab4dad8aa683f633878b1accda8"
+ integrity sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==
+ dependencies:
+ imurmurhash "^0.1.4"
+
+util-deprecate@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+validate-npm-package-license@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
+ integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
+ dependencies:
+ spdx-correct "^3.0.0"
+ spdx-expression-parse "^3.0.0"
+
+validate-npm-package-name@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-6.0.0.tgz#3add966c853cfe36e0e8e6a762edd72ae6f1d6ac"
+ integrity sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg==
+
vdirs@^0.1.4, vdirs@^0.1.8:
version "0.1.8"
resolved "https://registry.npmmirror.com/vdirs/-/vdirs-0.1.8.tgz"
@@ -913,6 +2334,13 @@ vscode-uri@^3.0.8:
resolved "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz"
integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==
+vue-router@4:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.5.0.tgz#58fc5fe374e10b6018f910328f756c3dae081f14"
+ integrity sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==
+ dependencies:
+ "@vue/devtools-api" "^6.6.4"
+
vue-tsc@^2.2.4:
version "2.2.8"
resolved "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-2.2.8.tgz"
@@ -944,3 +2372,58 @@ vueuc@^0.4.63:
seemly "^0.3.6"
vdirs "^0.1.4"
vooks "^0.2.4"
+
+walk-up-path@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-4.0.0.tgz#590666dcf8146e2d72318164f1f2ac6ef51d4198"
+ integrity sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==
+
+which@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+which@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/which/-/which-5.0.0.tgz#d93f2d93f79834d4363c7d0c23e00d07c466c8d6"
+ integrity sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==
+ dependencies:
+ isexe "^3.1.1"
+
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrap-ansi@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
+ integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
+ dependencies:
+ ansi-styles "^6.1.0"
+ string-width "^5.0.1"
+ strip-ansi "^7.0.1"
+
+write-file-atomic@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-6.0.0.tgz#e9c89c8191b3ef0606bc79fb92681aa1aa16fa93"
+ integrity sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==
+ dependencies:
+ imurmurhash "^0.1.4"
+ signal-exit "^4.0.1"
+
+yallist@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yallist@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533"
+ integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==
diff --git a/fund_service.py b/fund_service.py
deleted file mode 100644
index fa4218b..0000000
--- a/fund_service.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import akshare as ak
-import pandas as pd
-
-class FundService:
- def search_funds(self, keyword, market_type='ETF'):
- """
- 搜索基金代码
- :param keyword: 搜索关键词
- :return: 匹配的基金列表
- """
- try:
- # 获取ETF和LOF数据
- if market_type == 'ETF':
- df = ak.fund_etf_spot_em()
- else:
- df = ak.fund_lof_spot_em()
-
- # 转换列名
- df = df.rename(columns={
- "代码": "symbol",
- "名称": "name",
- "最新价": "price",
- "涨跌额": "price_change",
- "涨跌幅": "price_change_percent",
- "成交量": "volume",
- "流通市值": "market_value",
- "总市值": "total_value",
- "基金折价率": "discount_rate",
- })
-
- # 模糊匹配搜索(同时匹配代码和名称)
- mask = (df['name'].str.contains(keyword, case=False, na=False) |
- df['symbol'].str.contains(keyword, case=False, na=False))
- results = df[mask]
-
- # 格式化返回结果并处理 NaN 值
- formatted_results = []
- for _, row in results.iterrows():
- formatted_results.append({
- 'name': row['name'] if pd.notna(row['name']) else '',
- 'symbol': str(row['symbol']) if pd.notna(row['symbol']) else '',
- 'price': float(row['price']) if pd.notna(row['price']) else 0.0,
- 'volume': float(row['volume']) if pd.notna(row['volume']) else 0.0,
- 'market_value': float(row['market_value']) if pd.notna(row['market_value']) else 0.0,
- 'total_value': float(row['total_value']) if pd.notna(row['total_value']) else 0.0,
- })
-
- return formatted_results
-
- except Exception as e:
- raise Exception(f"搜索基金代码失败: {str(e)}")
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index f758152..988cf92 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -35,3 +35,5 @@ html5lib==1.1
lxml==4.9.4
jsonpath==0.82.2
openpyxl==3.1.5
+python-jose[cryptography]==3.4.0
+passlib==1.7.4
diff --git a/services/ai_analyzer.py b/services/ai_analyzer.py
index f7fa01e..eed82ff 100644
--- a/services/ai_analyzer.py
+++ b/services/ai_analyzer.py
@@ -231,35 +231,83 @@ class AIAnalyzer:
async for chunk in response.aiter_text():
if chunk:
- chunk_str = chunk.strip()
- if chunk_str.startswith("data: "):
- chunk_str = chunk_str[6:] # 去除"data: "前缀
-
- if chunk_str == "[DONE]":
- logger.debug("收到流结束标记 [DONE]")
- continue
-
- try:
- # 解析数据块
- chunk_data = json.loads(chunk_str)
- delta = chunk_data.get("choices", [{}])[0].get("delta", {})
- content = delta.get("content", "")
-
- if content:
- chunk_count += 1
- buffer += content
- collected_messages.append(content)
+ # 分割多行响应(处理某些API可能在一个chunk中返回多行)
+ lines = chunk.strip().split('\n')
+ for line in lines:
+ line = line.strip()
+ if not line:
+ continue
- # 直接发送每个内容片段,不累积
- yield json.dumps({
- "stock_code": stock_code,
- "ai_analysis_chunk": content,
- "status": "analyzing"
- })
- except json.JSONDecodeError:
- # 忽略无法解析的块
- logger.error(f"JSON解析错误,块内容: {chunk_str[:100]}...")
- continue
+ # 处理以data:开头的行
+ if line.startswith("data: "):
+ line = line[6:] # 去除"data: "前缀
+
+ if line == "[DONE]":
+ logger.debug("收到流结束标记 [DONE]")
+ continue
+
+ try:
+ # 处理特殊错误情况
+ if "error" in line.lower():
+ error_msg = line
+ try:
+ error_data = json.loads(line)
+ error_msg = error_data.get("error", line)
+ except:
+ pass
+
+ logger.error(f"流式响应中收到错误: {error_msg}")
+ yield json.dumps({
+ "stock_code": stock_code,
+ "error": f"流式响应错误: {error_msg}",
+ "status": "error"
+ })
+ continue
+
+ # 尝试解析JSON
+ chunk_data = json.loads(line)
+
+ # 检查是否有finish_reason
+ finish_reason = chunk_data.get("choices", [{}])[0].get("finish_reason")
+ if finish_reason == "stop":
+ logger.debug("收到finish_reason=stop,流结束")
+ continue
+
+ # 获取delta内容
+ delta = chunk_data.get("choices", [{}])[0].get("delta", {})
+
+ # 检查delta是否为空对象
+ if not delta or delta == {}:
+ logger.debug("收到空的delta对象,跳过")
+ continue
+
+ content = delta.get("content", "")
+
+ if content:
+ chunk_count += 1
+ buffer += content
+ collected_messages.append(content)
+
+ # 直接发送每个内容片段,不累积
+ yield json.dumps({
+ "stock_code": stock_code,
+ "ai_analysis_chunk": content,
+ "status": "analyzing"
+ })
+ except json.JSONDecodeError:
+ # 记录解析错误并尝试恢复
+ logger.error(f"JSON解析错误,块内容: {line}")
+
+ # 如果是特定错误模式,处理它
+ if "streaming failed after retries" in line.lower():
+ logger.error("检测到流式传输失败")
+ yield json.dumps({
+ "stock_code": stock_code,
+ "error": "流式传输失败,请稍后重试",
+ "status": "error"
+ })
+ return
+ continue
logger.info(f"AI流式处理完成,共收到 {chunk_count} 个内容片段,总长度: {len(buffer)}")
diff --git a/services/fund_service_async.py b/services/fund_service_async.py
index 8bafada..e232188 100644
--- a/services/fund_service_async.py
+++ b/services/fund_service_async.py
@@ -56,8 +56,11 @@ class FundServiceAsync:
'market_value': float(row['market_value']) if pd.notna(row['market_value']) else 0.0,
'total_value': float(row['total_value']) if pd.notna(row['total_value']) else 0.0,
})
+ # 限制只返回前10个结果
+ if len(formatted_results) >= 10:
+ break
- logger.info(f"基金搜索完成,找到 {len(formatted_results)} 个匹配项")
+ logger.info(f"基金搜索完成,找到 {len(formatted_results)} 个匹配项(限制显示前10个)")
return formatted_results
except Exception as e:
diff --git a/services/stock_analyzer_service.py b/services/stock_analyzer_service.py
index 191663f..d73738f 100644
--- a/services/stock_analyzer_service.py
+++ b/services/stock_analyzer_service.py
@@ -60,6 +60,30 @@ class StockAnalyzerService:
# 获取股票数据
df = await self.data_provider.get_stock_data(stock_code, market_type)
+ # 检查是否有错误
+ if hasattr(df, 'error'):
+ error_msg = df.error
+ logger.error(f"获取股票数据时出错: {error_msg}")
+ yield json.dumps({
+ "stock_code": stock_code,
+ "market_type": market_type,
+ "error": error_msg,
+ "status": "error"
+ })
+ return
+
+ # 检查数据是否为空
+ if df.empty:
+ error_msg = f"获取到的股票 {stock_code} 数据为空"
+ logger.error(error_msg)
+ yield json.dumps({
+ "stock_code": stock_code,
+ "market_type": market_type,
+ "error": error_msg,
+ "status": "error"
+ })
+ return
+
# 计算技术指标
df_with_indicators = self.indicator.calculate_indicators(df)
diff --git a/services/stock_data_provider.py b/services/stock_data_provider.py
index 48cebb4..af2afa0 100644
--- a/services/stock_data_provider.py
+++ b/services/stock_data_provider.py
@@ -5,6 +5,7 @@ import asyncio
import os
from typing import Dict, List, Optional, Tuple, Any
from logger import get_logger
+import re
# 获取日志器
logger = get_logger()
@@ -57,27 +58,16 @@ class StockDataProvider:
if end_date is None:
end_date = datetime.now().strftime('%Y%m%d')
+ # 确保日期格式统一(移除可能的'-'符号)
+ if isinstance(start_date, str) and '-' in start_date:
+ start_date = start_date.replace('-', '')
+ if isinstance(end_date, str) and '-' in end_date:
+ end_date = end_date.replace('-', '')
+
try:
- # 验证股票代码格式
if market_type == 'A':
- # 上海证券交易所股票代码以6开头
- # 深圳证券交易所股票代码以0或3开头
- # 科创板股票代码以688开头
- # 北京证券交易所股票代码以8开头
- valid_prefixes = ['0', '3', '6', '688', '8']
- valid_format = False
-
- for prefix in valid_prefixes:
- if stock_code.startswith(prefix):
- valid_format = True
- break
-
- if not valid_format:
- error_msg = f"无效的A股股票代码格式: {stock_code}。A股代码应以0、3、6、688或8开头"
- logger.error(f"[股票代码格式错误] {error_msg}")
- raise ValueError(error_msg)
-
logger.debug(f"获取A股数据: {stock_code}")
+
df = ak.stock_zh_a_hist(
symbol=stock_code,
start_date=start_date,
@@ -96,13 +86,72 @@ class StockDataProvider:
elif market_type in ['US']:
logger.debug(f"获取美股数据: {stock_code}")
- df = ak.stock_us_daily(
- symbol=stock_code,
- adjust="qfq"
- )
- # 过滤日期
- df = df[(df.index >= start_date) & (df.index <= end_date)]
+ try:
+ df = ak.stock_us_daily(
+ symbol=stock_code,
+ adjust="qfq"
+ )
+ logger.debug(f"美股数据原始列: {df.columns.tolist()}")
+ logger.debug(f"美股数据形状: {df.shape}")
+
+ # 确保索引是日期时间类型
+ if not isinstance(df.index, pd.DatetimeIndex):
+ # 如果存在命名为'date'的列,将其设为索引
+ if 'date' in df.columns:
+ df['date'] = pd.to_datetime(df['date'])
+ df.set_index('date', inplace=True)
+ logger.debug("已将'date'列设置为索引")
+ else:
+ # 否则将当前索引转换为日期类型
+ df.index = pd.to_datetime(df.index)
+ logger.debug("已将索引转换为DatetimeIndex")
+
+ # 计算美股的成交额(Amount)= 成交量(Volume)× 收盘价(Close)
+ volume_col = next((col for col in df.columns if col.lower() == 'volume'), None)
+ close_col = next((col for col in df.columns if col.lower() == 'close'), None)
+
+ if volume_col and close_col:
+ df['amount'] = df[volume_col] * df[close_col]
+ logger.debug("已为美股数据计算成交额(amount)字段")
+ else:
+ logger.warning(f"美股数据缺少volume或close列,无法计算amount。当前列: {df.columns.tolist()}")
+ # 添加空的amount列,避免后续处理错误
+ df['amount'] = 0.0
+
+ # 将所有列名转为小写以进行统一处理
+ df.columns = [col.lower() for col in df.columns]
+
+ except Exception as e:
+ logger.error(f"获取美股数据失败 {stock_code}: {str(e)}")
+ raise ValueError(f"获取美股数据失败 {stock_code}: {str(e)}")
+ # 将字符串日期转换为日期时间对象进行比较
+ try:
+ # 尝试多种格式解析日期
+ # 如果日期是数字格式(20220101),使用适当的格式
+ if start_date.isdigit() and len(start_date) == 8:
+ start_date_dt = pd.to_datetime(start_date, format='%Y%m%d')
+ else:
+ # 否则让pandas自动推断格式
+ start_date_dt = pd.to_datetime(start_date)
+
+ if end_date.isdigit() and len(end_date) == 8:
+ end_date_dt = pd.to_datetime(end_date, format='%Y%m%d')
+ else:
+ end_date_dt = pd.to_datetime(end_date)
+ except Exception as e:
+ logger.warning(f"日期转换出错: {str(e)},使用默认值")
+ # 如果转换失败,使用合理的默认值
+ start_date_dt = pd.to_datetime('20000101', format='%Y%m%d')
+ end_date_dt = pd.to_datetime(datetime.now().strftime('%Y%m%d'), format='%Y%m%d')
+
+ # 过滤日期
+ try:
+ df = df[(df.index >= start_date_dt) & (df.index <= end_date_dt)]
+ logger.debug(f"日期过滤后数据点数: {len(df)}")
+ except Exception as e:
+ logger.warning(f"日期过滤出错: {str(e)},返回原始数据")
+
elif market_type in ['ETF', 'LOF']:
logger.debug(f"获取{market_type}基金数据: {stock_code}")
df = ak.fund_etf_hist_sina(
@@ -122,8 +171,31 @@ class StockDataProvider:
# 实际数据列:['日期', '股票代码', '开盘', '收盘', '最高', '最低', '成交量', '成交额', '振幅', '涨跌幅', '涨跌额', '换手率']
df.columns = ['Date', 'Code', 'Open', 'Close', 'High', 'Low', 'Volume', 'Amount', 'Amplitude', 'Change_pct', 'Change', 'Turnover']
elif market_type in ['HK', 'US']:
- # 根据实际情况调整
- df.columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume', 'Amount']
+ # 美股数据列可能不同,需要通过映射处理
+ columns_mapping = {
+ 'open': 'Open',
+ 'high': 'High',
+ 'low': 'Low',
+ 'close': 'Close',
+ 'volume': 'Volume',
+ 'amount': 'Amount'
+ }
+
+ # 创建新的DataFrame以确保列顺序和存在性
+ new_df = pd.DataFrame(index=df.index)
+
+ # 遍历映射,填充新DataFrame
+ for orig_col, new_col in columns_mapping.items():
+ if orig_col in df.columns:
+ new_df[new_col] = df[orig_col]
+ else:
+ # 如果原始列不存在,创建一个填充0的列
+ logger.warning(f"数据中缺少{orig_col}列,使用0值填充")
+ new_df[new_col] = 0.0
+
+ # 替换原始df
+ df = new_df
+
elif market_type in ['ETF', 'LOF']:
# 基金数据可能有不同的列
df.columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume', 'Amount']
@@ -143,7 +215,11 @@ class StockDataProvider:
error_msg = f"获取{market_type}数据失败 {stock_code}: {str(e)}"
logger.error(error_msg)
logger.exception(e)
- raise Exception(error_msg)
+ # 使用空的DataFrame并添加错误信息,而不是抛出异常
+ # 这样上层调用者可以检查是否有错误并适当处理
+ df = pd.DataFrame()
+ df.error = error_msg # 添加错误属性
+ return df
async def get_multiple_stocks_data(self, stock_codes: List[str],
market_type: str = 'A',
@@ -181,4 +257,4 @@ class StockDataProvider:
results = await asyncio.gather(*tasks)
# 构建结果字典,过滤掉失败的请求
- return {code: df for code, df in results if df is not None}
\ No newline at end of file
+ return {code: df for code, df in results if df is not None}
\ No newline at end of file
diff --git a/services/us_stock_service_async.py b/services/us_stock_service_async.py
index d0a5b0b..97c94b1 100644
--- a/services/us_stock_service_async.py
+++ b/services/us_stock_service_async.py
@@ -49,8 +49,11 @@ class USStockServiceAsync:
'price': float(row['price']) if pd.notna(row['price']) else 0.0,
'market_value': float(row['market_value']) if pd.notna(row['market_value']) else 0.0
})
+ # 限制只返回前10个结果
+ if len(formatted_results) >= 10:
+ break
- logger.info(f"美股搜索完成,找到 {len(formatted_results)} 个匹配项")
+ logger.info(f"美股搜索完成,找到 {len(formatted_results)} 个匹配项(限制显示前10个)")
return formatted_results
except Exception as e:
diff --git a/stock_analyzer.py b/stock_analyzer.py
deleted file mode 100644
index c28d4ac..0000000
--- a/stock_analyzer.py
+++ /dev/null
@@ -1,758 +0,0 @@
-import pandas as pd
-import numpy as np
-from datetime import datetime, timedelta
-import os
-import requests
-from typing import Dict, List, Optional, Tuple, Generator
-from dotenv import load_dotenv
-import json
-from logger import get_logger
-from utils.api_utils import APIUtils
-
-# 获取日志器
-logger = get_logger()
-
-class StockAnalyzer:
- def __init__(self, initial_cash=1000000, custom_api_url=None, custom_api_key=None, custom_api_model=None, custom_api_timeout=None):
-
- # 加载环境变量
- load_dotenv()
-
- # 设置 API 配置,优先使用自定义配置,否则使用环境变量
- self.API_URL = custom_api_url or os.getenv('API_URL')
- self.API_KEY = custom_api_key or os.getenv('API_KEY')
- self.API_MODEL = custom_api_model or os.getenv('API_MODEL', 'gpt-3.5-turbo')
- self.API_TIMEOUT = int(custom_api_timeout or os.getenv('API_TIMEOUT', 60))
-
- logger.debug(f"初始化StockAnalyzer: API_URL={self.API_URL}, API_MODEL={self.API_MODEL}, API_KEY={'已提供' if self.API_KEY else '未提供'}, API_TIMEOUT={self.API_TIMEOUT}")
-
- # 配置参数
- self.params = {
- 'ma_periods': {'short': 5, 'medium': 20, 'long': 60},
- 'rsi_period': 14,
- 'bollinger_period': 20,
- 'bollinger_std': 2,
- 'volume_ma_period': 20,
- 'atr_period': 14
- }
-
-
- def get_stock_data(self, stock_code, market_type='A', start_date=None, end_date=None):
- """获取股票或基金数据"""
- import akshare as ak
-
- if start_date is None:
- start_date = (datetime.now() - timedelta(days=365)).strftime('%Y%m%d')
- if end_date is None:
- end_date = datetime.now().strftime('%Y%m%d')
-
- try:
- # 验证股票代码格式
- if market_type == 'A':
- # 上海证券交易所股票代码以6开头
- # 深圳证券交易所股票代码以0或3开头
- # 科创板股票代码以688开头
- # 北京证券交易所股票代码以8开头
- valid_prefixes = ['0', '3', '6', '688', '8']
- valid_format = False
-
- for prefix in valid_prefixes:
- if stock_code.startswith(prefix):
- valid_format = True
- break
-
- if not valid_format:
- error_msg = f"无效的A股股票代码格式: {stock_code}。A股代码应以0、3、6、688或8开头"
- logger.error(f"[股票代码格式错误] {error_msg}")
- raise ValueError(error_msg)
-
- df = ak.stock_zh_a_hist(
- symbol=stock_code,
- start_date=start_date,
- end_date=end_date,
- adjust="qfq"
- )
- elif market_type == 'HK':
- df = ak.stock_hk_daily(
- symbol=stock_code,
- adjust="qfq"
- )
- elif market_type == 'US':
- df = ak.stock_us_hist(
- symbol=stock_code,
- start_date=start_date,
- end_date=end_date,
- adjust="qfq"
- )
- elif market_type == 'ETF':
- df = ak.fund_etf_hist_em(
- symbol=stock_code,
- period="daily",
- start_date=start_date,
- end_date=end_date,
- adjust="qfq"
- )
- elif market_type == 'LOF':
- df = ak.fund_lof_hist_em(
- symbol=stock_code,
- period="daily",
- start_date=start_date,
- end_date=end_date,
- adjust="qfq"
- )
- else:
- raise ValueError(f"不支持的市场类型: {market_type}")
-
- # 重命名列名以匹配分析需求
- df = df.rename(columns={
- "日期": "date",
- "开盘": "open",
- "收盘": "close",
- "最高": "high",
- "最低": "low",
- "成交量": "volume"
- })
-
- # 确保日期格式正确
- df['date'] = pd.to_datetime(df['date'])
-
- # 数据类型转换
- numeric_columns = ['open', 'close', 'high', 'low', 'volume']
- df[numeric_columns] = df[numeric_columns].apply(pd.to_numeric, errors='coerce')
-
- # 删除空值
- df = df.dropna()
-
- return df.sort_values('date')
-
- # except ValueError as ve:
- # # 捕获格式验证错误
- # logger.error(f"[股票代码格式错误] {str(ve)}")
- # raise Exception(f"股票代码格式错误: {str(ve)}")
- except Exception as e:
- logger.error(f"[获取数据失败] {str(e)}")
- raise Exception(f"获取数据失败: {str(e)}")
-
- def calculate_ema(self, series, period):
- """计算指数移动平均线"""
- return series.ewm(span=period, adjust=False).mean()
-
- def calculate_rsi(self, series, period):
- """计算RSI指标"""
- delta = series.diff()
- gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
- loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
- rs = gain / loss
- return 100 - (100 / (1 + rs))
-
- def calculate_macd(self, series):
- """计算MACD指标"""
- exp1 = series.ewm(span=12, adjust=False).mean()
- exp2 = series.ewm(span=26, adjust=False).mean()
- macd = exp1 - exp2
- signal = macd.ewm(span=9, adjust=False).mean()
- hist = macd - signal
- return macd, signal, hist
-
- def calculate_bollinger_bands(self, series, period, std_dev):
- """计算布林带"""
- middle = series.rolling(window=period).mean()
- std = series.rolling(window=period).std()
- upper = middle + (std * std_dev)
- lower = middle - (std * std_dev)
- return upper, middle, lower
-
- def calculate_atr(self, df, period):
- """计算ATR指标"""
- high = df['high']
- low = df['low']
- close = df['close'].shift(1)
-
- tr1 = high - low
- tr2 = abs(high - close)
- tr3 = abs(low - close)
-
- tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
- return tr.rolling(window=period).mean()
-
- def calculate_indicators(self, df):
- """计算技术指标"""
- try:
- # 计算移动平均线
- df['MA5'] = self.calculate_ema(df['close'], self.params['ma_periods']['short'])
- df['MA20'] = self.calculate_ema(df['close'], self.params['ma_periods']['medium'])
- df['MA60'] = self.calculate_ema(df['close'], self.params['ma_periods']['long'])
-
- # 计算RSI
- df['RSI'] = self.calculate_rsi(df['close'], self.params['rsi_period'])
-
- # 计算MACD
- df['MACD'], df['Signal'], df['MACD_hist'] = self.calculate_macd(df['close'])
-
- # 计算布林带
- df['BB_upper'], df['BB_middle'], df['BB_lower'] = self.calculate_bollinger_bands(
- df['close'],
- self.params['bollinger_period'],
- self.params['bollinger_std']
- )
-
- # 成交量分析
- df['Volume_MA'] = df['volume'].rolling(window=self.params['volume_ma_period']).mean()
- df['Volume_Ratio'] = df['volume'] / df['Volume_MA']
-
- # 计算ATR和波动率
- df['ATR'] = self.calculate_atr(df, self.params['atr_period'])
- df['Volatility'] = df['ATR'] / df['close'] * 100
-
- # 动量指标
- df['ROC'] = df['close'].pct_change(periods=10) * 100
-
- return df
-
- except Exception as e:
- print(f"计算技术指标时出错: {str(e)}")
- raise
-
- def calculate_score(self, df):
- """计算评分"""
- try:
- score = 0
- latest = df.iloc[-1]
-
- # 趋势得分 (30分)
- if latest['MA5'] > latest['MA20']:
- score += 15
- if latest['MA20'] > latest['MA60']:
- score += 15
-
- # RSI得分 (20分)
- if 30 <= latest['RSI'] <= 70:
- score += 20
- elif latest['RSI'] < 30: # 超卖
- score += 15
-
- # MACD得分 (20分)
- if latest['MACD'] > latest['Signal']:
- score += 20
-
- # 成交量得分 (30分)
- if latest['Volume_Ratio'] > 1.5:
- score += 30
- elif latest['Volume_Ratio'] > 1:
- score += 15
-
- return score
-
- except Exception as e:
- print(f"计算评分时出错: {str(e)}")
- raise
-
- def get_ai_analysis(self, df, stock_code, market_type='A', stream=False):
- """使用 OpenAI 进行 AI 分析"""
- try:
- logger.info(f"开始AI分析 {stock_code}, 流式模式: {stream}")
- recent_data = df.tail(14).to_dict('records')
-
- technical_summary = {
- 'trend': 'upward' if df.iloc[-1]['MA5'] > df.iloc[-1]['MA20'] else 'downward',
- 'volatility': f"{df.iloc[-1]['Volatility']:.2f}%",
- 'volume_trend': 'increasing' if df.iloc[-1]['Volume_Ratio'] > 1 else 'decreasing',
- 'rsi_level': df.iloc[-1]['RSI']
- }
-
- # 根据市场类型调整分析提示
- if market_type in ['ETF', 'LOF']:
- prompt = f"""
- 分析基金 {stock_code}:
-
- 技术指标概要:
- {technical_summary}
-
- 近14日交易数据:
- {recent_data}
-
- 请提供:
- 1. 净值走势分析(包含支撑位和压力位)
- 2. 成交量分析及其对净值的影响
- 3. 风险评估(包含波动率和折溢价分析)
- 4. 短期和中期净值预测
- 5. 关键价格位分析
- 6. 申购赎回建议(包含止损位)
-
- 请基于技术指标和市场表现进行分析,给出具体数据支持。
- """
- elif market_type == 'US':
- prompt = f"""
- 分析美股 {stock_code}:
-
- 技术指标概要:
- {technical_summary}
-
- 近14日交易数据:
- {recent_data}
-
- 请提供:
- 1. 趋势分析(包含支撑位和压力位,美元计价)
- 2. 成交量分析及其含义
- 3. 风险评估(包含波动率和美股市场特有风险)
- 4. 短期和中期目标价位(美元)
- 5. 关键技术位分析
- 6. 具体交易建议(包含止损位)
-
- 请基于技术指标和美股市场特点进行分析,给出具体数据支持。
- """
- elif market_type == 'HK':
- prompt = f"""
- 分析港股 {stock_code}:
-
- 技术指标概要:
- {technical_summary}
-
- 近14日交易数据:
- {recent_data}
-
- 请提供:
- 1. 趋势分析(包含支撑位和压力位,港币计价)
- 2. 成交量分析及其含义
- 3. 风险评估(包含波动率和港股市场特有风险)
- 4. 短期和中期目标价位(港币)
- 5. 关键技术位分析
- 6. 具体交易建议(包含止损位)
-
- 请基于技术指标和港股市场特点进行分析,给出具体数据支持。
- """
- else: # A股
- prompt = f"""
- 分析A股 {stock_code}:
-
- 技术指标概要:
- {technical_summary}
-
- 近14日交易数据:
- {recent_data}
-
- 请提供:
- 1. 趋势分析(包含支撑位和压力位)
- 2. 成交量分析及其含义
- 3. 风险评估(包含波动率分析)
- 4. 短期和中期目标价位
- 5. 关键技术位分析
- 6. 具体交易建议(包含止损位)
-
- 请基于技术指标和A股市场特点进行分析,给出具体数据支持。
- """
-
- logger.debug(f"生成的AI分析提示词: {self._truncate_json_for_logging(prompt, 100)}...")
-
- # 检查API配置
- if not self.API_URL:
- error_msg = "API URL未配置,无法进行AI分析"
- logger.error(f"[API配置错误] {error_msg}")
- return error_msg if not stream else (yield json.dumps({"error": error_msg}))
-
- if not self.API_KEY:
- error_msg = "API Key未配置,无法进行AI分析"
- logger.error(f"[API配置错误] {error_msg}")
- return error_msg if not stream else (yield json.dumps({"error": error_msg}))
-
- # 标准化API URL
- api_url = APIUtils.format_api_url(self.API_URL)
-
- logger.debug(f"标准化后的API URL: {api_url}")
-
- # 构建请求头和请求体
- headers = {
- "Authorization": f"Bearer {self.API_KEY}",
- "Content-Type": "application/json"
- }
-
- payload = {
- "model": self.API_MODEL,
- "messages": [{"role": "user", "content": prompt}]
- }
-
- # 流式处理设置
- if stream:
- logger.debug(f"配置流式参数,使用API URL: {api_url}")
- payload["stream"] = True # 明确设置stream参数为True
-
- try:
- logger.debug(f"发起流式API请求: {api_url}")
- logger.debug(f"请求载荷: {self._truncate_json_for_logging(payload)}")
-
- response = requests.post(
- api_url,
- headers=headers,
- json=payload,
- timeout=self.API_TIMEOUT, # 增加超时时间
- stream=True
- )
-
- logger.debug(f"API流式响应状态码: {response.status_code}")
-
- if response.status_code == 200:
- logger.info(f"成功获取API流式响应,开始处理")
- yield from self._process_ai_stream(response, stock_code)
- else:
- try:
- error_response = response.json()
- error_text = self._truncate_json_for_logging(error_response)
- except:
- error_text = response.text[:500] if response.text else "无响应内容"
-
- error_msg = f"API请求失败: 状态码 {response.status_code}, 响应: {error_text}"
- logger.error(f"[API请求失败] {error_msg}")
- yield json.dumps({"stock_code": stock_code, "error": error_msg})
-
- except Exception as e:
- error_msg = f"流式API请求异常: {str(e)}"
- logger.error(f"[流式API异常] {error_msg}")
- logger.exception(e)
- yield json.dumps({"stock_code": stock_code, "error": error_msg})
- else:
- # 非流式处理
- logger.debug(f"发起非流式API请求: {api_url}")
-
- try:
- response = requests.post(
- api_url,
- headers=headers,
- json=payload,
- timeout=self.API_TIMEOUT
- )
-
- logger.debug(f"API非流式响应状态码: {response.status_code}")
-
- if response.status_code == 200:
- api_response = response.json()
- content = api_response['choices'][0]['message']['content']
- logger.info(f"成功获取AI分析结果,长度: {len(content)}")
- logger.debug(f"AI分析结果前100字符: {content[:100]}...")
- return content
- else:
- try:
- error_response = response.json()
- error_text = self._truncate_json_for_logging(error_response)
- except:
- error_text = response.text[:500] if response.text else "无响应内容"
-
- error_msg = f"API请求失败: 状态码 {response.status_code}, 响应: {error_text}"
- logger.error(f"[API请求失败] {error_msg}")
- return error_msg
-
- except Exception as e:
- error_msg = f"非流式API请求异常: {str(e)}"
- logger.error(f"[非流式API异常] {error_msg}")
- logger.exception(e)
- return error_msg
-
- except Exception as e:
- error_msg = f"AI 分析过程中发生错误: {str(e)}"
- logger.error(f"[AI分析异常] {error_msg}")
- logger.exception(e)
-
- if stream:
- logger.debug("在流式模式下返回异常信息")
- error_json = json.dumps({"stock_code": stock_code, "error": error_msg})
- logger.info(f"流式异常输出: {error_json}")
- yield error_json
- else:
- return error_msg
-
- def _truncate_json_for_logging(self, json_obj, max_length=500):
- """截断JSON对象用于日志记录,避免日志过大
-
- Args:
- json_obj: 要截断的JSON对象
- max_length: 最大字符长度,默认500
-
- Returns:
- str: 截断后的JSON字符串
- """
- json_str = json.dumps(json_obj, ensure_ascii=False)
- if len(json_str) <= max_length:
- return json_str
- return json_str[:max_length] + f"... [截断,总长度: {len(json_str)}字符]"
-
- def _process_ai_stream(self, response, stock_code) -> Generator[str, None, None]:
- """处理AI流式响应"""
- logger.info(f"开始处理股票 {stock_code} 的AI流式响应\n")
- buffer = ""
- chunk_count = 0
-
- try:
- for line in response.iter_lines():
- if line:
- line = line.decode('utf-8')
-
- # 跳过保持连接的空行
- if line.strip() == '':
- logger.debug("跳过空行")
- continue
-
- # 数据行通常以"data: "开头
- if line.startswith('data: '):
- data_content = line[6:] # 移除 "data: " 前缀
-
- # 检查是否为流的结束
- if data_content.strip() == '[DONE]':
- logger.debug("收到流结束标记 [DONE]")
- break
-
- try:
- json_data = json.loads(data_content)
-
- # 检查 choices 列表是否为空
- if 'choices' in json_data and json_data['choices']:
- delta = json_data['choices'][0].get('delta', {})
- content = delta.get('content', '')
-
- if content:
- chunk_count += 1
- buffer += content
-
- # 创建包含AI分析片段的JSON
- chunk_json = json.dumps({
- "stock_code": stock_code,
- "ai_analysis_chunk": content
- })
- yield chunk_json
- except json.JSONDecodeError as e:
- logger.error(f"[JSON解析错误] {str(e)}, 行内容: {data_content}")
- # 忽略无法解析的JSON
- pass
- else:
- logger.warning(f"收到非'data:'开头的行: {line}")
-
- logger.info(f"AI流式处理完成,共收到 {chunk_count} 个内容片段,总长度: {len(buffer)}")
-
- # 如果buffer不为空,最后一次发送完整内容
- if buffer and not buffer.endswith('\n'):
- logger.debug("发送换行符")
- yield json.dumps({"stock_code": stock_code, "ai_analysis_chunk": "\n"})
-
- except Exception as e:
- error_msg = f"处理AI流式响应时出错: {str(e)}"
- logger.error(f"[流式响应异常] {error_msg}")
- logger.exception(e)
- yield json.dumps({"stock_code": stock_code, "error": error_msg})
-
-
- def get_recommendation(self, score):
- """根据得分给出建议"""
- logger.debug(f"根据评分 {score} 生成投资建议")
- if score >= 80:
- return '强烈推荐买入'
- elif score >= 60:
- return '建议买入'
- elif score >= 40:
- return '观望'
- elif score >= 20:
- return '建议卖出'
- else:
- return '强烈建议卖出'
-
- def analyze_stock(self, stock_code, market_type='A', stream=False):
- """分析单只"""
- logger.info(f"开始分析 {stock_code}, 市场类型: {market_type}, 流式模式: {stream}")
-
- try:
- # 获取股票数据
- try:
- df = self.get_stock_data(stock_code, market_type)
- except Exception as e:
- # 捕获股票数据获取异常
- error_msg = str(e)
- logger.error(f"[数据获取异常] {error_msg}")
-
- # 格式化错误响应
- error_response = {
- 'stock_code': stock_code,
- 'error': error_msg,
- 'status': 'error',
- 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- }
-
- if stream:
- return (yield json.dumps(error_response))
- else:
- return error_response
-
- # 检查数据是否为空
- if df.empty:
- error_msg = f" {stock_code} 数据为空"
- logger.error(f"[空数据] {error_msg}")
-
- # 格式化错误响应
- error_response = {
- 'stock_code': stock_code,
- 'error': error_msg,
- 'status': 'error',
- 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- }
-
- if stream:
- return (yield json.dumps(error_response))
- else:
- return error_response
-
- # 计算技术指标
- logger.debug(f"计算 {stock_code} 技术指标")
- df = self.calculate_indicators(df)
-
- # 评分系统
- logger.debug(f"计算 {stock_code} 评分")
- score = self.calculate_score(df)
- logger.info(f"{stock_code} 评分结果: {score}")
-
- # 获取最新数据
- latest = df.iloc[-1]
- prev = df.iloc[-2]
-
- # 生成报告
- report = {
- 'stock_code': stock_code,
- 'market_type': market_type, # 添加市场类型
- 'analysis_date': datetime.now().strftime('%Y-%m-%d'),
- 'score': score,
- 'price': latest['close'],
- 'price_change': (latest['close'] - prev['close']) / prev['close'] * 100,
- 'ma_trend': 'UP' if latest['MA5'] > latest['MA20'] else 'DOWN',
- 'rsi': latest['RSI'] if not pd.isna(latest['RSI']) else None,
- 'macd_signal': 'BUY' if latest['MACD'] > latest['Signal'] else 'SELL',
- 'volume_status': 'HIGH' if latest['Volume_Ratio'] > 1.5 else 'NORMAL',
- 'recommendation': self.get_recommendation(score)
- }
- logger.debug(f"生成 {stock_code} 基础报告: {self._truncate_json_for_logging(report, 100)}...")
-
- if stream:
- logger.info(f"以流式模式返回 {stock_code} 分析结果")
- # 先返回基本报告结构
- base_report = dict(report)
- base_report['ai_analysis'] = ''
- base_report_json = json.dumps(base_report)
- logger.debug(f"基础报告JSON: {self._truncate_json_for_logging(base_report_json, 100)}...")
- logger.info(f"发送基础报告: {base_report_json}")
- yield base_report_json
-
- # 然后流式返回AI分析部分
- logger.debug(f"开始获取 {stock_code} 的流式AI分析")
- ai_chunks_count = 0
- for ai_chunk in self.get_ai_analysis(df, stock_code, market_type, stream=True):
- ai_chunks_count += 1
- yield ai_chunk
- logger.info(f" {stock_code} 流式AI分析完成,共发送 {ai_chunks_count} 个块")
- else:
- logger.info(f"以非流式模式返回 {stock_code} 分析结果")
- logger.debug(f"开始获取 {stock_code} 的AI分析")
- report['ai_analysis'] = self.get_ai_analysis(df, stock_code, market_type)
- logger.debug(f"AI分析结果长度: {len(report['ai_analysis'])}")
- return report
-
- except Exception as e:
- error_msg = f"分析 {stock_code} 时出错: {str(e)}\n"
- logger.error(f"[分析异常] {error_msg}")
- logger.exception(e)
-
- # 格式化错误响应
- error_response = {
- 'stock_code': stock_code,
- 'error': error_msg,
- 'status': 'error',
- 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- }
-
- if stream:
- return (yield json.dumps(error_response))
- else:
- return error_response
-
- def scan_stocks(self, stock_codes, market_type='A', min_score=60, stream=False):
- """扫描多只"""
- logger.info(f"开始扫描 {len(stock_codes)} 只, 市场类型: {market_type}, 最低评分: {min_score}, 流式模式: {stream}")
-
- if not stream:
- # 非流式模式
- recommended_stocks = []
- stock_count = 0
- error_count = 0
-
- for stock_code in stock_codes:
- stock_count += 1
- logger.info(f"扫描进度: {stock_count}/{len(stock_codes)}, 当前: {stock_code}")
-
- try:
- logger.debug(f"分析: {stock_code}")
- report = self.analyze_stock(stock_code, market_type)
-
- # 检查是否有错误
- if isinstance(report, dict) and 'error' in report:
- error_count += 1
- logger.warning(f"[扫描错误] {stock_code}: {report['error']}")
- continue
-
- # 检查评分是否达到最低要求
- if report['score'] >= min_score:
- logger.info(f" {stock_code} 评分 {report['score']} >= {min_score},添加到推荐列表")
- recommended_stocks.append(report)
- else:
- logger.debug(f" {stock_code} 评分 {report['score']} < {min_score},不添加到推荐列表")
- except Exception as e:
- error_count += 1
- error_msg = f"分析 {stock_code} 时出错: {str(e)}"
- logger.error(f"[扫描异常] {error_msg}")
- logger.exception(e)
-
- # 添加错误信息到推荐列表,确保前端能看到错误
- error_response = {
- 'stock_code': stock_code,
- 'error': error_msg,
- 'status': 'error',
- 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- }
- recommended_stocks.append(error_response)
- continue
-
- logger.info(f"扫描完成,共 {stock_count} 只,{error_count} 只出错,{len(recommended_stocks)} 只推荐")
- return recommended_stocks
- else:
- # 流式模式
- stock_count = 0
- error_count = 0
-
- for stock_code in stock_codes:
- stock_count += 1
- logger.info(f"流式扫描进度: {stock_count}/{len(stock_codes)}, 当前: {stock_code}")
-
- try:
- chunk_count = 0
- for chunk in self.analyze_stock(stock_code, market_type, stream=True):
- chunk_count += 1
- # 检查是否有错误信息
- try:
- chunk_data = json.loads(chunk)
- if 'error' in chunk_data:
- error_count += 1
- logger.warning(f"[流式扫描错误] {stock_code}: {chunk_data['error']}")
- except:
- pass
- yield chunk
- logger.debug(f" {stock_code} 流式分析完成,共 {chunk_count} 个块")
- except Exception as e:
- error_count += 1
- error_msg = f"分析 {stock_code} 时出错: {str(e)}"
- logger.error(f"[流式扫描异常] {error_msg}")
- logger.exception(e)
-
- # 格式化错误响应
- error_response = {
- 'stock_code': stock_code,
- 'error': error_msg,
- 'status': 'error',
- 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- }
- error_json = json.dumps(error_response)
- logger.info(f"流式错误输出: {error_json}")
- yield error_json
-
- logger.info(f"流式扫描完成,共处理 {stock_count} ,{error_count} 只出错")
diff --git a/us_stock_service.py b/us_stock_service.py
deleted file mode 100644
index 852d565..0000000
--- a/us_stock_service.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import akshare as ak
-import pandas as pd
-
-class USStockService:
-
- def search_us_stocks(self, keyword):
- """
- 搜索美股代码
- :param keyword: 搜索关键词
- :return: 匹配的股票列表
- """
- try:
- # 获取美股数据
- df = ak.stock_us_spot_em()
-
- # 转换列名
- df = df.rename(columns={
- "序号": "index",
- "名称": "name",
- "最新价": "price",
- "涨跌额": "price_change",
- "涨跌幅": "price_change_percent",
- "开盘价": "open",
- "最高价": "high",
- "最低价": "low",
- "昨收价": "pre_close",
- "总市值": "market_value",
- "市盈率": "pe_ratio",
- "成交量": "volume",
- "成交额": "turnover",
- "振幅": "amplitude",
- "换手率": "turnover_rate",
- "代码": "symbol"
- })
-
- # 模糊匹配搜索
- mask = df['name'].str.contains(keyword, case=False, na=False)
- results = df[mask]
-
- # 格式化返回结果并处理 NaN 值
- formatted_results = []
- for _, row in results.iterrows():
- formatted_results.append({
- 'name': row['name'] if pd.notna(row['name']) else '',
- 'symbol': str(row['symbol']) if pd.notna(row['symbol']) else '',
- 'price': float(row['price']) if pd.notna(row['price']) else 0.0,
- 'market_value': float(row['market_value']) if pd.notna(row['market_value']) else 0.0
- })
-
- return formatted_results
-
- except Exception as e:
- raise Exception(f"搜索美股代码失败: {str(e)}")
\ No newline at end of file
diff --git a/utils/api_control.py b/utils/api_control.py
deleted file mode 100644
index ba10342..0000000
--- a/utils/api_control.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env python
-# -*- encoding: utf-8 -*-
-
-from typing import Optional, Any
-from fastapi import Response
-from pydantic import BaseModel
-
-
-class ResponseModel(BaseModel):
- """
- 统一返回模型
- """
- code: int = 200
- msg: str = "Success"
- data: Optional[Any] = None
-
-
-class ApiResponse:
- @staticmethod
- def __response(code: int, msg: str, data: Optional[Any] = None) -> ResponseModel:
- return ResponseModel(code=code, msg=msg, data=data)
-
- @classmethod
- def success(cls, *, code: int = 200, msg: str = 'Success', data: Optional[Any] = None) -> Response:
- response_model = cls.__response(code=code, msg=msg, data=data)
- return cls(content=response_model.model_dump())
-
- @classmethod
- def fail(cls, *, code: int = 400, msg: str = 'Bad Request', data: Optional[Any] = None) -> Response:
- response_model = cls.__response(code=code, msg=msg, data=data)
- return cls(content=response_model.model_dump())
-
-response_api = ApiResponse()
-
-""" 示例
-@app.get("/example-success")
-async def example_success():
- return response_api.success(data={"key": "value"})
-
-@app.get("/example-fail")
-async def example_fail():
- return response_api.fail(msg="Something went wrong", data={"error": "details"})
-"""
diff --git a/web_server.py b/web_server.py
index 5550a49..8e06659 100644
--- a/web_server.py
+++ b/web_server.py
@@ -2,28 +2,40 @@ from fastapi import FastAPI, Request, Response, Depends, HTTPException, Backgrou
from fastapi.responses import JSONResponse, StreamingResponse, FileResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
+from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any, Generator
from services.stock_analyzer_service import StockAnalyzerService
-# 导入新的异步服务
from services.us_stock_service_async import USStockServiceAsync
from services.fund_service_async import FundServiceAsync
-import asyncio
-import threading
import os
-import traceback
import httpx
from logger import get_logger
from utils.api_utils import APIUtils
-# 加载环境变量
-from dotenv import load_dotenv
+from dotenv import load_dotenv, dotenv_values
import uvicorn
+import json
+import secrets
+from datetime import datetime, timedelta
+from jose import JWTError, jwt
load_dotenv()
# 获取日志器
logger = get_logger()
+# JWT相关配置
+SECRET_KEY = os.getenv("JWT_SECRET_KEY", secrets.token_hex(32))
+ALGORITHM = "HS256"
+ACCESS_TOKEN_EXPIRE_MINUTES = 10080 # Token过期时间一周
+
+LOGIN_PASSWORD = os.getenv("LOGIN_PASSWORD", "")
+print(LOGIN_PASSWORD)
+
+# 是否需要登录
+REQUIRE_LOGIN = bool(LOGIN_PASSWORD.strip())
+
+
app = FastAPI(
title="Stock Scanner API",
description="异步股票分析API",
@@ -42,7 +54,7 @@ app.add_middleware(
# 设置静态文件
frontend_dist = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'frontend', 'dist')
if os.path.exists(frontend_dist):
- app.mount("/", StaticFiles(directory=frontend_dist, html=True), name="frontend")
+ app.mount("/assets", StaticFiles(directory=os.path.join(frontend_dist, "assets")), name="assets")
# 初始化异步服务
# StockAnalyzerService 不需要全局初始化,在 /analyze 接口中按需创建
@@ -64,35 +76,121 @@ class TestAPIRequest(BaseModel):
api_model: Optional[str] = None
api_timeout: Optional[int] = 10
-@app.get("/")
-async def index(request: Request):
- # 检查是否使用前端构建版本
- if os.path.exists(frontend_dist):
- index_file = os.path.join(frontend_dist, 'index.html')
- return FileResponse(index_file)
- else:
- # 不再使用模板渲染,而是重定向到API文档页面
- logger.warning("前端构建目录不存在,重定向到API文档页面")
- return RedirectResponse(url="/docs")
+class LoginRequest(BaseModel):
+ password: str
+class Token(BaseModel):
+ access_token: str
+ token_type: str
+
+# 自定义依赖项,在REQUIRE_LOGIN=False时不要求token
+class OptionalOAuth2PasswordBearer(OAuth2PasswordBearer):
+ async def __call__(self, request: Request) -> Optional[str]:
+ if not REQUIRE_LOGIN:
+ return None
+ try:
+ return await super().__call__(request)
+ except HTTPException:
+ if not REQUIRE_LOGIN:
+ return None
+ raise
+
+# 使用自定义的依赖项
+optional_oauth2_scheme = OptionalOAuth2PasswordBearer(tokenUrl="login")
+
+# 创建访问令牌
+def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
+ to_encode = data.copy()
+ if expires_delta:
+ expire = datetime.utcnow() + expires_delta
+ else:
+ expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
+ to_encode.update({"exp": expire})
+ encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
+ return encoded_jwt
+
+# 验证令牌
+async def verify_token(token: Optional[str] = Depends(optional_oauth2_scheme)):
+ # 如果未设置密码,则不需要验证
+ if not REQUIRE_LOGIN:
+ return "guest"
+
+ # 如果没有token且不需要登录,返回guest
+ if token is None and not REQUIRE_LOGIN:
+ return "guest"
+
+ credentials_exception = HTTPException(
+ status_code=401,
+ detail="无效的认证凭据",
+ headers={"WWW-Authenticate": "Bearer"},
+ )
+
+ # 如果需要登录但没有token,抛出异常
+ if token is None:
+ raise credentials_exception
+
+ try:
+ payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
+ username: str = payload.get("sub")
+ if username is None:
+ raise credentials_exception
+ return username
+ except JWTError:
+ raise credentials_exception
+
+# 用户登录接口
+@app.post("/login")
+async def login(request: LoginRequest):
+ """用户登录接口"""
+ # 如果未设置密码,表示不需要登录
+ if not REQUIRE_LOGIN:
+ access_token = create_access_token(data={"sub": "guest"})
+ return {"access_token": access_token, "token_type": "bearer"}
+
+ if request.password != LOGIN_PASSWORD:
+ logger.warning("登录失败:密码错误")
+ raise HTTPException(status_code=401, detail="密码错误")
+
+ # 创建访问令牌
+ access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
+ access_token = create_access_token(
+ data={"sub": "user"}, expires_delta=access_token_expires
+ )
+ logger.info("用户登录成功")
+ return {"access_token": access_token, "token_type": "bearer"}
+
+# 检查用户认证状态
+@app.get("/check_auth")
+async def check_auth(username: str = Depends(verify_token)):
+ """检查用户认证状态"""
+ return {"authenticated": True, "username": username}
+
+# 获取系统配置
@app.get("/config")
async def get_config():
"""返回系统配置信息"""
config = {
'announcement': os.getenv('ANNOUNCEMENT_TEXT') or '',
'default_api_url': os.getenv('API_URL', ''),
- 'default_api_model': os.getenv('API_MODEL', 'gpt-3.5-turbo'),
+ 'default_api_model': os.getenv('API_MODEL', ''),
'default_api_timeout': os.getenv('API_TIMEOUT', '60')
}
return config
+# AI分析股票
@app.post("/analyze")
-async def analyze(request: AnalyzeRequest):
+async def analyze(request: AnalyzeRequest, username: str = Depends(verify_token)):
try:
logger.info("开始处理分析请求")
stock_codes = request.stock_codes
market_type = request.market_type
+ # 后端再次去重,确保安全
+ original_count = len(stock_codes)
+ stock_codes = list(dict.fromkeys(stock_codes)) # 保持原有顺序的去重方法
+ if len(stock_codes) < original_count:
+ logger.info(f"后端去重: 从{original_count}个代码中移除了{original_count - len(stock_codes)}个重复项")
+
logger.debug(f"接收到分析请求: stock_codes={stock_codes}, market_type={market_type}")
# 获取自定义API配置
@@ -122,7 +220,8 @@ async def analyze(request: AnalyzeRequest):
stock_code = stock_codes[0].strip()
logger.info(f"开始单股流式分析: {stock_code}")
- init_message = f'{{"stream_type": "single", "stock_code": "{stock_code}"}}\n'
+ stock_code_json = json.dumps(stock_code)
+ init_message = f'{{"stream_type": "single", "stock_code": {stock_code_json}}}\n'
yield init_message
logger.debug(f"开始处理股票 {stock_code} 的流式响应")
@@ -138,7 +237,8 @@ async def analyze(request: AnalyzeRequest):
# 批量分析流式处理
logger.info(f"开始批量流式分析: {stock_codes}")
- init_message = f'{{"stream_type": "batch", "stock_codes": {stock_codes}}}\n'
+ stock_codes_json = json.dumps(stock_codes)
+ init_message = f'{{"stream_type": "batch", "stock_codes": {stock_codes_json}}}\n'
yield init_message
logger.debug(f"开始处理批量股票的流式响应")
@@ -165,8 +265,9 @@ async def analyze(request: AnalyzeRequest):
logger.exception(e)
raise HTTPException(status_code=500, detail=error_msg)
+# 搜索美股代码
@app.get("/search_us_stocks")
-async def search_us_stocks(keyword: str = ""):
+async def search_us_stocks(keyword: str = "", username: str = Depends(verify_token)):
try:
if not keyword:
raise HTTPException(status_code=400, detail="请输入搜索关键词")
@@ -179,8 +280,9 @@ async def search_us_stocks(keyword: str = ""):
logger.error(f"搜索美股代码时出错: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
+# 搜索基金代码
@app.get("/search_funds")
-async def search_funds(keyword: str = "", market_type: str = ""):
+async def search_funds(keyword: str = "", market_type: str = "", username: str = Depends(verify_token)):
try:
if not keyword:
raise HTTPException(status_code=400, detail="请输入搜索关键词")
@@ -193,8 +295,39 @@ async def search_funds(keyword: str = "", market_type: str = ""):
logger.error(f"搜索基金代码时出错: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
+# 获取美股详情
+@app.get("/us_stock_detail/{symbol}")
+async def get_us_stock_detail(symbol: str, username: str = Depends(verify_token)):
+ try:
+ if not symbol:
+ raise HTTPException(status_code=400, detail="请提供股票代码")
+
+ # 使用异步服务获取详情
+ detail = await us_stock_service.get_us_stock_detail(symbol)
+ return detail
+
+ except Exception as e:
+ logger.error(f"获取美股详情时出错: {str(e)}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+# 获取基金详情
+@app.get("/fund_detail/{symbol}")
+async def get_fund_detail(symbol: str, market_type: str = "ETF", username: str = Depends(verify_token)):
+ try:
+ if not symbol:
+ raise HTTPException(status_code=400, detail="请提供基金代码")
+
+ # 使用异步服务获取详情
+ detail = await fund_service.get_fund_detail(symbol, market_type)
+ return detail
+
+ except Exception as e:
+ logger.error(f"获取基金详情时出错: {str(e)}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+# 测试API连接
@app.post("/test_api_connection")
-async def test_api_connection(request: TestAPIRequest):
+async def test_api_connection(request: TestAPIRequest, username: str = Depends(verify_token)):
"""测试API连接"""
try:
logger.info("开始测试API连接")
@@ -226,7 +359,7 @@ async def test_api_connection(request: TestAPIRequest):
"Content-Type": "application/json"
},
json={
- "model": api_model or "gpt-3.5-turbo",
+ "model": api_model or "",
"messages": [
{"role": "user", "content": "Hello, this is a test message. Please respond with 'API connection successful'."}
],
@@ -261,36 +394,34 @@ async def test_api_connection(request: TestAPIRequest):
content={"success": False, "message": f"API 测试连接时出错: {str(e)}"}
)
-# 新增 API 端点:获取美股详情
-@app.get("/us_stock_detail/{symbol}")
-async def get_us_stock_detail(symbol: str):
- try:
- if not symbol:
- raise HTTPException(status_code=400, detail="请提供股票代码")
-
- # 使用异步服务获取详情
- detail = await us_stock_service.get_us_stock_detail(symbol)
- return detail
-
- except Exception as e:
- logger.error(f"获取美股详情时出错: {str(e)}")
- raise HTTPException(status_code=500, detail=str(e))
+# 检查是否需要登录
+@app.get("/need_login")
+async def need_login():
+ """检查是否需要登录"""
+ return {"require_login": REQUIRE_LOGIN}
-# 新增 API 端点:获取基金详情
-@app.get("/fund_detail/{symbol}")
-async def get_fund_detail(symbol: str, market_type: str = "ETF"):
- try:
- if not symbol:
- raise HTTPException(status_code=400, detail="请提供基金代码")
-
- # 使用异步服务获取详情
- detail = await fund_service.get_fund_detail(symbol, market_type)
- return detail
-
- except Exception as e:
- logger.error(f"获取基金详情时出错: {str(e)}")
- raise HTTPException(status_code=500, detail=str(e))
+# 前端路由处理,必须放在所有API路由之后
+@app.get("/{full_path:path}")
+async def serve_frontend(full_path: str, request: Request):
+ """处理所有前端路由请求,返回index.html"""
+ # 排除API路径和静态资源
+ if full_path.startswith(("api/", "assets/", "docs", "openapi.json")) or \
+ full_path in ["check_auth", "config", "analyze",
+ "search_us_stocks", "search_funds",
+ "test_api_connection", "us_stock_detail",
+ "fund_detail"]:
+ # 对于API路径,让FastAPI继续处理
+ raise HTTPException(status_code=404, detail="API路径不存在")
+
+ # 检查是否使用前端构建版本
+ if os.path.exists(frontend_dist):
+ index_file = os.path.join(frontend_dist, 'index.html')
+ return FileResponse(index_file)
+ else:
+ # 不再使用模板渲染,而是重定向到API文档页面
+ logger.warning("前端构建目录不存在,重定向到API文档页面")
+ return RedirectResponse(url="/docs")
if __name__ == '__main__':
- logger.info("股票分析系统启动")
+ logger.info("股票AI分析系统启动")
uvicorn.run("web_server:app", host="127.0.0.1", port=8888, reload=True)
\ No newline at end of file