1
0

docs: 重构文档体系

This commit is contained in:
2026-05-24 20:18:18 +08:00
parent 483cdc596b
commit a6504d5a62
28 changed files with 2030 additions and 2155 deletions

View File

@@ -0,0 +1,25 @@
# 开发文档
本目录承载 DiAL 的开发和维护专题。日常开发入口见 [`../../DEVELOPMENT.md`](../../DEVELOPMENT.md),新增或修改 checker 前先阅读 [`../../CONTRIBUTING.md`](../../CONTRIBUTING.md)。
## 专题索引
| 文档 | 内容 |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| [architecture.md](architecture.md) | 项目结构、启动流程、运行时流程、HTTP 请求流程、前后端边界 |
| [backend.md](backend.md) | 后端库优先级、API 路由、共享 helpers、类型规范、配置契约、store、engine、logger、expect、错误模型 |
| [frontend.md](frontend.md) | React、TDesign、TanStack Query、组件、样式和前端测试规范 |
| [checker-development.md](checker-development.md) | 新增或修改 checker 的实现机制和完整 checklist |
| [build-release.md](build-release.md) | 开发服务、前后端集成、构建、Docker、release、脚本、环境变量 |
| [testing-quality.md](testing-quality.md) | lint、format、typecheck、test、hooks 和测试编写规范 |
| [../../DEVELOPMENT.md](../../DEVELOPMENT.md) | 包管理、依赖、目录、提交、OpenSpec 和项目级约定 |
| [../README.md](../README.md) | 文档影响分析、文档归属矩阵和按任务阅读路径 |
## 事实来源
| 主题 | 事实来源 |
| ---------------- | ---------------------------------------------------------- |
| 代码结构和实现 | `src/``scripts/``tests/` |
| 配置 schema | TypeBox fragments、`probe-config.schema.json`、schema 测试 |
| 项目全局规则 | `openspec/config.yaml``DEVELOPMENT.md`、本目录专题文档 |
| checker 贡献流程 | `CONTRIBUTING.md``checker-development.md` |

View File

@@ -0,0 +1,106 @@
# 架构与边界
## 项目结构
```text
src/
server/
bootstrap.ts
config.ts
dev.ts
logger.ts
main.ts
server.ts
helpers.ts
middleware.ts
version.ts
routes/
checker/
config-loader.ts
variables.ts
schema/
store.ts
engine.ts
expect/
runner/
shared/
api.ts
web/
app.tsx
main.tsx
styles.css
components/
constants/
hooks/
utils/
scripts/
tests/
docs/
openspec/
probe-config.schema.json
```
## 启动流程
```text
dev.ts / main.ts
-> readRuntimeConfig(cli args)
-> bootstrap({ configPath, mode })
-> loadConfig(yaml)
-> createRuntimeLogger(logging)
-> ProbeStore(db)
-> store.syncTargets(targets)
-> ProbeEngine(...).start()
-> startServer({ config, mode, store, logger })
-> 注册 SIGINT/SIGTERM shutdown
```
`loadConfig()` 的处理顺序YAML 解析 -> Authoring normalize变量替换 + expect 简写展开)-> Normalized 契约校验 -> 语义校验 -> resolve。
## 运行时流程
```text
定时器 tick
-> ProbeEngine.probeGroup()
-> checkerRegistry.get(target.type).execute()
-> runner/*/expect.ts 校验
-> engine.writeResult()
-> store.insertCheckResult()
```
数据清理由 engine 定时调用 `store.prune(retentionMs)`,每小时执行一次。
## HTTP 请求流程
```text
Request
-> Bun.serve routes 声明式匹配
-> routes/*.ts handler
-> middleware.ts 参数校验
-> helpers.ts 响应格式化
-> Response
```
生产模式下,非 API 路径由 fetch fallback 处理静态资源和 SPA fallback。开发模式下Vite proxy 将 `/api``/health` 请求转发到 Bun API server。
## 前后端边界
- 前端只通过 HTTP 调用后端API 路径为 `/api/*`
- 共享类型放在 `src/shared/`
- 前端不得 import `src/server/` 的运行时实现。
- 后端不得依赖 `src/web/` 运行时代码HTML import 集成除外。
## 主要模块职责
| 模块 | 职责 |
| ------------------------------------- | ------------------------------------------- |
| `src/server/bootstrap.ts` | 统一启动引导和 shutdown 编排 |
| `src/server/server.ts` | Bun HTTP server 和 routes 注册 |
| `src/server/routes/` | API handler按端点拆分 |
| `src/server/checker/config-loader.ts` | YAML 解析、契约校验、语义校验、resolve 调度 |
| `src/server/checker/store.ts` | SQLite 数据存储 |
| `src/server/checker/engine.ts` | 定时调度、并发控制、结果写入、数据清理 |
| `src/server/checker/runner/` | 各 checker 自包含实现 |
| `src/server/checker/expect/` | 跨 checker 复用的断言基础设施 |
| `src/web/` | React Dashboard |
| `src/shared/api.ts` | 前后端共享 API 类型 |

