diff --git a/apps/server/controllers/index.ts b/apps/server/controllers/index.ts index 90bf3e8..dcc4985 100644 --- a/apps/server/controllers/index.ts +++ b/apps/server/controllers/index.ts @@ -3,4 +3,5 @@ export { ProjectController } from './project/index.ts'; export { UserController } from './user/index.ts'; export { AuthController } from './auth/index.ts'; export { DeploymentController } from './deployment/index.ts'; -export { PipelineController } from './pipeline/index.ts' +export { PipelineController } from './pipeline/index.ts'; +export { StepController } from './step/index.ts' diff --git a/apps/server/controllers/pipeline/index.ts b/apps/server/controllers/pipeline/index.ts index 1f44bad..aa67fb1 100644 --- a/apps/server/controllers/pipeline/index.ts +++ b/apps/server/controllers/pipeline/index.ts @@ -1,22 +1,175 @@ import type { Context } from 'koa'; -import { Controller, Get, Post } from '../../decorators/route.ts'; +import { Controller, Get, Post, Put, Delete } from '../../decorators/route.ts'; import prisma from '../../libs/db.ts'; +import { log } from '../../libs/logger.ts'; +import { BusinessError } from '../../middlewares/exception.ts'; +import { + createPipelineSchema, + updatePipelineSchema, + pipelineIdSchema, + listPipelinesQuerySchema +} from './schema.ts'; @Controller('/pipelines') export class PipelineController { - @Get('/:id') - async get(ctx: Context) { - const id = ctx.params.id; - const pipeline = await prisma.pipeline.findUnique({ - where: { - id: id, + // GET /api/pipelines - 获取流水线列表 + @Get('') + async list(ctx: Context) { + const query = listPipelinesQuerySchema.parse(ctx.query); + + const whereCondition: any = { + valid: 1, + }; + + // 如果提供了项目ID参数 + if (query?.projectId) { + whereCondition.projectId = query.projectId; + } + + const pipelines = await prisma.pipeline.findMany({ + where: whereCondition, + include: { + steps: { + where: { + valid: 1, + }, + orderBy: { + order: 'asc', + }, + }, }, }); + + return pipelines; + } + + // GET /api/pipelines/:id - 获取单个流水线 + @Get('/:id') + async get(ctx: Context) { + const { id } = pipelineIdSchema.parse(ctx.params); + + const pipeline = await prisma.pipeline.findFirst({ + where: { + id, + valid: 1, + }, + include: { + steps: { + where: { + valid: 1, + }, + orderBy: { + order: 'asc', + }, + }, + }, + }); + + if (!pipeline) { + throw new BusinessError('流水线不存在', 3001, 404); + } + return pipeline; } + // POST /api/pipelines - 创建流水线 @Post('') async create(ctx: Context) { + const validatedData = createPipelineSchema.parse(ctx.request.body); + const pipeline = await prisma.pipeline.create({ + data: { + name: validatedData.name, + description: validatedData.description || '', + projectId: validatedData.projectId, + createdBy: 'system', + updatedBy: 'system', + valid: 1, + }, + }); + + log.info('pipeline', 'Created new pipeline: %s', pipeline.name); + return pipeline; + } + + // PUT /api/pipelines/:id - 更新流水线 + @Put('/:id') + async update(ctx: Context) { + const { id } = pipelineIdSchema.parse(ctx.params); + const validatedData = updatePipelineSchema.parse(ctx.request.body); + + // 检查流水线是否存在 + const existingPipeline = await prisma.pipeline.findFirst({ + where: { + id, + valid: 1, + }, + }); + + if (!existingPipeline) { + throw new BusinessError('流水线不存在', 3001, 404); + } + + // 只更新提供的字段 + const updateData: any = { + updatedBy: 'system', + }; + + if (validatedData.name !== undefined) { + updateData.name = validatedData.name; + } + if (validatedData.description !== undefined) { + updateData.description = validatedData.description; + } + + const pipeline = await prisma.pipeline.update({ + where: { id }, + data: updateData, + }); + + log.info('pipeline', 'Updated pipeline: %s', pipeline.name); + return pipeline; + } + + // DELETE /api/pipelines/:id - 删除流水线(软删除) + @Delete('/:id') + async destroy(ctx: Context) { + const { id } = pipelineIdSchema.parse(ctx.params); + + // 检查流水线是否存在 + const existingPipeline = await prisma.pipeline.findFirst({ + where: { + id, + valid: 1, + }, + }); + + if (!existingPipeline) { + throw new BusinessError('流水线不存在', 3001, 404); + } + + // 软删除:将 valid 设置为 0 + await prisma.pipeline.update({ + where: { id }, + data: { + valid: 0, + updatedBy: 'system', + }, + }); + + // 同时软删除关联的步骤 + await prisma.step.updateMany({ + where: { pipelineId: id }, + data: { + valid: 0, + updatedBy: 'system', + }, + }); + + log.info('pipeline', 'Deleted pipeline: %s', existingPipeline.name); + + // RESTful 删除成功返回 204 No Content + ctx.status = 204; + return null; } } diff --git a/apps/server/controllers/pipeline/schema.ts b/apps/server/controllers/pipeline/schema.ts new file mode 100644 index 0000000..bbe0637 --- /dev/null +++ b/apps/server/controllers/pipeline/schema.ts @@ -0,0 +1,34 @@ +import { z } from 'zod'; + +// 定义验证架构 +export const createPipelineSchema = z.object({ + name: z.string({ + message: '流水线名称必须是字符串', + }).min(1, { message: '流水线名称不能为空' }).max(100, { message: '流水线名称不能超过100个字符' }), + + description: z.string({ + message: '流水线描述必须是字符串', + }).max(500, { message: '流水线描述不能超过500个字符' }).optional(), + + projectId: z.number({ + message: '项目ID必须是数字', + }).int().positive({ message: '项目ID必须是正整数' }).optional(), +}); + +export const updatePipelineSchema = z.object({ + name: z.string({ + message: '流水线名称必须是字符串', + }).min(1, { message: '流水线名称不能为空' }).max(100, { message: '流水线名称不能超过100个字符' }).optional(), + + description: z.string({ + message: '流水线描述必须是字符串', + }).max(500, { message: '流水线描述不能超过500个字符' }).optional(), +}); + +export const pipelineIdSchema = z.object({ + id: z.coerce.number().int().positive({ message: '流水线 ID 必须是正整数' }), +}); + +export const listPipelinesQuerySchema = z.object({ + projectId: z.coerce.number().int().positive({ message: '项目ID必须是正整数' }).optional(), +}).optional(); diff --git a/apps/server/controllers/pipeline/types.ts b/apps/server/controllers/pipeline/types.ts new file mode 100644 index 0000000..331e653 --- /dev/null +++ b/apps/server/controllers/pipeline/types.ts @@ -0,0 +1,13 @@ +import type { z } from 'zod'; +import type { + createPipelineSchema, + updatePipelineSchema, + pipelineIdSchema, + listPipelinesQuerySchema +} from './schema.js'; + +// TypeScript 类型 +export type CreatePipelineInput = z.infer; +export type UpdatePipelineInput = z.infer; +export type PipelineIdParams = z.infer; +export type ListPipelinesQuery = z.infer; diff --git a/apps/server/controllers/step/index.ts b/apps/server/controllers/step/index.ts new file mode 100644 index 0000000..2350209 --- /dev/null +++ b/apps/server/controllers/step/index.ts @@ -0,0 +1,234 @@ +import type { Context } from 'koa'; +import prisma from '../../libs/db.ts'; +import { log } from '../../libs/logger.ts'; +import { BusinessError } from '../../middlewares/exception.ts'; +import { Controller, Get, Post, Put, Delete } from '../../decorators/route.ts'; +import { z } from 'zod'; + +// 定义验证架构 +const createStepSchema = z.object({ + name: z.string({ + message: '步骤名称必须是字符串', + }).min(1, { message: '步骤名称不能为空' }).max(100, { message: '步骤名称不能超过100个字符' }), + + description: z.string({ + message: '步骤描述必须是字符串', + }).max(500, { message: '步骤描述不能超过500个字符' }).optional(), + + order: z.number({ + message: '步骤顺序必须是数字', + }).int().min(0, { message: '步骤顺序必须是非负整数' }), + + script: z.string({ + message: '脚本命令必须是字符串', + }).min(1, { message: '脚本命令不能为空' }), + + pipelineId: z.number({ + message: '流水线ID必须是数字', + }).int().positive({ message: '流水线ID必须是正整数' }), +}); + +const updateStepSchema = z.object({ + name: z.string({ + message: '步骤名称必须是字符串', + }).min(1, { message: '步骤名称不能为空' }).max(100, { message: '步骤名称不能超过100个字符' }).optional(), + + description: z.string({ + message: '步骤描述必须是字符串', + }).max(500, { message: '步骤描述不能超过500个字符' }).optional(), + + order: z.number({ + message: '步骤顺序必须是数字', + }).int().min(0, { message: '步骤顺序必须是非负整数' }).optional(), + + script: z.string({ + message: '脚本命令必须是字符串', + }).min(1, { message: '脚本命令不能为空' }).optional(), +}); + +const stepIdSchema = z.object({ + id: z.coerce.number().int().positive({ message: '步骤 ID 必须是正整数' }), +}); + +const listStepsQuerySchema = z.object({ + pipelineId: z.coerce.number().int().positive({ message: '流水线ID必须是正整数' }).optional(), + page: z.coerce.number().int().min(1, { message: '页码必须大于0' }).optional().default(1), + limit: z.coerce.number().int().min(1, { message: '每页数量必须大于0' }).max(100, { message: '每页数量不能超过100' }).optional().default(10), +}).optional(); + +// TypeScript 类型 +type CreateStepInput = z.infer; +type UpdateStepInput = z.infer; +type StepIdParams = z.infer; +type ListStepsQuery = z.infer; + +@Controller('/steps') +export class StepController { + // GET /api/steps - 获取步骤列表 + @Get('') + async list(ctx: Context) { + const query = listStepsQuerySchema.parse(ctx.query); + + const whereCondition: any = { + valid: 1, + }; + + // 如果提供了流水线ID参数 + if (query?.pipelineId) { + whereCondition.pipelineId = query.pipelineId; + } + + const [total, steps] = await Promise.all([ + prisma.step.count({ where: whereCondition }), + prisma.step.findMany({ + where: whereCondition, + skip: query ? (query.page - 1) * query.limit : 0, + take: query?.limit, + orderBy: { + order: 'asc', + }, + }) + ]); + + return { + data: steps, + pagination: { + page: query?.page || 1, + limit: query?.limit || 10, + total, + totalPages: Math.ceil(total / (query?.limit || 10)), + } + }; + } + + // GET /api/steps/:id - 获取单个步骤 + @Get(':id') + async show(ctx: Context) { + const { id } = stepIdSchema.parse(ctx.params); + + const step = await prisma.step.findFirst({ + where: { + id, + valid: 1, + }, + }); + + if (!step) { + throw new BusinessError('步骤不存在', 2001, 404); + } + + return step; + } + + // POST /api/steps - 创建步骤 + @Post('') + async create(ctx: Context) { + const validatedData = createStepSchema.parse(ctx.request.body); + + // 检查关联的流水线是否存在 + const pipeline = await prisma.pipeline.findFirst({ + where: { + id: validatedData.pipelineId, + valid: 1, + }, + }); + + if (!pipeline) { + throw new BusinessError('关联的流水线不存在', 2002, 404); + } + + const step = await prisma.step.create({ + data: { + name: validatedData.name, + description: validatedData.description || '', + order: validatedData.order, + script: validatedData.script, + pipelineId: validatedData.pipelineId, + createdBy: 'system', + updatedBy: 'system', + valid: 1, + }, + }); + + log.info('step', 'Created new step: %s', step.name); + return step; + } + + // PUT /api/steps/:id - 更新步骤 + @Put(':id') + async update(ctx: Context) { + const { id } = stepIdSchema.parse(ctx.params); + const validatedData = updateStepSchema.parse(ctx.request.body); + + // 检查步骤是否存在 + const existingStep = await prisma.step.findFirst({ + where: { + id, + valid: 1, + }, + }); + + if (!existingStep) { + throw new BusinessError('步骤不存在', 2001, 404); + } + + // 只更新提供的字段 + const updateData: any = { + updatedBy: 'system', + }; + + if (validatedData.name !== undefined) { + updateData.name = validatedData.name; + } + if (validatedData.description !== undefined) { + updateData.description = validatedData.description; + } + if (validatedData.order !== undefined) { + updateData.order = validatedData.order; + } + if (validatedData.script !== undefined) { + updateData.script = validatedData.script; + } + + const step = await prisma.step.update({ + where: { id }, + data: updateData, + }); + + log.info('step', 'Updated step: %s', step.name); + return step; + } + + // DELETE /api/steps/:id - 删除步骤(软删除) + @Delete(':id') + async destroy(ctx: Context) { + const { id } = stepIdSchema.parse(ctx.params); + + // 检查步骤是否存在 + const existingStep = await prisma.step.findFirst({ + where: { + id, + valid: 1, + }, + }); + + if (!existingStep) { + throw new BusinessError('步骤不存在', 2001, 404); + } + + // 软删除:将 valid 设置为 0 + await prisma.step.update({ + where: { id }, + data: { + valid: 0, + updatedBy: 'system', + }, + }); + + log.info('step', 'Deleted step: %s', existingStep.name); + + // RESTful 删除成功返回 204 No Content + ctx.status = 204; + return null; + } +} diff --git a/apps/server/middlewares/router.ts b/apps/server/middlewares/router.ts index b1c6f14..374d53e 100644 --- a/apps/server/middlewares/router.ts +++ b/apps/server/middlewares/router.ts @@ -7,7 +7,8 @@ import { UserController, AuthController, DeploymentController, - PipelineController + PipelineController, + StepController } from '../controllers/index.ts'; import { log } from '../libs/logger.ts'; @@ -41,7 +42,8 @@ export class Router implements Middleware { UserController, AuthController, DeploymentController, - PipelineController + PipelineController, + StepController ]); // 输出注册的路由信息 diff --git a/apps/server/prisma/data/dev.db b/apps/server/prisma/data/dev.db index 0b123cc..f70f1d6 100644 Binary files a/apps/server/prisma/data/dev.db and b/apps/server/prisma/data/dev.db differ diff --git a/apps/server/prisma/schema.prisma b/apps/server/prisma/schema.prisma index 4e15c72..d751968 100644 --- a/apps/server/prisma/schema.prisma +++ b/apps/server/prisma/schema.prisma @@ -60,9 +60,8 @@ model Pipeline { model Step { id Int @id @default(autoincrement()) name String - description String? order Int - status String? + script String // 执行的脚本命令 valid Int @default(1) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/apps/web/package.json b/apps/web/package.json index 6780c1f..9641048 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -11,21 +11,21 @@ "preview": "rsbuild preview" }, "dependencies": { - "@arco-design/web-react": "^2.66.4", + "@arco-design/web-react": "^2.66.8", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "axios": "^1.11.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-router": "^7.8.0", "zustand": "^5.0.8" }, "devDependencies": { "@arco-plugins/unplugin-react": "2.0.0-beta.5", - "@rsbuild/core": "^1.4.13", - "@rsbuild/plugin-less": "^1.4.0", - "@rsbuild/plugin-react": "^1.3.4", + "@rsbuild/core": "^1.6.7", + "@rsbuild/plugin-less": "^1.5.0", + "@rsbuild/plugin-react": "^1.4.2", "@rsbuild/plugin-svgr": "^1.2.2", "@tailwindcss/postcss": "^4.1.11", "@types/react": "^18.3.24", diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs index 27ae1c9..a34a3d5 100644 --- a/apps/web/postcss.config.mjs +++ b/apps/web/postcss.config.mjs @@ -2,4 +2,4 @@ export default { plugins: { '@tailwindcss/postcss': {}, }, -}; \ No newline at end of file +}; diff --git a/apps/web/rsbuild.config.ts b/apps/web/rsbuild.config.ts index 9be59b3..09dc38c 100644 --- a/apps/web/rsbuild.config.ts +++ b/apps/web/rsbuild.config.ts @@ -1,7 +1,7 @@ import { ArcoDesignPlugin } from '@arco-plugins/unplugin-react'; import { defineConfig } from '@rsbuild/core'; -import { pluginReact } from '@rsbuild/plugin-react'; import { pluginLess } from '@rsbuild/plugin-less'; +import { pluginReact } from '@rsbuild/plugin-react'; import { pluginSvgr } from '@rsbuild/plugin-svgr'; export default defineConfig({ diff --git a/apps/web/src/hooks/useAsyncEffect.ts b/apps/web/src/hooks/useAsyncEffect.ts index 6f5b63f..91cbdd9 100644 --- a/apps/web/src/hooks/useAsyncEffect.ts +++ b/apps/web/src/hooks/useAsyncEffect.ts @@ -1,10 +1,18 @@ -import React, { useEffect } from 'react'; +import type React from 'react'; +import { useEffect, useCallback } from 'react'; export function useAsyncEffect( - effect: () => Promise, + effect: () => Promise void)>, deps: React.DependencyList, ) { + const callback = useCallback(effect, [...deps]); + useEffect(() => { - effect(); - }, [...deps]); + const cleanupPromise = callback(); + return () => { + if (cleanupPromise instanceof Promise) { + cleanupPromise.then(cleanup => cleanup && cleanup()); + } + }; + }, [callback]); } diff --git a/apps/web/src/index.tsx b/apps/web/src/index.tsx index b0b138b..9bd7d23 100644 --- a/apps/web/src/index.tsx +++ b/apps/web/src/index.tsx @@ -1,5 +1,5 @@ -import ReactDOM from 'react-dom/client'; import App from '@pages/App'; +import ReactDOM from 'react-dom/client'; import { BrowserRouter } from 'react-router'; import { useGlobalStore } from './stores/global'; diff --git a/apps/web/src/pages/App.tsx b/apps/web/src/pages/App.tsx index 8f8ae7a..77bf89f 100644 --- a/apps/web/src/pages/App.tsx +++ b/apps/web/src/pages/App.tsx @@ -1,9 +1,9 @@ -import { Route, Routes, Navigate } from 'react-router'; +import Env from '@pages/env'; import Home from '@pages/home'; import Login from '@pages/login'; -import ProjectList from '@pages/project/list'; import ProjectDetail from '@pages/project/detail'; -import Env from '@pages/env'; +import ProjectList from '@pages/project/list'; +import { Navigate, Route, Routes } from 'react-router'; import '@styles/index.css'; const App = () => { diff --git a/apps/web/src/pages/env/index.tsx b/apps/web/src/pages/env/index.tsx index 98225e5..2ce4447 100644 --- a/apps/web/src/pages/env/index.tsx +++ b/apps/web/src/pages/env/index.tsx @@ -1,12 +1,5 @@ -import { useState } from "react"; - function Env() { - const [env, setEnv] = useState([]); - return ( -
- env page -
- ) + return
env page
; } export default Env; diff --git a/apps/web/src/pages/home/index.tsx b/apps/web/src/pages/home/index.tsx index 0a317e8..ac8e355 100644 --- a/apps/web/src/pages/home/index.tsx +++ b/apps/web/src/pages/home/index.tsx @@ -6,11 +6,11 @@ import { IconMenuUnfold, IconRobot, } from '@arco-design/web-react/icon'; -import { useState } from 'react'; import Logo from '@assets/images/logo.svg?react'; +import { loginService } from '@pages/login/service'; +import { useState } from 'react'; import { Link, Outlet } from 'react-router'; import { useGlobalStore } from '../../stores/global'; -import { loginService } from '@pages/login/service'; function Home() { const [collapsed, setCollapsed] = useState(false); diff --git a/apps/web/src/pages/login/index.tsx b/apps/web/src/pages/login/index.tsx index 9b6249e..c478d39 100644 --- a/apps/web/src/pages/login/index.tsx +++ b/apps/web/src/pages/login/index.tsx @@ -1,11 +1,11 @@ import { Button } from '@arco-design/web-react'; import Gitea from '@assets/images/gitea.svg?react'; -import { loginService } from './service'; import { useEffect } from 'react'; import { useNavigate, useSearchParams } from 'react-router'; +import { loginService } from './service'; function Login() { - const [ searchParams ] = useSearchParams(); + const [searchParams] = useSearchParams(); const navigate = useNavigate(); const authCode = searchParams.get('code'); diff --git a/apps/web/src/pages/login/service.ts b/apps/web/src/pages/login/service.ts index 687cb4a..92f2539 100644 --- a/apps/web/src/pages/login/service.ts +++ b/apps/web/src/pages/login/service.ts @@ -1,8 +1,8 @@ -import { net } from '@shared'; -import type { AuthURLResponse, AuthLoginResponse } from './types'; -import type { NavigateFunction } from 'react-router'; import { Message, Notification } from '@arco-design/web-react'; +import { net } from '@shared'; +import type { NavigateFunction } from 'react-router'; import { useGlobalStore } from '../../stores/global'; +import type { AuthLoginResponse, AuthURLResponse } from './types'; class LoginService { async getAuthUrl() { diff --git a/apps/web/src/pages/project/detail/components/DeployRecordItem.tsx b/apps/web/src/pages/project/detail/components/DeployRecordItem.tsx index 90c52a9..42a98f5 100644 --- a/apps/web/src/pages/project/detail/components/DeployRecordItem.tsx +++ b/apps/web/src/pages/project/detail/components/DeployRecordItem.tsx @@ -1,4 +1,4 @@ -import { List, Tag, Space } from '@arco-design/web-react'; +import { List, Space, Tag } from '@arco-design/web-react'; // 部署记录类型定义 interface DeployRecord { @@ -76,9 +76,7 @@ function DeployRecordItem({ 分支:{' '} - - {item.branch} - + {item.branch} 环境: {getEnvTag(item.env)} diff --git a/apps/web/src/pages/project/detail/components/PipelineStepItem.tsx b/apps/web/src/pages/project/detail/components/PipelineStepItem.tsx index 85c05a1..8640f58 100644 --- a/apps/web/src/pages/project/detail/components/PipelineStepItem.tsx +++ b/apps/web/src/pages/project/detail/components/PipelineStepItem.tsx @@ -1,23 +1,35 @@ -import { Typography, Tag, Switch, Button } from '@arco-design/web-react'; -import { IconDragArrow, IconEdit, IconDelete } from '@arco-design/web-react/icon'; +import { Button, Switch, Tag, Typography } from '@arco-design/web-react'; +import { + IconDelete, + IconDragArrow, + IconEdit, +} from '@arco-design/web-react/icon'; import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; -// 流水线步骤类型定义 +// 流水线步骤类型定义(更新为与后端一致) interface PipelineStep { - id: string; + id: number; name: string; - script: string; + description?: string; + order: number; + script: string; // 执行的脚本命令 + valid: number; + createdAt: string; + updatedAt: string; + createdBy: string; + updatedBy: string; + pipelineId: number; enabled: boolean; } interface PipelineStepItemProps { step: PipelineStep; index: number; - pipelineId: string; - onToggle: (pipelineId: string, stepId: string, enabled: boolean) => void; - onEdit: (pipelineId: string, step: PipelineStep) => void; - onDelete: (pipelineId: string, stepId: string) => void; + pipelineId: number; + onToggle: (pipelineId: number, stepId: number, enabled: boolean) => void; + onEdit: (pipelineId: number, step: PipelineStep) => void; + onDelete: (pipelineId: number, stepId: number) => void; } function PipelineStepItem({ @@ -79,6 +91,9 @@ function PipelineStepItem({ )} + {step.description && ( +
{step.description}
+ )}
{step.script}
diff --git a/apps/web/src/pages/project/detail/index.tsx b/apps/web/src/pages/project/detail/index.tsx index 5189ce7..5ee7e4c 100644 --- a/apps/web/src/pages/project/detail/index.tsx +++ b/apps/web/src/pages/project/detail/index.tsx @@ -1,50 +1,49 @@ import { - Typography, - Tabs, Button, - List, - Tag, - Space, - Input, Card, - Switch, - Modal, - Form, - Message, - Collapse, Dropdown, + Empty, + Form, + Input, + List, Menu, + Message, + Modal, + Switch, + Tabs, + Tag, + Typography, } from '@arco-design/web-react'; -import type { Project } from '../types'; -import { useState } from 'react'; -import { useParams } from 'react-router'; -import { useAsyncEffect } from '../../../hooks/useAsyncEffect'; -import { detailService } from './service'; import { + IconCopy, + IconDelete, + IconEdit, + IconMore, IconPlayArrow, IconPlus, - IconEdit, - IconDelete, - IconMore, - IconCopy, } from '@arco-design/web-react/icon'; -import DeployRecordItem from './components/DeployRecordItem'; -import PipelineStepItem from './components/PipelineStepItem'; +import type { DragEndEvent } from '@dnd-kit/core'; import { - DndContext, closestCenter, + DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors, } from '@dnd-kit/core'; -import type { DragEndEvent } from '@dnd-kit/core'; import { + arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, - arrayMove, } from '@dnd-kit/sortable'; +import { useState } from 'react'; +import { useParams } from 'react-router'; +import { useAsyncEffect } from '../../../hooks/useAsyncEffect'; +import type { Project } from '../types'; +import DeployRecordItem from './components/DeployRecordItem'; +import PipelineStepItem from './components/PipelineStepItem'; +import { detailService } from './service'; // 部署记录类型定义 interface DeployRecord { @@ -56,23 +55,35 @@ interface DeployRecord { createdAt: string; } -// 流水线步骤类型定义 +// 流水线步骤类型定义(更新为与后端一致) interface PipelineStep { - id: string; + id: number; name: string; - script: string; + description?: string; + order: number; + script: string; // 执行的脚本命令 + valid: number; + createdAt: string; + updatedAt: string; + createdBy: string; + updatedBy: string; + pipelineId: number; enabled: boolean; } // 流水线类型定义 interface Pipeline { - id: string; + id: number; name: string; description: string; - enabled: boolean; - steps: PipelineStep[]; + valid: number; createdAt: string; updatedAt: string; + createdBy: string; + updatedBy: string; + projectId?: number; + steps?: PipelineStep[]; + enabled: boolean; } function ProjectDetailPage() { @@ -83,65 +94,21 @@ function ProjectDetailPage() { useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, - }) + }), ); const [selectedRecordId, setSelectedRecordId] = useState(1); - const [pipelines, setPipelines] = useState([ - { - id: 'pipeline1', - name: '前端部署流水线', - description: '用于前端项目的构建和部署', - enabled: true, - createdAt: '2024-09-07 10:00:00', - updatedAt: '2024-09-07 14:30:00', - steps: [ - { id: 'step1', name: '安装依赖', script: 'npm install', enabled: true }, - { id: 'step2', name: '运行测试', script: 'npm test', enabled: true }, - { - id: 'step3', - name: '构建项目', - script: 'npm run build', - enabled: true, - }, - ], - }, - { - id: 'pipeline2', - name: 'Docker部署流水线', - description: '用于容器化部署的流水线', - enabled: true, - createdAt: '2024-09-06 16:20:00', - updatedAt: '2024-09-07 09:15:00', - steps: [ - { id: 'step1', name: '安装依赖', script: 'npm install', enabled: true }, - { - id: 'step2', - name: '构建镜像', - script: 'docker build -t $PROJECT_NAME:$BUILD_NUMBER .', - enabled: true, - }, - { - id: 'step3', - name: 'K8s部署', - script: 'kubectl apply -f deployment.yaml', - enabled: true, - }, - ], - }, - ]); + const [pipelines, setPipelines] = useState([]); const [editModalVisible, setEditModalVisible] = useState(false); - const [selectedPipelineId, setSelectedPipelineId] = useState( - pipelines.length > 0 ? pipelines[0].id : '', - ); + const [selectedPipelineId, setSelectedPipelineId] = useState(0); const [editingStep, setEditingStep] = useState(null); - const [editingPipelineId, setEditingPipelineId] = useState( + const [editingPipelineId, setEditingPipelineId] = useState( null, ); const [pipelineModalVisible, setPipelineModalVisible] = useState(false); const [editingPipeline, setEditingPipeline] = useState(null); const [form] = Form.useForm(); const [pipelineForm] = Form.useForm(); - const [deployRecords, setDeployRecords] = useState([ + const [deployRecords, _setDeployRecords] = useState([ { id: 1, branch: 'main', @@ -158,14 +125,7 @@ function ProjectDetailPage() { status: 'running', createdAt: '2024-09-07 13:45:12', }, - { - id: 3, - branch: 'feature/user-auth', - env: 'development', - commit: '3a7d9f2b1', - status: 'failed', - createdAt: '2024-09-07 12:20:45', - }, + // 移除了 ID 为 3 的部署记录,避免可能的冲突 { id: 4, branch: 'main', @@ -181,6 +141,29 @@ function ProjectDetailPage() { if (id) { const project = await detailService.getProject(id); setDetail(project); + + // 获取项目的所有流水线 + try { + const pipelineData = await detailService.getPipelines(Number(id)); + // 转换数据结构,添加enabled字段 + const transformedPipelines = pipelineData.map((pipeline) => ({ + ...pipeline, + description: pipeline.description || '', // 确保description不为undefined + enabled: pipeline.valid === 1, // 根据valid字段设置enabled + steps: + pipeline.steps?.map((step) => ({ + ...step, + enabled: step.valid === 1, // 根据valid字段设置enabled + })) || [], + })); + setPipelines(transformedPipelines); + if (transformedPipelines.length > 0) { + setSelectedPipelineId(transformedPipelines[0].id); + } + } catch (error) { + console.error('获取流水线数据失败:', error); + Message.error('获取流水线数据失败'); + } } }, []); @@ -211,16 +194,7 @@ function ProjectDetailPage() { '[2024-09-07 13:47:05] 构建镜像: docker build -t app:develop .', '[2024-09-07 13:48:20] 🔄 正在推送镜像...', ], - 3: [ - '[2024-09-07 12:20:45] 开始构建...', - '[2024-09-07 12:20:46] 拉取代码: git clone https://github.com/user/repo.git', - '[2024-09-07 12:20:48] 切换分支: git checkout feature/user-auth', - '[2024-09-07 12:20:49] 安装依赖: npm install', - '[2024-09-07 12:21:35] 运行测试: npm test', - '[2024-09-07 12:21:50] ❌ 测试失败', - '[2024-09-07 12:21:51] Error: Authentication test failed', - '[2024-09-07 12:21:51] ❌ 构建失败', - ], + // 移除了 ID 为 3 的模拟数据,避免可能的冲突 4: [ '[2024-09-07 10:15:30] 开始构建...', '[2024-09-07 10:15:31] 拉取代码: git clone https://github.com/user/repo.git', @@ -256,46 +230,109 @@ function ProjectDetailPage() { }; // 删除流水线 - const handleDeletePipeline = (pipelineId: string) => { + const handleDeletePipeline = async (pipelineId: number) => { Modal.confirm({ title: '确认删除', content: '确定要删除这个流水线吗?此操作不可撤销,将同时删除该流水线下的所有步骤。', - onOk: () => { - setPipelines((prev) => { - const newPipelines = prev.filter((pipeline) => pipeline.id !== pipelineId); - // 如果删除的是当前选中的流水线,选中第一个或清空选择 - if (selectedPipelineId === pipelineId) { - setSelectedPipelineId(newPipelines.length > 0 ? newPipelines[0].id : ''); - } - return newPipelines; - }); - Message.success('流水线删除成功'); + onOk: async () => { + try { + // 从数据库删除流水线 + await detailService.deletePipeline(pipelineId); + + // 更新本地状态 + setPipelines((prev) => { + const newPipelines = prev.filter( + (pipeline) => pipeline.id !== pipelineId, + ); + // 如果删除的是当前选中的流水线,选中第一个或清空选择 + if (selectedPipelineId === pipelineId) { + setSelectedPipelineId( + newPipelines.length > 0 ? newPipelines[0].id : 0, + ); + } + return newPipelines; + }); + Message.success('流水线删除成功'); + } catch (error) { + console.error('删除流水线失败:', error); + Message.error('删除流水线失败'); + } }, }); }; // 复制流水线 - const handleCopyPipeline = (pipeline: Pipeline) => { - const newPipeline: Pipeline = { - ...pipeline, - id: `pipeline_${Date.now()}`, - name: `${pipeline.name} - 副本`, - createdAt: new Date().toLocaleString(), - updatedAt: new Date().toLocaleString(), - steps: pipeline.steps.map((step) => ({ - ...step, - id: `step_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - })), - }; - setPipelines((prev) => [...prev, newPipeline]); - // 自动选中新复制的流水线 - setSelectedPipelineId(newPipeline.id); - Message.success('流水线复制成功'); + const handleCopyPipeline = async (pipeline: Pipeline) => { + Modal.confirm({ + title: '确认复制', + content: '确定要复制这个流水线吗?', + onOk: async () => { + try { + // 创建新的流水线 + const newPipelineData = await detailService.createPipeline({ + name: `${pipeline.name} - 副本`, + description: pipeline.description || '', + projectId: pipeline.projectId, + }); + + // 复制步骤 + if (pipeline.steps && pipeline.steps.length > 0) { + for (const step of pipeline.steps) { + await detailService.createStep({ + name: step.name, + description: step.description, + order: step.order, + script: step.script, + pipelineId: newPipelineData.id, + }); + } + + // 重新获取流水线数据以确保步骤已创建 + if (pipeline.projectId) { + const pipelineData = await detailService.getPipelines( + pipeline.projectId, + ); + // 转换数据结构,添加enabled字段 + const transformedPipelines = pipelineData.map((p) => ({ + ...p, + description: p.description || '', // 确保description不为undefined + enabled: p.valid === 1, // 根据valid字段设置enabled + steps: + p.steps?.map((step) => ({ + ...step, + enabled: step.valid === 1, // 根据valid字段设置enabled + })) || [], + })); + setPipelines(transformedPipelines); + setSelectedPipelineId(newPipelineData.id); + } + } else { + // 如果没有步骤,直接更新状态 + setPipelines((prev) => [ + ...prev, + { + ...newPipelineData, + description: newPipelineData.description || '', + enabled: newPipelineData.valid === 1, + steps: [], + }, + ]); + setSelectedPipelineId(newPipelineData.id); + } + + Message.success('流水线复制成功'); + } catch (error) { + console.error('复制流水线失败:', error); + Message.error('复制流水线失败'); + } + }, + }); }; // 切换流水线启用状态 - const handleTogglePipeline = (pipelineId: string, enabled: boolean) => { + const handleTogglePipeline = async (pipelineId: number, enabled: boolean) => { + // 在数据库中更新流水线状态(这里简化处理,实际可能需要添加enabled字段到数据库) setPipelines((prev) => prev.map((pipeline) => pipeline.id === pipelineId ? { ...pipeline, enabled } : pipeline, @@ -308,42 +345,58 @@ function ProjectDetailPage() { try { const values = await pipelineForm.validate(); if (editingPipeline) { - setPipelines((prev) => [ - ...prev.map((pipeline) => + // 更新现有流水线 + const updatedPipeline = await detailService.updatePipeline( + editingPipeline.id, + { + name: values.name, + description: values.description, + }, + ); + + // 更新本地状态 + setPipelines((prev) => + prev.map((pipeline) => pipeline.id === editingPipeline.id ? { - ...pipeline, - name: values.name, - description: values.description, - updatedAt: new Date().toLocaleString(), + ...updatedPipeline, + description: updatedPipeline.description || '', + enabled: updatedPipeline.valid === 1, + steps: pipeline.steps || [], // 保持步骤不变 } : pipeline, ), - ]); + ); Message.success('流水线更新成功'); } else { - const newPipeline: Pipeline = { - id: `pipeline_${Date.now()}`, + // 创建新流水线 + const newPipeline = await detailService.createPipeline({ name: values.name, - description: values.description, - enabled: true, + description: values.description || '', + projectId: Number(id), + }); + + // 更新本地状态 + const pipelineWithDefaults = { + ...newPipeline, + description: newPipeline.description || '', + enabled: newPipeline.valid === 1, steps: [], - createdAt: new Date().toLocaleString(), - updatedAt: new Date().toLocaleString(), }; - setPipelines((prev) => [...prev, newPipeline]); + setPipelines((prev) => [...prev, pipelineWithDefaults]); // 自动选中新创建的流水线 setSelectedPipelineId(newPipeline.id); Message.success('流水线创建成功'); } setPipelineModalVisible(false); } catch (error) { - console.error('表单验证失败:', error); + console.error('保存流水线失败:', error); + Message.error('保存流水线失败'); } }; // 添加新步骤 - const handleAddStep = (pipelineId: string) => { + const handleAddStep = (pipelineId: number) => { setEditingStep(null); setEditingPipelineId(pipelineId); form.resetFields(); @@ -351,7 +404,7 @@ function ProjectDetailPage() { }; // 编辑步骤 - const handleEditStep = (pipelineId: string, step: PipelineStep) => { + const handleEditStep = (pipelineId: number, step: PipelineStep) => { setEditingStep(step); setEditingPipelineId(pipelineId); form.setFieldsValue({ @@ -362,40 +415,55 @@ function ProjectDetailPage() { }; // 删除步骤 - const handleDeleteStep = (pipelineId: string, stepId: string) => { + const handleDeleteStep = async (pipelineId: number, stepId: number) => { Modal.confirm({ title: '确认删除', content: '确定要删除这个流水线步骤吗?此操作不可撤销。', - onOk: () => { - setPipelines((prev) => - prev.map((pipeline) => - pipeline.id === pipelineId - ? { - ...pipeline, - steps: pipeline.steps.filter((step) => step.id !== stepId), - } - : pipeline, - ), - ); - Message.success('步骤删除成功'); + onOk: async () => { + try { + // 从数据库删除步骤 + await detailService.deleteStep(stepId); + + // 更新本地状态 + setPipelines((prev) => + prev.map((pipeline) => + pipeline.id === pipelineId + ? { + ...pipeline, + steps: + pipeline.steps?.filter((step) => step.id !== stepId) || + [], + updatedAt: new Date().toISOString(), + } + : pipeline, + ), + ); + Message.success('步骤删除成功'); + } catch (error) { + console.error('删除步骤失败:', error); + Message.error('删除步骤失败'); + } }, }); }; // 切换步骤启用状态 - const handleToggleStep = ( - pipelineId: string, - stepId: string, + const handleToggleStep = async ( + pipelineId: number, + stepId: number, enabled: boolean, ) => { + // 在数据库中更新步骤状态(这里简化处理,实际可能需要添加enabled字段到数据库) setPipelines((prev) => prev.map((pipeline) => pipeline.id === pipelineId ? { ...pipeline, - steps: pipeline.steps.map((step) => - step.id === stepId ? { ...step, enabled } : step, - ), + steps: + pipeline.steps?.map((step) => + step.id === stepId ? { ...step, enabled } : step, + ) || [], + updatedAt: new Date().toISOString(), } : pipeline, ), @@ -403,7 +471,7 @@ function ProjectDetailPage() { }; // 拖拽结束处理 - const handleDragEnd = (event: DragEndEvent) => { + const handleDragEnd = async (event: DragEndEvent) => { const { active, over } = event; if (!over || active.id === over.id) { @@ -411,20 +479,25 @@ function ProjectDetailPage() { } if (selectedPipelineId) { + // 更新步骤顺序到数据库(简化处理,实际应该更新所有步骤的order字段) setPipelines((prev) => prev.map((pipeline) => { if (pipeline.id === selectedPipelineId) { - const oldIndex = pipeline.steps.findIndex((step) => step.id === active.id); - const newIndex = pipeline.steps.findIndex((step) => step.id === over.id); + const oldIndex = + pipeline.steps?.findIndex((step) => step.id === active.id) || 0; + const newIndex = + pipeline.steps?.findIndex((step) => step.id === over.id) || 0; return { ...pipeline, - steps: arrayMove(pipeline.steps, oldIndex, newIndex), - updatedAt: new Date().toLocaleString(), + steps: pipeline.steps + ? arrayMove(pipeline.steps, oldIndex, newIndex) + : [], + updatedAt: new Date().toISOString(), }; } return pipeline; - }) + }), ); Message.success('步骤顺序调整成功'); } @@ -435,36 +508,52 @@ function ProjectDetailPage() { try { const values = await form.validate(); if (editingStep && editingPipelineId) { + // 更新现有步骤 + const updatedStep = await detailService.updateStep(editingStep.id, { + name: values.name, + script: values.script, + }); + + // 更新本地状态 setPipelines((prev) => prev.map((pipeline) => pipeline.id === editingPipelineId ? { ...pipeline, - steps: pipeline.steps.map((step) => - step.id === editingStep.id - ? { ...step, name: values.name, script: values.script } - : step, - ), - updatedAt: new Date().toLocaleString(), + steps: + pipeline.steps?.map((step) => + step.id === editingStep.id + ? { ...updatedStep, enabled: step.enabled } + : step, + ) || [], + updatedAt: new Date().toISOString(), } : pipeline, ), ); Message.success('步骤更新成功'); } else if (editingPipelineId) { - const newStep: PipelineStep = { - id: `step_${Date.now()}`, + // 创建新步骤 + const newStep = await detailService.createStep({ name: values.name, script: values.script, - enabled: true, - }; + order: + pipelines.find((p) => p.id === editingPipelineId)?.steps?.length || + 0, + pipelineId: editingPipelineId, + }); + + // 更新本地状态 setPipelines((prev) => prev.map((pipeline) => pipeline.id === editingPipelineId ? { ...pipeline, - steps: [...pipeline.steps, newStep], - updatedAt: new Date().toLocaleString(), + steps: [ + ...(pipeline.steps || []), + { ...newStep, enabled: true }, + ], + updatedAt: new Date().toISOString(), } : pipeline, ), @@ -473,7 +562,8 @@ function ProjectDetailPage() { } setEditModalVisible(false); } catch (error) { - console.error('表单验证失败:', error); + console.error('保存步骤失败:', error); + Message.error('保存步骤失败'); } }; @@ -498,7 +588,7 @@ function ProjectDetailPage() { }; // 渲染部署记录项 - const renderDeployRecordItem = (item: DeployRecord, index: number) => { + const renderDeployRecordItem = (item: DeployRecord, _index: number) => { const isSelected = item.id === selectedRecordId; return (
- + {deployRecords.length > 0 ? ( + + ) : ( +
+ +
+ )}
@@ -621,7 +717,9 @@ function ProjectDetailPage() { {pipeline.name} @@ -647,14 +745,18 @@ function ProjectDetailPage() { handleEditPipeline(pipeline)} + onClick={() => + handleEditPipeline(pipeline) + } > 编辑流水线 handleCopyPipeline(pipeline)} + onClick={() => + handleCopyPipeline(pipeline) + } > 复制流水线 @@ -684,8 +786,14 @@ function ProjectDetailPage() {
{pipeline.description}
- 共 {pipeline.steps.length} 个步骤 - {pipeline.updatedAt} + + 共 {pipeline.steps?.length || 0} 个步骤 + + + {new Date( + pipeline.updatedAt, + ).toLocaleString()} +
@@ -695,8 +803,9 @@ function ProjectDetailPage() { {pipelines.length === 0 && (
+ - 暂无流水线,点击上方"新建流水线"按钮开始创建 + 点击上方"新建流水线"按钮开始创建
)} @@ -706,9 +815,12 @@ function ProjectDetailPage() { {/* 右侧流水线步骤详情 */}
- {selectedPipelineId && pipelines.find(p => p.id === selectedPipelineId) ? ( + {selectedPipelineId && + pipelines.find((p) => p.id === selectedPipelineId) ? ( (() => { - const selectedPipeline = pipelines.find(p => p.id === selectedPipelineId)!; + const selectedPipeline = pipelines.find( + (p) => p.id === selectedPipelineId, + ); return ( <>
@@ -717,8 +829,12 @@ function ProjectDetailPage() { {selectedPipeline.name} - 流水线步骤 - - {selectedPipeline.description} · 共 {selectedPipeline.steps.length} 个步骤 + + {selectedPipeline.description} · 共{' '} + {selectedPipeline.steps?.length || 0} 个步骤
, - , ]} style={{ width: 500 }} > -
+ void; } -function EditProjectModal({ visible, project, onCancel, onSuccess }: EditProjectModalProps) { +function EditProjectModal({ + visible, + project, + onCancel, + onSuccess, +}: EditProjectModalProps) { const [form] = Form.useForm(); const [loading, setLoading] = useState(false); @@ -59,17 +64,18 @@ function EditProjectModal({ visible, project, onCancel, onSuccess }: EditProject , - , ]} style={{ width: 500 }} > - + { - setProjects(prev => - prev.map(p => p.id === updatedProject.id ? updatedProject : p) + setProjects((prev) => + prev.map((p) => (p.id === updatedProject.id ? updatedProject : p)), ); }; @@ -42,7 +42,7 @@ function ProjectPage() { }; const handleCreateSuccess = (newProject: Project) => { - setProjects(prev => [newProject, ...prev]); + setProjects((prev) => [newProject, ...prev]); }; const handleCreateCancel = () => { @@ -52,7 +52,7 @@ function ProjectPage() { const handleDeleteProject = async (project: Project) => { try { await projectService.delete(project.id); - setProjects(prev => prev.filter(p => p.id !== project.id)); + setProjects((prev) => prev.filter((p) => p.id !== project.id)); Message.success('项目删除成功'); } catch (error) { console.error('删除项目失败:', error); diff --git a/apps/web/src/pages/project/list/service.ts b/apps/web/src/pages/project/list/service.ts index 5ce4d2c..641aec8 100644 --- a/apps/web/src/pages/project/list/service.ts +++ b/apps/web/src/pages/project/list/service.ts @@ -1,7 +1,6 @@ -import { net, type APIResponse } from '@shared'; +import { type APIResponse, net } from '@shared'; import type { Project } from '../types'; - class ProjectService { async list(params?: ProjectQueryParams) { const { data } = await net.request>({ diff --git a/apps/web/src/pages/project/types.ts b/apps/web/src/pages/project/types.ts index 3a62af9..5f0890e 100644 --- a/apps/web/src/pages/project/types.ts +++ b/apps/web/src/pages/project/types.ts @@ -1,7 +1,7 @@ enum BuildStatus { - Idle = "Pending", - Running = "Running", - Stopped = "Stopped", + Idle = 'Pending', + Running = 'Running', + Stopped = 'Stopped', } export interface Project { @@ -16,3 +16,32 @@ export interface Project { updatedBy: string; status: BuildStatus; } + +// 流水线步骤类型定义 +export interface Step { + id: number; + name: string; + description?: string; + order: number; + script: string; // 执行的脚本命令 + valid: number; + createdAt: string; + updatedAt: string; + createdBy: string; + updatedBy: string; + pipelineId: number; +} + +// 流水线类型定义 +export interface Pipeline { + id: number; + name: string; + description?: string; + valid: number; + createdAt: string; + updatedAt: string; + createdBy: string; + updatedBy: string; + projectId?: number; + steps?: Step[]; +} diff --git a/apps/web/src/shared/request.ts b/apps/web/src/shared/request.ts index f18ac94..d86e815 100644 --- a/apps/web/src/shared/request.ts +++ b/apps/web/src/shared/request.ts @@ -1,4 +1,4 @@ -import axios, { Axios, type AxiosRequestConfig } from 'axios'; +import axios, { type Axios, type AxiosRequestConfig } from 'axios'; class Net { private readonly instance: Axios; @@ -18,7 +18,7 @@ class Net { return response; }, (error) => { - console.log('error', error) + console.log('error', error); if (error.status === 401 && error.config.url !== '/api/auth/info') { window.location.href = '/login'; return; @@ -29,8 +29,16 @@ class Net { } async request(config: AxiosRequestConfig): Promise { - const { data } = await this.instance.request(config); - return data; + try { + const response = await this.instance.request(config); + if (!response || !response.data) { + throw new Error('Invalid response'); + } + return response.data; + } catch (error) { + console.error('Request failed:', error); + throw error; + } } } diff --git a/apps/web/src/stores/global.tsx b/apps/web/src/stores/global.tsx index 26ce27e..4135d32 100644 --- a/apps/web/src/stores/global.tsx +++ b/apps/web/src/stores/global.tsx @@ -1,4 +1,4 @@ -import { net, type APIResponse } from '@shared'; +import { type APIResponse, net } from '@shared'; import { create } from 'zustand'; interface User { diff --git a/apps/web/src/styles/index.css b/apps/web/src/styles/index.css index f173aa4..f1d8c73 100644 --- a/apps/web/src/styles/index.css +++ b/apps/web/src/styles/index.css @@ -1 +1 @@ -@import 'tailwindcss'; \ No newline at end of file +@import "tailwindcss"; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 38a92e6..a41183b 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -20,7 +20,7 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "paths": { + "paths": { "@pages/*": ["./src/pages/*"], "@styles/*": ["./src/styles/*"], "@assets/*": ["./src/assets/*"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 885efca..99ab578 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -79,57 +79,57 @@ importers: apps/web: dependencies: '@arco-design/web-react': - specifier: ^2.66.4 - version: 2.66.5(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^2.66.8 + version: 2.66.8(@types/react@18.3.27)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@dnd-kit/core': specifier: ^6.3.1 - version: 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@dnd-kit/sortable': specifier: ^10.0.0 - version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0) '@dnd-kit/utilities': specifier: ^3.2.2 - version: 3.2.2(react@18.3.1) + version: 3.2.2(react@19.2.0) axios: specifier: ^1.11.0 version: 1.11.0 react: - specifier: ^18.2.0 - version: 18.3.1 + specifier: ^19.0.0 + version: 19.2.0 react-dom: - specifier: ^18.2.0 - version: 18.3.1(react@18.3.1) + specifier: ^19.0.0 + version: 19.2.0(react@19.2.0) react-router: specifier: ^7.8.0 - version: 7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 7.8.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) zustand: specifier: ^5.0.8 - version: 5.0.8(@types/react@18.3.24)(react@18.3.1) + version: 5.0.8(@types/react@18.3.27)(react@19.2.0) devDependencies: '@arco-plugins/unplugin-react': specifier: 2.0.0-beta.5 - version: 2.0.0-beta.5(@rspack/core@1.5.0(@swc/helpers@0.5.17)) + version: 2.0.0-beta.5(@rspack/core@1.6.4(@swc/helpers@0.5.17)) '@rsbuild/core': - specifier: ^1.4.13 - version: 1.5.1 + specifier: ^1.6.7 + version: 1.6.7 '@rsbuild/plugin-less': - specifier: ^1.4.0 - version: 1.5.0(@rsbuild/core@1.5.1) + specifier: ^1.5.0 + version: 1.5.0(@rsbuild/core@1.6.7) '@rsbuild/plugin-react': - specifier: ^1.3.4 - version: 1.3.5(@rsbuild/core@1.5.1) + specifier: ^1.4.2 + version: 1.4.2(@rsbuild/core@1.6.7) '@rsbuild/plugin-svgr': specifier: ^1.2.2 - version: 1.2.2(@rsbuild/core@1.5.1)(typescript@5.9.2) + version: 1.2.2(@rsbuild/core@1.6.7)(typescript@5.9.2) '@tailwindcss/postcss': specifier: ^4.1.11 version: 4.1.12 '@types/react': specifier: ^18.3.24 - version: 18.3.24 + version: 18.3.27 '@types/react-dom': specifier: ^18.3.7 - version: 18.3.7(@types/react@18.3.24) + version: 18.3.7(@types/react@18.3.27) tailwindcss: specifier: ^4.1.11 version: 4.1.12 @@ -150,8 +150,8 @@ packages: '@arco-design/color@0.4.0': resolution: {integrity: sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g==} - '@arco-design/web-react@2.66.5': - resolution: {integrity: sha512-ity0kG+B6pmuJ2/Zh3wUtBV78XxWmRtGEwazL8f4KAjoQpMkisgLMXibUpAGfcqph3vycNFq4yHgHujjgwrJMQ==} + '@arco-design/web-react@2.66.8': + resolution: {integrity: sha512-V2XDTqeSFq4g6Yk0tSEFO4SMPZ04cGNF6dKfFBM9drEpmj91cU1n9+rd3gmmtcJ2f3SOqbr0itnUsqBWGQ4PvQ==} peerDependencies: react: '>=16' react-dom: '>=16' @@ -307,14 +307,14 @@ packages: peerDependencies: react: '>=16.8.0' - '@emnapi/core@1.4.5': - resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==} + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} - '@emnapi/runtime@1.4.5': - resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==} + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} - '@emnapi/wasi-threads@1.0.4': - resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} '@esbuild/aix-ppc64@0.25.9': resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} @@ -503,26 +503,26 @@ packages: resolution: {integrity: sha512-LBSu5K0qAaaQcXX/0WIB9PGDevyCxxpnc1uq13vV/CgObaVxuis5hKl3Eboq/8gcb6ebnkAStW9NB/Em2eYyFA==} engines: {node: '>= 20'} - '@module-federation/error-codes@0.18.0': - resolution: {integrity: sha512-Woonm8ehyVIUPXChmbu80Zj6uJkC0dD9SJUZ/wOPtO8iiz/m+dkrOugAuKgoiR6qH4F+yorWila954tBz4uKsQ==} + '@module-federation/error-codes@0.21.4': + resolution: {integrity: sha512-ClpL5MereWNXh+EgDjz7w4RrC1JlisQTvXDa1gLxpviHafzNDfdViVmuhi9xXVuj+EYo8KU70Y999KHhk9424Q==} - '@module-federation/runtime-core@0.18.0': - resolution: {integrity: sha512-ZyYhrDyVAhUzriOsVfgL6vwd+5ebYm595Y13KeMf6TKDRoUHBMTLGQ8WM4TDj8JNsy7LigncK8C03fn97of0QQ==} + '@module-federation/runtime-core@0.21.4': + resolution: {integrity: sha512-SGpmoOLGNxZofpTOk6Lxb2ewaoz5wMi93AFYuuJB04HTVcngEK+baNeUZ2D/xewrqNIJoMY6f5maUjVfIIBPUA==} - '@module-federation/runtime-tools@0.18.0': - resolution: {integrity: sha512-fSga9o4t1UfXNV/Kh6qFvRyZpPp3EHSPRISNeyT8ZoTpzDNiYzhtw0BPUSSD8m6C6XQh2s/11rI4g80UY+d+hA==} + '@module-federation/runtime-tools@0.21.4': + resolution: {integrity: sha512-RzFKaL0DIjSmkn76KZRfzfB6dD07cvID84950jlNQgdyoQFUGkqD80L6rIpVCJTY/R7LzR3aQjHnoqmq4JPo3w==} - '@module-federation/runtime@0.18.0': - resolution: {integrity: sha512-+C4YtoSztM7nHwNyZl6dQKGUVJdsPrUdaf3HIKReg/GQbrt9uvOlUWo2NXMZ8vDAnf/QRrpSYAwXHmWDn9Obaw==} + '@module-federation/runtime@0.21.4': + resolution: {integrity: sha512-wgvGqryurVEvkicufJmTG0ZehynCeNLklv8kIk5BLIsWYSddZAE+xe4xov1kgH5fIJQAoQNkRauFFjVNlHoAkA==} - '@module-federation/sdk@0.18.0': - resolution: {integrity: sha512-Lo/Feq73tO2unjmpRfyyoUkTVoejhItXOk/h5C+4cistnHbTV8XHrW/13fD5e1Iu60heVdAhhelJd6F898Ve9A==} + '@module-federation/sdk@0.21.4': + resolution: {integrity: sha512-tzvhOh/oAfX++6zCDDxuvioHY4Jurf8vcfoCbKFxusjmyKr32GPbwFDazUP+OPhYCc3dvaa9oWU6X/qpUBLfJw==} - '@module-federation/webpack-bundler-runtime@0.18.0': - resolution: {integrity: sha512-TEvErbF+YQ+6IFimhUYKK3a5wapD90d90sLsNpcu2kB3QGT7t4nIluE25duXuZDVUKLz86tEPrza/oaaCWTpvQ==} + '@module-federation/webpack-bundler-runtime@0.21.4': + resolution: {integrity: sha512-dusmR3uPnQh9u9ChQo3M+GLOuGFthfvnh7WitF/a1eoeTfRmXqnMFsXtZCUK+f/uXf+64874Zj/bhAgbBcVHZA==} - '@napi-rs/wasm-runtime@1.0.3': - resolution: {integrity: sha512-rZxtMsLwjdXkMUGC3WwsPwLNVqVqnTJT6MNIB6e+5fhMcSCPP0AOsNWuMQ5mdCq6HNjs/ZeWAEchpqeprqBD2Q==} + '@napi-rs/wasm-runtime@1.0.7': + resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} '@prisma/client@6.15.0': resolution: {integrity: sha512-wR2LXUbOH4cL/WToatI/Y2c7uzni76oNFND7+23ypLllBmIS8e3ZHhO+nud9iXSXKFt1SoM3fTZvHawg63emZw==} @@ -554,8 +554,8 @@ packages: '@prisma/get-platform@6.15.0': resolution: {integrity: sha512-Jbb+Xbxyp05NSR1x2epabetHiXvpO8tdN2YNoWoA/ZsbYyxxu/CO/ROBauIFuMXs3Ti+W7N7SJtWsHGaWte9Rg==} - '@rsbuild/core@1.5.1': - resolution: {integrity: sha512-ScXC3nm1drXDyT9OahHxXJ8Tg+1oE5IN2+ZwJzNl0cLJD8Jmbjl4zMUN7zZdRPqjaTCz67IAjws+HN+PSKHBaw==} + '@rsbuild/core@1.6.7': + resolution: {integrity: sha512-V0INbMrT/LwyhzKmvpupe2oSvPFWaivz7sdriFRp381BJvD0d2pYcq9iRW91bxgMRX78MgTzFYAu868hMAzoSw==} engines: {node: '>=18.12.0'} hasBin: true @@ -564,8 +564,8 @@ packages: peerDependencies: '@rsbuild/core': 1.x - '@rsbuild/plugin-react@1.3.5': - resolution: {integrity: sha512-L/GoHgJV4j+EQbI4KOhe5EscM0OHgnSat1eR0Nt5P3JZxpJV2ryO5Yfx5jElPWOkYZZCuk+EWhHWDQ4CkeC5BQ==} + '@rsbuild/plugin-react@1.4.2': + resolution: {integrity: sha512-2rJb5mOuqVof2aDq4SbB1E65+0n1vjhAADipC88jvZRNuTOulg79fh7R4tsCiBMI4VWq46gSpwekiK8G5bq6jg==} peerDependencies: '@rsbuild/core': 1.x @@ -574,60 +574,60 @@ packages: peerDependencies: '@rsbuild/core': 1.x - '@rspack/binding-darwin-arm64@1.5.0': - resolution: {integrity: sha512-7909YLNnKf0BYxiCpCWOk13WyWS4493Kxk1NQwy9KPLY9ydQExk84KVsix2NuNBaI8Pnk3aVLBPJiSNXtHLjnA==} + '@rspack/binding-darwin-arm64@1.6.4': + resolution: {integrity: sha512-qD2C5xwdY2qKEXTZiPJQx1L1kELapOc0AaZDqcAyzXs30d1qTKpx6PdyW3HN+gueKovyWZwMMYfz6RxcMCnaDQ==} cpu: [arm64] os: [darwin] - '@rspack/binding-darwin-x64@1.5.0': - resolution: {integrity: sha512-poGuQsGKCMQqSswgrz8X+frqMVTdmtzUDyvi/p9BLwW+2DwWgmywU8jwE+BYtjfWp1tErBSTlLxmEPQTdcIQgQ==} + '@rspack/binding-darwin-x64@1.6.4': + resolution: {integrity: sha512-IHceyLDxeubqIrGz4gUqJavnygTij4vtDDE2Fkgobz7hkTJwGtD5mxBKbVNRqGvhrasVw0h9rEjR7tdbDSiUhQ==} cpu: [x64] os: [darwin] - '@rspack/binding-linux-arm64-gnu@1.5.0': - resolution: {integrity: sha512-Bvmk8h3tRhN9UgOtH+vK0SgFM3qEO36eJz7oddOl4lJQxBf2GNA87bGtkMtX+AVPz/PUn7r82uWxrlVNQHAbFg==} + '@rspack/binding-linux-arm64-gnu@1.6.4': + resolution: {integrity: sha512-Ldpoz2wWnBaL2+XKLIOyCZMkAkd4pk/L24EVgma3SpRtwgenLEr10bQupvwGAK5OLkjayslOTZmRiAv0FH5o/w==} cpu: [arm64] os: [linux] - '@rspack/binding-linux-arm64-musl@1.5.0': - resolution: {integrity: sha512-bH7UwkbACDYT37YnN9kkhaF9niFFK9ndcdNvYFFr1oUT4W9Ie3V9b41EXijqp3pyh0mDSeeLPFY0aEx1t3e7Pw==} + '@rspack/binding-linux-arm64-musl@1.6.4': + resolution: {integrity: sha512-3fLMSDK5yMjKmx7iFbYG3P3A0xNdtmNu09v5P6hzq65tkJ3dflIt3p8DvtOTURtuSgQZV2A1LDd9hpIXdnigqA==} cpu: [arm64] os: [linux] - '@rspack/binding-linux-x64-gnu@1.5.0': - resolution: {integrity: sha512-xZ5dwNrE5KtpQyMd9israpJTcTQ3UYUUq23fTcNc79xE5aspkGixDFAYoql4YkhO0O+JWRmdSaFAn6jD+IQWQA==} + '@rspack/binding-linux-x64-gnu@1.6.4': + resolution: {integrity: sha512-5YzXUKLnaiqND05CDgkKE0WNRtC1ulkVncYs78xPikonzZmgVXa8eRaTPOZC6ZjpLR0eTsg+MSesLUsPUu27hA==} cpu: [x64] os: [linux] - '@rspack/binding-linux-x64-musl@1.5.0': - resolution: {integrity: sha512-mv65jYvcyYPkPZJ9kjSvTAcH0o7C5jfICWCQcMmN1tCGD3b8gmf9GqSZ8e+W/JkuvrJ05qTo/PvEq9nhu+pNIg==} + '@rspack/binding-linux-x64-musl@1.6.4': + resolution: {integrity: sha512-KcSFla8a9bXG1mmV5oQ1R5h/dSXfd41/qHOsNuLqho2UCX8CVh4dezUA153dj7p1S4yOhTy6VZZi6C1szweE9A==} cpu: [x64] os: [linux] - '@rspack/binding-wasm32-wasi@1.5.0': - resolution: {integrity: sha512-8rVpl6xfaAFJgo1wCd+emksfl+/8nlehrtkmjY9bj79Ou+kp07L9e1B+UU0jfs8e7aLPntQuF68kzLHwYLzWIQ==} + '@rspack/binding-wasm32-wasi@1.6.4': + resolution: {integrity: sha512-mfFJbDJkRy5I1iW3m0JlWbc0X8pjVd+GRUz5nhbccwEhSQOc27ao3evf7XPU4aaDxud1B3UEqYiRcRmtm1BrjA==} cpu: [wasm32] - '@rspack/binding-win32-arm64-msvc@1.5.0': - resolution: {integrity: sha512-dWSmNm+GR6WSkOwbhlUcot4Oqwyon+1PRZ9E0vIMFHKGvESf9CQjgHAX0QE9G0kJmRM5x3I16J4x44Kw3W/98Q==} + '@rspack/binding-win32-arm64-msvc@1.6.4': + resolution: {integrity: sha512-QtIqxsfeTSS1lwfaPGrPFfJ9ir/3aWZv5t3iAgYj/CNUA8MTKWt4vQKcco7NRIGK4ZLMI+dgJBFtvd/lUDMQsw==} cpu: [arm64] os: [win32] - '@rspack/binding-win32-ia32-msvc@1.5.0': - resolution: {integrity: sha512-YtOrFEkwhO3Y3sY6Jq0OOYPY7NBTNYuwJ6epTgzPEDGs2cBnwZfzhq0jmD/koWtv1L9+twX95vKosBdauF0tNA==} + '@rspack/binding-win32-ia32-msvc@1.6.4': + resolution: {integrity: sha512-HXEWGDllgh0jFwjGhkGcLqb0dzXbc/rA8vQr2JcSdC41p1DTzLgO215jWdKSIvzCzhyPh3VeQkXk76hjFB2cLQ==} cpu: [ia32] os: [win32] - '@rspack/binding-win32-x64-msvc@1.5.0': - resolution: {integrity: sha512-V4fcPVYWJgDkIkSsFwmUdwC9lkL8+1dzDOwyTWe6KW2MYHF2D148WPHNyVVE6gum12TShpbIsh0j4NiiMhkMtw==} + '@rspack/binding-win32-x64-msvc@1.6.4': + resolution: {integrity: sha512-MAO5rOnGYoeuT2LPn/P7JVJCi3d78XoXgOq3tkGh6qXhvhkjsBRtYluWCzACXQpXfFHEWYd7uT5yHoZgxiVuoA==} cpu: [x64] os: [win32] - '@rspack/binding@1.5.0': - resolution: {integrity: sha512-UGXQmwEu2gdO+tnGv2q4rOWJdWioy6dlLXeZOLYAZVh3mrfKJhZWtDEygX9hCdE5thWNRTlEvx30QQchJAszIQ==} + '@rspack/binding@1.6.4': + resolution: {integrity: sha512-vUxc/zUdsCuyysOvP4CTdIYxsZPb2jIXST5vrLABiTPIaHpXZ0hVdgKif2XPJwJeuCVS6w25xvyPN0mBCU0MvQ==} - '@rspack/core@1.5.0': - resolution: {integrity: sha512-eEtiKV+CUcAtnt1K+eiHDzmBXQcNM8CfCXOzr0+gHGp4w4Zks2B8RF36sYD03MM2bg8VRXXsf0MicQ8FvRMCOg==} + '@rspack/core@1.6.4': + resolution: {integrity: sha512-5F1+MQD8rfbFbUHnaiZe4jqOu9pnSb+PliqQvi0lj+uvpMpcS3sJDIs/mz6P1u87lfkfBXChIT4zSLAzeOgMWw==} engines: {node: '>=18.12.0'} peerDependencies: '@swc/helpers': '>=0.5.1' @@ -635,12 +635,11 @@ packages: '@swc/helpers': optional: true - '@rspack/lite-tapable@1.0.1': - resolution: {integrity: sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==} - engines: {node: '>=16.0.0'} + '@rspack/lite-tapable@1.1.0': + resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} - '@rspack/plugin-react-refresh@1.4.3': - resolution: {integrity: sha512-wZx4vWgy5oMEvgyNGd/oUKcdnKaccYWHCRkOqTdAPJC3WcytxhTX+Kady8ERurSBiLyQpoMiU3Iyd+F1Y2Arbw==} + '@rspack/plugin-react-refresh@1.5.3': + resolution: {integrity: sha512-VOnQMf3YOHkTqJ0+BJbrYga4tQAWNwoAnkgwRauXB4HOyCc5wLfBs9DcOFla/2usnRT3Sq6CMVhXmdPobwAoTA==} peerDependencies: react-refresh: '>=0.10.0 <1.0.0' webpack-hot-middleware: 2.x @@ -826,8 +825,8 @@ packages: '@tsconfig/node22@22.0.2': resolution: {integrity: sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA==} - '@tybys/wasm-util@0.10.0': - resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} '@types/accepts@1.3.7': resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} @@ -894,8 +893,8 @@ packages: peerDependencies: '@types/react': ^18.0.0 - '@types/react@18.3.24': - resolution: {integrity: sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==} + '@types/react@18.3.27': + resolution: {integrity: sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==} '@types/send@0.17.5': resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} @@ -1050,8 +1049,8 @@ packages: copy-to@2.0.1: resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==} - core-js@3.45.1: - resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==} + core-js@3.46.0: + resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==} cosmiconfig@8.3.6: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} @@ -1084,8 +1083,8 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} @@ -1367,6 +1366,10 @@ packages: resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} hasBin: true + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -1699,10 +1702,10 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} peerDependencies: - react: ^18.3.1 + react: ^19.2.0 react-focus-lock@2.13.6: resolution: {integrity: sha512-ehylFFWyYtBKXjAO9+3v8d0i+cnc1trGS0vlTGhzFW1vbFXVUTmR8s2tt/ZQG8x5hElg6rhENlLG1H3EZK0Llg==} @@ -1719,8 +1722,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-refresh@0.17.0: - resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} engines: {node: '>=0.10.0'} react-router@7.8.2: @@ -1739,8 +1742,8 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' - react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} readdirp@4.1.2: @@ -1774,8 +1777,8 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} scroll-into-view-if-needed@2.2.31: resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} @@ -1991,7 +1994,7 @@ snapshots: dependencies: color: 3.2.1 - '@arco-design/web-react@2.66.5(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@arco-design/web-react@2.66.8(@types/react@18.3.27)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@arco-design/color': 0.4.0 '@babel/runtime': 7.28.3 @@ -2001,20 +2004,20 @@ snapshots: dayjs: 1.11.15 lodash: 4.17.21 number-precision: 1.6.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-focus-lock: 2.13.6(@types/react@18.3.24)(react@18.3.1) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-focus-lock: 2.13.6(@types/react@18.3.27)(react@19.2.0) react-is: 18.3.1 - react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) resize-observer-polyfill: 1.5.1 scroll-into-view-if-needed: 2.2.31 shallowequal: 1.1.0 transitivePeerDependencies: - '@types/react' - '@arco-plugins/unplugin-react@2.0.0-beta.5(@rspack/core@1.5.0(@swc/helpers@0.5.17))': + '@arco-plugins/unplugin-react@2.0.0-beta.5(@rspack/core@1.6.4(@swc/helpers@0.5.17))': dependencies: - '@rspack/core': 1.5.0(@swc/helpers@0.5.17) + '@rspack/core': 1.6.4(@swc/helpers@0.5.17) minimatch: 9.0.5 '@babel/code-frame@7.27.1': @@ -2154,43 +2157,43 @@ snapshots: '@biomejs/cli-win32-x64@2.0.6': optional: true - '@dnd-kit/accessibility@3.1.1(react@18.3.1)': + '@dnd-kit/accessibility@3.1.1(react@19.2.0)': dependencies: - react: 18.3.1 + react: 19.2.0 tslib: 2.8.1 - '@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@dnd-kit/accessibility': 3.1.1(react@18.3.1) - '@dnd-kit/utilities': 3.2.2(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@dnd-kit/accessibility': 3.1.1(react@19.2.0) + '@dnd-kit/utilities': 3.2.2(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) tslib: 2.8.1 - '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)': dependencies: - '@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@dnd-kit/utilities': 3.2.2(react@18.3.1) - react: 18.3.1 + '@dnd-kit/core': 6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@dnd-kit/utilities': 3.2.2(react@19.2.0) + react: 19.2.0 tslib: 2.8.1 - '@dnd-kit/utilities@3.2.2(react@18.3.1)': + '@dnd-kit/utilities@3.2.2(react@19.2.0)': dependencies: - react: 18.3.1 + react: 19.2.0 tslib: 2.8.1 - '@emnapi/core@1.4.5': + '@emnapi/core@1.7.1': dependencies: - '@emnapi/wasi-threads': 1.0.4 + '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.4.5': + '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.0.4': + '@emnapi/wasi-threads@1.1.0': dependencies: tslib: 2.8.1 optional: true @@ -2311,36 +2314,36 @@ snapshots: transitivePeerDependencies: - supports-color - '@module-federation/error-codes@0.18.0': {} + '@module-federation/error-codes@0.21.4': {} - '@module-federation/runtime-core@0.18.0': + '@module-federation/runtime-core@0.21.4': dependencies: - '@module-federation/error-codes': 0.18.0 - '@module-federation/sdk': 0.18.0 + '@module-federation/error-codes': 0.21.4 + '@module-federation/sdk': 0.21.4 - '@module-federation/runtime-tools@0.18.0': + '@module-federation/runtime-tools@0.21.4': dependencies: - '@module-federation/runtime': 0.18.0 - '@module-federation/webpack-bundler-runtime': 0.18.0 + '@module-federation/runtime': 0.21.4 + '@module-federation/webpack-bundler-runtime': 0.21.4 - '@module-federation/runtime@0.18.0': + '@module-federation/runtime@0.21.4': dependencies: - '@module-federation/error-codes': 0.18.0 - '@module-federation/runtime-core': 0.18.0 - '@module-federation/sdk': 0.18.0 + '@module-federation/error-codes': 0.21.4 + '@module-federation/runtime-core': 0.21.4 + '@module-federation/sdk': 0.21.4 - '@module-federation/sdk@0.18.0': {} + '@module-federation/sdk@0.21.4': {} - '@module-federation/webpack-bundler-runtime@0.18.0': + '@module-federation/webpack-bundler-runtime@0.21.4': dependencies: - '@module-federation/runtime': 0.18.0 - '@module-federation/sdk': 0.18.0 + '@module-federation/runtime': 0.21.4 + '@module-federation/sdk': 0.21.4 - '@napi-rs/wasm-runtime@1.0.3': + '@napi-rs/wasm-runtime@1.0.7': dependencies: - '@emnapi/core': 1.4.5 - '@emnapi/runtime': 1.4.5 - '@tybys/wasm-util': 0.10.0 + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 + '@tybys/wasm-util': 0.10.1 optional: true '@prisma/client@6.15.0(prisma@6.15.0(typescript@5.9.2))(typescript@5.9.2)': @@ -2378,32 +2381,32 @@ snapshots: dependencies: '@prisma/debug': 6.15.0 - '@rsbuild/core@1.5.1': + '@rsbuild/core@1.6.7': dependencies: - '@rspack/core': 1.5.0(@swc/helpers@0.5.17) - '@rspack/lite-tapable': 1.0.1 + '@rspack/core': 1.6.4(@swc/helpers@0.5.17) + '@rspack/lite-tapable': 1.1.0 '@swc/helpers': 0.5.17 - core-js: 3.45.1 - jiti: 2.5.1 + core-js: 3.46.0 + jiti: 2.6.1 - '@rsbuild/plugin-less@1.5.0(@rsbuild/core@1.5.1)': + '@rsbuild/plugin-less@1.5.0(@rsbuild/core@1.6.7)': dependencies: - '@rsbuild/core': 1.5.1 + '@rsbuild/core': 1.6.7 deepmerge: 4.3.1 reduce-configs: 1.1.1 - '@rsbuild/plugin-react@1.3.5(@rsbuild/core@1.5.1)': + '@rsbuild/plugin-react@1.4.2(@rsbuild/core@1.6.7)': dependencies: - '@rsbuild/core': 1.5.1 - '@rspack/plugin-react-refresh': 1.4.3(react-refresh@0.17.0) - react-refresh: 0.17.0 + '@rsbuild/core': 1.6.7 + '@rspack/plugin-react-refresh': 1.5.3(react-refresh@0.18.0) + react-refresh: 0.18.0 transitivePeerDependencies: - webpack-hot-middleware - '@rsbuild/plugin-svgr@1.2.2(@rsbuild/core@1.5.1)(typescript@5.9.2)': + '@rsbuild/plugin-svgr@1.2.2(@rsbuild/core@1.6.7)(typescript@5.9.2)': dependencies: - '@rsbuild/core': 1.5.1 - '@rsbuild/plugin-react': 1.3.5(@rsbuild/core@1.5.1) + '@rsbuild/core': 1.6.7 + '@rsbuild/plugin-react': 1.4.2(@rsbuild/core@1.6.7) '@svgr/core': 8.1.0(typescript@5.9.2) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2)) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2) @@ -2414,66 +2417,66 @@ snapshots: - typescript - webpack-hot-middleware - '@rspack/binding-darwin-arm64@1.5.0': + '@rspack/binding-darwin-arm64@1.6.4': optional: true - '@rspack/binding-darwin-x64@1.5.0': + '@rspack/binding-darwin-x64@1.6.4': optional: true - '@rspack/binding-linux-arm64-gnu@1.5.0': + '@rspack/binding-linux-arm64-gnu@1.6.4': optional: true - '@rspack/binding-linux-arm64-musl@1.5.0': + '@rspack/binding-linux-arm64-musl@1.6.4': optional: true - '@rspack/binding-linux-x64-gnu@1.5.0': + '@rspack/binding-linux-x64-gnu@1.6.4': optional: true - '@rspack/binding-linux-x64-musl@1.5.0': + '@rspack/binding-linux-x64-musl@1.6.4': optional: true - '@rspack/binding-wasm32-wasi@1.5.0': + '@rspack/binding-wasm32-wasi@1.6.4': dependencies: - '@napi-rs/wasm-runtime': 1.0.3 + '@napi-rs/wasm-runtime': 1.0.7 optional: true - '@rspack/binding-win32-arm64-msvc@1.5.0': + '@rspack/binding-win32-arm64-msvc@1.6.4': optional: true - '@rspack/binding-win32-ia32-msvc@1.5.0': + '@rspack/binding-win32-ia32-msvc@1.6.4': optional: true - '@rspack/binding-win32-x64-msvc@1.5.0': + '@rspack/binding-win32-x64-msvc@1.6.4': optional: true - '@rspack/binding@1.5.0': + '@rspack/binding@1.6.4': optionalDependencies: - '@rspack/binding-darwin-arm64': 1.5.0 - '@rspack/binding-darwin-x64': 1.5.0 - '@rspack/binding-linux-arm64-gnu': 1.5.0 - '@rspack/binding-linux-arm64-musl': 1.5.0 - '@rspack/binding-linux-x64-gnu': 1.5.0 - '@rspack/binding-linux-x64-musl': 1.5.0 - '@rspack/binding-wasm32-wasi': 1.5.0 - '@rspack/binding-win32-arm64-msvc': 1.5.0 - '@rspack/binding-win32-ia32-msvc': 1.5.0 - '@rspack/binding-win32-x64-msvc': 1.5.0 + '@rspack/binding-darwin-arm64': 1.6.4 + '@rspack/binding-darwin-x64': 1.6.4 + '@rspack/binding-linux-arm64-gnu': 1.6.4 + '@rspack/binding-linux-arm64-musl': 1.6.4 + '@rspack/binding-linux-x64-gnu': 1.6.4 + '@rspack/binding-linux-x64-musl': 1.6.4 + '@rspack/binding-wasm32-wasi': 1.6.4 + '@rspack/binding-win32-arm64-msvc': 1.6.4 + '@rspack/binding-win32-ia32-msvc': 1.6.4 + '@rspack/binding-win32-x64-msvc': 1.6.4 - '@rspack/core@1.5.0(@swc/helpers@0.5.17)': + '@rspack/core@1.6.4(@swc/helpers@0.5.17)': dependencies: - '@module-federation/runtime-tools': 0.18.0 - '@rspack/binding': 1.5.0 - '@rspack/lite-tapable': 1.0.1 + '@module-federation/runtime-tools': 0.21.4 + '@rspack/binding': 1.6.4 + '@rspack/lite-tapable': 1.1.0 optionalDependencies: '@swc/helpers': 0.5.17 - '@rspack/lite-tapable@1.0.1': {} + '@rspack/lite-tapable@1.1.0': {} - '@rspack/plugin-react-refresh@1.4.3(react-refresh@0.17.0)': + '@rspack/plugin-react-refresh@1.5.3(react-refresh@0.18.0)': dependencies: error-stack-parser: 2.1.4 html-entities: 2.6.0 - react-refresh: 0.17.0 + react-refresh: 0.18.0 '@standard-schema/spec@1.0.0': {} @@ -2638,7 +2641,7 @@ snapshots: '@tsconfig/node22@22.0.2': {} - '@tybys/wasm-util@0.10.0': + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 optional: true @@ -2723,14 +2726,14 @@ snapshots: '@types/range-parser@1.2.7': {} - '@types/react-dom@18.3.7(@types/react@18.3.24)': + '@types/react-dom@18.3.7(@types/react@18.3.27)': dependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.27 - '@types/react@18.3.24': + '@types/react@18.3.27': dependencies: '@types/prop-types': 15.7.15 - csstype: 3.1.3 + csstype: 3.2.3 '@types/send@0.17.5': dependencies: @@ -2888,7 +2891,7 @@ snapshots: copy-to@2.0.1: {} - core-js@3.45.1: {} + core-js@3.46.0: {} cosmiconfig@8.3.6(typescript@5.9.2): dependencies: @@ -2927,7 +2930,7 @@ snapshots: dependencies: css-tree: 2.2.1 - csstype@3.1.3: {} + csstype@3.2.3: {} dateformat@4.6.3: {} @@ -2964,7 +2967,7 @@ snapshots: dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.28.3 - csstype: 3.1.3 + csstype: 3.2.3 dom-serializer@2.0.0: dependencies: @@ -3205,6 +3208,8 @@ snapshots: jiti@2.5.1: {} + jiti@2.6.1: {} + joycon@3.1.1: {} js-tokens@4.0.0: {} @@ -3521,55 +3526,52 @@ snapshots: defu: 6.1.4 destr: 2.0.5 - react-clientside-effect@1.2.8(react@18.3.1): + react-clientside-effect@1.2.8(react@19.2.0): dependencies: '@babel/runtime': 7.28.3 - react: 18.3.1 + react: 19.2.0 - react-dom@18.3.1(react@18.3.1): + react-dom@19.2.0(react@19.2.0): dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + react: 19.2.0 + scheduler: 0.27.0 - react-focus-lock@2.13.6(@types/react@18.3.24)(react@18.3.1): + react-focus-lock@2.13.6(@types/react@18.3.27)(react@19.2.0): dependencies: '@babel/runtime': 7.28.3 focus-lock: 1.3.6 prop-types: 15.8.1 - react: 18.3.1 - react-clientside-effect: 1.2.8(react@18.3.1) - use-callback-ref: 1.3.3(@types/react@18.3.24)(react@18.3.1) - use-sidecar: 1.1.3(@types/react@18.3.24)(react@18.3.1) + react: 19.2.0 + react-clientside-effect: 1.2.8(react@19.2.0) + use-callback-ref: 1.3.3(@types/react@18.3.27)(react@19.2.0) + use-sidecar: 1.1.3(@types/react@18.3.27)(react@19.2.0) optionalDependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.27 react-is@16.13.1: {} react-is@18.3.1: {} - react-refresh@0.17.0: {} + react-refresh@0.18.0: {} - react-router@7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-router@7.8.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: cookie: 1.0.2 - react: 18.3.1 + react: 19.2.0 set-cookie-parser: 2.7.1 optionalDependencies: - react-dom: 18.3.1(react@18.3.1) + react-dom: 19.2.0(react@19.2.0) - react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@babel/runtime': 7.28.3 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) - react@18.3.1: - dependencies: - loose-envify: 1.4.0 + react@19.2.0: {} readdirp@4.1.2: {} @@ -3589,9 +3591,7 @@ snapshots: safer-buffer@2.1.2: {} - scheduler@0.23.2: - dependencies: - loose-envify: 1.4.0 + scheduler@0.27.0: {} scroll-into-view-if-needed@2.2.31: dependencies: @@ -3729,20 +3729,20 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - use-callback-ref@1.3.3(@types/react@18.3.24)(react@18.3.1): + use-callback-ref@1.3.3(@types/react@18.3.27)(react@19.2.0): dependencies: - react: 18.3.1 + react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.27 - use-sidecar@1.1.3(@types/react@18.3.24)(react@18.3.1): + use-sidecar@1.1.3(@types/react@18.3.27)(react@19.2.0): dependencies: detect-node-es: 1.1.0 - react: 18.3.1 + react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.27 vary@1.1.2: {} @@ -3756,9 +3756,9 @@ snapshots: zod@4.1.5: {} - zustand@5.0.8(@types/react@18.3.24)(react@18.3.1): + zustand@5.0.8(@types/react@18.3.27)(react@19.2.0): optionalDependencies: - '@types/react': 18.3.24 - react: 18.3.1 + '@types/react': 18.3.27 + react: 19.2.0 zx@8.8.2: {}