commit e2bf594719d70c205c587734e25a808d9aa46a3c Author: lanyuanxiaoyao Date: Wed May 20 00:18:07 2026 +0800 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..54ace9e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,32 @@ +# 跨平台行尾规范 +# 所有文本文件统一用 LF(Unix 风格),避免 CRLF/LF 混用 +* text=auto eol=lf + +# 二进制文件不转换行尾 +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.svg binary +*.pdf binary +*.zip binary +*.gz binary +*.tar binary +*.woff binary +*.woff2 binary +*.ttf binary +*.eot binary +*.mp4 binary +*.mov binary +*.mp3 binary + +# Shell 脚本必须 LF +*.sh text eol=lf + +# Windows 批处理必须 CRLF +*.bat text eol=crlf +*.cmd text eol=crlf + +# 锁定文件(如 package-lock.json)保持 LF +*.lock text eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a1e03c --- /dev/null +++ b/.gitignore @@ -0,0 +1,424 @@ +### Git.gitignore ### +# Created by git for backups. To disable backups in Git: +# $ git config --global mergetool.keepBackup false +*.orig + +# Created by git when using merge tools for conflicts +*.BACKUP.* +*.BASE.* +*.LOCAL.* +*.REMOTE.* +*_BACKUP_*.txt +*_BASE_*.txt +*_LOCAL_*.txt +*_REMOTE_*.txt + +### Go.gitignore ### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +### Go.patch ### +/vendor/ +/Godeps/ + +### JetBrains+all.gitignore ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all.patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Linux.gitignore ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node.gitignore ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Test +playwright-report +test-results + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +### VisualStudioCode.gitignore ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +### VisualStudioCode.patch ### +# Ignore all local history of files +.history + +### Windows.gitignore ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### macOS.gitignore ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python.gitignore ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Environments +.venv/ +venv/ +ENV/ +env/ +.python-version + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Pyre +.pyre/ + +# pytype +.pytype/ + +# Cython debug symbols +cython_debug/ + +# Custom +.claude/* +!.claude/settings.json +.opencode +.codex +openspec/changes/archive +temp +.agents +skills-lock.json +.worktrees +data/ +!scripts/build/ +backend/bin +backend/server +backend/desktop + +# Embedfs generated +embedfs/assets/ +embedfs/frontend-dist/ +backend/cmd/desktop/rsrc_windows_*.syso + +# Bun +.build/ +*.bun-build diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 0000000..3b9e0aa --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +bunx commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..ea5a55b --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +bunx lint-staged diff --git a/.lintstagedrc.json b/.lintstagedrc.json new file mode 100644 index 0000000..1f70e8d --- /dev/null +++ b/.lintstagedrc.json @@ -0,0 +1,4 @@ +{ + "*.{ts,tsx}": ["eslint --fix"], + "*.{md,json,yaml,yml}": ["prettier --write"] +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d578adf --- /dev/null +++ b/.prettierignore @@ -0,0 +1,13 @@ +node_modules/ +dist/ +.build/ +*.bun-build +openspec/ +bun.lock +.opencode/ +.claude/ +.codex/ +.agents/ +skills-lock.json +data/ +probe-config.schema.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..c785b06 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,11 @@ +{ + "printWidth": 120, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf", + "tabWidth": 2, + "useTabs": false +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..94bd0cf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.detectIndentation": false, + + "files.eol": "\n", + "files.encoding": "utf8", + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..0a07fe1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1 @@ +严格遵守openspec/config.yaml中context声明的项目规范 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0a07fe1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +严格遵守openspec/config.yaml中context声明的项目规范 diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000..6da5372 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,665 @@ +{{app-name}} 开发文档 + +本文档面向 `{{app-name}}` 项目的开发者,介绍项目结构、前后端架构、构建流程、测试、代码规范等内容。 + +用户使用说明请参阅 [README.md](README.md)。 + +## 目录 + +- [项目结构](#项目结构) +- [前后端边界](#前后端边界) +- [一、后端开发指引](#一后端开发指引) +- [二、前端开发指引](#二前端开发指引) +- [三、项目运行、集成与打包](#三项目运行集成与打包) +- [代码质量](#代码质量) +- [测试](#测试) +- [已知限制](#已知限制) + +--- + +## 项目结构 + +```text +src/ + server/ + bootstrap.ts 后端统一启动引导(loadServerConfig → startServer) + config.ts CLI 参数解析与配置文件加载(可选 YAML configPath,支持 --help/-h) + dev.ts 开发模式启动入口(mode: "development") + main.ts 生产模式启动入口(mode: "production",安全头启用) + server.ts HTTP server 启动工厂(Bun.serve routes 声明式路由 + fetch fallback 静态资源服务) + static.ts 生产模式静态资源服务(SPA fallback、Content-Type 映射、immutable 缓存) + helpers.ts 共享响应格式化工具(见下方函数清单) + middleware.ts API 参数校验中间件(validateIdParam、validatePagination、validateTimeRange) + routes/ API 路由 handler(按端点拆分) + health.ts GET /health + shared/ + api.ts 前后端共享 TypeScript 类型 + web/ React 前端(通过 Vite 构建) + index.html HTML 入口 + app.tsx 根组件(Layout + Header + Content,/health 联调示例展示) + main.tsx 入口(QueryClient 挂载 + ErrorBoundary + ReactQueryDevtools + TDesign CSS 导入) + styles.css 全局样式与自定义 CSS 变量 + css.d.ts CSS 模块类型声明 + components/ UI 组件 + ErrorBoundary.tsx React 错误边界,捕获渲染异常并展示降级 UI + hooks/ React hooks + use-theme-preference.ts 主题偏好 hook(system/light/dark,localStorage 记忆 + matchMedia 监听) + utils/ 前端工具函数 + time.ts 时间处理(formatCountdown、formatDurationUnit、formatRelativeTime、isOlderThan、subtractHours) +scripts/ + dev.ts 双进程开发服务(Bun API server + Vite dev server) + build.ts Vite → codegen → Bun compile 三步构建流水线 + clean.ts 清理构建产物与临时文件 +tests/ Bun test 测试(结构镜像 src 目录) + setup.ts 全局测试配置(jsdom、polyfill) + helpers.ts 测试辅助工具(rmRetry) + server/ 后端测试 + bootstrap.test.ts + config.test.ts + middleware.test.ts + static.test.ts + web/ 前端测试 + App.test.tsx + test-utils.tsx +openspec/ OpenSpec 变更与规格文档 +config.example.yaml 配置文件示例 +``` + +--- + +## 前后端边界 + +前端只通过 HTTP 调用后端,API 路径为 `/api/*` 和 `/health`。共享类型放在 `src/shared`,前端不得 `import src/server` 的运行时实现。 + +--- + +## 一、后端开发指引 + +### 1.1 架构概览 + +``` +启动流程: + dev.ts / main.ts → parseRuntimeArgs(cli args) + → bootstrap({ configPath, mode }) + → loadServerConfig(configPath):可选 YAML 解析 → ServerConfig{ host, port } + → startServer({ config, mode }):Bun.serve routes 声明式路由 + fetch fallback + → 注册 SIGINT/SIGTERM shutdown + +HTTP 请求: + Request → Bun.serve routes 声明式匹配 → routes/*.ts(handler) + → helpers.ts(响应格式化) → Response + 前端: fetch fallback → serveStaticAsset (生产) / Vite proxy (开发) +``` + +### 1.2 库使用优先级 + +后端代码开发遵循严格的库选择顺序: + +| 优先级 | 来源 | 典型用途 | +| ------ | ------------ | --------------------------------------------------------------------------------------------------- | +| 1 | Bun 内置 API | `Bun.serve`、`Bun.file`、`Bun.YAML`、`Bun.spawn`、`bun:sqlite`(如需数据存储) | +| 2 | es-toolkit | 类型判断(`isPlainObject`/`isNil`/`isEmptyObject`)、深度比较(`isEqual`)、并发控制(`Semaphore`) | +| 3 | 标准 Web API | `Object.fromEntries`、`Headers`、`fetch`、`AbortController`、`Response` | +| 4 | 主流三方库 | 按需引入,优先社区活跃、类型完善的库 | +| 5 | 自行实现 | 仅在以上都无法满足时(如 `parseDuration`、`parseSize` 等专项逻辑) | + +**原则**:新增依赖前先检查上述每一层级是否已有可用方案。禁止随意引入新依赖。 + +### 1.3 API 路由开发 + +路由文件位于 `src/server/routes/`,每个端点一个文件。路由通过 `server.ts` 的 `Bun.serve({ routes })` 声明式注册,使用 per-method handler 对象: + +```typescript +// server.ts 中的路由注册 +routes: { + "/api/*": () => jsonResponse(createApiError("API route not found", 404), { mode, status: 404 }), + "/health": { + GET: () => handleHealth(mode), + }, +} +``` + +Handler 函数签名: + +```typescript +// 无依赖的路由 +export function handleHealth(mode: RuntimeMode): Response; +``` + +**请求处理流程**: + +1. `Bun.serve` 的 `routes` 对象按路径 + HTTP 方法匹配请求 +2. 未匹配方法的请求落入 `/api/*` 通配符(返回 404) +3. 各 handler 内部通过 `helpers.ts` 的 `jsonResponse`、`createApiError` 等格式化输出 +4. 需要参数校验时使用 `middleware.ts` 提供的校验函数,返回 `Response` 实例表示校验失败(直接返回),返回数据对象表示通过 + +**新增路由步骤**: + +1. 在 `src/server/routes/` 下创建 `.ts` +2. 实现 handler 函数并 export +3. 在 `server.ts` 的 `routes` 对象中注册路径和 method handler +4. 在 `tests/server/` 中添加对应测试 + +### 1.4 共享工具 + +- **`helpers.ts`**:跨路由共用的响应工具函数 + - `createApiError(error, status)` — 构造 API 错误体 + - `createHeaders(mode, init)` — 创建响应 Headers(生产模式附加安全头:`X-Content-Type-Options`、`Referrer-Policy`) + - `createHealthResponse()` — 构造健康检查响应 `{ ok: true, service, timestamp }` + - `formatDuration(ms)` — 毫秒转为可读时长字符串 + - `jsonResponse(body, options)` — JSON 响应构造 + +- **`middleware.ts`**:API 参数校验函数 + - `validateIdParam(idStr, mode)` — 校验 ID 参数格式(字母数字下划线连字符,字母开头),返回 `{ id }` 或 `Response` + - `validatePagination(pageParam, pageSizeParam, mode)` — 校验分页参数(默认 page=1, pageSize=20,pageSize 上限 200),返回 `{ page, pageSize }` 或 `Response` + - `validateTimeRange(from, to, mode)` — 校验时间范围参数(ISO 格式、from < to),返回 `{ from, to }` 或 `Response` + +- **`static.ts`**:生产模式静态资源服务 + - `serveStaticAsset(pathname, assets)` — 静态资源分发(文件扩展名路由 → immutable 缓存,无扩展名 → SPA fallback 返回 index.html) + - `hasFileExtension(path)` / `contentTypeFor(path)` / `htmlResponse(html)` — 辅助函数 + +### 1.5 类型定义规范 + +- **共享类型**以 `src/shared/api.ts` 为唯一源头,前后端共同引用 +- 前端不得 `import src/server/` 下的任何文件 +- **严格联合类型**优先于宽类型:如 `RuntimeMode: "development" | "production" | "test"` 而非 `RuntimeMode: string` +- API 响应类型(`ApiErrorResponse`、`HealthResponse`)定义在 shared 中 + +### 1.6 配置文件规范 + +配置加载流程: + +``` +CLI argv → parseRuntimeArgs → { configPath? } + → loadServerConfig(configPath) + → 可选 YAML 文件解析 → env 覆盖 → 默认值 + → ServerConfig{ host, port } +``` + +`ServerConfig` 包含以下字段: + +| 字段 | 来源 | 默认值 | +| ------ | ------------------------------------------------- | ----------- | +| `host` | `process.env["HOST"]` → YAML `server.host` → 默认 | `127.0.0.1` | +| `port` | `process.env["PORT"]` → YAML `server.port` → 默认 | `3000` | + +配置文件示例(`config.example.yaml`): + +```yaml +server: + host: "127.0.0.1" + port: 3000 +``` + +### 1.7 错误模式 + +- **API 错误**:`{ error: "描述", status: }`,状态码 400/404 +- **日志**:非致命异常用 `console.warn`,启动失败用 `console.error` + `process.exit(1)` + +--- + +## 二、前端开发指引 + +### 2.1 技术栈概览 + +| 层面 | 技术 | 用途 | +| ------ | --------------------------------------------------- | ------------------------ | +| 框架 | React 19 | UI 组件开发 | +| 构建 | Vite(开发)+ Bun compile(生产) | 开发服务 HMR 与生产构建 | +| 语言 | TypeScript | 类型安全 | +| UI 库 | TDesign React + tdesign-icons-react | UI 组件与图标 | +| 数据层 | TanStack Query (React Query) + React Query Devtools | 服务端状态管理与自动刷新 | +| 路由 | 无(单页面应用) | 不引入 React Router | + +**不引入的依赖**:React Router(单页面场景不需要)、状态管理库(TanStack Query 即服务端状态层,组件内用 `useState` 足够) + +### 2.2 组件树与数据流 + +``` +main.tsx +└── StrictMode + └── ErrorBoundary(React 错误边界) + └── QueryClientProvider(TanStack Query 全局挂载) + ├── App(根组件,Layout + Header + Content 骨架) + │ ├── useThemePreference() ── Header 主题模式 RadioGroup(系统/明亮/黑暗,localStorage 记忆 + theme-mode 应用) + │ ├── useQuery["health"] ── GET /health(30s 轮询,前后端联调示例) + │ └── Content ── 欢迎页 + /health API 响应 JSON 展示 + └── ReactQueryDevtools(开发工具,仅开发环境) +``` + +**Hook 架构**: + +``` +hooks/use-theme-preference.ts(浏览器 UI 偏好) +├── ThemePreference: system / light / dark(RadioGroup 受控值) +├── EffectiveTheme: light / dark(写入 document.documentElement theme-mode) +├── localStorage key: {{app-name}}.theme.preference(同一浏览器记忆) +└── matchMedia("(prefers-color-scheme: dark)")(系统模式下跟随系统明暗变化) +``` + +### 2.3 TanStack Query 数据层 + +#### Query Key 规范 + +```typescript +// 使用 structured array(非字符串),以便精确匹配和按 prefix 失效 +const queryKeys = { + health: () => ["health"] as const, +}; +``` + +- Key 使用 **structured array**(非字符串),以便精确匹配和按 prefix 失效 +- 使用 `as const` 保持字面量类型 + +#### 查询配置规范 + +```typescript +// 全局级查询(需要持续刷新) +useQuery({ + queryKey: queryKeys.health(), + queryFn: () => fetchJson("/health"), + refetchInterval: 30000, // 30s 轮询 + refetchIntervalInBackground: false, // 切后台不轮询 + staleTime: 5000, // 5s 内视为 fresh +}); +``` + +#### fetch 封装 + +```typescript +async function fetchJson(url: string): Promise { + const response = await fetch(url); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json() as Promise; +} +``` + +- 统一使用 `fetch`(不引入 axios),与后端共享 Web API 生态 +- 错误抛异常,由 TanStack Query 的 `error` 状态承接 + +#### QueryClient 全局配置 + +```typescript +new QueryClient({ + defaultOptions: { + queries: { + retry: 1, // 失败重试 1 次 + refetchOnWindowFocus: true, // 窗口聚焦时刷新 + staleTime: 5000, // 5s 内视为 fresh,避免重复请求 + }, + }, +}); +``` + +### 2.4 组件开发规范 + +#### 文件命名与导入 + +- 每个 React 组件一个 `.tsx` 文件,文件名使用 PascalCase(如 `ErrorBoundary.tsx`) +- 组件 props 定义为 `interface XxxProps`,紧邻组件函数声明 +- 类型从 `../shared/api` 导入,使用 `type` 导入(`import type { ... }`) + +```typescript +import type { HealthResponse } from "../shared/api"; + +interface AppProps { + title?: string; +} + +export function App({ title }: AppProps) { + // ... +} +``` + +#### 组件拆分原则 + +- **展示组件**(`components/`):纯渲染逻辑,通过 props 接收数据,通过回调返回事件 +- **容器逻辑**放在 hooks 中,组件只做数据消费 +- **工具函数**(时间处理等)放在 `utils/`,保持纯函数无副作用 + +### 2.5 样式开发规范 + +前端基于 TDesign React 构建 UI,样式开发遵循以下优先级(从高到低): + +1. **使用 TDesign 组件**:布局、间距、排版优先使用 TDesign 组件(如 Space、Divider、Typography) +2. **使用 TDesign 组件 props**:通过组件的 props 参数控制外观(如 `theme`、`variant`、`size`) +3. **使用 TDesign CSS tokens**:颜色、间距、字体等使用 `--td-*` CSS 变量(如 `--td-success-color`、`--td-comp-margin-xxl`) +4. **在 styles.css 中定义 CSS 类**:无法通过上述方式满足的样式需求,集中定义在 `styles.css` 中 +5. **自行开发组件**:仅在 TDesign 无法满足需求时自行开发 + +**红线**: + +- **严禁在组件中使用 `style` 属性内联调整样式** +- **严禁通过 CSS 覆盖 TDesign 组件内部类名**(如 `.t-tab-panel`),如需定制使用组件的 `className` prop +- **严禁使用 `!important`** +- 颜色统一使用 TDesign CSS tokens(`--td-success-color`、`--td-error-color`、`--td-warning-color` 等),不使用硬编码色值 + +**styles.css 组织**: + +- 自定义 CSS 变量定义在 `:root` 中 +- 布局类(`.dashboard`、`.dashboard-header-controls`)定义全局页面结构 +- 组件修饰类为自定义视觉组件提供样式变体 +- 通用工具类(`.full-width`、`.text-disabled`、`.tabular-nums`)提供公用排版能力 + +### 2.6 前端测试规范 + +- 测试目录:`tests/web/`,结构对应 `src/web/` +- 重点测试 **纯函数**(时间处理、格式化等)和**组件渲染** +- 使用 `bun:test` 框架 +- 组件测试使用 `@testing-library/react` 的语义化查询(getByText、getByRole)而非 CSS 选择器 +- 测试用户行为而非实现细节:模拟用户点击、输入等操作,而非直接调用组件方法 +- 只 mock 系统边界:mock fetch 返回预设响应,使用真实的 QueryClientProvider 包裹组件 + +--- + +## 三、项目运行、集成与打包 + +### 3.1 开发期运行 + +```bash +bun run dev [config.yaml] +``` + +`scripts/dev.ts` 同时启动两个进程: + +- **Bun API server**(端口 3000):后端 API 服务,`--watch` 监听后端文件变更自动重启 +- **Vite dev server**(端口 5173):前端 SPA + HMR 热更新 + +开发时访问 `http://127.0.0.1:5173`,Vite 自动将 `/api` 和 `/health` 请求代理到后端。 + +也可以单独启动: + +```bash +bun run dev:server [config.yaml] # 仅启动后端 API server(--watch 模式) +bun run dev:web # 仅启动 Vite dev server +``` + +### 3.2 前后端集成方式 + +#### 双进程开发架构 + +开发模式下前后端分别由 Vite 和 Bun 服务: + +- Vite dev server 负责前端 SPA、HMR、模块热替换 +- Bun API server 负责后端 API 路由 +- Vite 通过 proxy 配置将 `/api/*` 和 `/health` 转发到 Bun + +#### 生产模式架构 + +生产模式下前端通过 Vite 构建为静态资源,通过 `import with { type: "file" }` 嵌入 Bun 可执行文件: + +```typescript +// server.ts +const server = Bun.serve({ + fetch(req) { + // staticAssets 存在时服务嵌入的前端资源 + if (staticAssets) { + return serveStaticAsset(new URL(req.url).pathname, staticAssets); + } + return new Response("Frontend is served by Vite dev server on :5173", { status: 404 }); + }, + routes: { + "/api/*": () => ..., + "/health": { GET: () => handleHealth(mode) }, + }, +}); +``` + +#### 路由优先级 + +Bun routes 的匹配规则:具体路径 > 通配符。`/health` 优先于 `/*`。 + +未匹配 method 的请求(如 POST /health)会落入 `/api/*` 通配符返回 404;若无该通配符会落入 fetch fallback。 + +非 API 路径由 fetch fallback 处理:有文件扩展名的返回对应静态资源或 404,无扩展名的返回 SPA index.html。 + +### 3.3 构建打包 + +#### 构建命令 + +```bash +bun run build +``` + +#### 构建流程 + +`scripts/build.ts` 执行三步流水线: + +``` +1. Vite build → dist/web/ (前端静态资源,含 code splitting) +2. Code generation → .build/static-assets.ts + .build/server-entry.ts +3. Bun compile → dist/dial-server (单可执行文件) +``` + +- Vite 构建前端资源到 `dist/web/`,自动 code splitting(vendor-react、vendor-tdesign、vendor-chart) +- Code generation 扫描 `dist/web/` 生成 `import with { type: "file" }` 声明,将资源嵌入 binary +- Bun compile 以 `.build/server-entry.ts` 为入口编译最终可执行文件 +- `.build/` 临时目录在构建完成后自动清理 + +#### 产物 + +| 产物 | 用途 | +| ------------------ | ---------------------------------------- | +| `dist/dial-server` | 生产可执行文件(含前端资源,单文件部署) | +| `dist/web/` | Vite 构建的前端资源(构建中间产物) | + +#### 构建参数 + +| 环境变量 | 说明 | +| --------------------------- | -------------------------------------- | +| `BUN_TARGET`/`BUILD_TARGET` | 交叉编译目标平台(如 `bun-linux-x64`) | + +#### 运行可执行文件 + +```bash +./dist/dial-server [config.yaml] +``` + +启动后: + +- 访问 `http://127.0.0.1:3000/` → 返回前端 SPA +- 访问 `http://127.0.0.1:3000/api/*` → 返回后端 API +- 访问 `http://127.0.0.1:3000/health` → 返回健康检查 JSON + +#### 清理 + +```bash +bun run clean +# 清理 dist/ 构建产物和 .build/ 临时文件 +``` + +### 3.4 开发工作流 + +#### 日常开发循环 + +```bash +bun run dev [config.yaml] # 启动双进程开发环境(Vite :5173 + API :3000) +# 访问 http://127.0.0.1:5173 +# 修改前端代码 → Vite HMR 热更新 / 修改后端代码 → --watch 自动重启 +bun run check # 提交前运行完整质量检查 +``` + +#### 完整验证流程 + +```bash +bun run verify +# = bun run check + bun run build +``` + +`verify` 适合 CI 或正式提交前,会完整验证类型检查、lint、格式、单元测试和生产构建。 + +### 3.5 脚本说明 + +| 脚本 | 文件 | 说明 | +| -------------------- | ------------------- | ---------------------------------------- | +| `bun run dev` | `scripts/dev.ts` | 双进程开发服务(Vite :5173 + API :3000) | +| `bun run dev:server` | `src/server/dev.ts` | 仅启动后端 API server(--watch 模式) | +| `bun run dev:web` | Vite CLI | 仅启动 Vite dev server | +| `bun run build` | `scripts/build.ts` | Vite → codegen → Bun compile 三步构建 | +| `bun run clean` | `scripts/clean.ts` | 清理构建缓存与临时文件 | + +### 3.6 环境变量 + +| 变量 | 用途 | 默认值 | +| --------------------------- | ----------------------------------------------- | ----------- | +| `BUN_TARGET`/`BUILD_TARGET` | 交叉编译目标平台(仅在 `bun run build` 时有效) | 当前平台 | +| `HOST` | 服务监听地址 | `127.0.0.1` | +| `PORT` | 服务监听端口 | `3000` | + +### 3.7 项目配置文件 + +| 文件 | 用途 | +| ---------------------- | ------------------------------------------------------------ | +| `package.json` | 项目信息、脚本、依赖声明 | +| `tsconfig.json` | TypeScript 配置(ESNext 模块、严格模式) | +| `eslint.config.js` | ESLint 规则(含前端不得 import server 的检查) | +| `commitlint.config.js` | commitlint 提交信息格式校验 | +| `.prettierrc.json` | Prettier 格式化规则(`printWidth: 120`) | +| `.prettierignore` | Prettier 排除路径 | +| `.lintstagedrc.json` | lint-staged 配置(TS/TSX → ESLint,MD/JSON/YAML → Prettier) | +| `config.example.yaml` | 配置文件示例 | +| `vite.config.ts` | Vite 构建配置(React 插件、代码分割、API proxy) | +| `bunfig.toml` | Bun 配置(测试 preload、排除规则) | +| `opencode.json` | OpenCode 工具配置 | + +### 3.8 依赖管理 + +- **包管理器**:仅使用 `bun`,禁止使用 npm、pnpm、yarn +- **安装依赖**:`bun install` +- **运行工具**:使用 `bunx`,禁止使用 `npx`、`pnpx` +- **锁文件**:`bun.lock` + +### 3.9 目录约定 + +| 目录 | 约定 | +| ------------- | ---------------------------------------------------- | +| `src/server/` | 后端代码,不能 import `src/web/`(HTML import 除外) | +| `src/web/` | 前端代码,不能 import `src/server/` | +| `src/shared/` | 前后端共享类型,双向可引用 | +| `scripts/` | 独立运行脚本,可 import 项目源码 | +| `tests/` | 测试目录,结构镜像 src 目录 | +| `dist/` | 构建产物(gitignore) | +| `openspec/` | OpenSpec 变更管理与规格文档 | + +--- + +## 代码质量 + +项目使用多层代码质量保障体系:ESLint 类型感知规则 + Perfectionist 导入排序 + Prettier 格式化(通过 eslint-plugin-prettier 集成至 ESLint)+ TypeScript 严格模式 + Git hooks 自动化。 + +```bash +bun run lint # ESLint 检查(含类型感知规则、导入排序、导入验证、Prettier 格式) +bun run format # Prettier 自动格式化 +bun run typecheck # TypeScript 类型检查 +bun test # 运行所有测试 +bun run check # 一键运行 typecheck + lint + test +bun run verify # 完整验证(check + build) +``` + +`check` 是日常开发推荐的质量检查命令。 + +### ESLint 规则 + +配置文件:`eslint.config.js` + +| 配置来源 | 用途 | +| --------------------------------------------------------------- | -------------------------------------------------- | +| `@eslint/js` recommended | JavaScript 基础规则 | +| `typescript-eslint` recommended-type-checked | TypeScript 类型感知规则(no-floating-promises 等) | +| `typescript-eslint` stylistic-type-checked | TypeScript 风格规则(命名规范、语法选择等) | +| `eslint-plugin-perfectionist` recommended-natural | 导入语句和命名导出自动排序 | +| `eslint-plugin-import` | 导入路径验证、循环依赖检测、重复导入合并 | +| `eslint-plugin-react-hooks` recommended | React Hooks 规则(依赖数组完整性检查等) | +| `eslint-plugin-react-refresh` | React Fast Refresh 兼容性检查 | +| `eslint-plugin-prettier` recommended + `eslint-config-prettier` | 将 Prettier 格式集成为 ESLint 规则,禁用冲突规则 | + +**前端导入限制**:`src/web/` 下的文件禁止 `import src/server/` 下的运行时实现,通过 `no-restricted-imports` 规则强制执行。 + +### Prettier 配置 + +配置文件:`.prettierrc.json`,通过 `eslint-plugin-prettier` 集成为 ESLint 规则(`lint` 命令同时检查格式),也可通过 `format` 命令独立运行。 + +显式声明所有格式化参数(`printWidth: 120`、`semi: true`、`singleQuote: false`、`trailingComma: "all"`、`endOfLine: "lf"` 等),确保不同开发环境产出完全一致的格式化结果。 + +### TypeScript 严格标志 + +| 标志 | 值 | 说明 | +| ------------------------------------ | ----- | ------------------------------------------------ | +| `strict` | true | 全局严格模式 | +| `noUnusedLocals` | true | 未使用局部变量视为错误 | +| `noUnusedParameters` | false | 保留关闭(路由 handler 统一签名需要) | +| `noPropertyAccessFromIndexSignature` | true | 禁止通过点号访问索引签名属性,强制使用括号语法 | +| `noUncheckedIndexedAccess` | true | 数组/Map 访问必须运行时真值检查 | +| `noImplicitOverride` | true | 子类覆盖父类方法时必须显式使用 `override` 关键字 | +| `verbatimModuleSyntax` | true | 强制 `import type` 纯类型导入,与 Bun 构建兼容 | + +### Git Hooks + +通过 husky 在 commit 阶段自动执行检查: + +| Hook | 行为 | +| ------------ | -------------------------------------------------------------------------------------------------------------- | +| `pre-commit` | lint-staged 对变更文件运行 `eslint --fix`(TS/TSX,含 Prettier 格式修复)或 `prettier --write`(MD/JSON/YAML) | +| `commit-msg` | commitlint 校验提交信息格式 `类型: 简短描述` | + +提交类型限定:`feat`、`fix`、`refactor`、`docs`、`style`、`test`、`chore`。 + +`bun install` 时自动初始化 husky hooks,无需手动配置。 + +### 质量检查完整清单 + +提交代码前建议运行: + +```bash +bun run verify +``` + +CI 或正式提交前执行完整验证(类型检查 + lint + 格式 + 测试 + 构建),确保代码可编译并通过所有检查。 + +--- + +## 测试 + +项目采用两层测试体系:单元测试 + 组件测试。所有测试使用 `bun:test` 运行。 + +### 测试分层 + +| 层级 | 覆盖范围 | 位置 | 命令 | +| -------- | ---------------------- | ------------------------------------------------------------------- | --------------------------------------------- | +| 单元测试 | 后端函数、纯函数、常量 | `tests/server/**/*.test.ts`、`tests/web/{utils,hooks}/**/*.test.ts` | `bun test tests/server`、`bun test tests/web` | +| 组件测试 | React 组件渲染和交互 | `tests/web/components/**/*.test.tsx` | `bun test tests/web` | + +### 运行命令 + +```bash +bun test # 运行所有单元测试和组件测试 +bun test tests/server # 只运行后端单元测试 +bun test tests/web # 只运行前端测试(单元 + 组件) +bun run check # 日常开发(类型检查 + lint + 测试) +bun run verify # 完整验证(check + 构建) +``` + +### 组件测试环境 + +组件测试使用 jsdom 模拟浏览器环境,配置位于 `tests/setup.ts`(通过 `bunfig.toml` preload 加载): + +- jsdom 提供完整的 DOM 环境 +- TDesign 组件所需的 polyfill:ResizeObserver、IntersectionObserver、MutationObserver、matchMedia、attachEvent +- 全局 `afterEach` 清理 document.body 内容,确保测试隔离 + +### 编写规范 + +- **优先使用 `@testing-library/react`** 的语义化查询(getByText、getByRole)而非 CSS 选择器 +- **测试用户行为而非实现细节**:模拟用户点击、输入等操作,而非直接调用组件方法 +- **只 mock 系统边界**:mock fetch 返回预设响应,使用真实的 QueryClientProvider 包裹组件 +- **组件测试文件命名**:`tests/web/ComponentName.test.tsx` +- **测试目录镜像源码目录**:`tests/server/config.test.ts` 对应 `src/server/config.ts` + +--- + +## 已知限制 + +- 当前仅为单页面应用,不涉及用户认证和权限控制 +- 不支持集群部署,单进程运行 +- 配置文件仅支持 YAML 格式,不支持热加载 +- 无国际化和多语言支持 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0ec5aff --- /dev/null +++ b/LICENSE @@ -0,0 +1,184 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that is +included in or attached to the work (an example is provided in the Appendix +below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy of +this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, then +any Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier identification within +third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..96caab4 --- /dev/null +++ b/README.md @@ -0,0 +1,208 @@ +# {{app-name}} + +(替换为你的项目介绍) + +Bun 全栈应用模板,基于 Bun + React + TDesign 的前后端一体化开发框架。 + +## 快速开始 + +```bash +git clone my-project +cd my-project +bun install +bun run dev +``` + +访问 http://127.0.0.1:5173 查看应用。 + +## 使用此模板 + +### 1. 克隆模板 + +```bash +git clone my-project +cd my-project +rm -rf .git && git init +``` + +### 2. 替换占位符 `{{app-name}}` + +在整个项目中全局搜索替换 `{{app-name}}` 为你的项目名称(如 `my-app`)。以下为所有出现位置: + +| # | 文件 | 说明 | +| --- | --------------------------------------- | ------------------------------------------ | +| 1 | `package.json` | `name` 字段 | +| 2 | `scripts/build.ts` | 可执行文件路径 | +| 3 | `src/server/config.ts` | CLI 帮助文本 | +| 4 | `src/server/helpers.ts` | `createHealthResponse()` 的 `service` 字段 | +| 5 | `src/server/server.ts` | 服务启动日志消息 | +| 6 | `src/web/index.html` | `` 和 `<meta name="description">` | +| 7 | `src/web/app.tsx` | Header 中的品牌名和欢迎标题 | +| 8 | `src/web/hooks/use-theme-preference.ts` | `localStorage` 键名 | +| 9 | `tests/web/App.test.tsx` | 测试中的品牌名断言 | + +> **提示**:可直接在编辑器中全局搜索 `{{app-name}}`,一次性替换。 + +### 3. 清理 OpenSpec 历史 + +删除模板自带的 OpenSpec 变更历史,保留框架配置: + +```bash +rm -rf openspec/specs/* +rm -rf openspec/changes/* +``` + +> `openspec/config.yaml` 需要保留,其中包含项目开发规范配置。 + +### 4. 安装依赖 + +```bash +bun install +``` + +### 5. 开始开发 + +```bash +bun run dev +``` + +## 项目管理 + +| 命令 | 说明 | +| -------------------- | ---------------------------------------------------------- | +| `bun run dev` | 启动开发模式(并行启动后端 + 前端 Vite 开发服务器) | +| `bun run dev:server` | 仅启动后端开发服务(`--watch` 热重载) | +| `bun run dev:web` | 仅启动前端 Vite 开发服务器 | +| `bun run build` | 生产构建(Vite 打包前端 → Bun compile 生成独立可执行文件) | +| `bun test` | 运行全部测试 | +| `bun run lint` | ESLint 代码风格检查 | +| `bun run format` | Prettier 代码格式化 | +| `bun run typecheck` | TypeScript 类型检查 | +| `bun run check` | 完整质量检查:typecheck + lint + test | +| `bun run verify` | 验证构建流程:check + build | +| `bun run clean` | 清理构建产物和临时文件 | + +## 项目结构 + +```text +. +├── config.example.yaml # 配置文件示例 +├── bunfig.toml # Bun 配置(测试预加载等) +├── tsconfig.json # TypeScript 配置 +├── vite.config.ts # Vite 构建配置(代码分包、代理) +├── eslint.config.js # ESLint 统一配置 +├── .prettierrc.json # Prettier 格式化配置 +├── commitlint.config.js # Commitlint 提交规范配置 +├── .lintstagedrc.json # lint-staged 暂存区检查配置 +├── scripts/ +│ ├── dev.ts # 开发启动脚本(并行启动 API + Vite) +│ ├── build.ts # 生产构建脚本(Vite → 代码生成 → Bun compile) +│ └── clean.ts # 清理脚本 +├── src/ +│ ├── server/ # 后端代码 +│ │ ├── bootstrap.ts # 统一启动引导(配置加载 → 服务启动 → 优雅关闭) +│ │ ├── config.ts # CLI 参数解析 + YAML 配置加载 +│ │ ├── dev.ts # 开发模式入口 +│ │ ├── main.ts # 生产模式入口 +│ │ ├── server.ts # HTTP 服务器(Bun.serve routes 声明式路由) +│ │ ├── helpers.ts # 共享响应工具(健康检查、JSON 响应) +│ │ ├── middleware.ts # API 参数校验中间件 +│ │ ├── static.ts # 静态资源服务 +│ │ └── routes/ # API 路由处理器 +│ │ └── health.ts # 健康检查端点 +│ ├── shared/ +│ │ └── api.ts # 前后端共享 TypeScript 类型定义 +│ └── web/ # 前端代码 +│ ├── index.html # HTML 入口 +│ ├── main.tsx # React 入口(QueryClient + ErrorBoundary) +│ ├── app.tsx # 根组件(Layout + 主题切换 + /health 展示) +│ ├── styles.css # 全局样式 +│ ├── css.d.ts # CSS 模块类型声明 +│ ├── components/ # UI 组件 +│ ├── hooks/ # React Hooks +│ └── utils/ # 前端工具函数 +├── tests/ # 测试文件(镜像 src 目录结构) +├── openspec/ # OpenSpec 规格与变更管理 +└── docs/ # 项目文档 +``` + +## 配置 + +项目使用 YAML 配置文件,支持环境变量覆盖。 + +### 配置文件 + +复制 `config.example.yaml` 为 `config.yaml`(或任意名称),根据需要修改: + +```yaml +server: + host: "127.0.0.1" + port: 3000 +``` + +### 环境变量覆盖 + +| 环境变量 | 对应配置字段 | 默认值 | +| -------- | ------------- | ----------- | +| `HOST` | `server.host` | `127.0.0.1` | +| `PORT` | `server.port` | `3000` | + +### 配置优先级 + +``` +环境变量 > YAML 配置文件 > 代码默认值 +``` + +### 使用自定义配置 + +```bash +bun run dev custom-config.yaml +``` + +## 技术栈 + +### 运行时 + +| 技术 | 说明 | +| --------------------------------------------- | ---------------------------------------------- | +| [Bun](https://bun.sh) | JavaScript/TypeScript 运行时、包管理器、打包器 | +| [TypeScript](https://www.typescriptlang.org/) | 类型安全的 JavaScript 超集 | + +### 后端 + +| 技术 | 说明 | +| -------------------------------------------- | ---------------------------- | +| `Bun.serve` | HTTP 服务器,声明式路由匹配 | +| `Bun.YAML` | YAML 配置文件解析 | +| [es-toolkit](https://es-toolkit.slash.page/) | 高性能工具库(推荐优先使用) | + +### 前端 + +| 技术 | 说明 | +| --------------------------------------------------- | ------------------------ | +| [React 19](https://react.dev/) | UI 组件框架 | +| [TDesign React](https://tdesign.tencent.com/react/) | 企业级 UI 组件库 | +| [@tanstack/react-query](https://tanstack.com/query) | 服务端状态管理与数据获取 | +| [Recharts](https://recharts.org/) | 图表可视化(推荐使用) | +| [Vite](https://vitejs.dev/) | 前端构建工具 | + +### 工程化 + +| 技术 | 说明 | +| ------------------------------------------ | ---------------- | +| [ESLint](https://eslint.org/) | 代码规范检查 | +| [Prettier](https://prettier.io/) | 代码格式化 | +| [Husky](https://typicode.github.io/husky/) | Git hooks 管理 | +| [Commitlint](https://commitlint.js.org/) | Git 提交消息校验 | + +### 测试 + +| 技术 | 说明 | +| ----------------------------------------------------------- | ---------------- | +| [bun:test](https://bun.sh/docs/cli/test) | Bun 内置测试框架 | +| [@testing-library/react](https://testing-library.com/react) | React 组件测试 | +| [jsdom](https://github.com/jsdom/jsdom) | DOM 环境模拟 | + +## 开源协议 + +MIT diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..208940b --- /dev/null +++ b/bun.lock @@ -0,0 +1,1204 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "gateway-checker", + "dependencies": { + "@tanstack/react-query": "^5.100.10", + "es-toolkit": "^1.46.1", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "recharts": "^3.8.1", + "tdesign-icons-react": "^0.6.4", + "tdesign-react": "^1.16.9", + }, + "devDependencies": { + "@commitlint/cli": "^21.0.1", + "@commitlint/config-conventional": "^21.0.1", + "@eslint/js": "^10.0.1", + "@tanstack/react-query-devtools": "^5.100.10", + "@testing-library/react": "^16.3.2", + "@types/bun": "^1.3.14", + "@types/jsdom": "^28.0.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.2", + "eslint": "^10.3.0", + "eslint-config-prettier": "^10.1.8", + "eslint-import-resolver-typescript": "^4.4.4", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-perfectionist": "^5.9.0", + "eslint-plugin-prettier": "^5.5.5", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "husky": "^9.1.7", + "jsdom": "^29.1.1", + "lint-staged": "^17.0.4", + "prettier": "^3.8.3", + "typescript": "^6.0.3", + "typescript-eslint": "^8.59.3", + "vite": "^8.0.13", + }, + }, + }, + "packages": { + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.1.11", "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@csstools/css-calc": "^3.2.0", "@csstools/css-color-parser": "^4.1.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg=="], + + "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@7.1.1", "https://registry.npmmirror.com/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1" } }, "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ=="], + + "@asamuzakjp/generational-cache": ["@asamuzakjp/generational-cache@1.0.1", "https://registry.npmmirror.com/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", {}, "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg=="], + + "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "https://registry.npmmirror.com/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.0", "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.0.tgz", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], + + "@babel/compat-data": ["@babel/compat-data@7.29.3", "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.3.tgz", {}, "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg=="], + + "@babel/core": ["@babel/core@7.29.0", "https://registry.npmmirror.com/@babel/core/-/core-7.29.0.tgz", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], + + "@babel/generator": ["@babel/generator@7.29.1", "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.1.tgz", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.29.2", "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.2.tgz", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="], + + "@babel/parser": ["@babel/parser@7.29.3", "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.3.tgz", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], + + "@babel/runtime": ["@babel/runtime@7.29.2", "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.2.tgz", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="], + + "@babel/template": ["@babel/template@7.28.6", "https://registry.npmmirror.com/@babel/template/-/template-7.28.6.tgz", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], + + "@babel/traverse": ["@babel/traverse@7.29.0", "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.0.tgz", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], + + "@babel/types": ["@babel/types@7.29.0", "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "@bramus/specificity": ["@bramus/specificity@2.4.2", "https://registry.npmmirror.com/@bramus/specificity/-/specificity-2.4.2.tgz", { "dependencies": { "css-tree": "^3.0.0" }, "bin": { "specificity": "bin/cli.js" } }, "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw=="], + + "@commitlint/cli": ["@commitlint/cli@21.0.1", "https://registry.npmmirror.com/@commitlint/cli/-/cli-21.0.1.tgz", { "dependencies": { "@commitlint/format": "^21.0.1", "@commitlint/lint": "^21.0.1", "@commitlint/load": "^21.0.1", "@commitlint/read": "^21.0.1", "@commitlint/types": "^21.0.1", "tinyexec": "^1.0.0", "yargs": "^18.0.0" }, "bin": { "commitlint": "cli.js" } }, "sha512-8vq10krmbJwBkvzXKhbs4o4JQEVscd3pqOlWuDUaDBwbeL694/P33UC29tZQFTAgPU9fVJ2+f2m3zw16yKWxHg=="], + + "@commitlint/config-conventional": ["@commitlint/config-conventional@21.0.1", "https://registry.npmmirror.com/@commitlint/config-conventional/-/config-conventional-21.0.1.tgz", { "dependencies": { "@commitlint/types": "^21.0.1", "conventional-changelog-conventionalcommits": "^9.2.0" } }, "sha512-gRorrkfWOh/+V5X8GYWWbQvrzPczopGMS4CCNrQdHkK4xWElv82BDvIsDhJZWTlI7TazOlYea6VATufCsFs+sw=="], + + "@commitlint/config-validator": ["@commitlint/config-validator@21.0.1", "https://registry.npmmirror.com/@commitlint/config-validator/-/config-validator-21.0.1.tgz", { "dependencies": { "@commitlint/types": "^21.0.1", "ajv": "^8.11.0" } }, "sha512-Zd2UFdndeMMaW2O96HK0tdfT4gOImUvidMpAd/pws2zZ4m1nrAZ/9b/v2JYuE8fs86GpXv9F7LNaIuCIWhY+pA=="], + + "@commitlint/ensure": ["@commitlint/ensure@21.0.1", "https://registry.npmmirror.com/@commitlint/ensure/-/ensure-21.0.1.tgz", { "dependencies": { "@commitlint/types": "^21.0.1", "es-toolkit": "^1.46.0" } }, "sha512-jJ1037967wU7YN/xkv+iRlOBlmaOXPhPO5KQSqya6GyXzBlwuLzELBFao16DVg9dZyqmNrhewzwZ3SAibetHBQ=="], + + "@commitlint/execute-rule": ["@commitlint/execute-rule@21.0.1", "https://registry.npmmirror.com/@commitlint/execute-rule/-/execute-rule-21.0.1.tgz", {}, "sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA=="], + + "@commitlint/format": ["@commitlint/format@21.0.1", "https://registry.npmmirror.com/@commitlint/format/-/format-21.0.1.tgz", { "dependencies": { "@commitlint/types": "^21.0.1", "picocolors": "^1.1.1" } }, "sha512-ksmG2+cHGtuDPQQbhBbC4unwm444+6TiPw0d1bKf67hntgZqZ8E0g1MuYKUuyT5IH4IMmXZhKq22/Z3jBvtQIw=="], + + "@commitlint/is-ignored": ["@commitlint/is-ignored@21.0.1", "https://registry.npmmirror.com/@commitlint/is-ignored/-/is-ignored-21.0.1.tgz", { "dependencies": { "@commitlint/types": "^21.0.1", "semver": "^7.6.0" } }, "sha512-iNDP8SFdw8JEkM0CHZ2XFnhTN4Zg5jKUY2d8kBOSFrI2aA+3YJI7fcqVpfgbpJ9xtxFVYpi+DBATU5AvhoTq8g=="], + + "@commitlint/lint": ["@commitlint/lint@21.0.1", "https://registry.npmmirror.com/@commitlint/lint/-/lint-21.0.1.tgz", { "dependencies": { "@commitlint/is-ignored": "^21.0.1", "@commitlint/parse": "^21.0.1", "@commitlint/rules": "^21.0.1", "@commitlint/types": "^21.0.1" } }, "sha512-gF+iYtUw1gBG3HUH9z3VxwUjGg2R2G5j+nmvPs8aIeYkiB7TtneBu3wO85I0bUl93bYNsvsCNI9Nte2fmDUMww=="], + + "@commitlint/load": ["@commitlint/load@21.0.1", "https://registry.npmmirror.com/@commitlint/load/-/load-21.0.1.tgz", { "dependencies": { "@commitlint/config-validator": "^21.0.1", "@commitlint/execute-rule": "^21.0.1", "@commitlint/resolve-extends": "^21.0.1", "@commitlint/types": "^21.0.1", "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", "es-toolkit": "^1.46.0", "is-plain-obj": "^4.1.0", "picocolors": "^1.1.1" } }, "sha512-Btg1q1mKmiihN4W3x0EsPDrJMOQfMa9NIqlzlJyXAfxvsOGdGXOW5p3R3RcSxDCaY7JabY9flIl+Om1af3PSrw=="], + + "@commitlint/message": ["@commitlint/message@21.0.1", "https://registry.npmmirror.com/@commitlint/message/-/message-21.0.1.tgz", {}, "sha512-R3dVQeJQ0B6yqrZEjkUHD4r7UJYLV9Lvk2xs3PTOmtWk2G3mI6Xgc+YdRxL1PwcDfBiUjv2SkIkW4AUc976w1w=="], + + "@commitlint/parse": ["@commitlint/parse@21.0.1", "https://registry.npmmirror.com/@commitlint/parse/-/parse-21.0.1.tgz", { "dependencies": { "@commitlint/types": "^21.0.1", "conventional-changelog-angular": "^8.2.0", "conventional-commits-parser": "^6.3.0" } }, "sha512-oh/nCSOqdoeQNA1tO8aAmxkq5EBo8/NzcFQRvv66AWc9HpED28sL2iSicCKU6hPintWuscL6BJEWi77Wq1LPMQ=="], + + "@commitlint/read": ["@commitlint/read@21.0.1", "https://registry.npmmirror.com/@commitlint/read/-/read-21.0.1.tgz", { "dependencies": { "@commitlint/top-level": "^21.0.1", "@commitlint/types": "^21.0.1", "git-raw-commits": "^5.0.0", "tinyexec": "^1.0.0" } }, "sha512-pMEu4lbpC8W0ZgKJj2U6WaobXIZWdFlULpIEewYhkPXx+WZcnoO53YrVPc7QErQuNolq2Me8dP58Wu7YAVXVOA=="], + + "@commitlint/resolve-extends": ["@commitlint/resolve-extends@21.0.1", "https://registry.npmmirror.com/@commitlint/resolve-extends/-/resolve-extends-21.0.1.tgz", { "dependencies": { "@commitlint/config-validator": "^21.0.1", "@commitlint/types": "^21.0.1", "es-toolkit": "^1.46.0", "global-directory": "^5.0.0", "resolve-from": "^5.0.0" } }, "sha512-0DhjYWL6uYrY16Efa032fYk3woGJDU4AGWiG1XXltT9AMUNYKyb5cIZU2ivbaMZ3+kKFqUjikD2cjh66Sbh/Sg=="], + + "@commitlint/rules": ["@commitlint/rules@21.0.1", "https://registry.npmmirror.com/@commitlint/rules/-/rules-21.0.1.tgz", { "dependencies": { "@commitlint/ensure": "^21.0.1", "@commitlint/message": "^21.0.1", "@commitlint/to-lines": "^21.0.1", "@commitlint/types": "^21.0.1" } }, "sha512-VMooYpz4nJg7xlaUso6CCOWEz8D/ChkvsvZUMARcoJ1ZpfKPyFCGrHNha2tbsETNAb6ErgiRuCr2DvghrvPDYQ=="], + + "@commitlint/to-lines": ["@commitlint/to-lines@21.0.1", "https://registry.npmmirror.com/@commitlint/to-lines/-/to-lines-21.0.1.tgz", {}, "sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw=="], + + "@commitlint/top-level": ["@commitlint/top-level@21.0.1", "https://registry.npmmirror.com/@commitlint/top-level/-/top-level-21.0.1.tgz", { "dependencies": { "escalade": "^3.2.0" } }, "sha512-4esUYqzY7K0FCgcJ/1xWEZekV7Ch4yZT1+xjEb7KzqbJ05XEkxHVsTfC8ADKNNtlCE2pj98KEbPGZWw9WwEnVw=="], + + "@commitlint/types": ["@commitlint/types@21.0.1", "https://registry.npmmirror.com/@commitlint/types/-/types-21.0.1.tgz", { "dependencies": { "conventional-commits-parser": "^6.3.0", "picocolors": "^1.1.1" } }, "sha512-4u7w8jcoCUFWhjWnASYzZHAP34OqOtuFBN87nQmFvqda03YU0T6z+yB4w0gSAMpekiRqqGk5rt+qSlW+a2vSEg=="], + + "@conventional-changelog/git-client": ["@conventional-changelog/git-client@2.7.0", "https://registry.npmmirror.com/@conventional-changelog/git-client/-/git-client-2.7.0.tgz", { "dependencies": { "@simple-libs/child-process-utils": "^1.0.0", "@simple-libs/stream-utils": "^1.2.0", "semver": "^7.5.2" }, "peerDependencies": { "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.4.0" }, "optionalPeers": ["conventional-commits-filter", "conventional-commits-parser"] }, "sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw=="], + + "@csstools/color-helpers": ["@csstools/color-helpers@6.0.2", "https://registry.npmmirror.com/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", {}, "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q=="], + + "@csstools/css-calc": ["@csstools/css-calc@3.2.1", "https://registry.npmmirror.com/@csstools/css-calc/-/css-calc-3.2.1.tgz", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg=="], + + "@csstools/css-color-parser": ["@csstools/css-color-parser@4.1.1", "https://registry.npmmirror.com/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "@csstools/css-calc": "^3.2.1" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g=="], + + "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@4.0.0", "https://registry.npmmirror.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", { "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w=="], + + "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.1.4", "https://registry.npmmirror.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz", { "peerDependencies": { "css-tree": "^3.2.1" }, "optionalPeers": ["css-tree"] }, "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw=="], + + "@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "https://registry.npmmirror.com/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="], + + "@emnapi/core": ["@emnapi/core@1.10.0", "https://registry.npmmirror.com/@emnapi/core/-/core-1.10.0.tgz", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.10.0.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/config-array": ["@eslint/config-array@0.23.5", "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.23.5.tgz", { "dependencies": { "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.5.5", "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", { "dependencies": { "@eslint/core": "^1.2.1" } }, "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w=="], + + "@eslint/core": ["@eslint/core@1.2.1", "https://registry.npmmirror.com/@eslint/core/-/core-1.2.1.tgz", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ=="], + + "@eslint/js": ["@eslint/js@10.0.1", "https://registry.npmmirror.com/@eslint/js/-/js-10.0.1.tgz", { "peerDependencies": { "eslint": "^10.0.0" }, "optionalPeers": ["eslint"] }, "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA=="], + + "@eslint/object-schema": ["@eslint/object-schema@3.0.5", "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-3.0.5.tgz", {}, "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.7.1", "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", { "dependencies": { "@eslint/core": "^1.2.1", "levn": "^0.4.1" } }, "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ=="], + + "@exodus/bytes": ["@exodus/bytes@1.15.0", "https://registry.npmmirror.com/@exodus/bytes/-/bytes-1.15.0.tgz", { "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" }, "optionalPeers": ["@noble/hashes"] }, "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ=="], + + "@humanfs/core": ["@humanfs/core@0.19.2", "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.2.tgz", { "dependencies": { "@humanfs/types": "^0.15.0" } }, "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA=="], + + "@humanfs/node": ["@humanfs/node@0.16.8", "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.8.tgz", { "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ=="], + + "@humanfs/types": ["@humanfs/types@0.15.0", "https://registry.npmmirror.com/@humanfs/types/-/types-0.15.0.tgz", {}, "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], + + "@oxc-project/types": ["@oxc-project/types@0.130.0", "https://registry.npmmirror.com/@oxc-project/types/-/types-0.130.0.tgz", {}, "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q=="], + + "@pkgr/core": ["@pkgr/core@0.2.9", "https://registry.npmmirror.com/@pkgr/core/-/core-0.2.9.tgz", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="], + + "@popperjs/core": ["@popperjs/core@2.11.8", "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.8.tgz", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="], + + "@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "https://registry.npmmirror.com/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg=="], + + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz", { "os": "none", "cpu": "arm64" }, "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ=="], + + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.1", "https://registry.npmmirror.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], + + "@rtsao/scc": ["@rtsao/scc@1.1.0", "https://registry.npmmirror.com/@rtsao/scc/-/scc-1.1.0.tgz", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + + "@simple-libs/child-process-utils": ["@simple-libs/child-process-utils@1.0.2", "https://registry.npmmirror.com/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz", { "dependencies": { "@simple-libs/stream-utils": "^1.2.0" } }, "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw=="], + + "@simple-libs/stream-utils": ["@simple-libs/stream-utils@1.2.0", "https://registry.npmmirror.com/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz", {}, "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "https://registry.npmmirror.com/@standard-schema/spec/-/spec-1.1.0.tgz", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "https://registry.npmmirror.com/@standard-schema/utils/-/utils-0.3.0.tgz", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], + + "@tanstack/query-core": ["@tanstack/query-core@5.100.10", "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.100.10.tgz", {}, "sha512-8UR0yJR+GiQ40m3lPhUr0xbfAupe6GSQiksSBSa9SM2NjezFyxXCIA69/lz8cSoNKZLrw1/PktIyQBJcVeMi3w=="], + + "@tanstack/query-devtools": ["@tanstack/query-devtools@5.100.10", "https://registry.npmmirror.com/@tanstack/query-devtools/-/query-devtools-5.100.10.tgz", {}, "sha512-3DmJf25hDPus5IpVvp6ujXv6bKV2zPzI9vpbAmpJigsL/H6DPvPjmf7/Q9yVKEke//8fgeQ45abjgnLuyYxAiw=="], + + "@tanstack/react-query": ["@tanstack/react-query@5.100.10", "https://registry.npmmirror.com/@tanstack/react-query/-/react-query-5.100.10.tgz", { "dependencies": { "@tanstack/query-core": "5.100.10" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-FLaZf2RCrA/Zgp4aiu5tG3TyasTRO7aZ99skxQpr3Hg/zXOhu6yq5FZCYQ/tRaJtM9ylnoK8tFK7PolXQadv6Q=="], + + "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.100.10", "https://registry.npmmirror.com/@tanstack/react-query-devtools/-/react-query-devtools-5.100.10.tgz", { "dependencies": { "@tanstack/query-devtools": "5.100.10" }, "peerDependencies": { "@tanstack/react-query": "^5.100.10", "react": "^18 || ^19" } }, "sha512-zes0+o9ef5rAZXJ9f/SeaLs2nufJaeVkZkl/Or9NGrWVF41kL9Od9ED9nCwtQlgiF2VGtrzhEw5AU/igAO+aAg=="], + + "@testing-library/dom": ["@testing-library/dom@10.4.1", "https://registry.npmmirror.com/@testing-library/dom/-/dom-10.4.1.tgz", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + + "@testing-library/react": ["@testing-library/react@16.3.2", "https://registry.npmmirror.com/@testing-library/react/-/react-16.3.2.tgz", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@types/aria-query": ["@types/aria-query@5.0.4", "https://registry.npmmirror.com/@types/aria-query/-/aria-query-5.0.4.tgz", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + + "@types/bun": ["@types/bun@1.3.14", "https://registry.npmmirror.com/@types/bun/-/bun-1.3.14.tgz", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], + + "@types/d3-array": ["@types/d3-array@3.2.2", "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.2.tgz", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], + + "@types/d3-color": ["@types/d3-color@3.1.3", "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], + + "@types/d3-ease": ["@types/d3-ease@3.0.2", "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], + + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], + + "@types/d3-path": ["@types/d3-path@3.1.1", "https://registry.npmmirror.com/@types/d3-path/-/d3-path-3.1.1.tgz", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], + + "@types/d3-scale": ["@types/d3-scale@4.0.9", "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.9.tgz", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], + + "@types/d3-shape": ["@types/d3-shape@3.1.8", "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.8.tgz", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="], + + "@types/d3-time": ["@types/d3-time@3.0.4", "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.4.tgz", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], + + "@types/d3-timer": ["@types/d3-timer@3.0.2", "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], + + "@types/esrecurse": ["@types/esrecurse@4.3.1", "https://registry.npmmirror.com/@types/esrecurse/-/esrecurse-4.3.1.tgz", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="], + + "@types/estree": ["@types/estree@1.0.9", "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/jsdom": ["@types/jsdom@28.0.3", "https://registry.npmmirror.com/@types/jsdom/-/jsdom-28.0.3.tgz", { "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", "parse5": "^8.0.0", "undici-types": "^7.21.0" } }, "sha512-/HQ2uFoetFTXuye8vzIcHw2z6Fwi7Hi/qcgC+RoS9NCyewiqxhVGqlG+ViGB6lkax481R6dmhf1I7lIGlzJStQ=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/json5": ["@types/json5@0.0.29", "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + + "@types/node": ["@types/node@25.6.2", "https://registry.npmmirror.com/@types/node/-/node-25.6.2.tgz", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw=="], + + "@types/react": ["@types/react@19.2.14", "https://registry.npmmirror.com/@types/react/-/react-19.2.14.tgz", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "https://registry.npmmirror.com/@types/react-dom/-/react-dom-19.2.3.tgz", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@types/sortablejs": ["@types/sortablejs@1.15.9", "https://registry.npmmirror.com/@types/sortablejs/-/sortablejs-1.15.9.tgz", {}, "sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ=="], + + "@types/tough-cookie": ["@types/tough-cookie@4.0.5", "https://registry.npmmirror.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="], + + "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "https://registry.npmmirror.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="], + + "@types/validator": ["@types/validator@13.15.10", "https://registry.npmmirror.com/@types/validator/-/validator-13.15.10.tgz", {}, "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/type-utils": "8.59.3", "@typescript-eslint/utils": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.59.3", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.59.3.tgz", { "dependencies": { "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.59.3.tgz", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.59.3", "@typescript-eslint/types": "^8.59.3", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.2", "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", { "dependencies": { "@typescript-eslint/types": "8.59.2", "@typescript-eslint/visitor-keys": "8.59.2" } }, "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz", { "dependencies": { "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/utils": "8.59.3", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.59.2", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.59.2.tgz", {}, "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz", { "dependencies": { "@typescript-eslint/project-service": "8.59.3", "@typescript-eslint/tsconfig-utils": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.2", "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.59.2.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.2", "@typescript-eslint/types": "8.59.2", "@typescript-eslint/typescript-estree": "8.59.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz", { "dependencies": { "@typescript-eslint/types": "8.59.3", "eslint-visitor-keys": "^5.0.0" } }, "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg=="], + + "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], + + "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="], + + "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g=="], + + "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ=="], + + "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw=="], + + "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw=="], + + "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw=="], + + "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ=="], + + "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w=="], + + "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA=="], + + "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ=="], + + "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew=="], + + "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg=="], + + "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w=="], + + "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA=="], + + "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ=="], + + "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw=="], + + "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ=="], + + "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="], + + "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.2", "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", { "dependencies": { "@rolldown/pluginutils": "^1.0.0" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg=="], + + "acorn": ["acorn@8.16.0", "https://registry.npmmirror.com/acorn/-/acorn-8.16.0.tgz", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.15.0", "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], + + "ansi-escapes": ["ansi-escapes@7.3.0", "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-7.3.0.tgz", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="], + + "ansi-regex": ["ansi-regex@5.0.1", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@5.2.0", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "argparse": ["argparse@2.0.1", "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-query": ["aria-query@5.3.0", "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.0.tgz", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + + "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + + "array-ify": ["array-ify@1.0.0", "https://registry.npmmirror.com/array-ify/-/array-ify-1.0.0.tgz", {}, "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng=="], + + "array-includes": ["array-includes@3.1.9", "https://registry.npmmirror.com/array-includes/-/array-includes-3.1.9.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="], + + "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "https://registry.npmmirror.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="], + + "array.prototype.flat": ["array.prototype.flat@1.3.3", "https://registry.npmmirror.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], + + "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "https://registry.npmmirror.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], + + "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "https://registry.npmmirror.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + + "async-function": ["async-function@1.0.0", "https://registry.npmmirror.com/async-function/-/async-function-1.0.0.tgz", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "balanced-match": ["balanced-match@4.0.4", "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.28", "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.28.tgz", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Ic44hnOtFIgravCunj1ifSoQPSUrkNiJuH9Mf6jr2jjoA74icqV8wU0KuadXeOR8zuIJMOoTv0GuQjZ9ZYNMeA=="], + + "bidi-js": ["bidi-js@1.0.3", "https://registry.npmmirror.com/bidi-js/-/bidi-js-1.0.3.tgz", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], + + "brace-expansion": ["brace-expansion@5.0.6", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.6.tgz", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], + + "browserslist": ["browserslist@4.28.2", "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.2.tgz", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + + "bun-types": ["bun-types@1.3.14", "https://registry.npmmirror.com/bun-types/-/bun-types-1.3.14.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], + + "call-bind": ["call-bind@1.0.9", "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.9.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001792", "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", {}, "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw=="], + + "classnames": ["classnames@2.5.1", "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz", {}, "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="], + + "cli-cursor": ["cli-cursor@5.0.0", "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-5.0.0.tgz", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-truncate": ["cli-truncate@5.2.0", "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-5.2.0.tgz", { "dependencies": { "slice-ansi": "^8.0.0", "string-width": "^8.2.0" } }, "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw=="], + + "cliui": ["cliui@9.0.1", "https://registry.npmmirror.com/cliui/-/cliui-9.0.1.tgz", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="], + + "clsx": ["clsx@2.1.1", "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "compare-func": ["compare-func@2.0.0", "https://registry.npmmirror.com/compare-func/-/compare-func-2.0.0.tgz", { "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="], + + "concat-map": ["concat-map@0.0.1", "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "conventional-changelog-angular": ["conventional-changelog-angular@8.3.1", "https://registry.npmmirror.com/conventional-changelog-angular/-/conventional-changelog-angular-8.3.1.tgz", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg=="], + + "conventional-changelog-conventionalcommits": ["conventional-changelog-conventionalcommits@9.3.1", "https://registry.npmmirror.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.3.1.tgz", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw=="], + + "conventional-commits-parser": ["conventional-commits-parser@6.4.0", "https://registry.npmmirror.com/conventional-commits-parser/-/conventional-commits-parser-6.4.0.tgz", { "dependencies": { "@simple-libs/stream-utils": "^1.2.0", "meow": "^13.0.0" }, "bin": { "conventional-commits-parser": "dist/cli/index.js" } }, "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw=="], + + "convert-source-map": ["convert-source-map@2.0.0", "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cosmiconfig": ["cosmiconfig@9.0.1", "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-9.0.1.tgz", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ=="], + + "cosmiconfig-typescript-loader": ["cosmiconfig-typescript-loader@6.3.0", "https://registry.npmmirror.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz", { "dependencies": { "jiti": "2.6.1" }, "peerDependencies": { "@types/node": "*", "cosmiconfig": ">=9", "typescript": ">=5" } }, "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA=="], + + "cross-spawn": ["cross-spawn@7.0.6", "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "css-tree": ["css-tree@3.2.1", "https://registry.npmmirror.com/css-tree/-/css-tree-3.2.1.tgz", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="], + + "csstype": ["csstype@3.2.3", "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "d3-array": ["d3-array@3.2.4", "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-color": ["d3-color@3.1.0", "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-ease": ["d3-ease@3.0.1", "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-format": ["d3-format@3.1.2", "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.2.tgz", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@3.1.0", "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-scale": ["d3-scale@4.0.2", "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-shape": ["d3-shape@3.2.0", "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-time": ["d3-time@3.1.0", "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "data-urls": ["data-urls@7.0.0", "https://registry.npmmirror.com/data-urls/-/data-urls-7.0.0.tgz", { "dependencies": { "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.0" } }, "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA=="], + + "data-view-buffer": ["data-view-buffer@1.0.2", "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], + + "data-view-byte-length": ["data-view-byte-length@1.0.2", "https://registry.npmmirror.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], + + "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "https://registry.npmmirror.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + + "dayjs": ["dayjs@1.11.10", "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz", {}, "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="], + + "debug": ["debug@4.4.3", "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decimal.js": ["decimal.js@10.6.0", "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + + "decimal.js-light": ["decimal.js-light@2.5.1", "https://registry.npmmirror.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], + + "deep-is": ["deep-is@0.1.4", "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "define-data-property": ["define-data-property@1.1.4", "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "dequal": ["dequal@2.0.3", "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "doctrine": ["doctrine@2.1.0", "https://registry.npmmirror.com/doctrine/-/doctrine-2.1.0.tgz", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], + + "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "https://registry.npmmirror.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + + "dom-helpers": ["dom-helpers@5.2.1", "https://registry.npmmirror.com/dom-helpers/-/dom-helpers-5.2.1.tgz", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], + + "dot-prop": ["dot-prop@5.3.0", "https://registry.npmmirror.com/dot-prop/-/dot-prop-5.3.0.tgz", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="], + + "dunder-proto": ["dunder-proto@1.0.1", "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.353", "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz", {}, "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w=="], + + "emoji-regex": ["emoji-regex@10.6.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "entities": ["entities@8.0.0", "https://registry.npmmirror.com/entities/-/entities-8.0.0.tgz", {}, "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA=="], + + "env-paths": ["env-paths@2.2.1", "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + + "environment": ["environment@1.1.0", "https://registry.npmmirror.com/environment/-/environment-1.1.0.tgz", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "error-ex": ["error-ex@1.3.4", "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.4.tgz", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], + + "es-abstract": ["es-abstract@1.24.2", "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.2.tgz", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="], + + "es-define-property": ["es-define-property@1.0.1", "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "https://registry.npmmirror.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], + + "es-to-primitive": ["es-to-primitive@1.3.0", "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], + + "es-toolkit": ["es-toolkit@1.46.1", "https://registry.npmmirror.com/es-toolkit/-/es-toolkit-1.46.1.tgz", {}, "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ=="], + + "escalade": ["escalade@3.2.0", "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@10.3.0", "https://registry.npmmirror.com/eslint/-/eslint-10.3.0.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.5.5", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw=="], + + "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], + + "eslint-import-context": ["eslint-import-context@0.1.9", "https://registry.npmmirror.com/eslint-import-context/-/eslint-import-context-0.1.9.tgz", { "dependencies": { "get-tsconfig": "^4.10.1", "stable-hash-x": "^0.2.0" }, "peerDependencies": { "unrs-resolver": "^1.0.0" }, "optionalPeers": ["unrs-resolver"] }, "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg=="], + + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.10", "https://registry.npmmirror.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.16.1", "resolve": "^2.0.0-next.6" } }, "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ=="], + + "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@4.4.4", "https://registry.npmmirror.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz", { "dependencies": { "debug": "^4.4.1", "eslint-import-context": "^0.1.8", "get-tsconfig": "^4.10.1", "is-bun-module": "^2.0.0", "stable-hash-x": "^0.2.0", "tinyglobby": "^0.2.14", "unrs-resolver": "^1.7.11" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw=="], + + "eslint-module-utils": ["eslint-module-utils@2.12.1", "https://registry.npmmirror.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", { "dependencies": { "debug": "^3.2.7" } }, "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw=="], + + "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "https://registry.npmmirror.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="], + + "eslint-plugin-perfectionist": ["eslint-plugin-perfectionist@5.9.0", "https://registry.npmmirror.com/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.9.0.tgz", { "dependencies": { "@typescript-eslint/utils": "^8.58.2", "natural-orderby": "^5.0.0" }, "peerDependencies": { "eslint": "^8.45.0 || ^9.0.0 || ^10.0.0" } }, "sha512-8TWzg02zmnBdZwCkWLi8jhzqXI+fE7Z/RwV8SL6xD45tJ8Bp3wGuYL2XtQgfe/Wd0eBqOUX+s6ey73IyszvKTA=="], + + "eslint-plugin-prettier": ["eslint-plugin-prettier@5.5.5", "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", { "dependencies": { "prettier-linter-helpers": "^1.0.1", "synckit": "^0.11.12" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", "prettier": ">=3.0.0" }, "optionalPeers": ["@types/eslint", "eslint-config-prettier"] }, "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.1.1", "https://registry.npmmirror.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g=="], + + "eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.5.2", "https://registry.npmmirror.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", { "peerDependencies": { "eslint": "^9 || ^10" } }, "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA=="], + + "eslint-scope": ["eslint-scope@9.1.2", "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-9.1.2.tgz", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], + + "espree": ["espree@11.2.0", "https://registry.npmmirror.com/espree/-/espree-11.2.0.tgz", { "dependencies": { "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^5.0.1" } }, "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw=="], + + "esquery": ["esquery@1.7.0", "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], + + "esrecurse": ["esrecurse@4.3.0", "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "eventemitter3": ["eventemitter3@5.0.4", "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.4.tgz", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-diff": ["fast-diff@1.3.0", "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fast-uri": ["fast-uri@3.1.2", "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.2.tgz", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="], + + "fdir": ["fdir@6.5.0", "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "find-up": ["find-up@5.0.0", "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.4.2", "https://registry.npmmirror.com/flatted/-/flatted-3.4.2.tgz", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], + + "for-each": ["for-each@0.3.5", "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "fsevents": ["fsevents@2.3.3", "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "function.prototype.name": ["function.prototype.name@1.1.8", "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], + + "functions-have-names": ["functions-have-names@1.2.3", "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + + "generator-function": ["generator-function@2.0.1", "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + + "gensync": ["gensync@1.0.0-beta.2", "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-caller-file": ["get-caller-file@2.0.5", "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.6.0", "https://registry.npmmirror.com/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-symbol-description": ["get-symbol-description@1.1.0", "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], + + "get-tsconfig": ["get-tsconfig@4.14.0", "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.14.0.tgz", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="], + + "git-raw-commits": ["git-raw-commits@5.0.1", "https://registry.npmmirror.com/git-raw-commits/-/git-raw-commits-5.0.1.tgz", { "dependencies": { "@conventional-changelog/git-client": "^2.6.0", "meow": "^13.0.0" }, "bin": { "git-raw-commits": "src/cli.js" } }, "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ=="], + + "glob-parent": ["glob-parent@6.0.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "global-directory": ["global-directory@5.0.0", "https://registry.npmmirror.com/global-directory/-/global-directory-5.0.0.tgz", { "dependencies": { "ini": "6.0.0" } }, "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w=="], + + "globalthis": ["globalthis@1.0.4", "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + + "gopd": ["gopd@1.2.0", "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-bigints": ["has-bigints@1.1.0", "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-proto": ["has-proto@1.2.0", "https://registry.npmmirror.com/has-proto/-/has-proto-1.2.0.tgz", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], + + "has-symbols": ["has-symbols@1.1.0", "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.3", "https://registry.npmmirror.com/hasown/-/hasown-2.0.3.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="], + + "hermes-estree": ["hermes-estree@0.25.1", "https://registry.npmmirror.com/hermes-estree/-/hermes-estree-0.25.1.tgz", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], + + "hermes-parser": ["hermes-parser@0.25.1", "https://registry.npmmirror.com/hermes-parser/-/hermes-parser-0.25.1.tgz", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], + + "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "https://registry.npmmirror.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="], + + "html-encoding-sniffer": ["html-encoding-sniffer@6.0.0", "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="], + + "husky": ["husky@9.1.7", "https://registry.npmmirror.com/husky/-/husky-9.1.7.tgz", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], + + "ignore": ["ignore@5.3.2", "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "immer": ["immer@10.2.0", "https://registry.npmmirror.com/immer/-/immer-10.2.0.tgz", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="], + + "import-fresh": ["import-fresh@3.3.1", "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "ini": ["ini@6.0.0", "https://registry.npmmirror.com/ini/-/ini-6.0.0.tgz", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], + + "internal-slot": ["internal-slot@1.1.0", "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + + "internmap": ["internmap@2.0.3", "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + + "is-array-buffer": ["is-array-buffer@3.0.5", "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], + + "is-arrayish": ["is-arrayish@0.2.1", "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-async-function": ["is-async-function@2.1.1", "https://registry.npmmirror.com/is-async-function/-/is-async-function-2.1.1.tgz", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], + + "is-bigint": ["is-bigint@1.1.0", "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], + + "is-boolean-object": ["is-boolean-object@1.2.2", "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], + + "is-bun-module": ["is-bun-module@2.0.0", "https://registry.npmmirror.com/is-bun-module/-/is-bun-module-2.0.0.tgz", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="], + + "is-callable": ["is-callable@1.2.7", "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-core-module": ["is-core-module@2.16.2", "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.2.tgz", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], + + "is-data-view": ["is-data-view@1.0.2", "https://registry.npmmirror.com/is-data-view/-/is-data-view-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], + + "is-date-object": ["is-date-object@1.1.0", "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + + "is-extglob": ["is-extglob@2.1.1", "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + + "is-generator-function": ["is-generator-function@1.1.2", "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.2.tgz", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + + "is-glob": ["is-glob@4.0.3", "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-map": ["is-map@2.0.3", "https://registry.npmmirror.com/is-map/-/is-map-2.0.3.tgz", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], + + "is-negative-zero": ["is-negative-zero@2.0.3", "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], + + "is-number-object": ["is-number-object@1.1.1", "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.1.1.tgz", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], + + "is-obj": ["is-obj@2.0.0", "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + + "is-regex": ["is-regex@1.2.1", "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-set": ["is-set@2.0.3", "https://registry.npmmirror.com/is-set/-/is-set-2.0.3.tgz", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], + + "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], + + "is-string": ["is-string@1.1.1", "https://registry.npmmirror.com/is-string/-/is-string-1.1.1.tgz", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], + + "is-symbol": ["is-symbol@1.1.1", "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.1.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], + + "is-typed-array": ["is-typed-array@1.1.15", "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-weakmap": ["is-weakmap@2.0.2", "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.2.tgz", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], + + "is-weakref": ["is-weakref@1.1.1", "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.1.1.tgz", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], + + "is-weakset": ["is-weakset@2.0.4", "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], + + "isarray": ["isarray@2.0.5", "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "isexe": ["isexe@2.0.0", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jiti": ["jiti@2.6.1", "https://registry.npmmirror.com/jiti/-/jiti-2.6.1.tgz", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "js-tokens": ["js-tokens@4.0.0", "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.1", "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsdom": ["jsdom@29.1.1", "https://registry.npmmirror.com/jsdom/-/jsdom-29.1.1.tgz", { "dependencies": { "@asamuzakjp/css-color": "^5.1.11", "@asamuzakjp/dom-selector": "^7.1.1", "@bramus/specificity": "^2.4.2", "@csstools/css-syntax-patches-for-csstree": "^1.1.3", "@exodus/bytes": "^1.15.0", "css-tree": "^3.2.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.3.5", "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.1", "undici": "^7.25.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.1", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q=="], + + "jsesc": ["jsesc@3.1.0", "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@1.0.2", "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + + "keyv": ["keyv@4.5.4", "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "levn": ["levn@0.4.1", "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.32.0", "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.32.0.tgz", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "lint-staged": ["lint-staged@17.0.4", "https://registry.npmmirror.com/lint-staged/-/lint-staged-17.0.4.tgz", { "dependencies": { "listr2": "^10.2.1", "picomatch": "^4.0.4", "string-argv": "^0.3.2", "tinyexec": "^1.1.2" }, "optionalDependencies": { "yaml": "^2.8.4" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-+rU9lSUyVOZ/hDUmRLVGzyS2v73cDdQjX+XQz1AaOdIE4RysLq0HoPW2HrrgeNCLklkhi904VBU1bmgWLHVnkA=="], + + "listr2": ["listr2@10.2.1", "https://registry.npmmirror.com/listr2/-/listr2-10.2.1.tgz", { "dependencies": { "cli-truncate": "^5.2.0", "eventemitter3": "^5.0.4", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^10.0.0" } }, "sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q=="], + + "locate-path": ["locate-path@6.0.0", "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash-es": ["lodash-es@4.18.1", "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.18.1.tgz", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="], + + "log-update": ["log-update@6.1.0", "https://registry.npmmirror.com/log-update/-/log-update-6.1.0.tgz", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], + + "loose-envify": ["loose-envify@1.4.0", "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lru-cache": ["lru-cache@11.3.6", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.3.6.tgz", {}, "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A=="], + + "lz-string": ["lz-string@1.5.0", "https://registry.npmmirror.com/lz-string/-/lz-string-1.5.0.tgz", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mdn-data": ["mdn-data@2.27.1", "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.27.1.tgz", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="], + + "meow": ["meow@13.2.0", "https://registry.npmmirror.com/meow/-/meow-13.2.0.tgz", {}, "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA=="], + + "mimic-function": ["mimic-function@5.0.1", "https://registry.npmmirror.com/mimic-function/-/mimic-function-5.0.1.tgz", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "minimatch": ["minimatch@10.2.5", "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.5.tgz", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + + "minimist": ["minimist@1.2.8", "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "mitt": ["mitt@3.0.1", "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], + + "ms": ["ms@2.1.3", "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.12", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], + + "napi-postinstall": ["napi-postinstall@0.3.4", "https://registry.npmmirror.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], + + "natural-compare": ["natural-compare@1.4.0", "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "natural-orderby": ["natural-orderby@5.0.0", "https://registry.npmmirror.com/natural-orderby/-/natural-orderby-5.0.0.tgz", {}, "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg=="], + + "node-exports-info": ["node-exports-info@1.6.0", "https://registry.npmmirror.com/node-exports-info/-/node-exports-info-1.6.0.tgz", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw=="], + + "node-releases": ["node-releases@2.0.38", "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.38.tgz", {}, "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw=="], + + "object-assign": ["object-assign@4.1.1", "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "object-keys": ["object-keys@1.1.1", "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "object.entries": ["object.entries@1.1.9", "https://registry.npmmirror.com/object.entries/-/object.entries-1.1.9.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="], + + "object.fromentries": ["object.fromentries@2.0.8", "https://registry.npmmirror.com/object.fromentries/-/object.fromentries-2.0.8.tgz", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], + + "object.groupby": ["object.groupby@1.0.3", "https://registry.npmmirror.com/object.groupby/-/object.groupby-1.0.3.tgz", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="], + + "object.values": ["object.values@1.2.1", "https://registry.npmmirror.com/object.values/-/object.values-1.2.1.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + + "onetime": ["onetime@7.0.0", "https://registry.npmmirror.com/onetime/-/onetime-7.0.0.tgz", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "optionator": ["optionator@0.9.4", "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "own-keys": ["own-keys@1.0.1", "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], + + "p-limit": ["p-limit@3.1.0", "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "parent-module": ["parent-module@1.0.1", "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@5.2.0", "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "parse5": ["parse5@8.0.1", "https://registry.npmmirror.com/parse5/-/parse5-8.0.1.tgz", { "dependencies": { "entities": "^8.0.0" } }, "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw=="], + + "path-exists": ["path-exists@4.0.0", "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "performance-now": ["performance-now@2.1.0", "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz", {}, "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="], + + "picocolors": ["picocolors@1.1.1", "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.4", "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "postcss": ["postcss@8.5.14", "https://registry.npmmirror.com/postcss/-/postcss-8.5.14.tgz", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.8.3", "https://registry.npmmirror.com/prettier/-/prettier-3.8.3.tgz", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="], + + "prettier-linter-helpers": ["prettier-linter-helpers@1.0.1", "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg=="], + + "pretty-format": ["pretty-format@27.5.1", "https://registry.npmmirror.com/pretty-format/-/pretty-format-27.5.1.tgz", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + + "prop-types": ["prop-types@15.8.1", "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "punycode": ["punycode@2.3.1", "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "raf": ["raf@3.4.1", "https://registry.npmmirror.com/raf/-/raf-3.4.1.tgz", { "dependencies": { "performance-now": "^2.1.0" } }, "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA=="], + + "react": ["react@19.2.6", "https://registry.npmmirror.com/react/-/react-19.2.6.tgz", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], + + "react-dom": ["react-dom@19.2.6", "https://registry.npmmirror.com/react-dom/-/react-dom-19.2.6.tgz", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], + + "react-fast-compare": ["react-fast-compare@3.2.2", "https://registry.npmmirror.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz", {}, "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="], + + "react-is": ["react-is@18.3.1", "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "react-redux": ["react-redux@9.2.0", "https://registry.npmmirror.com/react-redux/-/react-redux-9.2.0.tgz", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="], + + "react-transition-group": ["react-transition-group@4.4.5", "https://registry.npmmirror.com/react-transition-group/-/react-transition-group-4.4.5.tgz", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], + + "recharts": ["recharts@3.8.1", "https://registry.npmmirror.com/recharts/-/recharts-3.8.1.tgz", { "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg=="], + + "redux": ["redux@5.0.1", "https://registry.npmmirror.com/redux/-/redux-5.0.1.tgz", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="], + + "redux-thunk": ["redux-thunk@3.1.0", "https://registry.npmmirror.com/redux-thunk/-/redux-thunk-3.1.0.tgz", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="], + + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], + + "regenerator-runtime": ["regenerator-runtime@0.14.1", "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="], + + "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], + + "require-from-string": ["require-from-string@2.0.2", "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "reselect": ["reselect@5.1.1", "https://registry.npmmirror.com/reselect/-/reselect-5.1.1.tgz", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], + + "resolve": ["resolve@2.0.0-next.6", "https://registry.npmmirror.com/resolve/-/resolve-2.0.0-next.6.tgz", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="], + + "resolve-from": ["resolve-from@5.0.0", "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "restore-cursor": ["restore-cursor@5.1.0", "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-5.1.0.tgz", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "rfdc": ["rfdc@1.4.1", "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "rolldown": ["rolldown@1.0.1", "https://registry.npmmirror.com/rolldown/-/rolldown-1.0.1.tgz", { "dependencies": { "@oxc-project/types": "=0.130.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.1", "@rolldown/binding-darwin-arm64": "1.0.1", "@rolldown/binding-darwin-x64": "1.0.1", "@rolldown/binding-freebsd-x64": "1.0.1", "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", "@rolldown/binding-linux-arm64-gnu": "1.0.1", "@rolldown/binding-linux-arm64-musl": "1.0.1", "@rolldown/binding-linux-ppc64-gnu": "1.0.1", "@rolldown/binding-linux-s390x-gnu": "1.0.1", "@rolldown/binding-linux-x64-gnu": "1.0.1", "@rolldown/binding-linux-x64-musl": "1.0.1", "@rolldown/binding-openharmony-arm64": "1.0.1", "@rolldown/binding-wasm32-wasi": "1.0.1", "@rolldown/binding-win32-arm64-msvc": "1.0.1", "@rolldown/binding-win32-x64-msvc": "1.0.1" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ=="], + + "safe-array-concat": ["safe-array-concat@1.1.4", "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.4.tgz", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg=="], + + "safe-push-apply": ["safe-push-apply@1.0.0", "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "saxes": ["saxes@6.0.0", "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], + + "scheduler": ["scheduler@0.27.0", "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "semver": ["semver@6.3.1", "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "set-function-length": ["set-function-length@1.2.2", "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "set-function-name": ["set-function-name@2.0.2", "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], + + "set-proto": ["set-proto@1.0.0", "https://registry.npmmirror.com/set-proto/-/set-proto-1.0.0.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + + "shebang-command": ["shebang-command@2.0.0", "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.1", "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.1.tgz", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "signal-exit": ["signal-exit@4.1.0", "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "slice-ansi": ["slice-ansi@8.0.0", "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-8.0.0.tgz", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="], + + "sortablejs": ["sortablejs@1.15.7", "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.7.tgz", {}, "sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A=="], + + "source-map-js": ["source-map-js@1.2.1", "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "stable-hash-x": ["stable-hash-x@0.2.0", "https://registry.npmmirror.com/stable-hash-x/-/stable-hash-x-0.2.0.tgz", {}, "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ=="], + + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + + "string-argv": ["string-argv@0.3.2", "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.2.tgz", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], + + "string-width": ["string-width@7.2.0", "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "string.prototype.trim": ["string.prototype.trim@1.2.10", "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], + + "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="], + + "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + + "strip-ansi": ["strip-ansi@7.2.0", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "strip-bom": ["strip-bom@3.0.0", "https://registry.npmmirror.com/strip-bom/-/strip-bom-3.0.0.tgz", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "symbol-tree": ["symbol-tree@3.2.4", "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + + "synckit": ["synckit@0.11.12", "https://registry.npmmirror.com/synckit/-/synckit-0.11.12.tgz", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ=="], + + "tdesign-icons-react": ["tdesign-icons-react@0.6.4", "https://registry.npmmirror.com/tdesign-icons-react/-/tdesign-icons-react-0.6.4.tgz", { "dependencies": { "@babel/runtime": "^7.16.5", "classnames": "^2.2.6" }, "peerDependencies": { "react": ">=16.13.1", "react-dom": ">=16.13.1" } }, "sha512-USAoi9vBWcwcJT45VqR3dRqX1MeAsn/RhHVx4bLwplhrlvE80ZQ1N9V+6F3HqE1Qe9mMDbtRM8Ul80+lALScww=="], + + "tdesign-react": ["tdesign-react@1.16.9", "https://registry.npmmirror.com/tdesign-react/-/tdesign-react-1.16.9.tgz", { "dependencies": { "@babel/runtime": "~7.26.7", "@popperjs/core": "~2.11.2", "@types/sortablejs": "^1.10.7", "@types/validator": "^13.1.3", "classnames": "~2.5.1", "dayjs": "1.11.10", "hoist-non-react-statics": "~3.3.2", "lodash-es": "^4.17.21", "mitt": "^3.0.0", "raf": "~3.4.1", "react-fast-compare": "^3.2.2", "react-is": "^18.2.0", "react-transition-group": "~4.4.1", "sortablejs": "^1.15.0", "tdesign-icons-react": "^0.6.4", "tslib": "~2.3.1", "validator": "~13.15.0" }, "peerDependencies": { "react": ">=16.13.1", "react-dom": ">=16.13.1" } }, "sha512-C3uZRTkJ1iQ62BrMkuvqvBK+4HEuhl82rABxa6kAHGHL3eBI4DPfzAJGF0T3b+DKCBeJxb0x10elumT6NkQEaw=="], + + "tiny-invariant": ["tiny-invariant@1.3.3", "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + + "tinyexec": ["tinyexec@1.1.2", "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.1.2.tgz", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="], + + "tinyglobby": ["tinyglobby@0.2.16", "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.16.tgz", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], + + "tldts": ["tldts@7.0.30", "https://registry.npmmirror.com/tldts/-/tldts-7.0.30.tgz", { "dependencies": { "tldts-core": "^7.0.30" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw=="], + + "tldts-core": ["tldts-core@7.0.30", "https://registry.npmmirror.com/tldts-core/-/tldts-core-7.0.30.tgz", {}, "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q=="], + + "tough-cookie": ["tough-cookie@6.0.1", "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-6.0.1.tgz", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw=="], + + "tr46": ["tr46@6.0.0", "https://registry.npmmirror.com/tr46/-/tr46-6.0.0.tgz", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="], + + "ts-api-utils": ["ts-api-utils@2.5.0", "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], + + "tsconfig-paths": ["tsconfig-paths@3.15.0", "https://registry.npmmirror.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], + + "tslib": ["tslib@2.3.1", "https://registry.npmmirror.com/tslib/-/tslib-2.3.1.tgz", {}, "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="], + + "type-check": ["type-check@0.4.0", "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "https://registry.npmmirror.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], + + "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "https://registry.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="], + + "typed-array-length": ["typed-array-length@1.0.7", "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.7.tgz", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], + + "typescript": ["typescript@6.0.3", "https://registry.npmmirror.com/typescript/-/typescript-6.0.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], + + "typescript-eslint": ["typescript-eslint@8.59.3", "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.59.3.tgz", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.59.3", "@typescript-eslint/parser": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/utils": "8.59.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg=="], + + "unbox-primitive": ["unbox-primitive@1.1.0", "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + + "undici": ["undici@7.25.0", "https://registry.npmmirror.com/undici/-/undici-7.25.0.tgz", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="], + + "undici-types": ["undici-types@7.25.0", "https://registry.npmmirror.com/undici-types/-/undici-types-7.25.0.tgz", {}, "sha512-AXNgS1Byr27fTI+2bsPEkV9CxkT8H6xNyRI68b3TatlZo3RkzlqQBLL+w7SmGPVpokjHbcuNVQUWE7FRTg+LRA=="], + + "unrs-resolver": ["unrs-resolver@1.11.1", "https://registry.npmmirror.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "uri-js": ["uri-js@4.4.1", "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "use-sync-external-store": ["use-sync-external-store@1.6.0", "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + + "validator": ["validator@13.15.35", "https://registry.npmmirror.com/validator/-/validator-13.15.35.tgz", {}, "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw=="], + + "victory-vendor": ["victory-vendor@37.3.6", "https://registry.npmmirror.com/victory-vendor/-/victory-vendor-37.3.6.tgz", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="], + + "vite": ["vite@8.0.13", "https://registry.npmmirror.com/vite/-/vite-8.0.13.tgz", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.14", "rolldown": "1.0.1", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw=="], + + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], + + "webidl-conversions": ["webidl-conversions@8.0.1", "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-8.0.1.tgz", {}, "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ=="], + + "whatwg-mimetype": ["whatwg-mimetype@5.0.0", "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="], + + "whatwg-url": ["whatwg-url@16.0.1", "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-16.0.1.tgz", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw=="], + + "which": ["which@2.0.2", "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], + + "which-builtin-type": ["which-builtin-type@1.2.1", "https://registry.npmmirror.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], + + "which-collection": ["which-collection@1.0.2", "https://registry.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], + + "which-typed-array": ["which-typed-array@1.1.20", "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.20.tgz", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], + + "word-wrap": ["word-wrap@1.2.5", "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@10.0.0", "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-10.0.0.tgz", { "dependencies": { "ansi-styles": "^6.2.3", "string-width": "^8.2.0", "strip-ansi": "^7.1.2" } }, "sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ=="], + + "xml-name-validator": ["xml-name-validator@5.0.0", "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "xmlchars": ["xmlchars@2.2.0", "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + + "y18n": ["y18n@5.0.8", "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yallist": ["yallist@3.1.1", "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yaml": ["yaml@2.9.0", "https://registry.npmmirror.com/yaml/-/yaml-2.9.0.tgz", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], + + "yargs": ["yargs@18.0.0", "https://registry.npmmirror.com/yargs/-/yargs-18.0.0.tgz", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="], + + "yargs-parser": ["yargs-parser@22.0.0", "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-22.0.0.tgz", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "zod": ["zod@4.4.3", "https://registry.npmmirror.com/zod/-/zod-4.4.3.tgz", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "zod-validation-error": ["zod-validation-error@4.0.2", "https://registry.npmmirror.com/zod-validation-error/-/zod-validation-error-4.0.2.tgz", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + + "@babel/core/json5": ["json5@2.2.3", "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "@commitlint/config-validator/ajv": ["ajv@8.20.0", "https://registry.npmmirror.com/ajv/-/ajv-8.20.0.tgz", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + + "@commitlint/is-ignored/semver": ["semver@7.8.0", "https://registry.npmmirror.com/semver/-/semver-7.8.0.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], + + "@conventional-changelog/git-client/semver": ["semver@7.8.0", "https://registry.npmmirror.com/semver/-/semver-7.8.0.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], + + "@emnapi/core/tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@emnapi/runtime/tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@reduxjs/toolkit/immer": ["immer@11.1.8", "https://registry.npmmirror.com/immer/-/immer-11.1.8.tgz", {}, "sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA=="], + + "@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@tybys/wasm-util/tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@types/node/undici-types": ["undici-types@7.19.2", "https://registry.npmmirror.com/undici-types/-/undici-types-7.19.2.tgz", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + + "@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", { "dependencies": { "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3" } }, "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA=="], + + "@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.59.3.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", { "dependencies": { "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3" } }, "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA=="], + + "@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.59.3.tgz", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], + + "@typescript-eslint/project-service/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.59.3.tgz", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], + + "@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.2", "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", { "dependencies": { "@typescript-eslint/types": "8.59.2", "eslint-visitor-keys": "^5.0.0" } }, "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA=="], + + "@typescript-eslint/type-utils/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.59.3.tgz", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], + + "@typescript-eslint/type-utils/@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.59.3.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg=="], + + "@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.59.3.tgz", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], + + "@typescript-eslint/typescript-estree/semver": ["semver@7.8.0", "https://registry.npmmirror.com/semver/-/semver-7.8.0.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], + + "@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.2", "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", { "dependencies": { "@typescript-eslint/project-service": "8.59.2", "@typescript-eslint/tsconfig-utils": "8.59.2", "@typescript-eslint/types": "8.59.2", "@typescript-eslint/visitor-keys": "8.59.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg=="], + + "@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.59.3.tgz", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], + + "cli-truncate/string-width": ["string-width@8.2.1", "https://registry.npmmirror.com/string-width/-/string-width-8.2.1.tgz", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="], + + "cliui/wrap-ansi": ["wrap-ansi@9.0.2", "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "eslint-import-resolver-node/debug": ["debug@3.2.7", "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-module-utils/debug": ["debug@3.2.7", "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/debug": ["debug@3.2.7", "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/minimatch": ["minimatch@3.1.5", "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "hoist-non-react-statics/react-is": ["react-is@16.13.1", "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "import-fresh/resolve-from": ["resolve-from@4.0.0", "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "is-bun-module/semver": ["semver@7.8.0", "https://registry.npmmirror.com/semver/-/semver-7.8.0.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], + + "log-update/slice-ansi": ["slice-ansi@7.1.2", "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-7.1.2.tgz", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], + + "log-update/wrap-ansi": ["wrap-ansi@9.0.2", "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "pretty-format/react-is": ["react-is@17.0.2", "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + + "prop-types/react-is": ["react-is@16.13.1", "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "tdesign-react/@babel/runtime": ["@babel/runtime@7.26.10", "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.26.10.tgz", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw=="], + + "typescript-eslint/@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.59.3.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "wrap-ansi/string-width": ["string-width@8.2.1", "https://registry.npmmirror.com/string-width/-/string-width-8.2.1.tgz", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="], + + "@commitlint/config-validator/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.59.3.tgz", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], + + "@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.59.3.tgz", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], + + "@typescript-eslint/type-utils/@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", { "dependencies": { "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3" } }, "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA=="], + + "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.59.2", "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.59.2", "@typescript-eslint/types": "^8.59.2", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw=="], + + "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.59.2", "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw=="], + + "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.2", "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", { "dependencies": { "@typescript-eslint/types": "8.59.2", "eslint-visitor-keys": "^5.0.0" } }, "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA=="], + + "@typescript-eslint/utils/@typescript-eslint/typescript-estree/semver": ["semver@7.8.0", "https://registry.npmmirror.com/semver/-/semver-7.8.0.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], + + "cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "eslint-plugin-import/minimatch/brace-expansion": ["brace-expansion@1.1.14", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.14.tgz", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="], + + "log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "log-update/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", { "dependencies": { "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3" } }, "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA=="], + + "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.59.3.tgz", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], + + "eslint-plugin-import/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + } +} diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..f57c6e1 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,3 @@ +[test] +preload = ["./tests/setup.ts"] +exclude = ["./tests/e2e/**"] diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..5ca3350 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,8 @@ +export default { + extends: ["@commitlint/config-conventional"], + rules: { + "subject-case": [0], + "subject-full-stop": [0], + "type-enum": [2, "always", ["feat", "fix", "refactor", "docs", "style", "test", "chore"]], + }, +}; diff --git a/config.example.yaml b/config.example.yaml new file mode 100644 index 0000000..596c828 --- /dev/null +++ b/config.example.yaml @@ -0,0 +1,3 @@ +server: + host: "127.0.0.1" + port: 3000 diff --git a/docs/prompts/README.md b/docs/prompts/README.md new file mode 100644 index 0000000..1b8fb77 --- /dev/null +++ b/docs/prompts/README.md @@ -0,0 +1,292 @@ +# Prompts + +面向 AI 大模型的执行型提示词集合。每份提示词都应能被单独复制使用,并驱动 AI 以一致的方式完成收集、分析、确认、执行和收尾。 + +## 提示词 + +| 文件 | 用途 | +| ------------------------------------------------------ | ------------------------------------------------------------------------ | +| [prompt-smart-merge.md](prompt-smart-merge.md) | 批量合并 `dev*` 分支到目标分支,含规则探测、依赖分析、冲突处理、安全回退 | +| [prompt-spec-review.md](prompt-spec-review.md) | 审查和整理 `openspec/specs/` 下的稳定规范,提升可检索性和一致性 | +| [prompt-proposal-review.md](prompt-proposal-review.md) | 审查 proposal/design/tasks/specs 与讨论、代码现状、OpenSpec 规范的一致性 | +| [prompt-apply-review.md](prompt-apply-review.md) | 审查 apply 后代码、测试、变更文档的一致性,并补齐遗漏或回写文档 | + +## 设计目标 + +从现有提示词提炼出的共同目标: + +- 面向执行,不面向讲解 +- 先收集证据,再做判断 +- 先计划和确认,再做有副作用的修改 +- 对缺失信息、规则冲突、上下文不足有降级路径 +- 对删除、重写、提交、回退、推送等高风险动作有明确授权边界 +- 执行后必须复核,形成闭环 + +## 命名规则 + +文件名格式:`prompt-{action}.md` + +- `{action}` 使用明确、可搜索、无歧义的英文短语 +- 用连字符连接,不使用缩写、代号或过泛词 +- 优先体现动作和对象,如 `smart-merge`、`spec-review`、`apply-review` + +## 通用骨架 + +大多数提示词应遵循以下结构;按任务类型增删章节,但顺序尽量保持一致: + +```md +一句话描述任务目标 + +## 约束 + +## 1. 收集 / 准备 + +## 2. 分析 + +## 3. 报告 或 计划(用户确认) + +## 4. 执行(用户确认) + +## 5. 清理 / 收尾 +``` + +适用方式: + +- 审查型:`收集 → 分析 → 报告 → 计划 → 执行 → 收尾` +- 执行型:`准备 → 分析 → 计划 → 执行 → 清理` +- 纯报告型:可省略执行,但仍要保留“信息不足时如何降级”和“结果如何输出” + +## 编写原则 + +### 1. 面向 AI,不写背景说明 + +- 不写业务背景、适用场景、周期性说明、方法论阐释 +- 不写“为什么这么做”的长解释,直接写成规则或步骤 +- 不写示例输出模板,除非输出格式本身就是约束的一部分 + +### 2. 证据先于结论 + +- 明确列出需要读取的文档、代码、测试、配置、命令结果 +- 能并行的步骤明确写“并行” +- 默认优先使用当前会话信息、现有文档和仓库规则 +- 只有在无法定位对象、范围或规则时,才引导 AI 向用户提问或运行补充命令 +- 不要求 AI 无差别全量扫描整个仓库;先建立索引,再做定向读取 + +### 3. 约束集中声明 + +- 全局不可违反的规则统一放在 `## 约束` +- 不在后续步骤反复重复同一条规则 +- 约束优先写边界、禁令、授权条件、同步要求、非目标 + +常见约束类型: + +- 只允许修改哪些对象,不允许修改哪些对象 +- 是否默认按某个 workflow 执行 +- 是否以代码、文档、讨论或用户确认为准 +- 何时必须使用提问工具确认 +- 删除、重写前是否必须备份 +- 改动后是否必须同步 README、测试、变更文档 + +### 4. 计划与执行分离 + +- 分析阶段只产出问题、风险、候选动作,不直接修改 +- 执行前必须先给出计划或批次方案 +- 批次计划必须能让用户看懂“改什么、为什么、影响什么、如何验证” +- 用户确认执行,不等于授权所有危险动作;额外高风险动作要单独确认 + +### 5. 闭环优先 + +- 执行后必须重新读取受影响对象并复核 +- 对代码修改要说明测试或验证方式 +- 对文档修改要检查相关文档之间是否同步一致 +- 收尾时要列出修改文件、备份文件、验证结果和残留风险 + +## 各章节写法 + +### 目标句 + +第一句只做三件事: + +- 说明任务对象 +- 说明最终目标 +- 说明执行方式或范围 + +要求: + +- 一句话写完 +- 不写背景铺垫 +- 尽量包含最终产物,如“生成计划”“回写文档”“整理稳定规范” + +### 约束 + +推荐写法: + +- 作用域边界:改什么,不改什么 +- 真相来源优先级:代码 / README / spec / 讨论 / 用户确认 +- 风险动作边界:删除、重写、提交、推送、回退、stash、merge 等 +- 同步要求:测试、README、变更文档、现有 spec 是否要同步 +- 降级规则:信息不足时如何处理 + +避免: + +- 在约束里塞步骤顺序 +- 同一规则在约束和执行里重复出现多次 + +### 收集 / 准备 + +要明确三类内容: + +- 读什么 +- 是否并行 +- 无法确定时如何补充定位 + +推荐做法: + +- 先读仓库规则来源,如 `README.md`、配置、架构文档、近期提交、任务入口 +- 先读直接相关 artifacts,再扩展到相关代码和测试 +- 需要探测时,要求 AI 先探测再决定,不把仓库结构写死在提示词里 + +### 分析 + +分析部分优先使用表格表达维度、优先级和判定规则。 + +推荐包含: + +- 优先级或维度表 +- 差异分类规则 +- 风险分级规则 +- 是否进入待确认清单的判定条件 + +常见分析模式: + +- `P0 / P1 / P2 / P3` 优先级 +- “过时 / 重复 / 冲突 / 错位 / 命名 / 格式” 维度 +- “文档要求未实现 / 代码修补未回写 / 双方冲突待确认” 差异分类 + +### 报告 / 计划 + +这是现有提示词最稳定的共性区块,建议固定包含: + +- 问题总览表 +- 逐项分析 +- 待确认清单 +- 分批执行方案 +- 预期结果或目录结构 + +若进入计划阶段,必须写清: + +- 改哪些文件或对象 +- 动作类型:删除、重命名、迁移、合并、拆分、补充、回写、修复 +- 修改原因 +- 预期影响 +- 验证方式 + +### 执行 + +执行部分要强调可操作性: + +- 明确顺序执行还是可并行执行 +- 明确每批执行前是否确认 +- 明确删除、重写、回退前是否要备份或创建锚点 +- 明确执行后最少要复核哪些点 + +推荐写法: + +- “逐批执行”或“逐项执行” +- “每批执行后重新读取受影响文件并复核” +- “若涉及删除或重写,先创建备份文件 `{file}.bak.{timestamp}`” + +### 清理 / 收尾 + +收尾要输出结果,不只说“完成”。 + +建议包含: + +- 修改文件清单 +- 备份文件清单 +- 测试 / 构建 / 验证命令与结果 +- 文档同步摘要 +- 残留问题、未验证项、待确认事项 + +## 交互与安全规范 + +### 必须确认的动作 + +以下动作默认都要在提示词中要求 AI 使用提问工具确认: + +- 删除文件或目录 +- 重写文件 +- 创建或恢复 stash +- 回退、reset、revert、abort +- 合并提交、推送、删分支、删远端分支、删 tag +- 任何会改变工作区现场且用户未明确授权的操作 + +### 授权边界要写清 + +提示词要明确区分: + +- “确认当前计划” +- “确认执行当前批次” +- “确认某个具体危险动作” + +不要把这些授权混为一谈。 + +### 回退路径要提前写好 + +对高风险流程,提示词应提供至少一种回退机制: + +- 备份文件 +- 安全锚点 tag +- `abort` 路径 +- 终止后的现场说明 + +## 表达与格式 + +- 优先使用表格表达规则、维度、状态、输出项 +- 列表优于段落 +- 每句话只写一条指令 +- 用 `{占位符}` 表示需要 AI 或用户填入的参数 +- 步骤编号使用 `## 1.`、`## 2.`,不用“第 X 步” +- 信息展示遵循“概览 → 详情 → 原始数据” + +## 仓库适配规则 + +- 若仓库文档已经定义命令、目录、提交格式、包管理器、工作流,提示词必须先遵守仓库文档 +- 若仓库未定义,再允许 AI 根据脚本、清单文件、近期提交历史推断 +- 不把当前仓库的偶然路径结构、命令名、分支名写死到所有提示词中 +- 只有当提示词本身就是为当前仓库的特定流程编写时,才写入仓库专属术语和路径 + +## 反模式 + +以下写法应避免: + +- 让 AI 一上来就全仓库无差别扫描 +- 在未分析前直接要求修改 +- 把代码现状直接当成唯一真相 +- 把历史变更文档直接当成稳定规范来源 +- 用抽象表述代替可执行动作 +- 把多个危险动作打包成一次默认授权 +- 没有备份、没有锚点、没有终止路径 +- 只要求“完成修改”,不要求复核和收尾 + +## 编写检查清单 + +编写完一份提示词后,至少自检以下问题: + +| 检查项 | 说明 | +| ---------------- | ------------------------------------------------- | +| 目标是否单句明确 | 是否能一眼看出任务对象、目标和范围 | +| 约束是否集中 | 全局规则是否只在 `## 约束` 中声明 | +| 数据源是否具体 | 是否明确读哪些文档、代码、测试、命令结果 | +| 是否先分析再执行 | 是否存在独立的分析和计划阶段 | +| 是否有确认节点 | 高风险动作前是否要求提问工具确认 | +| 是否有降级路径 | change 不明、规则不明、上下文不足时是否有处理方式 | +| 是否可操作 | 是否给出命令、工具、路径或结构化动作 | +| 是否可验证 | 执行后是否定义复核或测试方式 | +| 是否能收尾 | 是否要求输出修改清单、备份、验证结果、残留风险 | + +## 维护原则 + +- 新增提示词前,先判断能否复用现有提示词的结构和术语 +- 修改提示词时,优先提炼共性,不为单次场景堆特例 +- 若某条规则已在多个提示词中稳定出现,应回收进本 README,作为统一编写规范 diff --git a/docs/prompts/prompt-apply-review.md b/docs/prompts/prompt-apply-review.md new file mode 100644 index 0000000..a34f3b2 --- /dev/null +++ b/docs/prompts/prompt-apply-review.md @@ -0,0 +1,130 @@ +审查 OpenSpec apply 完成后以及后续手动修补后的实际实现,判断代码、测试、变更文档是否一致,识别偏离、漏记和可优化点,并将确认后的实际变更同步回变更文档,按以下流程执行。 + +## 约束 + +- 先审查再修复;未经用户确认,不修改代码或变更文档 +- 默认按 `spec-driven` workflow 审查;识别 change 后先确认 `schemaName`;若实际 schema 不同,说明差异,仅对实际存在的 artifacts 做审查 +- 优先使用当前会话中的实现说明、测试结论、手动修补记录和已生成的变更文档;仅在无法明确 change、`schemaName`、改动范围或修补来源时,再用提问工具或 OpenSpec 命令补充定位 +- 不要因为代码已经存在就自动以代码为准;先判断差异属于"文档要求未实现"、"测试后新增修补"还是"意外偏离/回归" +- 每批代码或文档修改执行前用提问工具获得用户确认 +- 删除/重写前用提问工具获得用户确认,并先备份原文件为 `{file}.bak.{timestamp}` +- 若修改代码涉及新逻辑、模块结构、API、实体或用户可见行为,同步更新测试、相关变更文档和 README + +## 1. 收集 + +并行读取: + +- 本次 change 的实际 artifacts;在 `spec-driven` 下通常包括 `proposal.md`、`design.md`、`tasks.md`、`specs/*.md` +- 当前会话中与本次变更相关的实现说明、apply 过程中的偏离、测试失败、手动修补原因、待确认事项 +- 与本次变更相关的代码和测试文件;优先依据 `git diff --name-only`、`git diff --name-only --cached`、`tasks.md`、Impact、失败测试栈定位;若工作区已干净,再结合文档和代码模块反推 +- 最近一次相关测试命令、测试结果、失败信息和修补后的验证结果 +- `openspec/config.yaml` +- 与本次改动相关的 README、架构文档,以及现有 `openspec/specs/**/spec.md` 中与本次变更相关的规范,相关性来源包括:`proposal.md` 的 `Capabilities` / `Modified Capabilities`、手动修补涉及的受影响能力、`design.md` / Impact 中提到的模块、相关代码对应的现有能力 + +若当前上下文无法明确 change 或文档路径: + +- 先用提问工具让用户确认 change 名称或文档范围 +- 仍无法确认时,再执行 `openspec status --change "{name}" --json` 和 `openspec instructions apply --change "{name}" --json` 辅助定位 + +若已明确 change,但尚未确认 `schemaName`,先读取 change 元数据或执行 `openspec status --change "{name}" --json` 确认。 + +若缺少测试结果或手动修补记录,明确说明本次无法可靠判断部分差异的来源,仅能基于代码与文档现状审查。 + +## 2. 分析 + +按以下优先级检查: + +| 优先级 | 维度 | 检查点 | +| ------ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| P0 | 实际实现与测试结论 | 当前代码的真实行为是什么;apply 后是否有手动改动或测试后修补;测试是否证明这些实现有效;若缺少测试结果,标记相关结论为"未验证";检查是否存在回归、未覆盖场景或被掩盖的问题 | +| P1 | 文档同步性 | 对实际存在的 artifacts 检查:已落地的实现、测试后新增修补、边界处理、异常路径、验证结论是否已同步回变更文档;若影响模块结构、API、实体或用户可见行为,再检查 README 是否同步 | +| P2 | 文档要求覆盖 | 对实际存在的 artifacts 检查:文档中承诺的目标、方案、Requirement、Scenario 是否都已实现;在 `spec-driven` 下重点检查 `proposal.md`、`design.md`、`specs/*.md`、`tasks.md` | +| P3 | 实现质量 | 代码结构、复用、命名、复杂度、错误处理、测试质量、与项目现有模式的一致性是否存在明显问题或可优化点 | + +分析时区分三类差异: + +- 文档要求已明确,但代码未实现或实现不完整 → 需补充代码或测试 +- 代码因测试暴露问题、手动修补或合理落地细化而新增/变更 → 需回写文档 +- 代码与文档不一致,且无法判断应以哪边为准 → 列入待确认清单 + +不要把以下情况直接视为合理修补: + +- 通过 `skip`、`only`、弱化断言、绕过错误处理来让测试通过 +- 为了贴合现有代码而降低已确认的 Requirement 或行为约束 +- 未经过讨论和验证就扩大功能范围 + +重点识别: + +- 文档要求但未落地的功能、场景、异常处理或验证步骤 +- apply 完成后新增的代码修补、边界处理、接口调整、行为变化未同步到文档 +- `tasks.md` 标记完成,但代码、测试或文档未闭环 +- `Modified Capabilities` 应更新但未更新的现有 spec +- 代码存在明显的重复、复杂度过高、命名不清、错误处理薄弱、测试质量不足等问题 + +输出审查结果: + +1. **问题总览表**:问题类型 × 涉及文件数 +2. **实际改动与修补清单**:本次实现中已落地的主要功能、后续修补和验证结论;若缺少测试结果,对未验证部分单独标记 +3. **未覆盖清单**:文档要求但未在代码中实现或未充分验证的内容 +4. **需回写文档清单**:代码和测试中已确认、但文档未体现的实现、修补或约束变化 +5. **方向待确认清单**:代码与文档不一致,且无法判断应以哪边为准的事项 +6. **任务状态问题清单**:未真正完成、状态错误或需补充的新任务 +7. **测试问题清单**:缺失覆盖、掩盖错误、验证不足或修补后未回归验证的测试问题 +8. **代码质量/优化清单**:可优化的实现问题和建议 +9. **逐项分析**:每个问题说明位置、问题、影响、建议和建议修复方向 + +若所有清单均为空,输出"审查通过,未发现问题",跳至步骤 5。 + +## 3. 计划(用户确认) + +先针对"方向待确认清单"用提问工具逐项向用户确认。 + +再整理完整修复方案,按类别列出: + +- 代码或测试补充:补实现、补异常处理、补回归测试、修复掩盖错误的测试 +- 文档回写:同步 `proposal.md`、`design.md`、`tasks.md`、`specs/*.md`、README 中遗漏或过时的内容 +- 任务状态修正:修正已完成/未完成状态,补充 apply 后新增但已完成的修补任务或验证任务 +- 代码质量优化:在不改变目标行为的前提下优化结构、复用、命名或可维护性 + +对每个拟修改的文件说明: + +- 修改内容 +- 修改原因 +- 预期影响 +- 验证方式 +- 若存在分支方案,分别说明适用前提 + +用提问工具展示完整修复方案,获得用户确认后执行。 + +## 4. 执行 + +逐项执行已确认的代码、测试和文档修复。 + +若涉及删除或重写: + +- 先创建备份文件 `{file}.bak.{timestamp}` +- 再执行修改 + +若修改了代码或测试: + +- 同步更新相关变更文档;若影响模块结构、API、实体或用户可见行为,再同步 README +- 运行相关测试;若修补影响范围较大,再补充执行受影响的回归测试 + +若修改了文档: + +- 确认实际存在的变更文档之间保持一致;在 `spec-driven` 下重点检查 `proposal.md`、`design.md`、`tasks.md`、`specs/*.md` +- 若 apply 后新增修补改变了能力边界或行为约束,同步更新 `Capabilities` / `Modified Capabilities` + +执行后重新读取所有被修改的代码、测试和文档,并复核: + +- "未覆盖清单" 是否已清空或已标注保留原因 +- "需回写文档清单" 是否已清空 +- "方向待确认清单" 是否已清空或已记录用户决策 +- "任务状态问题清单" 和 "测试问题清单" 是否已清空或已标注残留原因 +- "代码质量/优化清单" 中哪些已处理,哪些有意延期 + +## 5. 收尾 + +列出所有修改的文件、备份文件、测试命令与结果、文档同步摘要和剩余风险。 + +若本次因缺少测试结果、修补记录或上下文而降级执行,或有问题因信息不足暂未处理,单独说明。 diff --git a/docs/prompts/prompt-proposal-review.md b/docs/prompts/prompt-proposal-review.md new file mode 100644 index 0000000..9fa8f1b --- /dev/null +++ b/docs/prompts/prompt-proposal-review.md @@ -0,0 +1,99 @@ +审查本次 OpenSpec 变更文档是否与前序讨论、当前代码现状和 OpenSpec 文档规范一致,识别遗漏、冲突和不合理假设,并给出可执行的补充建议,按以下流程执行。 + +## 约束 + +- 仅修改本次变更文档,不修改源码 +- 默认按 `spec-driven` workflow 审查;识别 change 后先确认 `schemaName`;若实际 schema 不同,说明差异,仅对实际存在的 artifacts 做审查 +- 优先使用当前会话中的讨论和已生成的变更文档;仅在无法明确 change、`schemaName` 或文档范围时,再用提问工具或 OpenSpec 命令补充定位 +- 每批文档修改建议执行前用提问工具获得用户确认 +- 删除/重写前用提问工具获得用户确认,并先备份原文件为 `{file}.bak.{timestamp}` + +## 1. 收集 + +并行读取: + +- 本次 change 的实际 artifacts;在 `spec-driven` 下通常包括 `proposal.md`、`design.md`、`tasks.md`、`specs/*.md` +- 当前会话中与本次变更相关的讨论、澄清、边界约束、非目标、待确认事项 +- 与本次变更直接相关的源码、测试、README、架构文档 +- `openspec/config.yaml` +- 现有 `openspec/specs/**/spec.md` 中与本次变更相关的规范,相关性来源包括:`proposal.md` 的 `Capabilities` / `Modified Capabilities`、讨论中提到的受影响能力、`design.md` / Impact 中提到的模块、相关代码对应的现有能力 + +若当前上下文无法明确 change 或文档路径: + +- 先用提问工具让用户确认 change 名称或文档范围 +- 仍无法确认时,再执行 `openspec status --change "{name}" --json` 和 `openspec instructions apply --change "{name}" --json` 辅助定位 + +若已明确 change,但尚未确认 `schemaName`,先读取 change 元数据或执行 `openspec status --change "{name}" --json` 确认。 + +若缺少讨论记录,明确说明本次降级为"文档 + 代码现状审查",不做讨论一致性结论。 + +## 2. 分析 + +按以下优先级检查: + +| 优先级 | 维度 | 检查点 | +| ------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| P0 | 讨论一致性 | 仅在存在讨论记录时检查:文档是否完整覆盖已确认的目标、范围、非目标、约束、边界条件、风险、决策点、待办事项;若无讨论记录,标记为"跳过" | +| P1 | 代码现实性 | 文档对当前模块、接口、数据结构、命名、依赖、目录结构、复用路径的描述是否准确;是否把"计划变更"误写成"当前现状";是否遗漏真实受影响的现有能力 | +| P2 | 文档内部一致性 | 对实际存在的 artifacts 检查是否互相支撑;在 `spec-driven` 下重点检查 `proposal.md`、`design.md`、`tasks.md`、`specs/*.md`;`Capabilities` / `Modified Capabilities` 是否完整;每个 capability 是否有对应 spec;`tasks.md` 是否覆盖 `design.md` 和 `specs/*.md` | +| P3 | OpenSpec 合规性 | 对实际存在的 artifacts 检查是否遵循 OpenSpec 格式和术语;`specs/*.md` 是否只描述行为与约束、不混入实现细节;`tasks.md` 是否一行一个任务;是否混入 git 操作任务 | + +分析时区分两类情况: + +- 文档对当前代码现状的描述错误 +- 文档描述的是预期变更,本来就应当与当前代码不同 + +重点识别: + +- 讨论中已确定但文档未记录的内容 +- 文档基于错误现状做出的设计或任务拆分 +- 文档之间相互冲突的目标、方案、约束、任务 +- `proposal -> specs -> design -> tasks` 链路中的断点 +- `Modified Capabilities` 应更新但未更新的现有 spec + +输出审查结果: + +1. **问题总览表**:问题类型 × 涉及文档数 +2. **讨论遗漏清单**:讨论已确定但文档未体现的内容;若缺少讨论记录,标记为"未审查" +3. **现实性问题清单**:与当前代码现状不符的描述、假设或影响分析 +4. **文档冲突清单**:proposal、design、tasks、specs 之间的不一致 +5. **OpenSpec 规范问题清单**:格式、术语、结构问题 +6. **待澄清清单**:仅靠讨论和代码仍无法判断的事项 +7. **逐项分析**:每个问题说明位置、问题、影响、建议 +8. **补充建议方案**:按文件列出建议补充/修正的内容、原因和可选方案 + +若所有清单均为空,输出"审查通过,未发现问题",跳至步骤 5。 + +## 3. 计划(用户确认) + +先针对"待澄清清单"用提问工具逐项向用户确认。 + +再整理完整修复方案,按文件列出: + +- 建议修改的文件 +- 需要补充或修正的内容 +- 修改原因 +- 若存在分支方案,分别说明适用前提 + +用提问工具展示完整修复方案,获得用户确认后执行。 + +## 4. 执行 + +逐项修改已确认的变更文档,不修改源码。 + +若涉及删除或重写: + +- 先创建备份文件 `{file}.bak.{timestamp}` +- 再执行修改 + +执行后重新读取所有被修改的文档,并复核: + +- "讨论遗漏清单" 是否已清空或已标注保留原因 +- "现实性问题清单" 是否已清空或已标注为预期变更 +- "文档冲突清单" 和 "OpenSpec 规范问题清单" 是否已清空 + +## 5. 收尾 + +列出所有修改的文件、备份文件和变更摘要。 + +若本次因缺少讨论记录而降级执行,或有问题因信息不足暂未处理,单独说明。 diff --git a/docs/prompts/prompt-smart-merge.md b/docs/prompts/prompt-smart-merge.md new file mode 100644 index 0000000..9b4e8ca --- /dev/null +++ b/docs/prompts/prompt-smart-merge.md @@ -0,0 +1,595 @@ +将所有 `dev*` 分支按计划合并到目标分支(默认 `main`),按准备→分析→计划→执行→清理执行;先探测当前仓库的规则、模块边界、验证命令和提交风格,再基于探测结果执行,避免写死仓库结构。 + +## 约束 + +- 全程仅使用 `git merge` 完成分支集成,禁止 `rebase` +- `git add` 仅允许明确文件路径,禁止 `git add .`、`git add -A` +- 不直接使用默认 `git pull`;同步目标分支时仅允许显式策略:`git fetch` + `git merge`,或 `git pull --no-rebase` +- `git reset --hard` 仅允许回到已记录的安全锚点 tag,且执行前必须再次确认 +- `git stash push`、`git stash apply`、`git stash drop`、`git revert`、`git branch -d`、`git push {remote} --delete {branch}` 执行前都必须再次确认 +- 禁止自动推送代码;远端分支删除仅在步骤 5 获得确认后执行 +- 冲突文件禁止 AI 自主决定写入内容 +- 用户选择 `--ours`、`--theirs`、保留删除的一侧、保留重命名的一侧等机械方案后,AI 可执行对应写入 +- 凡涉及“双保留”“重组逻辑”“补写缺失代码”等内容生成,必须逐文件展示最终结果并再次获得确认后写入 +- 步骤 3 的“确认执行”仅授权按计划执行正常 merge,不授权自动执行 `stash`、`reset --hard`、`revert`、`drop`、删除分支、远端删除、冲突内容重写 +- 合并提交与语义审查修复提交必须分离;语义审查修复允许拆成多条独立提交 +- 若仓库文档定义了命令、包管理器、提交格式、目录规范,优先遵守文档;若未定义,再根据清单文件、脚本和近期提交历史推断 +- 不把当前仓库的路径结构、技术栈、构建命令写死到流程里;一切按探测结果执行 +- 信息展示按“概览 → 详情 → 原始数据”分层输出,避免一次性输出全部 diff + +## 记录项 + +执行中持续维护以下记录,后续所有决策和总结都基于这些记录: + +| 记录项 | 内容 | +| ----------------------------------- | --------------------------------------------------------------------------------------- | +| `session_timestamp` | 本次流程唯一时间戳 | +| `target` | 目标分支名 | +| `target_upstream` | 目标分支上游,如 `{remote}/{target}`;无则记为空 | +| `target_remote` | 从 `target_upstream` 解析出的远端名;无上游则优先使用默认远端,否则为空 | +| `repo_rules` | 从 README、CONTRIBUTING、开发文档、脚本、清单文件中识别出的规则 | +| `commit_style` | 仓库现有提交信息风格和默认合并/修复提交模板 | +| `module_map` | 按当前仓库结构推断出的模块边界、公共目录、配置目录、基础设施目录 | +| `validation_commands` | 按模块或作用域归纳出的 lint/build/test 命令 | +| `auto_stashes[]` | 本流程创建的 stash 列表,记录唯一 message、创建时 ref、创建顺序、后续是否恢复/保留 | +| `created_local_tracking_branches[]` | 为远端独有 `dev*` 分支创建的本地跟踪分支 | +| `global_tag` | 全局安全锚点 | +| `branch_tags[]` | 每个待合并分支对应的分支级安全锚点 | +| `analysis[]` | 每个分支的分析快照:HEAD hash、提交数、变更文件、依赖、初始冲突预测、风险、语义审查模式 | +| `results[]` | 每个分支的最终状态:已合并、已跳过、已回退、失败原因、修复提交列表、验证结果 | + +## 安全锚点 + +| 锚点 | 创建时机 | 用途 | +| ----------------------------------- | ---------------------------- | -------------------------------------------------------- | +| `pre-merge-backup-{timestamp}` | 步骤 1 完成目标分支准备后 | 全局回退点,恢复到本轮合并开始前的目标分支状态 | +| `merge-before-{branch}-{timestamp}` | 步骤 4 每个分支正式 merge 前 | 分支级回退点,回退当前分支的合并提交和其后的语义修复提交 | + +除非用户明确要求“全部回退”或“放弃当前分支并回到分支级锚点”,否则不主动使用 `git reset --hard`。 + +## 1. 准备 + +### 1.1 仓库规则探测 + +并行收集当前仓库的规则来源: + +- 根目录文档:`README*`、`CONTRIBUTING*`、`DEVELOPMENT*`、`docs/**` 中与开发、构建、测试、提交流程有关的文档 +- 常见任务入口:`Makefile`、`justfile`、`Taskfile.yml`、`package.json`、工作区配置、CI 文件、脚本目录 +- 常见清单/锁文件:`package-lock.json`、`pnpm-lock.yaml`、`yarn.lock`、`bun.lock*`、`go.mod`、`Cargo.toml`、`pyproject.toml`、`pom.xml`、`build.gradle*` 等 +- 近期提交:`git log --oneline -20` + +探测目标: + +| 项目 | 识别方式 | 记录结果 | +| ----------------- | ------------------------------------------------------------- | ---------------------------- | +| 包管理器/任务入口 | 文档、锁文件、任务文件、脚本 | 记录允许的执行方式和优先级 | +| 模块边界 | 顶层目录、工作区配置、语言清单文件、子项目 README | 记录 `module_map` | +| 公共/基础设施目录 | shared/common/lib/core/config/scripts/ci 等目录和根级配置文件 | 记录到 `module_map` | +| 验证命令 | 文档中的 lint/build/test 命令,或脚本中的标准任务 | 记录到 `validation_commands` | +| 提交风格 | 文档约束优先,否则看近期 `git log` | 记录 `commit_style` | + +识别规则: + +- 文档和脚本冲突时,以文档为准 +- 文档缺失时,以仓库当前可见的任务入口和近期提交习惯为准 +- 无法确定时,先在步骤 3 的计划表中展示“待确认规则”,由用户确认 + +### 1.2 初始化现场 + +- 执行 `git status --short --branch`,记录当前分支和工作区状态 +- 执行 `git remote -v`,记录可用远端 +- 用提问工具确认目标分支:`main` / `master` / `develop` / 用户自定义 + +### 1.3 处理非干净工作区 + +- 若 `git status --porcelain` 非空,先展示变更文件概览,再用提问工具让用户选择: + - `stash 后继续` + - `终止` +- 用户选择 `stash 后继续` 后,执行 `git stash push --include-untracked -m smart-merge-{timestamp}-precheck-{n}` +- 记录 stash 唯一 message 和创建时 ref 到 `auto_stashes[]` + +### 1.4 切换并校验目标分支 + +- 执行 `git checkout {target}` +- 若失败,使用提问工具让用户选择: + - `重新指定目标分支` + - `终止` + +### 1.5 解析上游并同步远端引用 + +- 执行 `git for-each-ref --format='%(upstream:short)' refs/heads/{target}` 获取 `target_upstream` +- 若存在 `target_upstream`: + - 解析 `target_remote` + - 执行 `git fetch {target_remote} --prune` + - 执行 `git rev-list --left-right --count {target}...{target_upstream}` 计算 ahead/behind +- 若不存在 `target_upstream`: + - 若仓库存在默认远端,记为 `target_remote` + - 否则 `target_remote` 置空 + +目标分支同步决策: + +| 状态 | 处理 | +| ---------------- | ----------------------------------------------------------------------------------------------------------------- | +| 无 upstream | 记录后继续,不自动同步 | +| 仅落后 upstream | 用提问工具选择:`快进同步` / `保持当前本地 HEAD` / `终止`;若选同步,执行 `git merge --ff-only {target_upstream}` | +| 仅领先 upstream | 记录后继续,不自动 push | +| 与 upstream 分叉 | 用提问工具选择:`保持当前本地 HEAD` / `终止`;不自动把 upstream merge 进 target | + +### 1.6 收集候选分支 + +- 本地分支:`git branch --list 'dev*'` +- 若 `target_remote` 非空,远端分支:`git branch -r --list '{target_remote}/dev*'` +- 过滤掉 `HEAD ->` 这类符号引用 +- 计算“远端存在、本地不存在”的分支列表 + +对远端独有分支,不直接 `checkout`,改为先展示清单,再用提问工具选择: + +- `全部创建本地跟踪分支` +- `选择部分创建` +- `仅使用已有本地分支` +- `终止` + +创建本地跟踪分支使用:`git branch --track {local_branch} {remote}/{remote_branch}` + +若本地已存在同名分支,使用提问工具逐项选择: + +- `使用现有本地分支` +- `改名创建跟踪分支` +- `跳过该远端分支` + +所有自动创建的本地跟踪分支都记录到 `created_local_tracking_branches[]`。 + +### 1.7 无候选分支时直接结束 + +- 若最终没有任何 `dev*` 分支,输出概览:目标分支、工作区状态、探测到的仓库规则、是否创建过 stash、是否创建过本地跟踪分支 +- 若本流程已创建 stash,进入步骤 5 的“工作区恢复” +- 否则直接结束 + +### 1.8 创建全局安全锚点 + +- 执行 `git tag pre-merge-backup-{timestamp}` +- 记录为 `global_tag` + +## 2. 分析 + +### 2.1 信息收集(并行) + +对每个候选分支并行收集以下信息: + +| 维度 | 命令/方式 | 结果 | +| -------- | ------------------------------------------------------------------ | -------------------------------------- | +| 基础 | `git rev-parse {branch}` | 记录分支 HEAD hash | +| 合并状态 | `git merge-base --is-ancestor {branch} {target}` | 判断该分支是否已完全被目标分支包含 | +| 提交范围 | `git log --oneline {target}..{branch}` | 记录独有提交数和提交消息,推断分支意图 | +| 变更范围 | `git diff --name-status {target}...{branch}` | 记录文件列表、状态、变更集中区域 | +| 行数统计 | `git diff --stat {target}...{branch}` | 估算改动体量 | +| 模块归属 | 按 `module_map` 归类;若未命中则按顶层目录或最近的语言清单文件归类 | 识别受影响模块 | + +模块归类优先级: + +1. 仓库文档明确声明的子项目、包、服务、应用、库 +2. 工作区配置或语言清单文件定义的模块边界 +3. 顶层目录边界 +4. 根级共享文件、配置文件、脚本、CI 文件,统一归为 `shared/config/infra` + +常见的 `shared/config/infra` 候选包括但不限于: + +- 根目录配置文件、锁文件、工作区文件 +- 构建脚本、部署脚本、CI 配置、容器配置、基础设施配置 +- 公共库目录、共享类型目录、通用组件目录、公共工具目录 + +### 2.2 依赖判定 + +依赖同时从 ancestry 和文件重叠两个维度判断: + +| 维度 | 方法 | 判定规则 | +| ------------- | ----------------------------------------------------- | ------------------------------------- | +| ancestry 依赖 | `git merge-base --is-ancestor {a} {b}` | 若 `a` 是 `b` 的祖先,记为 `b 依赖 a` | +| 文件重叠 | 比较各分支 `git diff --name-only {target}...{branch}` | 同一文件被多个分支修改,记为重叠 | +| 公共文件依赖 | 关注 `shared/config/infra` 范围和公共抽象 | 即使文件数少,也记为高优先级依赖 | + +为每个分支输出: + +- `depends_on[]`:它依赖哪些分支 +- `blocks[]`:哪些分支依赖它 +- `shared_files[]`:与其他分支重叠的文件 +- `common_files[]`:被识别为公共/基础设施的文件 + +### 2.3 初始冲突预测(串行) + +仅对“未合并且有实际差异”的分支串行执行: + +1. 执行 `git merge --no-commit --no-ff {branch}` +2. 若工作区进入 merge 状态: + - 执行 `git diff --name-only --diff-filter=U` 记录冲突文件 + - 若存在 `MERGE_HEAD`,执行 `git merge --abort` +3. 若命令输出为 `Already up to date`、无 `MERGE_HEAD`、无冲突文件,记为“已包含或无差异”,不执行 `git merge --abort` +4. 若 `git merge --abort` 失败: + - 报告错误和当前状态 + - 用提问工具让用户选择: + - `回到全局锚点后继续分析`:执行 `git reset --hard {global_tag}` + - `终止` + - `reset --hard` 失败则终止并提示用户手动处理 + +### 2.4 风险分级与排序 + +风险评级: + +- 低:无预测冲突、无依赖、单一模块、小体量改动 +- 中:存在依赖,或存在预测冲突,或跨两个以上模块,或触及公共文件 +- 高:预测冲突文件 `>= 3`,或冲突占改动文件比例 `>= 30%`,或存在 ancestry 依赖且触及公共文件 / 根级配置 / 基础设施文件 + +初始排序规则: + +1. 已合并 / 无差异(默认跳过,仅展示) +2. 被其他分支依赖的基础分支 +3. 仅改公共/基础设施文件的分支 +4. 独立、低风险分支 +5. 存在依赖或共享文件的分支 +6. 高风险、跨模块分支 + +### 2.5 验证命令归纳 + +根据步骤 1 探测结果,为后续语义修复和构建验证归纳命令: + +| 作用域 | 优先级 | +| ---------------------- | --------------------------------------- | +| 模块级 lint/build/test | 优先使用模块文档或模块脚本中声明的命令 | +| 仓库级 lint/build/test | 若模块级命令缺失,使用仓库统一命令 | +| 无现成命令 | 记录为“无法自动验证”,步骤 3 显示给用户 | + +归纳原则: + +- 优先使用仓库文档明确写出的命令 +- 其次使用标准任务入口中的现有命令 +- 再其次使用就近模块的标准脚本 +- 不自行发明新的构建或测试命令 + +### 2.6 分析结果汇总 + +为每个分支形成分析快照: + +| 字段 | 内容 | +| ---------------------- | ---------------------------------- | +| `status` | 已合并 / 无差异 / 待合并 | +| `head` | 步骤 2 记录的 HEAD hash | +| `modules` | 受影响模块 | +| `files` | 改动文件数 | +| `depends_on` | 依赖分支 | +| `predicted_conflicts` | 初始冲突文件列表 | +| `risk` | 低 / 中 / 高 | +| `semantic_review_mode` | 默认 `仅报告` | +| `validation_scope` | 该分支后续优先使用的验证范围和命令 | + +## 3. 计划(用户确认) + +输出合并计划表,列至少包含: + +| 分支 | 状态 | 模块 | 文件数 | 依赖 | 预测冲突 | 风险 | 语义审查 | 验证命令 | +| ---- | ---- | ---- | ------ | ---- | -------- | ---- | -------- | -------- | + +默认规则: + +- `已合并`、`无差异` 分支仅展示,不进入执行队列 +- `待合并` 分支按步骤 2.4 的排序进入执行队列 +- `语义审查` 默认值为 `仅报告` +- 若某分支缺少可自动执行的验证命令,在计划表中明确标记 + +同时展示仓库级探测结果摘要: + +- 识别出的模块边界 +- 公共/基础设施范围 +- 识别出的验证命令 +- 提交风格摘要 +- 无法确定、需要用户补充的规则 + +用提问工具让用户选择: + +- `确认执行` +- `调整顺序` +- `排除分支` +- `调整语义审查模式` +- `补充或修正规则` +- `重新分析全部分支` +- `终止` + +语义审查模式仅允许三种: + +- `关闭` +- `仅报告` +- `报告并修复` + +若用户选择 `调整顺序`、`排除分支`、`调整语义审查模式`、`补充或修正规则`、`重新分析全部分支`,则更新计划表后再次确认,直到用户选择 `确认执行` 或 `终止`。 + +最终确认后,记录最终执行队列。并明确提示: + +- 正常 merge 已获授权 +- 冲突内容重写、`stash`、`revert`、`reset --hard`、删除分支、远端删除仍需单独确认 + +## 4. 执行(顺序执行,冲突/异常时中断) + +对执行队列中的分支依次处理。若队列为空,直接进入步骤 5。 + +### 4.1 单分支合并前检查 + +每个分支开始前都执行以下检查: + +1. `git status --short --branch`,确认当前仍在 `{target}` +2. 若不在 `{target}`,执行 `git checkout {target}`;失败则中断并询问 +3. 若工作区非干净,展示异常文件,并用提问工具让用户选择: + - `stash 后继续` + - `终止` +4. 若用户选择 `stash 后继续`,执行 `git stash push --include-untracked -m smart-merge-{timestamp}-runtime-{n}`,记录到 `auto_stashes[]` +5. 检查分支是否仍存在;不存在则记录为 `已跳过:分支不存在` +6. 执行 `git rev-parse {branch}`,对比步骤 2 记录的 `head` +7. 若 HEAD 已变化,先对该分支重新执行步骤 2 的单分支分析,再更新计划快照 + +### 4.2 基于当前 HEAD 的动态复核 + +步骤 2 的分析基于初始目标分支;每次实际 merge 前都必须基于“当前最新 HEAD”重新复核当前分支: + +1. 执行 `git diff --name-status HEAD...{branch}`,获取当前变更文件列表 +2. 串行执行 `git merge --no-commit --no-ff {branch}` 做实时 dry-run +3. 若进入 merge 状态,收集 `git diff --name-only --diff-filter=U` 冲突文件后执行 `git merge --abort` +4. 若无 `MERGE_HEAD`,按实际结果记为“无冲突”或“已包含 / 无差异” +5. 将实时结果与步骤 3 的计划快照对比 + +视为“计划漂移”的情况: + +- 冲突文件集合发生变化 +- 改动文件数变化明显(以 `20%` 为阈值) +- 新出现公共文件 / 根级配置 / 基础设施文件 +- 原本 `无冲突` 变成 `有冲突` +- 原本模块级验证命令不再覆盖当前改动范围 + +若发生计划漂移,用提问工具让用户选择: + +- `按更新后的当前结果继续` +- `重新生成剩余分支计划`:对“当前分支 + 尚未处理的剩余分支”重新执行步骤 2,再回到步骤 3 +- `跳过当前分支` +- `终止` + +### 4.3 生成提交模板并创建分支级锚点 + +先根据 `commit_style` 生成本轮使用的提交模板: + +- 若仓库文档明确约束了提交格式,严格遵守 +- 若文档未约束,但近期提交风格稳定,沿用仓库现有风格 +- 若无法识别,合并提交使用 `chore: merge {branch} into {target}`,语义修复提交使用 `refactor: address post-merge issues in {branch}` + +然后: + +- 执行 `git tag merge-before-{branch}-{timestamp}` +- 记录到 `branch_tags[]` + +### 4.4 正式合并 + +- 执行正式合并:`git merge --no-ff -m "{merge_commit_message}" {branch}` + +正式合并结果分三类: + +- 无冲突:进入步骤 4.6 +- 有冲突:进入步骤 4.5 +- 非冲突错误:展示错误信息,用提问工具选择:`跳过当前分支` / `终止` + +### 4.5 冲突处理(中断点) + +先执行 `git diff --name-only --diff-filter=U` 获取冲突文件清单,再按文件生成冲突决策面板: + +| # | 文件 | 冲突类型 | HEAD 改动摘要 | 分支改动摘要 | 可批量机械处理 | 推荐方案 | +| --- | ---- | -------- | ------------- | ------------ | -------------- | -------- | + +推荐方案规则: + +- 仅当解决方案明确等价于 `--ours`、`--theirs`、保留删除的一侧、保留重命名的一侧时,才允许给出“可批量机械处理”的推荐 +- 只要需要合并两边逻辑、补全缺失分支、重排代码顺序、解决同一函数 / 类 / 配置段双改,就标记为 `需逐文件确认` +- `需逐文件确认` 的文件不能被“全部按推荐方案”一键处理 + +用提问工具让用户选择: + +- `处理全部机械型冲突` +- `审查部分文件` +- `放弃当前合并` + +若用户选择 `审查部分文件`,先让用户输入文件编号,再对这些文件逐个展示: + +- 上下文 diff +- 原始冲突块 `<<<<<<<` / `=======` / `>>>>>>>` +- 当前建议与风险说明 + +逐文件可选方案: + +- `保留目标 (--ours)` +- `保留分支 (--theirs)` +- `AI 起草双保留结果` +- `用户手动编辑` + +执行规则: + +- `--ours` / `--theirs`:执行 `git checkout --ours/--theirs {file}` +- `AI 起草双保留结果`: + - 先生成完整最终结果或统一 diff + - 说明具体合并逻辑 + - 再次用提问工具确认后写入文件 +- `用户手动编辑`:告知文件路径,等待用户回复“完成”后再执行 `git add {file}` +- 所有冲突文件都必须逐文件 `git add {file}` +- 执行 `git diff --name-only --diff-filter=U`,确认已无未解决冲突 +- 再执行 `git commit --no-edit` + +若用户选择 `放弃当前合并`: + +- 用提问工具确认是否执行 `git merge --abort` +- 若 `abort` 成功,记录当前分支为 `已跳过:用户放弃合并` +- 若 `abort` 失败,再用提问工具确认是否执行 `git reset --hard merge-before-{branch}-{timestamp}` 回到分支级锚点 + +### 4.6 语义审查 + +语义审查基于 `git diff merge-before-{branch}-{timestamp}..HEAD`。 + +辅助数据源: + +| 数据 | 获取方式 | 用途 | +| ------------- | -------------------------------------------------- | -------------------------------- | +| 本次合入 diff | `git diff merge-before-{branch}-{timestamp}..HEAD` | 查看本次实际引入的改动 | +| 分支意图 | 步骤 2 的 commit 消息 | 推断业务目的 | +| 主干近期趋势 | `git log --oneline {target} -20` 及相关 diff | 识别近期重构、迁移、废弃方向 | +| 公共文件现状 | 步骤 2 标记的 `common_files[]` | 判断是否重复造轮子或遗漏基础设施 | +| 仓库规则 | `repo_rules`、`module_map`、`validation_commands` | 判断是否偏离当前仓库约定 | + +审查维度: + +| 维度 | 检查点 | +| ------------ | ------------------------------------------------------------------------------------------------------ | +| 代码冗余 | 新增函数、组件、类型、工具、配置是否已在仓库公共位置存在等价实现 | +| 过时模式 | 是否继续使用近期已迁移、废弃、替换的 API、脚本、配置项、目录模式 | +| 基础设施遗漏 | 新代码是否绕过既有公共抽象、统一错误处理、统一日志、统一配置、统一数据访问、统一请求封装等既有基础设施 | +| 风格不一致 | 命名、文件组织、测试组织、错误处理、注释风格是否偏离当前主干 | +| 模块边界破坏 | 是否把本应落在公共层、共享层、基础设施层的逻辑塞进业务分支目录 | + +输出审查结果表: + +| # | 类别 | 严重程度 | 文件 | 描述 | 建议修复方式 | +| --- | ---- | -------- | ---- | ---- | ------------ | + +严重程度仅分两类: + +- `问题`:有运行时风险、明显架构偏差或维护成本高 +- `建议`:功能可用,但与主干一致性较差 + +按计划中的 `semantic_review_mode` 处理: + +- `关闭`:跳过语义审查 +- `仅报告`:展示结果后直接进入步骤 4.7 +- `报告并修复`:展示结果后,用提问工具选择:`全部修复` / `选择修复` / `跳过` + +修复执行规则: + +1. 每个问题编号视为一个独立修复单元 +2. 一次只处理一个编号,避免回退时互相污染 +3. 每个修复单元只修改其必要文件,保持最小变更 +4. 修复完成后,按 `validation_commands` 和当前改动作用域执行最小充分检查: + +| 作用域 | 检查策略 | +| -------------------------- | -------------------------------------- | +| 仅单模块 | 优先执行该模块的 lint 或等价静态检查 | +| 多模块 | 执行覆盖这些模块的仓库级检查或组合检查 | +| 触及 `shared/config/infra` | 执行受影响范围内最严格的可用检查 | +| 无自动检查命令 | 记录为“未自动验证”,并在结果中明确说明 | + +5. 若检查通过,立即提交独立修复 commit,message 遵循 `commit_style` +6. 若检查失败,只回退当前修复单元的未提交修改:`git restore --staged --worktree --source=HEAD -- {files}` +7. 记录该编号为 `修复失败并已回退`,继续处理下一个编号 + +### 4.7 构建验证 + +根据当前分支实际引入的改动范围,从 `validation_commands` 中选择最小充分的构建 / 测试 / 检查命令: + +| 改动范围 | 验证策略 | +| -------------------------- | -------------------------------------------- | +| 单模块业务改动 | 优先运行该模块的 build / test / lint 组合 | +| 多模块改动 | 运行覆盖这些模块的聚合命令 | +| 触及共享层、配置、基础设施 | 优先运行仓库级验证或覆盖共享层的更高等级验证 | +| 仓库没有可自动执行命令 | 记录为“无法自动验证”,并向用户说明 | + +构建成功: + +- 记录当前分支为 `已合并` +- 记录冲突处理方式、语义审查结果、修复提交列表、实际执行的验证命令 +- 继续下一个分支 + +构建失败: + +1. 展示失败命令和错误摘要 +2. 若当前分支存在语义修复提交,用提问工具让用户选择: + - `依次 revert 当前分支的语义修复提交后重试验证` + - `放弃当前分支并回到分支级锚点` + - `终止` +3. 若用户选择 revert: + - 按提交时间倒序执行 `git revert --no-edit {commit}` + - 每 revert 一条后重试验证,直到通过或全部 revert 完成 + - 若 revert 冲突,立即中断并询问:`回到分支级锚点` / `保留冲突现场并终止` +4. 若无语义修复提交,或全部 revert 后验证仍失败,用提问工具让用户选择: + - `git reset --hard merge-before-{branch}-{timestamp}` 放弃当前分支 + - `终止并保留当前现场` +5. 若用户确认回到分支级锚点,记录当前分支为 `已跳过:验证失败并回退` + +## 5. 清理(用户确认) + +所有分支处理完成后,输出汇总表: + +| 项目 | 内容 | +| ---------- | ------------------------------------------------------------------------------------ | +| 目标分支 | `{target}` | +| 当前 HEAD | 当前提交 hash | +| 合并结果 | 成功 / 跳过 / 已合并 / 无差异 数量 | +| 冲突统计 | 冲突分支数、冲突文件数、机械处理数、逐文件确认数 | +| 语义审查 | 每个分支的问题数、建议数、修复成功数、修复失败数 | +| 实际验证 | 每个分支实际执行了哪些验证命令,哪些未验证 | +| 已创建资源 | `global_tag`、`branch_tags[]`、`auto_stashes[]`、`created_local_tracking_branches[]` | +| 候选删除 | 可删除的本地分支、远端分支、可删除 tag | + +用提问工具一次性确认清理策略: + +- `确认清理` +- `调整清理范围` +- `跳过清理` + +清理项包括: + +- 删除已成功合并的本地分支:`git branch -d {branch}` +- 删除已成功合并且用户确认的远端分支:`git push {remote} --delete {branch}` +- 删除用户明确不再保留的安全锚点 tag +- 删除本流程仅为分析/合并而创建、且最终不需要保留的本地跟踪分支 + +远端分支删除规则: + +- 优先删除该分支自己的 upstream remote +- 若分支无 upstream,但来自 `target_remote`,则删除 `target_remote/{branch}` +- 删除失败只记录错误,不影响其余清理项 + +### 工作区恢复 + +若 `auto_stashes[]` 非空,用提问工具让用户选择: + +- `按顺序恢复全部 stash` +- `查看 stash 列表后选择恢复` +- `暂不恢复,保留 stash` + +恢复时按后进先出顺序处理。由于 `stash@{n}` 会随列表变化而漂移,先根据已记录的唯一 message 在 `git stash list` 中定位当前 ref,再对该 stash 执行 `git stash apply {ref}`,不直接 `pop`: + +1. `apply` 成功后,再询问是否 `git stash drop {ref}` +2. 若 `apply` 冲突,展示 stash 冲突决策面板,逐文件选择: + - `保留 stash 内容` + - `保留当前内容` + - `AI 起草双保留结果` + - `用户手动编辑` + - `放弃恢复该 stash` +3. 冲突解决完成后,再询问是否 `drop` 对应 stash + +输出最终总结:目标分支、当前 HEAD、探测到的仓库规则摘要、保留的锚点、各分支最终状态、未删除的远端分支、stash 恢复结果。 + +## 终止处理 + +任一步骤选择 `终止` 时,输出终止摘要: + +| 项目 | 内容 | +| ---------- | ------------------------------------------------------------------------------------ | +| 当前步骤 | 准备 / 分析 / 计划 / 执行 / 清理 | +| 当前 HEAD | 当前提交 hash | +| 已完成分支 | 成功合并的分支 | +| 已跳过分支 | 分支及原因 | +| 未处理分支 | 剩余执行队列 | +| 已创建资源 | `global_tag`、`branch_tags[]`、`auto_stashes[]`、`created_local_tracking_branches[]` | +| 已识别规则 | `repo_rules`、`module_map`、`validation_commands` | + +若终止时存在活动 merge 状态,再用提问工具让用户选择: + +- `执行 git merge --abort 并结束` +- `回到当前分支级锚点并结束` +- `保留当前冲突现场,交给用户手动处理` + +若终止时不存在活动 merge 状态,不自动变更现场;仅提示用户: + +- 全部回退可使用 `git reset --hard {global_tag}` +- 当前分支级回退可使用 `git reset --hard merge-before-{branch}-{timestamp}` +- 本流程创建的 stash 和本地跟踪分支仍保留,是否恢复/删除由步骤 5 或用户后续手动决定 diff --git a/docs/prompts/prompt-spec-review.md b/docs/prompts/prompt-spec-review.md new file mode 100644 index 0000000..1237151 --- /dev/null +++ b/docs/prompts/prompt-spec-review.md @@ -0,0 +1,142 @@ +请审查并整理 `openspec/specs/` 下的稳定规范,使其成为可搜索、边界清晰、无冗余、与当前业务一致的能力索引,按以下流程执行。 + +## 约束 + +- `openspec/specs/` 描述长期稳定的业务能力、规则和外部行为,不记录变更过程、迁移说明、实现路径、内部类型名、组件 props、样式数值、层级分层等实现细节 +- 用户可感知或对外暴露的契约可以保留:公开 API 路径、请求/响应字段、协议名、错误码、数据约束、交互结果 +- `Requirement` 和 `Scenario` 应描述业务能力、外部行为或稳定约束,不以“使用某层/某组件/某库实现”作为标题或核心表述 +- 不把当前代码自动视为唯一真相;若代码、README、现有 spec 冲突且无法判断应以哪边为准,列入待确认清单,不直接改写规范 +- 仅删除内容已被其他规范完整覆盖且无独立检索价值的规范;非冗余内容仅迁移、合并、拆分或重命名 +- 每批重构执行前用提问工具获得用户确认;删除或重写前先备份原文件为 `{file}.bak.{timestamp}` +- 命名、Purpose、Requirement 标题都必须保留用户下一次最可能搜索的业务关键词 + +## 1. 收集 + +并行读取: + +- `openspec/config.yaml` +- `README.md`,以及与模块结构、API、架构相关的 README 或文档 +- `openspec/specs/*/spec.md` + +默认不读取 `openspec/changes/**`、历史 proposal/design/tasks 作为稳定规范整理依据;仅在用户明确要求“连同历史变更一起校对”时再纳入。 + +先建立索引,不直接开始改写: + +| 索引 | 内容 | +| -------------- | ----------------------------------------------------------------------------- | +| `spec_index[]` | 每个 spec 的目录名、Purpose、Requirement 摘要、关键词、外部契约、疑似重叠对象 | +| `domain_map[]` | 从 README、API、模块文档中提炼的核心业务域、横切能力和术语 | +| `term_map[]` | 同义词、旧名、缩写和推荐标准术语 | +| `suspects[]` | 需要进一步对照代码或测试确认的 spec | + +仅对 `suspects[]` 做定向读取: + +- 读取与该 spec 对应的源码、测试、README 或架构文档 +- 不对 `backend/`、`frontend/` 做无差别逐文件扫描 + +判定依据优先级: + +- 当前稳定 spec 与 README 共同支持的事实,可直接视为高置信度 +- 仅代码可见但 README 和 spec 未体现的内容,先判断它是稳定外部行为还是临时实现细节 +- 代码、README、现有 spec 互相冲突且无法自动定夺时,进入 `待确认清单` + +## 2. 审查 + +按 spec、Requirement、Scenario 三层检查: + +| 维度 | 检查点 | +| --------- | --------------------------------------------------------------------------------- | +| 过时 | 描述的能力、术语、外部契约是否仍成立;是否存在 `TBD`、`TODO`、占位说明 | +| 冲突 | 不同规范是否对同一行为给出不同约束、命名或边界 | +| 重复/重叠 | 是否在文件级、Requirement 级、Scenario 级重复描述同一能力 | +| 错位 | 内容是否放错能力域;横切规则是否混入实体规范;平台实现是否混入通用能力规范 | +| 粒度 | 是否过大导致难检索,或过碎导致回答一个问题必须同时打开多个 spec | +| 术语 | 同一概念是否混用多个名字;旧名、别名、缩写是否需要归一并保留检索入口 | +| 命名/检索 | 目录名、Purpose、Requirement 标题是否准确;是否能被 README、API、业务术语直接命中 | +| 规范性 | 是否使用 SHALL/WHEN/THEN;是否混入变更记录、迁移说明、内部实现或 UI/代码细节 | +| 完整性 | Purpose 是否明确;是否存在空目录、非 spec 噪音文件、无清晰归属的孤立规范 | + +重构判定规则: + +- 若两个 spec 回答的是同一个核心问题,或其中一个只是另一个的子集,优先合并 +- 若一个 spec 混合多个独立检索意图,或同时包含横切规则与业务流程,优先拆分 +- 若内容正确但目录名、Purpose 或 Requirement 标题不利于检索,优先重命名或改写标题 +- 若多个术语指向同一概念,统一到一个标准术语,并在 Purpose 或 Requirement 中保留必要别名以支持搜索 +- 若某段内容只是内部实现细节,且不影响外部行为理解,删除该段而不是为其单独保留 spec +- 若某个具体值同时属于外部契约与内部实现,按“是否对调用方可见、是否影响兼容性”判断是否保留 + +### 命名约定 + +命名优先复用仓库已存在的稳定术语,如 `provider`、`model`、`stats`、`protocol`、`proxy`、`logging`、`validation`、`migration`、`frontend`、`desktop`、`mysql`。 + +| 类型 | 模式 | 示例 | +| ------------ | ---------------------------------------------------------- | -------------------------------------------------- | +| 实体生命周期 | `{entity}-management` | `provider-management`、`model-management` | +| 横切能力 | `{concern}` 或 `{concern}-{qualifier}` | `error-handling`、`structured-logging` | +| 协议/适配 | `{protocol}-{capability}` 或 `protocol-adapter-{protocol}` | `openai-protocol-proxy`、`protocol-adapter-openai` | +| 运行面/入口 | `{surface}` 或 `{surface}-{capability}` | `frontend`、`desktop-app` | +| 基础设施 | `{resource}-{operation}` | `database-migration`、`mysql-driver` | + +命名原则: + +- 1-4 个词,保持单一主题 +- 优先使用业务名词,不使用 `basic`、`general`、`misc`、`info`、`data` 等泛化词 +- 不使用 `crud`、`list`、`table`、`display` 等实现模式词,除非它本身就是外部契约的一部分 +- 同一主题的命名模式保持一致,不同时混用多套前后缀 + +## 3. 报告 + +输出分析结果: + +1. **问题总览表**:问题类型 × 涉及规范数 +2. **规范关系表**:每个 spec 的主主题、重叠对象、冲突对象、建议动作 +3. **术语归一表**:旧术语 / 别名 / 缩写 → 推荐标准术语 +4. **逐项分析**:每个有问题的规范说明位置、问题、影响、建议和目标规范 +5. **待确认清单**:代码、README、现有 spec 冲突且无法自动定夺的事项 +6. **重构方案**:按优先级分批 +7. **重构后目录结构**:预期的新 `openspec/specs/` 目录树 + +优先级建议: + +- P0:删除空目录、非 spec 噪音文件、占位内容 +- P1:删除完全冗余规范;将其内容映射到主规范 +- P2:合并重复/子集规范;拆分错位或过大规范 +- P3:重命名目录、改写 Purpose 和 Requirement 标题以提升检索性 +- P4:修正过时描述,清理实现细节、迁移说明和变更记录 + +若所有问题清单为空,输出“审查通过,未发现问题”,跳至步骤 5。 + +## 4. 计划(用户确认) + +先针对 `待确认清单` 用提问工具逐项向用户确认。 + +再按批次展示完整重构计划,每批必须包含: + +- 操作类型:删除、重命名、迁移、合并、拆分、改写 +- 路径变化:源路径 → 目标路径 +- 内容映射:源 spec 的 Requirement / Scenario 将迁移到哪里 +- 术语处理:哪些旧词保留为检索入口,哪些词统一替换 +- 执行原因:为什么这样做更利于检索、去重和边界清晰 +- 验证方式:如何确认没有丢失约束或引入新的冲突 + +用提问工具获得当前批次确认后再执行。 + +## 5. 执行 + +按 P0 → P4 逐批执行已确认的重构。 + +执行要求: + +- 合并或拆分时先写目标 spec,再删除或重命名源 spec +- 删除前确认其 Requirement 和 Scenario 已被完整保留、迁移或判定为纯冗余 +- 每批执行后重新读取受影响的 spec,并复核结构和内容 + +每批执行后至少验证: + +- 目录结构完整,`openspec/specs/*/spec.md` 可正常读取 +- 不存在未承接的 Requirement 或 Scenario +- Purpose、Requirement 标题、目录名可以直接表达主能力 +- 不再包含 `TBD`、变更记录、迁移说明、内部实现细节或噪音文件 +- 若本批涉及代码对照项,相关外部契约描述与当前仓库现状一致,或已列入残留待确认 + +收尾时输出:修改文件清单、备份文件清单、最终目录树、残留待确认事项和整理摘要。 diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..2c9e2bf --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,93 @@ +import js from "@eslint/js"; +import importPlugin from "eslint-plugin-import"; +import perfectionist from "eslint-plugin-perfectionist"; +import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { + ignores: [ + "node_modules/**", + "dist/**", + ".build/**", + "*.bun-build", + "openspec/**", + ".opencode/**", + ".claude/**", + ".codex/**", + ".agents/**", + "bun.lock", + "data/**", + ], + }, + js.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + importPlugin.flatConfigs.recommended, + importPlugin.flatConfigs.typescript, + perfectionist.configs["recommended-natural"], + { + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + settings: { + "import/resolver": { node: true, typescript: true }, + }, + }, + { + rules: { + "@typescript-eslint/array-type": ["error", { default: "array-simple" }], + "@typescript-eslint/consistent-type-assertions": ["error", { assertionStyle: "as" }], + "@typescript-eslint/consistent-type-imports": ["error", { prefer: "type-imports" }], + "@typescript-eslint/only-throw-error": "error", + "@typescript-eslint/prefer-nullish-coalescing": "error", + "@typescript-eslint/prefer-optional-chain": "error", + "import/no-unresolved": ["error", { ignore: ["^bun:"] }], + "no-undef": "off", + }, + }, + { + files: ["eslint.config.js"], + rules: { + "import/no-named-as-default": "off", + "import/no-named-as-default-member": "off", + }, + }, + { + files: ["src/web/**/*.{ts,tsx}"], + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "no-restricted-imports": [ + "error", + { + patterns: [ + { + group: [ + "../server/*", + "../server/**", + "../**/server/*", + "../**/server/**", + "../../server/*", + "../../server/**", + "src/server/*", + "src/server/**", + ], + message: "前端不得导入 src/server 后端运行时实现;请改用 src/shared 类型或 HTTP API。", + }, + ], + }, + ], + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + }, + }, + eslintPluginPrettierRecommended, +); diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..fc32d47 --- /dev/null +++ b/opencode.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://opencode.ai/config.json", + "mcp": { + "tdesign-mcp-server": { + "enabled": true, + "type": "local", + "command": ["bunx", "tdesign-mcp-server@latest"] + } + } +} diff --git a/openspec/config.yaml b/openspec/config.yaml new file mode 100644 index 0000000..384b52f --- /dev/null +++ b/openspec/config.yaml @@ -0,0 +1,31 @@ +schema: spec-driven + +context: | + - 使用中文(注释、文档、交流),面向中文开发者 + - openspec文档的关键字按openspec规范使用,不要翻译为中文 + - **优先阅读README.md和DEVELOPMENT.md**获取项目概览与开发规范,所有代码风格、命名、注解、依赖、API等规范以DEVELOPMENT.md为准 + - 涉及模块结构、API、实体等变更时同步更新README.md + - 新增代码优先复用已有组件、工具、依赖库,不引入新依赖 + - 新增的逻辑必须编写完善的测试,并保证测试的正确性,不允许跳过任何测试 + - 这是基于bun实现的前端后一体化项目,使用bun作为唯一包管理器,严禁使用pnpm、npm,使用bunx运行工具,严禁使用npx、pnpx + - src/server目录下是基于bun实现的后端代码 + - 后端库使用优先级:Bun 内置 API > es-toolkit > 主流三方库 > 项目公共工具 > 自行实现 + - src/web目录下是基于Bun HTML import、React、TDesign实现的前端代码 + - 前端样式开发优先级:TDesign组件 > 组件props > TDesign CSS tokens(--td-*) > styles.css CSS类 > 自行开发组件 + - 前端严禁:组件内联style属性、CSS覆盖TD内部类名、使用!important、硬编码色值 + - Git提交: 仅中文; 格式"类型: 简短描述", 类型: feat/fix/refactor/docs/style/test/chore; 多行描述空行后写详细说明 + - 禁止创建git操作task + - 积极使用subagents精心设计并行任务,节省上下文空间,加速任务执行 + - 优先使用提问工具对用户进行提问 + - 本项目为 Bun 全栈应用模板,es-toolkit 为后端首选工具库、recharts 为前端首选图表库 + +rules: + proposal: + - 仔细审查每一个过往spec判断是否存在Modified Capabilities + design: + - 先前的讨论技术方案要尽可能体现在设计文档中,便于指导实现阶段不偏离已定的技术路线 + tasks: + - 一行一个任务,严禁任务内容跨行 + - 如果是代码存在更新必须 + - 执行完整的测试、代码检查、格式检查等质量保障手段 + - 更新 README.md 和/或 DEVELOPMENT.md diff --git a/package.json b/package.json new file mode 100644 index 0000000..83d5497 --- /dev/null +++ b/package.json @@ -0,0 +1,55 @@ +{ + "name": "{{app-name}}", + "type": "module", + "private": true, + "scripts": { + "dev": "bun run scripts/dev.ts", + "dev:server": "bun --watch src/server/dev.ts", + "dev:web": "bunx --bun vite --host", + "build": "bun run scripts/build.ts", + "lint": "eslint .", + "format": "prettier . --write", + "check": "bun run typecheck && bun run lint && bun test", + "verify": "bun run check && bun run build", + "test": "bun test", + "clean": "bun run scripts/clean.ts", + "typecheck": "tsc --noEmit", + "prepare": "husky" + }, + "devDependencies": { + "@commitlint/cli": "^21.0.1", + "@commitlint/config-conventional": "^21.0.1", + "@eslint/js": "^10.0.1", + "@tanstack/react-query-devtools": "^5.100.10", + "@testing-library/react": "^16.3.2", + "@types/bun": "^1.3.14", + "@types/jsdom": "^28.0.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.2", + "eslint": "^10.3.0", + "eslint-config-prettier": "^10.1.8", + "eslint-import-resolver-typescript": "^4.4.4", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-perfectionist": "^5.9.0", + "eslint-plugin-prettier": "^5.5.5", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "husky": "^9.1.7", + "jsdom": "^29.1.1", + "lint-staged": "^17.0.4", + "prettier": "^3.8.3", + "typescript": "^6.0.3", + "typescript-eslint": "^8.59.3", + "vite": "^8.0.13" + }, + "dependencies": { + "@tanstack/react-query": "^5.100.10", + "es-toolkit": "^1.46.1", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "recharts": "^3.8.1", + "tdesign-icons-react": "^0.6.4", + "tdesign-react": "^1.16.9" + } +} diff --git a/scripts/build.ts b/scripts/build.ts new file mode 100644 index 0000000..6102dba --- /dev/null +++ b/scripts/build.ts @@ -0,0 +1,155 @@ +import { readdir, rm, writeFile } from "node:fs/promises"; +import { join, relative, sep } from "node:path"; +import { fileURLToPath } from "node:url"; + +const projectRoot = fileURLToPath(new URL("..", import.meta.url)); +const distWebDir = join(projectRoot, "dist/web"); +const buildDir = join(projectRoot, ".build"); +const executablePath = join(projectRoot, "dist/{{app-name}}"); + +async function build() { + try { + await viteBuild(); + await codeGeneration(); + await bunCompile(); + await cleanup(); + console.log(`Built executable: ${executablePath}`); + } catch (error) { + await cleanup(); + console.error("Build failed:", error); + process.exit(1); + } +} + +async function bunCompile() { + console.log("Step 3/3: Bun compile..."); + await rm(executablePath, { force: true }); + + const target = process.env["BUN_TARGET"] ?? process.env["BUILD_TARGET"]; + const result = await Bun.build({ + compile: target + ? { + autoloadBunfig: true, + autoloadDotenv: true, + outfile: executablePath, + target: target as Bun.Build.CompileTarget, + } + : { + autoloadBunfig: true, + autoloadDotenv: true, + outfile: executablePath, + }, + entrypoints: [join(buildDir, "server-entry.ts")], + minify: true, + sourcemap: "linked", + }); + + if (!result.success) { + console.error("Bun compile failed:", result.logs); + await cleanup(); + process.exit(1); + } +} + +async function cleanup() { + await rm(buildDir, { force: true, recursive: true }); +} + +async function codeGeneration() { + console.log("Step 2/3: Code generation..."); + await rm(buildDir, { force: true, recursive: true }); + await Bun.write(join(buildDir, ".gitkeep"), ""); + + const allFiles = await scanDir(distWebDir, "/"); + const importLines: string[] = []; + const fileEntries: string[] = []; + let indexHtmlVar = ""; + + for (let i = 0; i < allFiles.length; i++) { + const urlPath = allFiles[i]!; + const varName = `f${i}`; + const filePath = toImportSpecifier(buildDir, join(distWebDir, urlPath.slice(1))); + importLines.push(`import ${varName} from "./${filePath}" with { type: "file" };`); + + if (urlPath === "/index.html") { + indexHtmlVar = varName; + } else { + fileEntries.push(` "${urlPath}": Bun.file(${varName}),`); + } + } + + if (!indexHtmlVar) { + console.error("index.html not found in dist/web/"); + process.exit(1); + } + + const staticAssetsTs = [ + `import type { StaticAssets } from "../src/server/static";`, + "", + ...importLines, + "", + `export const staticAssets: StaticAssets = {`, + ` files: {`, + ...fileEntries, + ` },`, + ` indexHtml: Bun.file(${indexHtmlVar}),`, + `};`, + "", + ].join("\n"); + + await writeFile(join(buildDir, "static-assets.ts"), staticAssetsTs); + + const serverEntryTs = [ + `import { bootstrap } from "../src/server/bootstrap";`, + `import { parseRuntimeArgs } from "../src/server/config";`, + `import { staticAssets } from "./static-assets";`, + "", + `async function main() {`, + ` const { configPath } = parseRuntimeArgs();`, + ` await bootstrap({ configPath, mode: "production", staticAssets });`, + `}`, + "", + `void main().catch((error) => {`, + ` console.error("启动失败:", error instanceof Error ? error.message : error);`, + ` process.exit(1);`, + `});`, + "", + ].join("\n"); + + await writeFile(join(buildDir, "server-entry.ts"), serverEntryTs); +} + +async function scanDir(dir: string, prefix: string): Promise<string[]> { + const entries = await readdir(dir, { withFileTypes: true }); + const paths: string[] = []; + for (const entry of entries) { + const fullPath = join(dir, entry.name); + const urlPath = `${prefix}${entry.name}`; + if (entry.isDirectory()) { + paths.push(...(await scanDir(fullPath, `${urlPath}/`))); + } else { + paths.push(urlPath); + } + } + return paths; +} + +function toImportSpecifier(fromDir: string, targetPath: string) { + return relative(fromDir, targetPath).split(sep).join("/"); +} + +async function viteBuild() { + console.log("Step 1/3: Vite build..."); + const proc = Bun.spawn(["bunx", "--bun", "vite", "build"], { + cwd: projectRoot, + stderr: "inherit", + stdout: "inherit", + }); + const exitCode = await proc.exited; + if (exitCode !== 0) { + console.error("Vite build failed"); + process.exit(1); + } +} + +await build(); diff --git a/scripts/clean.ts b/scripts/clean.ts new file mode 100644 index 0000000..59e30ec --- /dev/null +++ b/scripts/clean.ts @@ -0,0 +1,29 @@ +import { rm } from "node:fs/promises"; +import { resolve } from "node:path"; + +const root = resolve(import.meta.dir, ".."); + +const dirs: Array<{ desc: string; path: string }> = [ + { desc: "构建产物", path: "dist" }, + { desc: "Bun 构建缓存", path: ".build" }, + { desc: "Playwright 测试报告", path: "playwright-report" }, + { desc: "测试结果", path: "test-results" }, +]; + +const filePatterns: Array<{ desc: string; glob: string }> = [{ desc: "Bun 构建临时文件", glob: ".*.bun-build" }]; + +for (const { desc, path } of dirs) { + const full = resolve(root, path); + await rm(full, { force: true, recursive: true }); + console.log(`已清理 ${desc}: ${path}`); +} + +for (const { desc, glob } of filePatterns) { + const entries = await Array.fromAsync(new Bun.Glob(glob).scan({ cwd: root, dot: true })); + if (entries.length === 0) continue; + for (const entry of entries) { + const full = resolve(root, entry); + await rm(full, { force: true, recursive: true }); + console.log(`已清理 ${desc}: ${entry}`); + } +} diff --git a/scripts/dev.ts b/scripts/dev.ts new file mode 100644 index 0000000..a0f8133 --- /dev/null +++ b/scripts/dev.ts @@ -0,0 +1,26 @@ +import { fileURLToPath } from "node:url"; + +const projectRoot = fileURLToPath(new URL("..", import.meta.url)); + +const apiServer = Bun.spawn(["bun", "--watch", "src/server/dev.ts", ...process.argv.slice(2)], { + cwd: projectRoot, + stderr: "inherit", + stdout: "inherit", +}); + +const viteServer = Bun.spawn(["bunx", "--bun", "vite", "--host"], { + cwd: projectRoot, + stderr: "inherit", + stdout: "inherit", +}); + +function shutdown() { + apiServer.kill(); + viteServer.kill(); +} + +process.on("SIGINT", shutdown); +process.on("SIGTERM", shutdown); + +await Promise.race([apiServer.exited, viteServer.exited]); +shutdown(); diff --git a/src/server/bootstrap.ts b/src/server/bootstrap.ts new file mode 100644 index 0000000..71fa9ac --- /dev/null +++ b/src/server/bootstrap.ts @@ -0,0 +1,46 @@ +import type { RuntimeMode } from "../shared/api"; +import type { ServerConfig } from "./config"; +import type { StartServerOptions } from "./server"; + +import { loadServerConfig } from "./config"; +import { startServer } from "./server"; + +export interface BootstrapDependencies { + loadConfig?: (configPath?: string) => Promise<ServerConfig>; + logError?: (...data: unknown[]) => void; + onSignal?: (signal: "SIGINT" | "SIGTERM", handler: () => void) => void; + startServer?: (options: StartServerOptions) => unknown; +} + +export interface BootstrapOptions { + config?: ServerConfig; + configPath?: string; + mode: RuntimeMode; + staticAssets?: StartServerOptions["staticAssets"]; +} + +export async function bootstrap(options: BootstrapOptions, dependencies: BootstrapDependencies = {}): Promise<void> { + const load = dependencies.loadConfig ?? loadServerConfig; + const serve = dependencies.startServer ?? startServer; + const onSignal = + dependencies.onSignal ?? + ((signal: "SIGINT" | "SIGTERM", handler: () => void) => { + process.on(signal, handler); + }); + const logError = dependencies.logError ?? console.error; + + try { + const config = options.config ?? (await load(options.configPath)); + + const shutdown = () => { + process.exit(0); + }; + onSignal("SIGINT", shutdown); + onSignal("SIGTERM", shutdown); + + serve({ config, mode: options.mode, staticAssets: options.staticAssets }); + } catch (error) { + logError("启动失败:", error instanceof Error ? error.message : error); + process.exit(1); + } +} diff --git a/src/server/config.ts b/src/server/config.ts new file mode 100644 index 0000000..dccdcfe --- /dev/null +++ b/src/server/config.ts @@ -0,0 +1,51 @@ +export interface ServerConfig { + host: string; + port: number; +} + +const DEFAULT_HOST = "127.0.0.1"; +const DEFAULT_PORT = 3000; + +interface YAMLConfigFile { + server?: YAMLServerBlock; +} + +interface YAMLServerBlock { + host?: string; + port?: number; +} + +export async function loadServerConfig(configPath?: string): Promise<ServerConfig> { + const fileConfig: { host?: string; port?: number } = {}; + + if (configPath) { + const file = Bun.file(configPath); + if (!(await file.exists())) { + throw new Error(`配置文件不存在: ${configPath}`); + } + const content = await file.text(); + const parsed = Bun.YAML.parse(content) as YAMLConfigFile; + if (parsed.server) { + if (parsed.server.host !== undefined) fileConfig.host = parsed.server.host; + if (parsed.server.port !== undefined) fileConfig.port = parsed.server.port; + } + } + + const envPortNum = parseInt(process.env["PORT"] ?? "", 10); + + return { + host: process.env["HOST"] ?? fileConfig.host ?? DEFAULT_HOST, + port: !isNaN(envPortNum) ? envPortNum : (fileConfig.port ?? DEFAULT_PORT), + }; +} + +export function parseRuntimeArgs(argv: string[] = Bun.argv.slice(2)): { configPath?: string } { + if (argv.length === 0) return {}; + const firstArg = argv[0]; + if (firstArg === "--help" || firstArg === "-h") { + console.log("用法: {{app-name}} [config.yaml]"); + console.log(" config.yaml 可选 YAML 配置文件路径(不存在时使用默认配置)"); + process.exit(0); + } + return { configPath: firstArg }; +} diff --git a/src/server/dev.ts b/src/server/dev.ts new file mode 100644 index 0000000..690e126 --- /dev/null +++ b/src/server/dev.ts @@ -0,0 +1,12 @@ +import { bootstrap } from "./bootstrap"; +import { parseRuntimeArgs } from "./config"; + +async function main() { + const { configPath } = parseRuntimeArgs(); + await bootstrap({ configPath, mode: "development" }); +} + +void main().catch((error) => { + console.error("启动失败:", error instanceof Error ? error.message : error); + process.exit(1); +}); diff --git a/src/server/helpers.ts b/src/server/helpers.ts new file mode 100644 index 0000000..e4fa0a3 --- /dev/null +++ b/src/server/helpers.ts @@ -0,0 +1,45 @@ +import type { ApiErrorResponse, HealthResponse, RuntimeMode } from "../shared/api"; + +export function createApiError(error: string, status: number): ApiErrorResponse { + return { error, status }; +} + +export function createHeaders(mode: RuntimeMode, init: HeadersInit): Headers { + const headers = new Headers(init); + + if (mode === "production") { + headers.set("X-Content-Type-Options", "nosniff"); + headers.set("Referrer-Policy", "strict-origin-when-cross-origin"); + } + + return headers; +} + +export function createHealthResponse(): HealthResponse { + return { + ok: true, + service: "{{app-name}}", + timestamp: new Date().toISOString(), + }; +} + +export function formatDuration(ms: number): string { + if (ms >= 60000 && ms % 60000 === 0) return `${ms / 60000}m`; + if (ms >= 1000 && ms % 1000 === 0) return `${ms / 1000}s`; + return `${ms}ms`; +} + +export function jsonResponse( + body: unknown, + options: { headers?: HeadersInit; mode: RuntimeMode; status?: number }, +): Response { + const headers = createHeaders(options.mode, { + "Content-Type": "application/json; charset=utf-8", + ...options.headers, + }); + + return new Response(JSON.stringify(body), { + headers, + status: options.status, + }); +} diff --git a/src/server/main.ts b/src/server/main.ts new file mode 100644 index 0000000..d17164c --- /dev/null +++ b/src/server/main.ts @@ -0,0 +1,12 @@ +import { bootstrap } from "./bootstrap"; +import { parseRuntimeArgs } from "./config"; + +async function main() { + const { configPath } = parseRuntimeArgs(); + await bootstrap({ configPath, mode: "production" }); +} + +void main().catch((error) => { + console.error("启动失败:", error instanceof Error ? error.message : error); + process.exit(1); +}); diff --git a/src/server/middleware.ts b/src/server/middleware.ts new file mode 100644 index 0000000..c08da65 --- /dev/null +++ b/src/server/middleware.ts @@ -0,0 +1,63 @@ +import type { RuntimeMode } from "../shared/api"; + +import { createApiError, jsonResponse } from "./helpers"; + +const MAX_PAGE_SIZE = 200; + +export function validateIdParam(idStr: string, mode: RuntimeMode): Response | { id: string } { + if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(idStr)) { + return jsonResponse(createApiError("Invalid ID parameter", 400), { mode, status: 400 }); + } + return { id: idStr }; +} + +export function validatePagination( + pageParam: null | string, + pageSizeParam: null | string, + mode: RuntimeMode, +): Response | { page: number; pageSize: number } { + let page = 1; + let pageSize = 20; + + if (pageParam !== null) { + page = Number(pageParam); + if (!Number.isInteger(page) || page <= 0) { + return jsonResponse(createApiError("Invalid page parameter", 400), { mode, status: 400 }); + } + } + + if (pageSizeParam !== null) { + pageSize = Number(pageSizeParam); + if (!Number.isInteger(pageSize) || pageSize <= 0) { + return jsonResponse(createApiError("Invalid pageSize parameter", 400), { mode, status: 400 }); + } + if (pageSize > MAX_PAGE_SIZE) { + return jsonResponse(createApiError(`pageSize must not exceed ${MAX_PAGE_SIZE}`, 400), { mode, status: 400 }); + } + } + + return { page, pageSize }; +} + +export function validateTimeRange( + from: null | string, + to: null | string, + mode: RuntimeMode, +): Response | { from: string; to: string } { + if (!from || !to) { + return jsonResponse(createApiError("from and to parameters are required", 400), { mode, status: 400 }); + } + + const fromDate = new Date(from); + const toDate = new Date(to); + + if (isNaN(fromDate.getTime()) || isNaN(toDate.getTime())) { + return jsonResponse(createApiError("Invalid from or to parameter format", 400), { mode, status: 400 }); + } + + if (fromDate.getTime() > toDate.getTime()) { + return jsonResponse(createApiError("from must be earlier than to", 400), { mode, status: 400 }); + } + + return { from: fromDate.toISOString(), to: toDate.toISOString() }; +} diff --git a/src/server/routes/health.ts b/src/server/routes/health.ts new file mode 100644 index 0000000..6fbca35 --- /dev/null +++ b/src/server/routes/health.ts @@ -0,0 +1,7 @@ +import type { RuntimeMode } from "../../shared/api"; + +import { createHealthResponse, jsonResponse } from "../helpers"; + +export function handleHealth(mode: RuntimeMode): Response { + return jsonResponse(createHealthResponse(), { mode }); +} diff --git a/src/server/server.ts b/src/server/server.ts new file mode 100644 index 0000000..e9a61bb --- /dev/null +++ b/src/server/server.ts @@ -0,0 +1,38 @@ +import type { RuntimeMode } from "../shared/api"; +import type { ServerConfig } from "./config"; +import type { StaticAssets } from "./static"; + +import { createApiError, jsonResponse } from "./helpers"; +import { handleHealth } from "./routes/health"; +import { serveStaticAsset } from "./static"; + +export interface StartServerOptions { + config: ServerConfig; + mode: RuntimeMode; + staticAssets?: StaticAssets; +} + +export function startServer(options: StartServerOptions) { + const { config, mode, staticAssets } = options; + + const server = Bun.serve({ + fetch(req) { + if (staticAssets) { + return serveStaticAsset(new URL(req.url).pathname, staticAssets); + } + return new Response("Frontend is served by Vite dev server on :5173", { status: 404 }); + }, + hostname: config.host, + port: config.port, + routes: { + "/api/*": () => jsonResponse(createApiError("API route not found", 404), { mode, status: 404 }), + "/health": { + GET: () => handleHealth(mode), + }, + }, + }); + + console.log(`{{app-name}} listening on ${server.url}`); + + return server; +} diff --git a/src/server/static.ts b/src/server/static.ts new file mode 100644 index 0000000..384931b --- /dev/null +++ b/src/server/static.ts @@ -0,0 +1,60 @@ +export interface StaticAssets { + files: Record<string, Blob>; + indexHtml: Blob; +} + +const CONTENT_TYPES: Record<string, string> = { + ".css": "text/css; charset=utf-8", + ".html": "text/html; charset=utf-8", + ".js": "text/javascript; charset=utf-8", + ".json": "application/json; charset=utf-8", + ".mjs": "text/javascript; charset=utf-8", + ".png": "image/png", + ".svg": "image/svg+xml", + ".woff": "font/woff", + ".woff2": "font/woff2", +}; + +export function contentTypeFor(path: string): string { + const dot = path.lastIndexOf("."); + if (dot === -1) return "application/octet-stream"; + const ext = path.slice(dot); + return CONTENT_TYPES[ext] ?? "application/octet-stream"; +} + +export function hasFileExtension(path: string): boolean { + const lastSlash = path.lastIndexOf("/"); + const segment = lastSlash === -1 ? path : path.slice(lastSlash + 1); + return segment.includes("."); +} + +export function htmlResponse(html: Blob): Response { + return new Response(html, { + headers: { + "Cache-Control": "no-cache", + "Content-Type": "text/html; charset=utf-8", + }, + }); +} + +export function serveStaticAsset(pathname: string, assets: StaticAssets): Response { + if (pathname === "/") { + return htmlResponse(assets.indexHtml); + } + + const file = assets.files[pathname]; + if (file) { + return new Response(file, { + headers: { + "Cache-Control": "public, max-age=31536000, immutable", + "Content-Type": contentTypeFor(pathname), + }, + }); + } + + if (hasFileExtension(pathname)) { + return new Response("Not found", { status: 404 }); + } + + return htmlResponse(assets.indexHtml); +} diff --git a/src/shared/api.ts b/src/shared/api.ts new file mode 100644 index 0000000..bd0d9b7 --- /dev/null +++ b/src/shared/api.ts @@ -0,0 +1,17 @@ +export interface ApiErrorResponse { + error: string; + status: number; +} + +export interface HealthResponse { + ok: true; + service: string; + timestamp: string; +} + +export type RuntimeMode = "development" | "production" | "test"; + +// ========================================== +// 在此定义你的业务类型 +// 前后端共享的类型都放在这个文件中 +// ========================================== diff --git a/src/web/app.tsx b/src/web/app.tsx new file mode 100644 index 0000000..fccddf3 --- /dev/null +++ b/src/web/app.tsx @@ -0,0 +1,67 @@ +import { useQuery } from "@tanstack/react-query"; +import { Layout, Menu, RadioGroup, Space } from "tdesign-react"; + +import type { HealthResponse } from "../shared/api"; + +import { type ThemePreference, useThemePreference } from "./hooks/use-theme-preference"; + +const { Content, Header } = Layout; +const THEME_OPTIONS = [ + { label: "系统", value: "system" }, + { label: "明亮", value: "light" }, + { label: "黑暗", value: "dark" }, +] as const; + +export function App() { + const { preference: themePreference, setPreference: setThemePreference } = useThemePreference(); + const { data: health } = useQuery({ + queryFn: fetchHealth, + queryKey: ["health"], + refetchInterval: 30000, + staleTime: 5000, + }); + + const handleThemeChange = (value: ThemePreference) => { + setThemePreference(value); + }; + + return ( + <Layout className="dashboard"> + <Header> + <Menu.HeadMenu + logo={ + <span className="dashboard-brand"> + <span className="dashboard-logo">{"{{app-name}}"}</span> + </span> + } + operations={ + <div className="dashboard-header-controls"> + <RadioGroup + onChange={handleThemeChange} + options={THEME_OPTIONS.map((option) => ({ label: option.label, value: option.value }))} + theme="button" + value={themePreference} + variant="default-filled" + /> + </div> + } + /> + </Header> + <Content> + <div className="dashboard-content"> + <Space direction="vertical" size="large" style={{ width: "100%" }}> + <h2>欢迎使用 {"{{app-name}}"}</h2> + <p>在此构建你的应用。以下是 /health API 的返回数据(前后端联调示例):</p> + {health && <pre className="health-response">{JSON.stringify(health, null, 2)}</pre>} + </Space> + </div> + </Content> + </Layout> + ); +} + +async function fetchHealth(): Promise<HealthResponse> { + const response = await fetch("/health"); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json() as Promise<HealthResponse>; +} diff --git a/src/web/components/ErrorBoundary.tsx b/src/web/components/ErrorBoundary.tsx new file mode 100644 index 0000000..ddf2dae --- /dev/null +++ b/src/web/components/ErrorBoundary.tsx @@ -0,0 +1,38 @@ +import type { ErrorInfo, ReactNode } from "react"; + +import { Component } from "react"; +import { Alert, Button, Space } from "tdesign-react"; + +interface Props { + children: ReactNode; +} + +interface State { + hasError: boolean; +} + +export class ErrorBoundary extends Component<Props, State> { + override state: State = { hasError: false }; + + static getDerivedStateFromError(): State { + return { hasError: true }; + } + + override componentDidCatch(error: Error, info: ErrorInfo): void { + console.error("渲染错误:", error, info.componentStack); + } + + override render() { + if (this.state.hasError) { + return ( + <Space align="center" className="error-boundary-fallback" direction="vertical" size="large"> + <Alert message="页面渲染出现异常,请刷新重试" theme="error" title="页面出错" /> + <Button onClick={() => window.location.reload()} theme="primary"> + 刷新页面 + </Button> + </Space> + ); + } + return this.props.children; + } +} diff --git a/src/web/css.d.ts b/src/web/css.d.ts new file mode 100644 index 0000000..cbe652d --- /dev/null +++ b/src/web/css.d.ts @@ -0,0 +1 @@ +declare module "*.css"; diff --git a/src/web/hooks/use-theme-preference.ts b/src/web/hooks/use-theme-preference.ts new file mode 100644 index 0000000..41c4f69 --- /dev/null +++ b/src/web/hooks/use-theme-preference.ts @@ -0,0 +1,73 @@ +import { useEffect, useState } from "react"; + +export type EffectiveTheme = "dark" | "light"; +export type ThemePreference = "dark" | "light" | "system"; + +export const THEME_PREFERENCE_STORAGE_KEY = "{{app-name}}.theme.preference"; +export const THEME_MEDIA_QUERY = "(prefers-color-scheme: dark)"; + +export function applyInitialThemePreference() { + applyThemeMode(resolveEffectiveTheme(readThemePreference(), getSystemPrefersDark())); +} + +export function applyThemeMode(theme: EffectiveTheme, root: HTMLElement = document.documentElement) { + root.setAttribute("theme-mode", theme); +} + +export function getSystemPrefersDark(matchMedia: Window["matchMedia"] = window.matchMedia): boolean { + try { + return matchMedia(THEME_MEDIA_QUERY).matches; + } catch { + return false; + } +} + +export function parseThemePreference(value: unknown): ThemePreference { + return value === "dark" || value === "light" || value === "system" ? value : "system"; +} + +export function readThemePreference(storage: Storage = window.localStorage): ThemePreference { + try { + return parseThemePreference(storage.getItem(THEME_PREFERENCE_STORAGE_KEY)); + } catch { + return "system"; + } +} + +export function resolveEffectiveTheme(preference: ThemePreference, systemPrefersDark: boolean): EffectiveTheme { + if (preference === "dark" || preference === "light") return preference; + return systemPrefersDark ? "dark" : "light"; +} + +export function useThemePreference() { + const [preference, setPreferenceState] = useState<ThemePreference>(() => readThemePreference()); + const [systemPrefersDark, setSystemPrefersDark] = useState(() => getSystemPrefersDark()); + const effectiveTheme = resolveEffectiveTheme(preference, systemPrefersDark); + + useEffect(() => { + applyThemeMode(effectiveTheme); + }, [effectiveTheme]); + + useEffect(() => { + const mediaQueryList = window.matchMedia(THEME_MEDIA_QUERY); + + const handleChange = (event: MediaQueryListEvent) => setSystemPrefersDark(event.matches); + mediaQueryList.addEventListener("change", handleChange); + return () => mediaQueryList.removeEventListener("change", handleChange); + }, []); + + const setPreference = (nextPreference: ThemePreference) => { + setPreferenceState(nextPreference); + writeThemePreference(nextPreference); + }; + + return { effectiveTheme, preference, setPreference }; +} + +export function writeThemePreference(preference: ThemePreference, storage: Storage = window.localStorage) { + try { + storage.setItem(THEME_PREFERENCE_STORAGE_KEY, preference); + } catch { + // 存储不可用时仅使用当前内存状态,避免阻断 Dashboard 渲染。 + } +} diff --git a/src/web/index.html b/src/web/index.html new file mode 100644 index 0000000..4387944 --- /dev/null +++ b/src/web/index.html @@ -0,0 +1,13 @@ +<!doctype html> +<html lang="zh-CN"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta name="description" content="{{app-name}}" /> + <title>{{app-name}} + + +
+ + + diff --git a/src/web/main.tsx b/src/web/main.tsx new file mode 100644 index 0000000..1baa0bd --- /dev/null +++ b/src/web/main.tsx @@ -0,0 +1,41 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; + +import { App } from "./app"; +import { ErrorBoundary } from "./components/ErrorBoundary"; +import { applyInitialThemePreference } from "./hooks/use-theme-preference"; +import "tdesign-react/dist/reset.css"; +import "tdesign-react/dist/tdesign.min.css"; + +import "./styles.css"; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: true, + retry: 1, + staleTime: 5000, + }, + }, +}); + +const rootElement = document.getElementById("root"); + +if (!rootElement) { + throw new Error("找不到前端挂载节点 #root"); +} + +applyInitialThemePreference(); + +createRoot(rootElement).render( + + + + + + + + , +); diff --git a/src/web/styles.css b/src/web/styles.css new file mode 100644 index 0000000..a309ef4 --- /dev/null +++ b/src/web/styles.css @@ -0,0 +1,65 @@ +:root { + --td-brand-color: var(--td-brand-color-7); +} + +.dashboard { + min-height: 100vh; + background: var(--td-bg-color-page); + width: 100%; +} + +.dashboard-content { + box-sizing: border-box; + max-width: 1400px; + margin: 0 auto; + padding: var(--td-comp-paddingTB-xl) var(--td-comp-paddingLR-xl); + width: 100%; +} + +.dashboard-brand { + display: inline-flex; + align-items: baseline; + justify-content: center; + gap: var(--td-comp-margin-s); + line-height: 1.2; +} + +.dashboard-logo { + margin: 0; + color: var(--td-text-color-primary); + font-size: calc(var(--td-font-size-title-large) + 6px); + font-weight: 700; +} + +.dashboard-header-controls { + display: inline-flex; + align-items: center; + gap: var(--td-comp-margin-s); + margin-right: var(--td-comp-margin-xxl); +} + +.health-response { + background: var(--td-bg-color-component); + border-radius: var(--td-radius-default); + padding: var(--td-comp-paddingTB-l) var(--td-comp-paddingLR-l); + font-size: var(--td-font-size-body-medium); + color: var(--td-text-color-primary); + overflow-x: auto; +} + +.error-boundary-fallback { + padding-top: 20vh; + width: 100%; +} + +.full-width { + width: 100%; +} + +.text-disabled { + color: var(--td-text-color-disabled); +} + +.tabular-nums { + font-variant-numeric: tabular-nums; +} diff --git a/src/web/utils/time.ts b/src/web/utils/time.ts new file mode 100644 index 0000000..824ad7b --- /dev/null +++ b/src/web/utils/time.ts @@ -0,0 +1,46 @@ +export function formatCountdown(seconds: number): string { + if (seconds < 60) return `${seconds}秒`; + return `${Math.floor(seconds / 60)}分${seconds % 60}秒`; +} + +export function formatDurationUnit(ms: null | number): { suffix: string; value: number } { + if (ms === null) return { suffix: "", value: 0 }; + if (ms < 60000) return { suffix: "秒", value: roundToOne(ms / 1000) }; + if (ms < 3600000) return { suffix: "分钟", value: roundToOne(ms / 60000) }; + return { suffix: "小时", value: roundToOne(ms / 3600000) }; +} + +export function formatRelativeTime(timestamp: null | string, now = new Date()): string { + if (!timestamp) return "尚无检查数据"; + + const time = new Date(timestamp).getTime(); + if (Number.isNaN(time)) return "尚无检查数据"; + + const diffSeconds = Math.max(0, Math.floor((now.getTime() - time) / 1000)); + if (diffSeconds < 60) return `${diffSeconds}秒前`; + + const diffMinutes = Math.floor(diffSeconds / 60); + if (diffMinutes < 60) return `${diffMinutes}分钟前`; + + const diffHours = Math.floor(diffMinutes / 60); + if (diffHours < 24) return `${diffHours}小时前`; + + return `${Math.floor(diffHours / 24)}天前`; +} + +export function isOlderThan(timestamp: null | string, ageMs: number, now = new Date()): boolean { + if (!timestamp) return false; + const time = new Date(timestamp).getTime(); + if (Number.isNaN(time)) return false; + return now.getTime() - time > ageMs; +} + +export function subtractHours(date: Date, hours: number): Date { + const result = new Date(date); + result.setTime(result.getTime() - hours * 60 * 60 * 1000); + return result; +} + +function roundToOne(value: number): number { + return Math.round(value * 10) / 10; +} diff --git a/tests/helpers.ts b/tests/helpers.ts new file mode 100644 index 0000000..7cbb863 --- /dev/null +++ b/tests/helpers.ts @@ -0,0 +1,13 @@ +import { rm } from "node:fs/promises"; + +export async function rmRetry(dir: string, retries = 10, delayMs = 500) { + for (let i = 0; i < retries; i++) { + try { + await rm(dir, { force: true, recursive: true }); + return; + } catch (e) { + if (i === retries - 1) throw e; + await new Promise((r) => setTimeout(r, delayMs)); + } + } +} diff --git a/tests/server/bootstrap.test.ts b/tests/server/bootstrap.test.ts new file mode 100644 index 0000000..6c7f40e --- /dev/null +++ b/tests/server/bootstrap.test.ts @@ -0,0 +1,71 @@ +/* eslint-disable @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars, @typescript-eslint/require-await, @typescript-eslint/unbound-method */ +import { describe, expect, test } from "bun:test"; + +import type { StartServerOptions } from "../../src/server/server"; + +import { bootstrap, type BootstrapDependencies } from "../../src/server/bootstrap"; + +const origExit = process.exit; + +describe("bootstrap", () => { + test("使用默认依赖启动", async () => { + let started = false; + let signalRegistered = false; + + const mockLoadConfig = (async () => ({ + host: "127.0.0.1", + port: 0, + })) as unknown as BootstrapDependencies["loadConfig"]; + const mockLogError = () => {}; + const mockOnSignal = (_signal: string, _handler: () => void) => { + signalRegistered = true; + }; + const mockStartServer = (_options: StartServerOptions) => { + started = true; + return {}; + }; + + const deps: BootstrapDependencies = { + loadConfig: mockLoadConfig, + logError: mockLogError, + onSignal: mockOnSignal, + startServer: mockStartServer, + }; + + await bootstrap({ mode: "production" }, deps); + + expect(started).toBe(true); + expect(signalRegistered).toBe(true); + }); + + test("启动失败时调用 logError", async () => { + let errorLogged = false; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + process.exit = ((code?: number) => { + throw new Error("process.exit called"); + }) as unknown as typeof process.exit; + + const deps: BootstrapDependencies = { + loadConfig: async () => { + throw new Error("test config error"); + }, + logError: () => { + errorLogged = true; + }, + startServer: () => { + throw new Error("should not reach"); + }, + }; + + try { + await bootstrap({ mode: "production" }, deps); + } catch { + // process.exit throws to interrupt flow + } + + process.exit = origExit; + + expect(errorLogged).toBe(true); + }); +}); diff --git a/tests/server/config.test.ts b/tests/server/config.test.ts new file mode 100644 index 0000000..d93c4fd --- /dev/null +++ b/tests/server/config.test.ts @@ -0,0 +1,95 @@ +import { describe, expect, test } from "bun:test"; +import { rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { loadServerConfig, parseRuntimeArgs } from "../../src/server/config"; + +describe("parseRuntimeArgs", () => { + test("无参数返回空对象", () => { + const result = parseRuntimeArgs([]); + expect(result).toEqual({}); + }); + + test("有参数返回 configPath", () => { + const result = parseRuntimeArgs(["config.yaml"]); + expect(result).toEqual({ configPath: "config.yaml" }); + }); +}); + +describe("loadServerConfig", () => { + test("无 configPath 使用默认值", async () => { + const config = await loadServerConfig(); + expect(config.host).toBe("127.0.0.1"); + expect(config.port).toBe(3000); + }); + + test("环境变量 HOST 覆盖默认值", async () => { + const prev = process.env["HOST"]; + process.env["HOST"] = "0.0.0.0"; + try { + const config = await loadServerConfig(); + expect(config.host).toBe("0.0.0.0"); + } finally { + if (prev === undefined) { + delete process.env["HOST"]; + } else { + process.env["HOST"] = prev; + } + } + }); + + test("环境变量 PORT 覆盖默认值", async () => { + const prev = process.env["PORT"]; + process.env["PORT"] = "8080"; + try { + const config = await loadServerConfig(); + expect(config.port).toBe(8080); + } finally { + if (prev === undefined) { + delete process.env["PORT"]; + } else { + process.env["PORT"] = prev; + } + } + }); + + test("YAML 配置文件不存在时报错", async () => { + try { + await loadServerConfig("/nonexistent/path/config.yaml"); + expect.unreachable(); + } catch (error) { + expect((error as Error).message).toContain("配置文件不存在"); + } + }); + + test("YAML 配置文件加载 server 配置", async () => { + const temp = tmpdir(); + const yamlPath = join(temp, "test-config.yaml"); + const yamlContent = 'server:\n host: "0.0.0.0"\n port: 9999\n'; + await writeFile(yamlPath, yamlContent); + + try { + const config = await loadServerConfig(yamlPath); + expect(config.host).toBe("0.0.0.0"); + expect(config.port).toBe(9999); + } finally { + await rm(yamlPath, { force: true }); + } + }); + + test("YAML 缺少 server 字段时使用默认值", async () => { + const temp = tmpdir(); + const yamlPath = join(temp, "test-empty.yaml"); + const yamlContent = "runtime:\n debug: true\n"; + await writeFile(yamlPath, yamlContent); + + try { + const config = await loadServerConfig(yamlPath); + expect(config.host).toBe("127.0.0.1"); + expect(config.port).toBe(3000); + } finally { + await rm(yamlPath, { force: true }); + } + }); +}); diff --git a/tests/server/middleware.test.ts b/tests/server/middleware.test.ts new file mode 100644 index 0000000..00fa6ef --- /dev/null +++ b/tests/server/middleware.test.ts @@ -0,0 +1,97 @@ +import { describe, expect, test } from "bun:test"; + +import { validateIdParam, validatePagination, validateTimeRange } from "../../src/server/middleware"; + +describe("validateIdParam", () => { + test("有效的 ID 返回字符串", () => { + const result = validateIdParam("api-health_01", "production"); + expect(result).not.toHaveProperty("status"); + expect((result as { id: string }).id).toBe("api-health_01"); + }); + + test("无效的 ID 返回 400", () => { + const invalid = ["-1", "_abc", "has space", "1.5", ""]; + + for (const id of invalid) { + const result = validateIdParam(id, "production"); + expect(result).toHaveProperty("status", 400); + } + }); +}); + +describe("validateTimeRange", () => { + test("有效的 from/to 返回 ISO 字符串", () => { + const result = validateTimeRange("2024-01-01T00:00:00.000Z", "2024-01-02T00:00:00.000Z", "production"); + expect(result).not.toHaveProperty("status"); + expect((result as { from: string; to: string }).from).toBe("2024-01-01T00:00:00.000Z"); + expect((result as { from: string; to: string }).to).toBe("2024-01-02T00:00:00.000Z"); + }); + + test("缺失 from 或 to 返回 400", () => { + const missingFrom = validateTimeRange(null, "2024-01-02T00:00:00.000Z", "production"); + const missingTo = validateTimeRange("2024-01-01T00:00:00.000Z", null, "production"); + const missingBoth = validateTimeRange(null, null, "production"); + + expect(missingFrom).toHaveProperty("status", 400); + expect(missingTo).toHaveProperty("status", 400); + expect(missingBoth).toHaveProperty("status", 400); + }); + + test("空字符串 from 或 to 返回 400", () => { + const emptyFrom = validateTimeRange("", "2024-01-02T00:00:00.000Z", "production"); + const emptyTo = validateTimeRange("2024-01-01T00:00:00.000Z", "", "production"); + + expect(emptyFrom).toHaveProperty("status", 400); + expect(emptyTo).toHaveProperty("status", 400); + }); + + test("无效的日期格式返回 400", () => { + const result = validateTimeRange("invalid-date", "2024-01-02T00:00:00.000Z", "production"); + expect(result).toHaveProperty("status", 400); + }); + + test("from 晚于 to 返回 400", () => { + const result = validateTimeRange("2024-01-02T00:00:00.000Z", "2024-01-01T00:00:00.000Z", "production"); + expect(result).toHaveProperty("status", 400); + }); +}); + +describe("validatePagination", () => { + test("默认值:page=1, pageSize=20", () => { + const result = validatePagination(null, null, "production"); + expect(result).toEqual({ page: 1, pageSize: 20 }); + }); + + test("有效的 page 和 pageSize 参数", () => { + const result = validatePagination("2", "50", "production"); + expect(result).toEqual({ page: 2, pageSize: 50 }); + }); + + test("无效的 page 参数返回 400", () => { + const invalidPage = ["0", "-1", "abc", "1.5"]; + + for (const page of invalidPage) { + const result = validatePagination(page, "20", "production"); + expect(result).toHaveProperty("status", 400); + } + }); + + test("无效的 pageSize 参数返回 400", () => { + const invalidPageSize = ["0", "-1", "abc", "1.5"]; + + for (const pageSize of invalidPageSize) { + const result = validatePagination("1", pageSize, "production"); + expect(result).toHaveProperty("status", 400); + } + }); + + test("pageSize 超过上限返回 400", () => { + const result = validatePagination("1", "201", "production"); + expect(result).toHaveProperty("status", 400); + }); + + test("pageSize 等于上限 200 返回成功", () => { + const result = validatePagination("1", "200", "production"); + expect(result).toEqual({ page: 1, pageSize: 200 }); + }); +}); diff --git a/tests/server/static.test.ts b/tests/server/static.test.ts new file mode 100644 index 0000000..8e654a8 --- /dev/null +++ b/tests/server/static.test.ts @@ -0,0 +1,127 @@ +import { describe, expect, test } from "bun:test"; + +import { + contentTypeFor, + hasFileExtension, + htmlResponse, + serveStaticAsset, + type StaticAssets, +} from "../../src/server/static"; + +function createTestAssets(): StaticAssets { + return { + files: { + "/assets/index-a1b2c3.css": new Blob([".app{}"], { type: "text/css" }), + "/assets/index-a1b2c3.js": new Blob(["console.log(1)"], { type: "text/javascript" }), + "/assets/vendor-react-x9y8z7.js": new Blob(["react"], { type: "text/javascript" }), + "/favicon.svg": new Blob([""], { type: "image/svg+xml" }), + }, + indexHtml: new Blob([""], { type: "text/html" }), + }; +} + +describe("contentTypeFor", () => { + test("JavaScript 文件", () => { + expect(contentTypeFor("/assets/index-a1b2c3.js")).toBe("text/javascript; charset=utf-8"); + }); + + test("mjs 文件", () => { + expect(contentTypeFor("/assets/chunk.mjs")).toBe("text/javascript; charset=utf-8"); + }); + + test("CSS 文件", () => { + expect(contentTypeFor("/assets/style.css")).toBe("text/css; charset=utf-8"); + }); + + test("SVG 文件", () => { + expect(contentTypeFor("/icon.svg")).toBe("image/svg+xml"); + }); + + test("未知扩展名返回 octet-stream", () => { + expect(contentTypeFor("/file.xyz")).toBe("application/octet-stream"); + }); + + test("无扩展名返回 octet-stream", () => { + expect(contentTypeFor("/noext")).toBe("application/octet-stream"); + }); +}); + +describe("hasFileExtension", () => { + test("有扩展名", () => { + expect(hasFileExtension("/assets/index.js")).toBe(true); + expect(hasFileExtension("/favicon.svg")).toBe(true); + }); + + test("无扩展名", () => { + expect(hasFileExtension("/dashboard")).toBe(false); + expect(hasFileExtension("/")).toBe(false); + expect(hasFileExtension("/api/targets")).toBe(false); + }); +}); + +describe("htmlResponse", () => { + test("返回 HTML 响应带正确 headers", async () => { + const blob = new Blob([""]); + const response = htmlResponse(blob); + + expect(response.headers.get("Content-Type")).toBe("text/html; charset=utf-8"); + expect(response.headers.get("Cache-Control")).toBe("no-cache"); + expect(await response.text()).toBe(""); + }); +}); + +describe("serveStaticAsset", () => { + test("根路径返回 indexHtml", async () => { + const assets = createTestAssets(); + const response = serveStaticAsset("/", assets); + + expect(response.status).toBe(200); + expect(response.headers.get("Content-Type")).toBe("text/html; charset=utf-8"); + expect(response.headers.get("Cache-Control")).toBe("no-cache"); + expect(await response.text()).toBe(""); + }); + + test("已知资源返回对应文件和 immutable 缓存", async () => { + const assets = createTestAssets(); + const response = serveStaticAsset("/assets/index-a1b2c3.js", assets); + + expect(response.status).toBe(200); + expect(response.headers.get("Content-Type")).toBe("text/javascript; charset=utf-8"); + expect(response.headers.get("Cache-Control")).toBe("public, max-age=31536000, immutable"); + expect(await response.text()).toBe("console.log(1)"); + }); + + test("未知带扩展名路径返回 404", () => { + const assets = createTestAssets(); + const response = serveStaticAsset("/assets/missing.js", assets); + + expect(response.status).toBe(404); + }); + + test("SPA fallback — 无扩展名路径返回 indexHtml", async () => { + const assets = createTestAssets(); + const response = serveStaticAsset("/dashboard", assets); + + expect(response.status).toBe(200); + expect(response.headers.get("Content-Type")).toBe("text/html; charset=utf-8"); + expect(response.headers.get("Cache-Control")).toBe("no-cache"); + expect(await response.text()).toBe(""); + }); + + test("SVG 资源返回正确 Content-Type", () => { + const assets = createTestAssets(); + const response = serveStaticAsset("/favicon.svg", assets); + + expect(response.status).toBe(200); + expect(response.headers.get("Content-Type")).toBe("image/svg+xml"); + expect(response.headers.get("Cache-Control")).toBe("public, max-age=31536000, immutable"); + }); + + test("CSS 资源返回正确 Content-Type", () => { + const assets = createTestAssets(); + const response = serveStaticAsset("/assets/index-a1b2c3.css", assets); + + expect(response.status).toBe(200); + expect(response.headers.get("Content-Type")).toBe("text/css; charset=utf-8"); + }); +}); diff --git a/tests/setup.ts b/tests/setup.ts new file mode 100644 index 0000000..ad6cc1a --- /dev/null +++ b/tests/setup.ts @@ -0,0 +1,106 @@ +/** + * 全局测试配置 + * 主要为后端测试提供基础环境 + * 组件测试使用各自的 test-utils.tsx + */ + +/* eslint-disable @typescript-eslint/no-empty-function */ +// Set up jsdom for ALL tests (both backend and frontend) +import { JSDOM } from "jsdom"; + +const dom = new JSDOM("", { + pretendToBeVisual: true, + url: "http://localhost", +}); + +globalThis.document = dom.window.document; +globalThis.window = dom.window as unknown as typeof globalThis & Window; +globalThis.navigator = dom.window.navigator; +globalThis.HTMLElement = dom.window.HTMLElement; +globalThis.Element = dom.window.Element; +globalThis.getComputedStyle = dom.window.getComputedStyle; + +// Ensure document.body exists +if (!globalThis.document.body) { + const body = globalThis.document.createElement("body"); + globalThis.document.documentElement.appendChild(body); +} + +// CRITICAL: Set up polyfills BEFORE any other imports +// This ensures @testing-library/react sees these when it loads + +// IE-style event handling polyfill (React fallback) +const nodeProto = dom.window.Node.prototype; +const elementProto = dom.window.Element.prototype; +const htmlElementProto = dom.window.HTMLElement.prototype; + +const attachEventFn = () => {}; +const detachEventFn = () => {}; + +Object.defineProperty(nodeProto, "attachEvent", { configurable: true, value: attachEventFn, writable: true }); +Object.defineProperty(nodeProto, "detachEvent", { configurable: true, value: detachEventFn, writable: true }); +Object.defineProperty(elementProto, "attachEvent", { configurable: true, value: attachEventFn, writable: true }); +Object.defineProperty(elementProto, "detachEvent", { configurable: true, value: detachEventFn, writable: true }); +Object.defineProperty(htmlElementProto, "attachEvent", { configurable: true, value: attachEventFn, writable: true }); +Object.defineProperty(htmlElementProto, "detachEvent", { configurable: true, value: detachEventFn, writable: true }); + +// Other polyfills +globalThis.ResizeObserver = class { + disconnect() {} + observe() {} + unobserve() {} +}; + +globalThis.MutationObserver = class { + disconnect() {} + observe() {} + takeRecords() { + return []; + } + unobserve() {} +}; + +globalThis.IntersectionObserver = class { + disconnect() {} + observe() {} + takeRecords() { + return []; + } + unobserve() {} +} as unknown as typeof IntersectionObserver; + +globalThis.requestAnimationFrame = (cb: FrameRequestCallback) => setTimeout(cb, 16); +globalThis.cancelAnimationFrame = (id: number) => clearTimeout(id); + +Object.defineProperty(dom.window, "matchMedia", { + value: (query: string) => ({ + addEventListener: () => {}, + addListener: () => {}, + dispatchEvent: () => true, + matches: false, + media: query, + onchange: null, + removeEventListener: () => {}, + removeListener: () => {}, + }), + writable: true, +}); + +dom.window.Element.prototype.scrollTo = () => {}; +dom.window.Element.prototype.scrollIntoView = () => {}; + +Object.defineProperty(dom.window, "customElements", { + value: { + define: () => {}, + get: () => undefined, + }, + writable: true, +}); + +globalThis.customElements = dom.window.customElements; + +import { afterEach } from "bun:test"; + +afterEach(() => { + document.body.innerHTML = ""; +}); diff --git a/tests/web/App.test.tsx b/tests/web/App.test.tsx new file mode 100644 index 0000000..53ef405 --- /dev/null +++ b/tests/web/App.test.tsx @@ -0,0 +1,54 @@ +/* eslint-disable @typescript-eslint/require-await */ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { render, screen } from "@testing-library/react"; +import { describe, expect, test } from "bun:test"; +import { createElement, StrictMode } from "react"; + +import { App } from "../../src/web/app"; +import { ErrorBoundary } from "../../src/web/components/ErrorBoundary"; + +function createTestQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + retry: false, + staleTime: 0, + }, + }, + }); +} + +function renderApp() { + const queryClient = createTestQueryClient(); + + return render( + createElement( + StrictMode, + null, + createElement( + ErrorBoundary, + null, + createElement(QueryClientProvider, { client: queryClient }, createElement(App)), + ), + ), + ); +} + +describe("App", () => { + test("渲染 Layout 骨架和品牌名", () => { + // mock /health fetch 避免网络错误 + window.fetch = (async () => { + return new Response(JSON.stringify({ ok: true, service: "test-app", timestamp: new Date().toISOString() }), { + headers: { "Content-Type": "application/json" }, + status: 200, + }); + }) as unknown as typeof fetch; + + renderApp(); + + expect(screen.getByText("{{app-name}}")).not.toBeNull(); + expect(screen.getByText("系统")).not.toBeNull(); + expect(screen.getByText("明亮")).not.toBeNull(); + expect(screen.getByText("黑暗")).not.toBeNull(); + }); +}); diff --git a/tests/web/test-utils.tsx b/tests/web/test-utils.tsx new file mode 100644 index 0000000..2baa56f --- /dev/null +++ b/tests/web/test-utils.tsx @@ -0,0 +1,51 @@ +import { mock } from "bun:test"; + +// Note: jsdom and polyfills are now set up in tests/setup.ts +// This file only contains component-specific mocks + +// Mock recharts BEFORE any component imports +void mock.module("recharts", () => ({ + Area: () => null, + CartesianGrid: () => null, + Line: () => null, + LineChart: ({ children }: { children: unknown }) => children, + ResponsiveContainer: ({ children }: { children: unknown }) => children, + Tooltip: () => null, + XAxis: () => null, + YAxis: () => null, +})); + +// Custom test helpers (替代 jest-dom matchers) +export const testHelpers = { + toBeInTheDocument: (element: Element | null) => { + const pass = element !== null && document.contains(element); + return { + message: () => (pass ? "Expected element not to be in document" : "Expected element to be in document"), + pass, + }; + }, + toHaveAttribute: (element: Element | null, attr: string, value?: string) => { + const pass = value === undefined ? (element?.hasAttribute(attr) ?? false) : element?.getAttribute(attr) === value; + return { + message: () => + pass ? `Expected element not to have attribute "${attr}"` : `Expected element to have attribute "${attr}"`, + pass, + }; + }, + toHaveClass: (element: Element | null, className: string) => { + const pass = element?.classList.contains(className) ?? false; + return { + message: () => + pass ? `Expected element not to have class "${className}"` : `Expected element to have class "${className}"`, + pass, + }; + }, + toHaveTextContent: (element: Element | null, text: RegExp | string) => { + const content = element?.textContent ?? ""; + const pass = element !== null && (typeof text === "string" ? content.includes(text) : text.test(content)); + return { + message: () => (pass ? `Expected element not to have text "${text}"` : `Expected element to have text "${text}"`), + pass, + }; + }, +}; diff --git a/tests/web/utils/time.test.ts b/tests/web/utils/time.test.ts new file mode 100644 index 0000000..7999c32 --- /dev/null +++ b/tests/web/utils/time.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, test } from "bun:test"; + +import { + formatCountdown, + formatDurationUnit, + formatRelativeTime, + isOlderThan, + subtractHours, +} from "../../../src/web/utils/time"; + +describe("subtractHours", () => { + test("正常扣减小时", () => { + const result = subtractHours(new Date("2025-01-15T12:00:00.000Z"), 3); + + expect(result.toISOString()).toBe("2025-01-15T09:00:00.000Z"); + }); + + test("跨天扣减", () => { + const result = subtractHours(new Date("2025-01-15T02:00:00.000Z"), 6); + + expect(result.toISOString()).toBe("2025-01-14T20:00:00.000Z"); + }); + + test("跨月扣减", () => { + const result = subtractHours(new Date("2025-03-01T01:00:00.000Z"), 2); + + expect(result.toISOString()).toBe("2025-02-28T23:00:00.000Z"); + }); + + test("扣减 0 小时返回相同时间", () => { + const result = subtractHours(new Date("2025-01-15T12:00:00.000Z"), 0); + + expect(result.toISOString()).toBe("2025-01-15T12:00:00.000Z"); + }); +}); + +describe("formatRelativeTime", () => { + const now = new Date("2025-01-01T00:02:00.000Z"); + + test("格式化秒和分钟", () => { + expect(formatRelativeTime("2025-01-01T00:01:45.000Z", now)).toBe("15秒前"); + expect(formatRelativeTime("2025-01-01T00:00:00.000Z", now)).toBe("2分钟前"); + }); + + test("无时间返回占位", () => { + expect(formatRelativeTime(null, now)).toBe("尚无检查数据"); + expect(formatRelativeTime("invalid", now)).toBe("尚无检查数据"); + }); +}); + +describe("formatDurationUnit", () => { + test("按秒、分钟、小时动态格式化", () => { + expect(formatDurationUnit(1500)).toEqual({ suffix: "秒", value: 1.5 }); + expect(formatDurationUnit(120000)).toEqual({ suffix: "分钟", value: 2 }); + expect(formatDurationUnit(5400000)).toEqual({ suffix: "小时", value: 1.5 }); + }); + + test("空时长返回占位", () => { + expect(formatDurationUnit(null)).toEqual({ suffix: "", value: 0 }); + }); +}); + +describe("formatCountdown", () => { + test("格式化秒级和分钟级倒计时", () => { + expect(formatCountdown(0)).toBe("0秒"); + expect(formatCountdown(59)).toBe("59秒"); + expect(formatCountdown(60)).toBe("1分0秒"); + expect(formatCountdown(299)).toBe("4分59秒"); + }); +}); + +describe("isOlderThan", () => { + test("判断时间是否超过阈值", () => { + const now = new Date("2025-01-01T00:02:00.000Z"); + + expect(isOlderThan("2025-01-01T00:00:59.000Z", 60000, now)).toBe(true); + expect(isOlderThan("2025-01-01T00:01:30.000Z", 60000, now)).toBe(false); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..fbde3c6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + "types": ["bun"], + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": true, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": true + } +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..33e72c8 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,37 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + build: { + emptyOutDir: true, + outDir: "../../dist/web", + rolldownOptions: { + output: { + codeSplitting: { + groups: [ + { + name: "vendor-react", + test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/, + }, + { + name: "vendor-tdesign", + test: /[\\/]node_modules[\\/](tdesign-react|tdesign-icons-react)[\\/]/, + }, + { + name: "vendor-chart", + test: /[\\/]node_modules[\\/](recharts|d3-.*)[\\/]/, + }, + ], + }, + }, + }, + }, + plugins: [react()], + root: "src/web", + server: { + proxy: { + "/api": "http://127.0.0.1:3000", + "/health": "http://127.0.0.1:3000", + }, + }, +});