123
docs/development/backend.md Normal file
View File

@@ -0,0 +1,123 @@
# 后端开发
## 库使用优先级
| 优先级 | 来源 | 典型用途 |
| ------ | ------------ | -------------------------------------------------------------- |
| 1 | Bun 内置 API | `Bun.serve``bun:sqlite``Bun.spawn``Bun.file``Bun.YAML` |
| 2 | es-toolkit | 类型判断、深度比较、错误判断、并发控制、集合操作 |
| 3 | 标准 Web API | `Object.fromEntries``Headers``fetch``AbortController` |
| 4 | 主流三方库 | cheerio、xpath、@xmldom/xmldom |
| 5 | 自行实现 | 仅在以上都无法满足时 |
新增依赖前必须先检查上述每一层是否已有可用方案。
## API 路由开发
路由文件位于 `src/server/routes/`,每个端点一个文件。路由通过 `server.ts``Bun.serve({ routes })` 声明式注册,使用 per-method handler 对象。
新增路由步骤:
1.`src/server/routes/` 下创建 `<name>.ts`
2. 实现 handler 函数并 export。
3.`server.ts``routes` 对象中注册路径和 method handler。
4.`tests/server/app.test.ts` 中添加集成测试。
请求参数校验使用 `middleware.ts` 提供的 `validateTargetId``validateTimeRange``validatePagination``validateDashboardWindow``validateRecentLimit``validateMetricsBucket`
## 共享 helpers
| 函数 | 用途 |
| ------------------------------- | ------------------------------------ |
| `createApiError(error, status)` | 构造 API 错误体 |
| `createHeaders(mode, init)` | 创建响应 Headers生产模式附加安全头 |
| `createHealthResponse()` | 构造健康检查响应 |
| `formatDuration(ms)` | 毫秒转为可读时长字符串 |
| `jsonResponse(body, options)` | JSON 响应构造 |
| `mapCheckResult(row, type)` | 数据库行转 API CheckResult |
## 类型规范
- 共享类型以 `src/shared/api.ts` 为唯一源头。
- 严格联合类型优先于宽类型。
- 存储层类型与 API 类型分离。
- checker 具体类型在各自目录定义,中间层通过 base interface 和 registry 完成类型擦除。
- 纯类型导入使用 `import type`
## 配置契约与校验
配置加载流程固定为:`unknown -> AuthoringProbeConfig -> NormalizedProbeConfig -> ValidatedProbeConfig -> ResolvedConfig`
| 层级 | 职责 |
| ---------- | ------------------------------------------------ |
| Authoring | 用户 YAML 可书写形态,允许变量引用和 expect 简写 |
| Normalized | 变量替换和 expect 简写展开后的契约校验形态 |
| Validated | 通过契约校验和语义校验的形态 |
| Resolved | checker `resolve()` 后的运行期配置 |
Ajv 保持严格拒绝模式:`allErrors: true`、不启用类型强制转换、不注入默认值、不自动删除未知字段。默认对象策略是 `additionalProperties: false`,只有明确的动态键值表可以开放任意键名。
新增或修改配置字段时必须同步更新 TypeBox schema fragments、`probe-config.schema.json`、语义 validator、测试和对应用户文档并运行 `bun run schema:check`
## 数据存储
存储层基于 `bun:sqlite`WAL 模式运行,数据库文件位于配置的 `dataDir` 下。
| 方法 | 用途 |
| ------------------------------------------ | ---------------------------------- |
| `syncTargets(targets)` | 启动期同步 targets |
| `insertCheckResult()` | 写入单条检查结果 |
| `getTargets()` | 查询全部 targets |
| `getLatestChecksMap()` | 批量获取每个 target 的最新检查结果 |
| `getAllTargetWindowStats(from, to)` | 批量获取窗口基础计数 |
| `getDashboardIncidentStates(from, to)` | 获取 Dashboard 窗口状态序列 |
| `getAllRecentSamples(limit)` | 批量获取最近采样 |
| `getTargetCheckpoints(targetId, from, to)` | 获取单目标窗口检查点序列 |
| `getTargetDurations(targetId, from, to)` | 获取单目标成功耗时数组 |
| `getHistory()` | 分页查询历史记录 |
| `prune(retentionMs)` | 清理过期数据 |
数据库只负责存储、筛选、排序、分页、LIMIT 和基础聚合。指标语义在后端应用层实现。
## 拨测引擎
- 按 interval 分组,每组独立定时触发。
- 使用 `es-toolkit/Semaphore` 限制全局最大并发数。
- 通过 `checkerRegistry.get(target.type)` 选择 runner。
- 每次检查创建 `AbortController` 并按 `target.timeoutMs` 触发 abort。
- 状态变化通过注入的 `Logger` 输出结构化日志。
## 日志模块
后端运行时代码统一通过 `Logger` 接口输出日志,禁止直接使用 `console.*`。配置加载失败前使用 `ConsoleFallbackLogger`
| 实现 | 用途 |
| ----------------------- | --------------------------------------------- |
| `PinoLoggerWrapper` | 生产运行时,封装 Pino、pino-pretty、pino-roll |
| `NoopLogger` | 静默丢弃日志 |
| `MemoryLogger` | 测试替身 |
| `ConsoleFallbackLogger` | 配置加载失败前的降级日志 |
敏感信息会自动 redact `authorization``cookie``set-cookie``authToken``key``password``token``apiKey` 及其嵌套路径。
## expect 系统
共享断言基础设施位于 `src/server/checker/expect/`。新增或修改 checker 的 expect 字段时,按以下原则选择模型:
| 模型 | 用途 | 典型字段 |
| --------------------- | ---------------------------- | ------------------------------------------------------------------- |
| enum / boolean | 状态类结果,结果集合小且稳定 | HTTP status、Cmd exitCode、TCP connected、UDP responded、ICMP alive |
| `ValueMatcher` | 数字指标和字符串元数据 | durationMs、rowCount、finishReason、usage |
| `ContentExpectations` | 返回内容或半结构化内容 | body、stdout、stderr、banner、response、output、result |
| `KeyedExpectations` | 动态键值断言 | headers、DB rows 列值 |
详细 checker 开发流程见 [Checker 开发](checker-development.md)。
## 错误模型
| 类型 | 结构 |
| ------------ | ----------------------------------- | ------------------------------------------------------- |
| API 错误 | `{ error: "描述", status: <code> }` |
| CheckFailure | `{ kind: "error" | "mismatch", phase, path, expected?, actual?, message }` |
expect 校验失败记录首个失败原因;网络、超时、进程崩溃统一为 `kind: "error"`

