1
0

docs: 添加 checker 内聚化重构方案及归档历史变更

新增 refactor-checker-cohesion 变更提案,包含 proposal、design、
specs 和 tasks,定义 checker 目录内聚结构规范。同时归档已完成的
历史变更记录。
This commit is contained in:
2026-05-13 13:30:05 +08:00
parent aade0bbff7
commit c396c29402
6 changed files with 534 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
## ADDED Requirements
### Requirement: Checker 目录内聚结构
每个 checker SHALL 以独立目录形式存在于 `src/server/checker/runner/<type>/`,目录内 SHALL 包含该 checker 的全部类型定义、schema 声明、语义校验、执行逻辑和断言逻辑。
#### Scenario: HTTP checker 目录完整性
- **WHEN** 开发者查看 `src/server/checker/runner/http/` 目录
- **THEN** 该目录 SHALL 包含 `index.ts``types.ts``schema.ts``execute.ts``expect.ts``body.ts``validate.ts`
#### Scenario: Command checker 目录完整性
- **WHEN** 开发者查看 `src/server/checker/runner/command/` 目录
- **THEN** 该目录 SHALL 包含 `index.ts``types.ts``schema.ts``execute.ts``expect.ts``text.ts``validate.ts`
#### Scenario: 新增 checker 最小改动
- **WHEN** 开发者新增一个 checker 类型(如 dns
- **THEN** 开发者 SHALL 只需创建 `src/server/checker/runner/dns/` 目录及其内部文件,并在 `runner/index.ts` 注册列表中添加一行 import 和一行数组项
### Requirement: Checker 目录文件职责
每个 checker 目录内的文件 SHALL 遵循统一的职责划分。
#### Scenario: index.ts 仅做 re-export
- **WHEN** 开发者查看某 checker 的 `index.ts`
- **THEN** 该文件 SHALL 仅包含对 `execute.ts` 中 Checker 类的 re-export不包含任何逻辑
#### Scenario: types.ts 包含该 checker 全部专属类型
- **WHEN** 开发者需要该 checker 的配置类型、resolved 类型或 expect 类型
- **THEN** 这些类型 SHALL 全部定义在该 checker 目录的 `types.ts` 中,不在顶层 `types.ts`
#### Scenario: schema.ts 包含 TypeBox schema 定义
- **WHEN** 开发者需要该 checker 的 config/defaults/expect schema
- **THEN** 这些 schema SHALL 定义在该 checker 目录的 `schema.ts`
#### Scenario: execute.ts 包含 Checker 类实现
- **WHEN** 开发者需要查看该 checker 的执行逻辑
- **THEN** Checker 类(实现 CheckerDefinition 接口SHALL 定义在 `execute.ts`
#### Scenario: validate.ts 包含该 checker 全部语义校验
- **WHEN** 开发者需要查看该 checker 的配置校验逻辑
- **THEN** 该 checker 专属的语义校验函数 SHALL 全部定义在 `validate.ts`
#### Scenario: expect.ts 包含该 checker 专属断言
- **WHEN** 开发者需要查看该 checker 的断言逻辑
- **THEN** 该 checker 专属的断言函数 SHALL 定义在 `expect.ts`
### Requirement: 断言基础设施目录
系统 SHALL 在 `src/server/checker/expect/` 目录中提供所有 checker 共享的断言基础设施。
#### Scenario: expect 共享类型位置
- **WHEN** 任何 checker 需要使用断言相关的共享类型(如 `ExpectResult`
- **THEN** 这些类型 SHALL 从 `src/server/checker/expect/types.ts` 导入
#### Scenario: operator 断言引擎位置
- **WHEN** 任何 checker 需要使用 `applyOperator``evaluateJsonPath``checkExpectValue`
- **THEN** 这些函数 SHALL 从 `src/server/checker/expect/operator.ts` 导入
#### Scenario: duration 断言位置
- **WHEN** 任何 checker 需要使用 `checkDuration`
- **THEN** 该函数 SHALL 从 `src/server/checker/expect/duration.ts` 导入
#### Scenario: failure 构造器位置
- **WHEN** 任何 checker 需要使用 `errorFailure``mismatchFailure`
- **THEN** 这些函数 SHALL 从 `src/server/checker/expect/failure.ts` 导入
#### Scenario: operator 校验位置
- **WHEN** 任何 checker 的 validate 需要使用 `validateOperatorObject`
- **THEN** 该函数 SHALL 从 `src/server/checker/expect/validate-operator.ts` 导入
#### Scenario: ExpectResult 类型位置
- **WHEN** 任何 checker 需要使用 `ExpectResult` 类型
- **THEN** 该类型 SHALL 从 `src/server/checker/expect/types.ts` 导入
### Requirement: Schema 目录结构
系统 SHALL 在 `src/server/checker/schema/` 目录中组织配置 schema 体系,替代原 `config-contract/` 目录。
#### Scenario: schema 目录包含 builder
- **WHEN** 系统需要从 registry 动态构建整体配置 schema
- **THEN** 该逻辑 SHALL 位于 `src/server/checker/schema/builder.ts`
#### Scenario: schema 目录包含 fragments
- **WHEN** checker 的 schema.ts 需要引用共享 schema 片段(如 durationSchema、sizeSchema
- **THEN** 这些片段 SHALL 从 `src/server/checker/schema/fragments.ts` 导入
#### Scenario: schema 目录包含 Ajv 校验入口
- **WHEN** config-loader 需要执行契约校验
- **THEN** 校验入口 SHALL 位于 `src/server/checker/schema/validate.ts`
#### Scenario: schema 目录包含 issue 工具
- **WHEN** 任何校验逻辑需要构造 ConfigValidationIssue
- **THEN** issue 类型和工具函数 SHALL 从 `src/server/checker/schema/issues.ts` 导入
### Requirement: 工具函数归集
系统 SHALL 在 `src/server/checker/utils.ts` 中提供纯工具函数。
#### Scenario: parseSize 位置
- **WHEN** 任何模块需要解析 size 字符串(如 "100MB"
- **THEN** `parseSize` SHALL 从 `src/server/checker/utils.ts` 导入
#### Scenario: parseDuration 位置
- **WHEN** 任何模块需要解析 duration 字符串(如 "30s"
- **THEN** `parseDuration` SHALL 从 `src/server/checker/utils.ts` 导入
### Requirement: 依赖方向约束
checker 系统内的模块依赖 SHALL 遵循严格的分层方向。
#### Scenario: checker 之间无横向依赖
- **WHEN** 开发者查看任何 checker 目录的 import 语句
- **THEN** 该 checker SHALL NOT 导入其他 checker 目录的任何模块
#### Scenario: expect/ 不依赖 runner/
- **WHEN** 开发者查看 `expect/` 目录的 import 语句
- **THEN** `expect/` 中的文件 SHALL NOT 导入 `runner/` 目录的任何模块
#### Scenario: schema/ 不依赖 runner/ 的具体 checker
- **WHEN** 开发者查看 `schema/` 目录的 import 语句
- **THEN** `schema/` 中的文件 SHALL 仅通过 `CheckerDefinition` 接口与 checker 交互SHALL NOT 直接导入具体 checker 目录
### Requirement: 显式注册列表
系统 SHALL 在 `src/server/checker/runner/index.ts` 中使用显式 import 列表注册所有 checker。
#### Scenario: 注册入口结构
- **WHEN** 开发者查看 `runner/index.ts`
- **THEN** 该文件 SHALL 包含所有 checker 的静态 import 和一个 checker 实例数组,通过循环调用 `registry.register()` 完成注册
#### Scenario: 新增 checker 注册
- **WHEN** 开发者新增一个 checker
- **THEN** 开发者 SHALL 在 `runner/index.ts` 中添加一行 import 和一行数组项,无需修改其他文件
### Requirement: 公共类型文件瘦身
顶层 `src/server/checker/types.ts` SHALL 仅保留跨 checker 共享的 base 类型和存储相关类型。
#### Scenario: types.ts 不包含 checker 专属类型
- **WHEN** 开发者查看顶层 `types.ts`
- **THEN** 该文件 SHALL NOT 包含 `HttpTargetConfig``ResolvedHttpTarget``CommandExpectConfig``BodyRule``TextRule` 等 checker 专属类型
#### Scenario: types.ts 保留 base 类型
- **WHEN** 开发者查看顶层 `types.ts`
- **THEN** 该文件 SHALL 包含 `ResolvedTargetBase``RawTargetConfig``DefaultsConfig``CheckResult``ExpectOperator``CheckFailure``StoredTarget``StoredCheckResult``JsonValue` 等公共类型
#### Scenario: ResolvedTargetBase 替代联合类型
- **WHEN** engine、store、config-loader 需要引用 resolved target 类型
- **THEN** 这些模块 SHALL 使用 `ResolvedTargetBase` interface不再使用硬编码联合类型
#### Scenario: DefaultsConfig 为宽松 base 形式
- **WHEN** 开发者查看顶层 `types.ts` 中的 `DefaultsConfig`
- **THEN** 该 interface SHALL 仅包含公共字段(`interval?``timeout?`)和 index signature`[checkerKey: string]: unknown`SHALL NOT 包含 `command?``http?` 等 checker 专属字段
#### Scenario: 各 checker validate 自行 narrow defaults
- **WHEN** checker 的 `validate()` 方法需要访问自身的 defaults 配置
- **THEN** checker SHALL 从 `DefaultsConfig` 中通过 `defaults[configKey]` 获取并自行 narrow 为具体类型

View File

@@ -0,0 +1,59 @@
## MODIFIED Requirements
### Requirement: Checker 接口定义
系统 SHALL 在 `src/server/checker/runner/types.ts` 中定义面向扩展的 `CheckerDefinition`,包含 `type``configKey`、TypeBox 配置契约、启动期语义校验、`resolve``execute``serialize` 成员。`CheckerContext` SHALL 包含引擎注入的 `AbortSignal`。接口方法的参数和返回值 SHALL 使用 base interface 类型(`RawTargetConfig``ResolvedTargetBase`),各 checker 实现内部自行 narrow 到具体类型。
#### Scenario: Checker 接口包含必要方法
- **WHEN** 开发者实现一个新的 Checker
- **THEN** 该实现 MUST 提供 `type`(字符串标识)、`configKey`配置分组名、TypeBox 配置契约、启动期语义校验、`resolve(target, context)`(解析配置并填充默认值)、`execute(target, ctx)`(执行探测返回 CheckResult`serialize(target)`(返回 target 展示文本和 config JSON
#### Scenario: CheckerContext 注入 signal
- **WHEN** 引擎调用 `checker.execute(target, ctx)`
- **THEN** `ctx.signal` SHALL 是一个由引擎创建的 `AbortSignal`,在超时或引擎关闭时 abort
#### Scenario: resolve 不承担通用契约校验
- **WHEN** config-loader 调用 checker.resolve()
- **THEN** checker.resolve() SHALL 假定配置已经通过 TypeBox/Ajv 契约校验和启动期语义校验,只负责默认值填充、路径解析和领域配置转换
#### Scenario: type 与 configKey 默认一致
- **WHEN** checker 定义 `type: "tcp"`
- **THEN** checker 的 `configKey` SHALL 默认使用 `"tcp"`,对应 target 的 `tcp` 分组和 defaults.tcp 分组
#### Scenario: 接口方法使用 base 类型
- **WHEN** 开发者查看 `CheckerDefinition` 接口签名
- **THEN** `resolve` 的参数 SHALL 为 `RawTargetConfig`,返回值 SHALL 为 `ResolvedTargetBase``execute` 的参数 SHALL 为 `ResolvedTargetBase``serialize` 的参数 SHALL 为 `ResolvedTargetBase`
#### Scenario: checker 实现内部 narrow
- **WHEN** HttpChecker 的 execute 方法接收 `ResolvedTargetBase` 参数
- **THEN** 方法内部 SHALL 将参数 narrow 为 `ResolvedHttpTarget`(通过 type assertion然后使用具体类型的字段
### Requirement: 共享 expect 断言函数
系统 SHALL 在 `src/server/checker/expect/` 中提供可被多个 checker 复用的 expect 函数。checker 专用的 expect 函数 SHALL 保留在各自子包内。仅被单个 checker 使用的断言模块 SHALL 位于该 checker 目录内。
#### Scenario: 共享 duration 断言
- **WHEN** 任何 checker 需要校验执行耗时
- **THEN** SHALL 调用 `expect/duration.ts` 中的 `checkDuration(durationMs, maxDurationMs?)`,返回统一的 `ExpectResult`
#### Scenario: 共享 operator 断言
- **WHEN** 任何 checker 需要对值执行 operator 匹配
- **THEN** SHALL 调用 `expect/operator.ts` 中的 `applyOperator(actual, op)`
#### Scenario: 共享 failure 构造
- **WHEN** 任何 checker 需要构造 CheckFailure 对象
- **THEN** SHALL 调用 `expect/failure.ts` 中的 `errorFailure()``mismatchFailure()`
#### Scenario: HTTP body 断言位于 HTTP 目录
- **WHEN** HTTP checker 需要对响应体执行 contains/regex/json/css/xpath 规则校验
- **THEN** SHALL 调用 `runner/http/body.ts` 中的 `checkBodyExpect(body, rules)`
#### Scenario: Command text 断言位于 Command 目录
- **WHEN** Command checker 需要对 stdout/stderr 执行文本规则校验
- **THEN** SHALL 调用 `runner/command/text.ts` 中的 `checkTextRules(text, rules, phase)`
#### Scenario: HTTP 专用 expect
- **WHEN** HTTP checker 需要校验响应状态码和响应头
- **THEN** SHALL 调用 `runner/http/expect.ts` 中的 `checkStatus()``checkHeaders()`
#### Scenario: Command 专用 expect
- **WHEN** Command checker 需要校验退出码
- **THEN** SHALL 调用 `runner/command/expect.ts` 中的 `checkExitCode()`