View File

@@ -0,0 +1,107 @@
# 构建与发布
## 开发期运行
```bash
bun run dev probes.yaml
```
`scripts/dev.ts` 同时启动两个进程:
| 进程 | 用途 |
| --------------- | ------------------------------------------------- |
| Bun API server | 后端 API 服务,`--watch` 监听后端文件变更自动重启 |
| Vite dev server | 前端 SPA、HMR、模块热替换 |
也可以单独启动:
```bash
bun run dev:server probes.yaml
bun run dev:web
```
## 前后端集成
开发模式下Vite 通过 proxy 将 `/api/*``/health` 转发到 Bun。
生产模式下,前端通过 Vite 构建为静态资源,通过 `import with { type: "file" }` 嵌入 Bun 可执行文件。非 API 路径由 fetch fallback 处理:有文件扩展名的返回静态资源或 404无扩展名的返回 SPA index.html。
## 构建
```bash
bun run build
```
构建流程:
```text
1. Vite build -> dist/web/
2. Code generation -> .build/static-assets.ts + .build/server-entry.ts
3. Bun compile -> dist/dial-server
```
构建参数:
| 环境变量 | 说明 |
| -------------- | ---------------- |
| `BUN_TARGET` | 交叉编译目标平台 |
| `BUILD_TARGET` | 交叉编译目标平台 |
## Docker 镜像
Docker 镜像使用 Alpine 多阶段构建,保持与生产单可执行文件交付模型一致。
```text
oven/bun:1-alpine -> bun install --frozen-lockfile
-> BUN_TARGET=bun-linux-*-musl bun run build
-> dist/dial-server
alpine -> 仅复制 /usr/local/bin/dial-server
-> 安装 ca-certificates、iputils-ping、libgcc、libstdc++、tzdata
-> 使用非 root dial 用户运行
```
Dockerfile 通过 `TARGETARCH` 选择 Bun compile target。
| `TARGETARCH` | `BUN_TARGET` |
| ------------ | ---------------------- |
| `amd64` | `bun-linux-x64-musl` |
| `arm64` | `bun-linux-arm64-musl` |
## Release
```bash
bun run release
bun run release --target linux-x64
bun run release --target linux-x64,windows-x64,darwin-arm64
```
release 流程:
```text
1. Vite build -> dist/web/
2. Code generation -> .build/
3. 多目标 Bun compile -> dist/release/binaries/
4. tar.gz 打包 -> dist/release/packages/
```
支持的平台见 [用户部署文档](../user/deployment.md#跨平台发布包)。
## 脚本说明
| 脚本 | 文件 | 说明 |
| ---------------------- | ----------------------------------- | ------------------------------ |
| `bun run dev` | `scripts/dev.ts` | 双进程开发服务 |
| `bun run dev:server` | `src/server/dev.ts` | 仅启动后端 API server |
| `bun run dev:web` | Vite CLI | 仅启动 Vite dev server |
| `bun run build` | `scripts/build.ts` | Vite -> codegen -> Bun compile |
| `bun run release` | `scripts/release.ts` | 多目标交叉编译和打包 |
| `bun run schema` | `scripts/generate-config-schema.ts` | 生成配置 JSON Schema |
| `bun run schema:check` | `scripts/generate-config-schema.ts` | 检查配置 JSON Schema 同步 |
| `bun run clean` | `scripts/clean.ts` | 清理构建缓存与临时文件 |
## 维护约定
- `scripts/build-common.ts` 中的 import specifier 输出必须使用 `/` 分隔符。
- 跨平台路径测试不得用当前平台 `path.sep` 伪装其他平台,应使用 `node:path.win32` 或等价注入方式模拟。
- 如本地 Docker 环境不支持 buildx 或多架构模拟,需在变更记录中说明未执行原因。

View File

@@ -0,0 +1,161 @@
# Checker 开发
Checker 是 DiAL 的核心扩展单元。每个 checker 是 `src/server/checker/runner/<type>/` 下的自包含目录,包含该 checker 所需的类型、schema、校验、执行逻辑和断言。
新增或修改 checker 前请同时阅读 [`../../CONTRIBUTING.md`](../../CONTRIBUTING.md)、[配置文件](../user/configuration.md)、[校验规则](../user/expectations.md) 和 [Checker 用户文档](../user/checkers/README.md)。
## 架构目标
```text
checkerRegistry
├── runner/index.ts
├── schema/builder.ts
├── schema/validate.ts
├── config-loader.ts
├── engine.ts
└── store.ts
```
注册后,中间层通过 registry 自动委托 schema 生成、契约校验、配置 resolve、执行和序列化。新增 checker 不应在中间层新增 `switch/case` 或类型分支。
## 标准文件结构
| 文件 | 职责 |
| ------------- | ----------------------------------------------------- |
| `index.ts` | 模块入口re-export Checker 类 |
| `types.ts` | Checker 专属类型 |
| `schema.ts` | TypeBox 契约 schema包含 config 和 expect |
| `validate.ts` | 启动期语义校验 |
| `execute.ts` | Checker 类,实现 resolve、execute、serialize |
| `expect.ts` | Checker 专用断言函数 |
| 其他文件 | 协议解析、编码、provider 适配、平台命令封装等专属逻辑 |
## 类型定义
`types.ts` 中定义:
- `RawXxxTargetConfig`
- `RawXxxExpectConfig`
- `ResolvedXxxExpectConfig`
- `ResolvedXxxTarget extends ResolvedTargetBase`
不需要修改顶层 `checker/types.ts`。base interface 使用 index signature 支持扩展。
## Schema
checker 必须提供 `CheckerSchemas`,包含 Authoring 和 Normalized 两套 config/expect 片段。Authoring 描述用户 YAML 可写 DSLNormalized 描述 normalizer 输出。
常用 fragments
| Fragment | 用途 |
| ----------------------------------- | ------------------------- |
| `durationSchema` | 时长字符串 |
| `sizeSchema` | 大小单位 |
| `statusCodePatternSchema` | HTTP 状态码或范围 |
| `stringMapSchema` | headers、env 等字符串映射 |
| `createValueMatcherSchema()` | ValueMatcher |
| `createContentExpectationsSchema()` | ContentExpectations |
| `createKeyedExpectationsSchema()` | KeyedExpectations |
默认对象策略为 `additionalProperties: false`。只有明确的动态键值表可以开放任意键名。
## 语义校验
`validate.ts` 中实现 JSON Schema 无法表达的规则,统一返回 `ConfigValidationIssue[]`,不要直接拼接最终错误字符串。
共享校验工具包括:
| 函数 | 用途 |
| -------------------------------- | ---------------------------- |
| `validateRawValueExpectation` | 校验 Raw ValueExpectation |
| `validateRawContentExpectations` | 校验 ContentExpectations |
| `validateRawKeyedExpectations` | 校验 KeyedExpectations |
| `validateJsonPath` | 校验项目支持的 JSONPath 子集 |
| `isJsonValue` | 判断合法 JSON value |
## resolve 规范
`resolve()` 只做内置默认值填充、路径解析、单位转换,不执行校验。输入已经通过 Normalized schema 和语义校验expect 已是 normalized 形态。
```typescript
const expect = target.expect as ResolvedXxxExpectConfig | undefined;
const resolvedExpect: ResolvedXxxExpectConfig = expect
? { ...expect, status: expect.status ?? [200] }
: { status: [200] };
```
返回值使用 `satisfies ResolvedXxxTarget` 确保类型正确。
## execute 规范
- 始终记录 `timestamp``start = performance.now()`
- 通过 `ctx.signal` 支持超时取消。
- 首个 expect 失败即停止,返回带 `failure` 的结果。
- 成功时 `failure: null, matched: true`
- 异常时使用 `errorFailure()`
- 不匹配时使用 `mismatchFailure()`
- `expected` 参数应传用户可读值,必要时使用 `displayValueExpectation()`
## expect 字段选择
| 场景 | 模型 |
| ------------------------------------ | ------------------- |
| 状态类结果且集合小而稳定 | enum 或 boolean |
| 单值数字指标或字符串元数据 | ValueMatcher |
| 文本、JSON、HTML、XML 或半结构化内容 | ContentExpectations |
| 动态键值表 | KeyedExpectations |
不要为了统一而把状态类字段改成 ValueMatcher。一个 expect 字段只能对应一种断言模型。
## 注册
1. 创建 `src/server/checker/runner/<type>/index.ts`
2.`src/server/checker/runner/index.ts` 添加导入。
3. 在 registry 初始化数组中添加 checker 实例。
注册后schema builder、validate、config-loader、engine、store 会自动按 registry 分发。
## 测试要求
测试文件放在 `tests/server/checker/runner/<type>/`,结构镜像源文件。
| 测试类别 | 覆盖内容 |
| ------------ | ---------------------------------------- |
| 契约测试 | TypeBox schema 与 JSON Schema 导出一致性 |
| 语义校验测试 | 合法和非法配置 |
| resolve 测试 | 默认值合并、路径解析、单位转换 |
| execute 测试 | 成功、失败、超时、expect 组合 |
| 注册测试 | registry 注册行为 |
| 配置加载测试 | 含新 checker 的 YAML 完整加载流程 |
## 文档和 schema 更新
新增或修改 checker 时通常需要更新:
- `probes.example.yaml`
- `probe-config.schema.json`,通过 `bun run schema` 生成
- `docs/user/checkers/<type>.md`
- `docs/user/checkers/README.md`
- `docs/user/expectations.md`,仅当断言模型或通用规则变化
- `docs/development/checker-development.md`,仅当开发机制变化
- `CONTRIBUTING.md`,仅当贡献流程或 checklist 变化
## 完成检查清单
```text
□ src/server/checker/runner/<type>/types.ts
□ src/server/checker/runner/<type>/schema.ts
□ src/server/checker/runner/<type>/validate.ts
□ src/server/checker/runner/<type>/execute.ts
□ src/server/checker/runner/<type>/expect.ts
□ src/server/checker/runner/<type>/index.ts
□ src/server/checker/runner/index.ts
□ tests/server/checker/runner/<type>/
□ probes.example.yaml
□ probe-config.schema.json
□ docs/user/checkers/<type>.md
□ bun run schema
□ bun run schema:check
□ bun run check
□ bun run verify
```

View File

@@ -0,0 +1,117 @@
# 前端开发
## 技术栈
| 层面 | 技术 | 用途 |
| ------ | ------------------------------------- | ---------------------------------------------- |
| 框架 | React 19 | UI 组件开发 |
| 构建 | Bun HTML import + Vite dev server | 开发服务与生产构建 |
| 语言 | TypeScript 6 | 类型安全 |
| UI 库 | TDesign React + tdesign-icons-react | UI 组件与图标 |
| 数据层 | TanStack Query + React Query Devtools | 服务端状态管理与自动轮询 |
| 图表 | Recharts | 拨测趋势图 |
| 动画 | @number-flow/react | 倒计时数字滚动过渡 |
| 路由 | 无 | 单页面 Dashboard通过 Drawer/Tab 做页面内导航 |
不引入 React Router 或额外状态管理库。TanStack Query 承担服务端状态,组件内状态使用 `useState`
## 组件树与数据流
```text
main.tsx
└── StrictMode
└── ErrorBoundary
└── QueryClientProvider
├── App
│ ├── useThemePreference()
│ ├── useDashboard(refreshInterval)
│ ├── SummaryCards
│ └── TargetBoard
│ └── TargetGroup[]
│ └── PrimaryTable
│ └── TargetDetailDrawer
│ └── useTargetDetail()
│ ├── OverviewTab
│ └── HistoryTab
└── ReactQueryDevtools
```
## TanStack Query 规范
Query key 使用 structured array排序为 scope -> id -> 参数。
```typescript
const queryKeys = {
dashboard: () => ["dashboard", "24h", 30] as const,
meta: () => ["meta"] as const,
metrics: (targetId: number, from: string, to: string, bucket: "auto" | MetricsBucket) =>
["metrics", targetId, from, to, bucket] as const,
history: (targetId: number, from: string, to: string, page: number) => ["history", targetId, from, to, page] as const,
};
```
全局面板级查询可持续刷新,详情级查询必须按 Drawer 状态和 Tab 状态条件启用。
## fetch 封装
统一使用 `fetch`,不引入 axios。错误抛异常由 TanStack Query 的 `error` 状态承接。
```typescript
async function fetchJson<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json() as Promise<T>;
}
```
## 组件开发规范
- 每个 React 组件一个 `.tsx` 文件,文件名使用 PascalCase。
- 组件 props 定义为 `interface XxxProps`,紧邻组件函数声明。
- 类型从 `../../shared/api` 导入,使用 `import type`
- 展示组件放在 `components/`,通过 props 接收数据,通过回调返回事件。
- 容器逻辑放在 hooks 中,组件只做数据消费。
- 列定义、排序器、筛选器、颜色阈值等常量放在 `constants/`
- 时间处理等纯函数放在 `utils/`
## 现有组件
| 组件 | 用途 |
| -------------------- | ----------------------------------------------------------------- |
| `App` | 根组件Layout + HeadMenu 骨架、主题模式、刷新控制、Skeleton 加载 |
| `ErrorBoundary` | React 错误边界 |
| `SummaryCards` | 总览统计卡片 |
| `TargetBoard` | 按分组渲染目标表格列表 |
| `TargetGroup` | 单个分组 Card + PrimaryTable |
| `TargetDetailDrawer` | 目标详情抽屉 |
| `OverviewTab` | 目标详情概览 |
| `HistoryTab` | 目标历史记录表格和分页 |
| `TrendChart` | 趋势折线图 |
| `StatusDot` | 圆形状态指示点 |
| `StatusBar` | 最近采样状态条 |
| `RefreshCountdown` | Header 刷新倒计时和手动刷新按钮 |
## 样式规范
前端基于 TDesign React 构建 UI样式开发优先级
1. TDesign 组件
2. TDesign 组件 props
3. TDesign CSS tokens`--td-*`
4. `styles.css` CSS 类
5. 自行开发组件
红线:
- 严禁在组件中使用 `style` 属性内联调整样式。
- 严禁通过 CSS 覆盖 TDesign 组件内部类名。
- 严禁使用 `!important`
- 颜色统一使用 TDesign CSS tokens不使用硬编码色值。
## 前端测试
- 测试目录为 `tests/web/`,结构对应 `src/web/`
- 重点测试 `constants/` 中的纯函数。
- 组件测试使用 jsdom 和 `@testing-library/react`
- 测试用户行为而非实现细节。
- 只 mock 系统边界,例如 `fetch`

View File

@@ -0,0 +1,93 @@
# 测试与质量
## 质量命令
| 命令 | 说明 |
| ---------------------- | -------------------------------------------------------------- |
| `bun run lint` | ESLint 检查含类型感知规则、导入排序、导入验证、Prettier 格式 |
| `bun run format` | Prettier 自动格式化 |
| `bun run schema:check` | 检查 `probe-config.schema.json` 是否与 TypeBox fragments 同步 |
| `bun run typecheck` | TypeScript 类型检查 |
| `bun test` | 运行所有测试 |
| `bun run check` | `schema:check + typecheck + lint + test` |
| `bun run verify` | `check + build` |
## ESLint
配置文件:`eslint.config.js`
| 配置来源 | 用途 |
| -------------------------------------------- | ---------------------------------------- |
| `@eslint/js` recommended | JavaScript 基础规则 |
| `typescript-eslint` recommended-type-checked | TypeScript 类型感知规则 |
| `typescript-eslint` stylistic-type-checked | TypeScript 风格规则 |
| `eslint-plugin-perfectionist` | 导入语句和命名导出排序 |
| `eslint-plugin-import` | 导入路径验证、循环依赖检测、重复导入合并 |
| `eslint-plugin-prettier` | 将 Prettier 格式集成为 ESLint 规则 |
后端运行时代码禁止直接使用 `console.*`,请通过注入的 Logger 实例输出日志。
## Prettier
配置文件:`.prettierrc.json`。显式声明格式化参数,包括 `printWidth: 120``semi: true``singleQuote: false``trailingComma: "all"``endOfLine: "lf"`
## TypeScript 严格标志
| 标志 | 值 | 说明 |
| ------------------------------------ | ----- | ------------------------------- |
| `strict` | true | 全局严格模式 |
| `noUnusedLocals` | true | 未使用局部变量视为错误 |
| `noUnusedParameters` | false | 保留关闭 |
| `noPropertyAccessFromIndexSignature` | true | 索引签名必须用括号访问 |
| `noUncheckedIndexedAccess` | true | 数组和 Map 访问必须运行时检查 |
| `noImplicitOverride` | true | 覆盖父类方法必须显式 `override` |
| `verbatimModuleSyntax` | true | 强制 `import type` 纯类型导入 |
## Git hooks
| Hook | 行为 |
| ------------ | ------------------------------------------ |
| `pre-commit` | lint-staged 对变更文件运行 eslint/prettier |
| `commit-msg` | commitlint 校验提交信息格式 |
提交信息格式为 `类型: 简短描述`,类型限定为 `feat``fix``refactor``docs``style``test``chore`
## 测试分层
| 层级 | 覆盖范围 | 位置 |
| -------- | ---------------------- | ----------------------------------------------------------------------------- |
| 单元测试 | 后端函数、纯函数、常量 | `tests/server/**/*.test.ts``tests/web/{constants,utils,hooks}/**/*.test.ts` |
| 组件测试 | React 组件渲染和交互 | `tests/web/components/**/*.test.tsx` |
## 测试命令
```bash
bun test
bun test tests/server
bun test tests/web
bun run check
bun run verify
```
## 组件测试环境
组件测试使用 jsdom配置位于 `tests/setup.ts`,通过 `bunfig.toml` preload 加载。
包含的 polyfill 和 mock
- ResizeObserver
- IntersectionObserver
- matchMedia
- attachEvent
- recharts 图表 mock
## 编写规范
- 优先使用 `@testing-library/react` 的语义化查询。
- 测试用户行为而非实现细节。
- 只 mock 系统边界。
- 使用真实的 QueryClientProvider 包裹组件。
- 组件测试文件命名为 `tests/web/components/ComponentName.test.tsx`
- 异步错误断言使用 helper 或显式 try/catch避免依赖 Bun `expect(...).rejects``await-thenable` 规则的类型不匹配。
- polyfill 中的 intentional no-op 使用显式可解释写法。
-`process.exit` 等系统 API 使用 `spyOn` 受控 mock。