diff --git a/openspec/changes/refactor-checker-cohesion/.openspec.yaml b/openspec/changes/refactor-checker-cohesion/.openspec.yaml deleted file mode 100644 index 93831bd..0000000 --- a/openspec/changes/refactor-checker-cohesion/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-05-13 diff --git a/openspec/changes/refactor-checker-cohesion/design.md b/openspec/changes/refactor-checker-cohesion/design.md deleted file mode 100644 index 322d68e..0000000 --- a/openspec/changes/refactor-checker-cohesion/design.md +++ /dev/null @@ -1,232 +0,0 @@ -## Context - -当前 `src/server/checker/` 的代码组织存在内聚性不足的问题: - -- 顶层 `types.ts` 混合了所有 checker 的类型定义(`HttpTargetConfig`、`ResolvedHttpTarget`、`CommandExpectConfig` 等),形成硬编码联合类型 `ResolvedTarget = ResolvedHttpTarget | ResolvedCommandTarget` -- `runner/shared/` 中混合了真正跨 checker 共享的断言基础设施和仅单个 checker 使用的模块 -- `config-contract/` 命名不直观,内部 `schema.ts` 与目录名语义重叠 -- 新增 checker 需要修改 3-4 个文件(顶层 types、联合类型、注册入口、可能还有 shared) - -项目核心是 checker 系统,需要让每个 checker 的代码尽可能内聚,降低新增和维护成本。 - -## Goals / Non-Goals - -**Goals:** - -- 新增 checker 只需:创建一个目录 + 在注册列表加一行 import -- 每个 checker 目录包含完整的类型、schema、校验、执行逻辑 -- 共享断言基础设施有明确的物理位置和语义命名 -- 依赖方向清晰:checker → expect/(断言基础设施)、schema/fragments(schema 片段)、utils(工具函数) - -**Non-Goals:** - -- 不改变任何运行时行为、配置文件格式、API 接口 -- 不引入自动目录扫描机制(保持显式注册) -- 不改变测试覆盖范围(只更新 import 路径) - -## Decisions - -### Decision 1: ResolvedTarget 和 TargetConfig 改为 base interface - -**选择**: 删除硬编码联合类型,改为 base interface + 各 checker 内部 narrow - -**理由**: engine、store、config-loader 从来不关心具体 checker 类型——它们只用 base 字段(type、name、group、intervalMs、timeoutMs)+ 通过 registry dispatch。各 checker 的 execute/resolve/serialize 内部第一行就是 `as ResolvedXxxTarget`,联合类型对它们没有实际约束价值。 - -同理,`DefaultsConfig` 当前也是硬编码联合(`command?: CommandDefaultsConfig; http?: HttpDefaultsConfig`),新增 checker 仍需改这个 interface。改为宽松 base 形式: - -```typescript -export interface DefaultsConfig { - interval?: string; - timeout?: string; - [checkerKey: string]: unknown; -} -``` - -各 checker 的 `validate()` 方法接收 `DefaultsConfig` 后自行 narrow(如 `defaults["http"] as HttpDefaultsConfig`)。`CheckerValidationInput` 和 `ResolveContext` 中的 `defaults` 字段类型保持为 `DefaultsConfig`,对外部透明。 - -**替代方案**: 保留联合但用 barrel 自动聚合——仍需改文件,不够彻底。 - -**具体设计**: - -```typescript -// checker/types.ts — 公共 base -export interface ResolvedTargetBase { - type: string; - name: string; - group: string; - intervalMs: number; - timeoutMs: number; - expect?: unknown; -} - -export interface RawTargetConfig { - type: string; - name: string; - group?: string; - interval?: string; - timeout?: string; - expect?: unknown; - [configKey: string]: unknown; -} - -// runner/http/types.ts — HTTP 专属 -export interface ResolvedHttpTarget extends ResolvedTargetBase { - type: "http"; - http: ResolvedHttpConfig; - expect?: HttpExpectConfig; -} -``` - -### Decision 2: 显式列表注册 - -**选择**: `runner/index.ts` 维护 import 列表,新增 checker 加一行 - -**理由**: Bun bundler 和 tree-shaking 依赖静态 import;运行时扫描目录引入不确定性(临时文件、.bak 等);一行 import 的成本几乎为零。 - -**具体设计**: - -```typescript -// runner/index.ts -import { CheckerRegistry } from "./registry"; -import { HttpChecker } from "./http"; -import { CommandChecker } from "./command"; - -const checkers = [ - new HttpChecker(), - new CommandChecker(), -]; - -export function createDefaultCheckerRegistry(): CheckerRegistry { - const registry = new CheckerRegistry(); - for (const checker of checkers) { - registry.register(checker); - } - return registry; -} - -export const checkerRegistry = createDefaultCheckerRegistry(); -``` - -### Decision 3: 各 checker 的 index.ts 仅做 re-export - -**选择**: class 定义在 `execute.ts`,`index.ts` 只做 `export { HttpChecker } from "./execute"` - -**理由**: 保持单一职责——`execute.ts` 专注执行逻辑,`index.ts` 是对外入口。 - -### Decision 4: runner/shared/ 拆分策略 - -**选择**: 按实际使用情况拆分为三个去向 - -| 原文件 | 去向 | 理由 | -|--------|------|------| -| `operator.ts` | `checker/expect/operator.ts` | 所有 checker 的 expect 最终都走这里 | -| `duration.ts` | `checker/expect/duration.ts` | 任何 checker 都可能有 maxDurationMs | -| `failure.ts` | `checker/expect/failure.ts` | 构造 CheckFailure,所有 checker 共用 | -| `validate.ts` 中 `validateOperatorObject` | `checker/expect/validate-operator.ts` | 通用 operator 校验 | -| `body.ts` | `runner/http/body.ts` | 仅 HTTP 使用 | -| `text.ts` | `runner/command/text.ts` | 仅 Command 使用 | -| `validate.ts` 中 `validateBodyRules` 等 | `runner/http/validate.ts` | HTTP 专属校验 | -| `validate.ts` 中 `validateTextRules` | `runner/command/validate.ts` | Command 专属校验 | - -**理由**: `expect/` 命名比 `shared/` 更能表达语义——这些是断言系统的基础设施。仅单个 checker 使用的模块搬入对应目录实现真正内聚。 - -### Decision 5: config-contract/ 重命名为 schema/ - -**选择**: 目录改名 `schema/`,内部 `schema.ts` 改名 `builder.ts` - -**理由**: "config-contract" 过于抽象,`schema/` 直接表达"这里是 schema 定义和校验"。内部 `schema.ts` 与目录名冲突,改为 `builder.ts` 表达"从 registry 动态构建整体 schema"的职责。 - -### Decision 6: 纯工具函数归入 utils.ts - -**选择**: `size.ts` 的 `parseSize` 和 `config-loader.ts` 中的 `parseDuration` 合并到 `checker/utils.ts` - -**理由**: 这些是无状态的纯解析函数,不属于任何特定领域。统一放置便于复用。 - -### Decision 7: 文件重命名 - -| 原名 | 新名 | 理由 | -|------|------|------| -| 各 checker 的 `runner.ts` | `execute.ts` | 避免和目录名 `runner/` 混淆 | -| 各 checker 的 `contract.ts` | `schema.ts` | 和顶层 `schema/` 目录呼应,统一术语 | - -## 最终目录结构 - -``` -src/server/checker/ -├── index.ts -├── engine.ts -├── store.ts -├── config-loader.ts -├── types.ts ← ResolvedTargetBase, RawTargetConfig, CheckResult, -│ ExpectOperator, CheckFailure, StoredTarget 等公共类型 -├── utils.ts ← parseSize, parseDuration -│ -├── expect/ ← 断言基础设施(所有 checker 共享) -│ ├── types.ts ← ExpectResult 等共享类型 -│ ├── operator.ts ← applyOperator, evaluateJsonPath, checkExpectValue -│ ├── duration.ts ← checkDuration -│ ├── failure.ts ← errorFailure, mismatchFailure, truncateActual -│ └── validate-operator.ts ← validateOperatorObject, isJsonValue -│ -├── schema/ ← 配置 schema 体系 -│ ├── builder.ts ← createProbeConfigSchema(从 registry 动态构建) -│ ├── fragments.ts ← 共享 schema 片段 -│ ├── validate.ts ← Ajv 校验入口 -│ ├── issues.ts ← issue 类型和工具 -│ ├── types.ts -│ └── export.ts -│ -└── runner/ - ├── index.ts ← 显式注册列表 - ├── registry.ts ← CheckerRegistry - ├── types.ts ← CheckerDefinition 接口(使用 base 类型) - │ - ├── http/ - │ ├── index.ts ← re-export HttpChecker - │ ├── types.ts ← HttpTargetConfig, ResolvedHttpTarget, HttpExpectConfig 等 - │ ├── schema.ts ← TypeBox schemas - │ ├── execute.ts ← class HttpChecker - │ ├── expect.ts ← checkStatus, checkHeaders - │ ├── body.ts ← checkBodyExpect(从 shared 搬来) - │ └── validate.ts ← validateHttpConfig + validateBodyRules 等 - │ - ├── command/ - │ ├── index.ts ← re-export CommandChecker - │ ├── types.ts ← CommandTargetConfig, ResolvedCommandTarget 等 - │ ├── schema.ts ← TypeBox schemas - │ ├── execute.ts ← class CommandChecker - │ ├── expect.ts ← checkExitCode - │ ├── text.ts ← checkTextRules(从 shared 搬来) - │ └── validate.ts ← validateCommandConfig + validateTextRules - │ - └── [future-checker]/ ← 新增 checker 模板 - ├── index.ts - ├── types.ts - ├── schema.ts - ├── execute.ts - ├── expect.ts - └── validate.ts -``` - -## 依赖方向约束 - -``` -engine.ts / store.ts / config-loader.ts - │ - ▼ 依赖 -runner/registry.ts + runner/types.ts + types.ts (base) - │ - ▼ 依赖 -runner/http/ / runner/command/ / runner/[future]/ - │ - ▼ 依赖 -expect/ (断言基础设施) + schema/fragments.ts + utils.ts -``` - -禁止:checker 之间横向依赖、expect/ 依赖 runner/、schema/ 依赖 runner/。 - -## Risks / Trade-offs - -- **[编译期类型安全降低]** → 外部消费方(engine、store)不再能通过联合类型 narrow 到具体 checker 类型。缓解:这些消费方本来就不应该知道具体 checker 内部结构,这是设计意图而非缺陷。 -- **[大量 import 路径变更]** → 所有测试文件和内部引用都需要更新。缓解:纯机械操作,可批量处理;项目未上线无兼容性负担。 -- **[validate.ts 拆分复杂度]** → 原文件混合了通用和专属逻辑,拆分时需要仔细处理共享的辅助函数。缓解:`isPlainRecord` 等小工具可以在两处各自内联或放入 utils。 diff --git a/openspec/changes/refactor-checker-cohesion/proposal.md b/openspec/changes/refactor-checker-cohesion/proposal.md deleted file mode 100644 index 60712ab..0000000 --- a/openspec/changes/refactor-checker-cohesion/proposal.md +++ /dev/null @@ -1,36 +0,0 @@ -## Why - -当前 checker 的类型定义、校验逻辑、共享工具分散在多个层级目录中,新增一个 checker 需要修改顶层 `types.ts`(联合类型)、`runner/shared/`(如果有新的 expect 模式)、`config-contract/`(如果有新的 schema 片段)等多处文件。项目的核心就是 checker,需要让每个 checker 的代码尽可能内聚——新增 checker 只需创建一个目录 + 在注册列表加一行,其他文件零修改。 - -## What Changes - -- 将 `ResolvedTarget` 和 `TargetConfig` 从硬编码联合类型改为 base interface,各 checker 内部自行 narrow -- 将各 checker 专属类型(`HttpTargetConfig`、`ResolvedHttpTarget`、`CommandExpectConfig` 等)从顶层 `types.ts` 搬入各自 checker 目录的 `types.ts` -- 将 `runner/shared/` 中仅单个 checker 使用的模块搬入对应 checker 目录(`body.ts` → `http/`,`text.ts` → `command/`) -- 将 `runner/shared/` 中真正跨 checker 共享的断言基础设施(`operator.ts`、`duration.ts`、`failure.ts`)提升为 `checker/expect/` 目录 -- 将 `shared/validate.ts` 拆分:通用 operator 校验 → `expect/validate-operator.ts`,body 规则校验 → `http/validate.ts`,text 规则校验 → `command/validate.ts` -- 将 `config-contract/` 重命名为 `schema/`,内部 `schema.ts` 重命名为 `builder.ts` -- 将 `size.ts` 和 `config-loader.ts` 中的 `parseDuration` 合并到 `utils.ts` -- 各 checker 的 `runner.ts` 重命名为 `execute.ts`,`contract.ts` 重命名为 `schema.ts` -- 各 checker 新增 `index.ts` 作为统一出口(仅 re-export) -- `runner/index.ts` 改为显式列表注册模式 -- 将 `DefaultsConfig` 从硬编码联合(`command?: CommandDefaultsConfig; http?: HttpDefaultsConfig`)改为宽松 base 形式(`Record` + 公共字段),各 checker validate 时自行 narrow - -## Capabilities - -### New Capabilities - -- `checker-cohesion-structure`: 定义重构后 checker 目录的内聚结构规范,包括每个 checker 目录应包含的文件、职责划分、依赖方向约束,以及新增 checker 的最小改动清单 - -### Modified Capabilities - -- `checker-runner-abstraction`: `CheckerDefinition` 接口的 `resolve`/`execute`/`serialize` 方法签名从具体联合类型改为 base interface;共享 expect 断言函数的物理位置从 `runner/shared/` 变更为 `checker/expect/`(仅路径变化,行为不变) - -## Impact - -- 影响 `src/server/checker/` 目录下几乎所有文件的 import 路径 -- `types.ts` 大幅瘦身,联合类型删除 -- `runner/shared/` 目录删除,内容分散到 `expect/` 和各 checker 目录 -- `config-contract/` 目录重命名为 `schema/` -- 所有现有测试的 import 路径需要同步更新 -- 不影响外部 API、配置文件格式、运行时行为 diff --git a/openspec/changes/refactor-checker-cohesion/specs/checker-runner-abstraction/spec.md b/openspec/changes/refactor-checker-cohesion/specs/checker-runner-abstraction/spec.md deleted file mode 100644 index 2d37e77..0000000 --- a/openspec/changes/refactor-checker-cohesion/specs/checker-runner-abstraction/spec.md +++ /dev/null @@ -1,59 +0,0 @@ -## 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()` diff --git a/openspec/changes/refactor-checker-cohesion/tasks.md b/openspec/changes/refactor-checker-cohesion/tasks.md deleted file mode 100644 index 054ebda..0000000 --- a/openspec/changes/refactor-checker-cohesion/tasks.md +++ /dev/null @@ -1,56 +0,0 @@ -## 1. 基础设施搭建 - -- [x] 1.1 创建 `src/server/checker/utils.ts`,将 `size.ts` 的 `parseSize` 和 `config-loader.ts` 的 `parseDuration` 及 `DURATION_REGEX` 迁入 -- [x] 1.2 创建 `src/server/checker/expect/` 目录,创建 `expect/types.ts` 放置 `ExpectResult` 等共享类型 -- [x] 1.3 将 `runner/shared/operator.ts` 迁入为 `expect/operator.ts` -- [x] 1.4 将 `runner/shared/failure.ts` 迁入为 `expect/failure.ts` -- [x] 1.5 将 `runner/shared/duration.ts` 迁入为 `expect/duration.ts`(`ExpectResult` 类型提取到 `expect/types.ts`) -- [x] 1.6 从 `runner/shared/validate.ts` 中提取 `validateOperatorObject`、`isJsonValue`、`validateOperatorValue`、`isPlainRecord` 到 `expect/validate-operator.ts` - -## 2. Schema 目录重组 - -- [x] 2.1 将 `config-contract/` 目录重命名为 `schema/` -- [x] 2.2 将 `schema/schema.ts` 重命名为 `schema/builder.ts` -- [x] 2.3 更新 `schema/` 内部文件的相互引用路径 -- [x] 2.4 更新外部对 `config-contract/` 的所有 import 路径(config-loader.ts、runner/shared/validate.ts 等) - -## 3. 类型系统重构 - -- [x] 3.1 在顶层 `types.ts` 中创建 `ResolvedTargetBase` 和 `RawTargetConfig` base interface -- [x] 3.2 将 HTTP 专属类型(`HttpTargetConfig`、`ResolvedHttpTarget`、`HttpExpectConfig`、`HttpDefaultsConfig`、`ResolvedHttpConfig`、`BodyRule`、`CssRule`、`JsonRule`、`XpathRule`、`HeaderExpect`)迁入 `runner/http/types.ts` -- [x] 3.3 将 Command 专属类型(`CommandTargetConfig`、`ResolvedCommandTarget`、`CommandExpectConfig`、`CommandDefaultsConfig`、`ResolvedCommandConfig`)迁入 `runner/command/types.ts` -- [x] 3.4 删除顶层 `types.ts` 中的 `ResolvedTarget` 联合类型和 `TargetConfig` 联合类型,将 `TextRule` 迁入 command/types.ts -- [x] 3.5 将 `DefaultsConfig` 改为宽松 base 形式(仅保留 `interval?`、`timeout?` + index signature),将 `CommandDefaultsConfig` 迁入 command/types.ts,将 `HttpDefaultsConfig` 迁入 http/types.ts -- [x] 3.6 更新 `runner/types.ts` 中 `CheckerDefinition` 接口签名,使用 `RawTargetConfig` 和 `ResolvedTargetBase` -- [x] 3.7 更新 `engine.ts`、`store.ts`、`config-loader.ts` 的类型引用为 `ResolvedTargetBase` 和 `RawTargetConfig` - -## 4. HTTP Checker 内聚化 - -- [x] 4.1 将 `runner/http/runner.ts` 重命名为 `runner/http/execute.ts` -- [x] 4.2 将 `runner/http/contract.ts` 重命名为 `runner/http/schema.ts` -- [x] 4.3 将 `runner/shared/body.ts` 迁入 `runner/http/body.ts` -- [x] 4.4 将 `runner/shared/validate.ts` 中的 `validateBodyRules`、`validateCssRule`、`validateJsonRule`、`validateXpathRule`、`validateRegexRule`、`validateSingleBodyRule`、`validateJsonPath` 合并到 `runner/http/validate.ts` -- [x] 4.5 创建 `runner/http/index.ts`,re-export `HttpChecker` -- [x] 4.6 更新 `runner/http/` 内所有文件的 import 路径 - -## 5. Command Checker 内聚化 - -- [x] 5.1 将 `runner/command/runner.ts` 重命名为 `runner/command/execute.ts` -- [x] 5.2 将 `runner/command/contract.ts` 重命名为 `runner/command/schema.ts` -- [x] 5.3 将 `runner/shared/text.ts` 迁入 `runner/command/text.ts` -- [x] 5.4 将 `runner/shared/validate.ts` 中的 `validateTextRules` 合并到 `runner/command/validate.ts` -- [x] 5.5 创建 `runner/command/index.ts`,re-export `CommandChecker` -- [x] 5.6 更新 `runner/command/` 内所有文件的 import 路径 - -## 6. 注册入口改造 - -- [x] 6.1 重写 `runner/index.ts` 为显式列表注册模式(import 列表 + checker 数组 + 循环注册) -- [x] 6.2 删除 `runner/shared/` 目录(确认所有内容已迁移完毕) -- [x] 6.3 删除 `src/server/checker/size.ts`(已迁入 utils.ts) - -## 7. 测试与质量保障 - -- [x] 7.1 更新所有测试文件的 import 路径 -- [x] 7.2 执行完整测试套件,确保所有测试通过 -- [x] 7.3 执行 lint 和格式检查,确保代码质量 -- [x] 7.4 确认新增 checker 只需一个目录 + 一行注册 diff --git a/openspec/changes/refactor-checker-cohesion/specs/checker-cohesion-structure/spec.md b/openspec/specs/checker-cohesion-structure/spec.md similarity index 96% rename from openspec/changes/refactor-checker-cohesion/specs/checker-cohesion-structure/spec.md rename to openspec/specs/checker-cohesion-structure/spec.md index 1ad6477..79b0618 100644 --- a/openspec/changes/refactor-checker-cohesion/specs/checker-cohesion-structure/spec.md +++ b/openspec/specs/checker-cohesion-structure/spec.md @@ -1,4 +1,8 @@ -## ADDED Requirements +## Purpose + +定义 checker 模块的内聚化组织结构,确保每个 checker 以独立目录形式存在,包含其全部类型定义、schema 声明、语义校验、执行逻辑和断言逻辑。同时定义共享的 expect/ 和 schema/ 基础设施,以及严格的依赖方向约束。 + +## Requirements ### Requirement: Checker 目录内聚结构 每个 checker SHALL 以独立目录形式存在于 `src/server/checker/runner//`,目录内 SHALL 包含该 checker 的全部类型定义、schema 声明、语义校验、执行逻辑和断言逻辑。 diff --git a/openspec/specs/checker-runner-abstraction/spec.md b/openspec/specs/checker-runner-abstraction/spec.md index 1064cc8..5b2e901 100644 --- a/openspec/specs/checker-runner-abstraction/spec.md +++ b/openspec/specs/checker-runner-abstraction/spec.md @@ -50,7 +50,7 @@ - **THEN** checker SHALL 返回 `ConfigValidationIssue`,而不是直接抛出最终用户错误字符串 ### Requirement: Checker 接口定义 -系统 SHALL 在 `src/server/checker/runner/types.ts` 中定义面向扩展的 `CheckerDefinition`,包含 `type`、`configKey`、TypeBox 配置契约、启动期语义校验、`resolve`、`execute`、`serialize` 成员。`CheckerContext` SHALL 包含引擎注入的 `AbortSignal`。 +系统 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 @@ -68,6 +68,14 @@ - **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: CheckerRegistry 注册中心 系统 SHALL 在 `src/server/checker/runner/registry.ts` 中提供 `CheckerRegistry` 类,支持 `register(checker)`、`get(type)` 和 `supportedTypes`。重复注册同一 type SHALL 抛出错误。 @@ -133,19 +141,27 @@ - **THEN** store SHALL 对每个 target 调用对应 checker 的 `serialize()` 方法获取 `{ target, config }` ### Requirement: 共享 expect 断言函数 -系统 SHALL 在 `src/server/checker/runner/shared/` 中提供可被多个 checker 复用的 expect 函数。checker 专用的 expect 函数 SHALL 保留在各自子包内。 +系统 SHALL 在 `src/server/checker/expect/` 中提供可被多个 checker 复用的 expect 函数。checker 专用的 expect 函数 SHALL 保留在各自子包内。仅被单个 checker 使用的断言模块 SHALL 位于该 checker 目录内。 #### Scenario: 共享 duration 断言 - **WHEN** 任何 checker 需要校验执行耗时 -- **THEN** SHALL 调用 `runner/shared/duration.ts` 中的 `checkDuration(durationMs, maxDurationMs?)`,返回统一的 `ExpectResult` +- **THEN** SHALL 调用 `expect/duration.ts` 中的 `checkDuration(durationMs, maxDurationMs?)`,返回统一的 `ExpectResult` -#### Scenario: 共享 text 规则断言 -- **WHEN** 任何 checker 需要对文本输出执行有序规则校验 -- **THEN** SHALL 调用 `runner/shared/text.ts` 中的 `checkTextRules(text, rules, phase)`,返回统一的 `ExpectResult` +#### Scenario: 共享 operator 断言 +- **WHEN** 任何 checker 需要对值执行 operator 匹配 +- **THEN** SHALL 调用 `expect/operator.ts` 中的 `applyOperator(actual, op)` -#### Scenario: 共享 body 规则断言 -- **WHEN** 任何 checker 需要对文本体执行 contains/regex/json/css/xpath 规则校验 -- **THEN** SHALL 调用 `runner/shared/body.ts` 中的 `checkBodyExpect(body, rules)`,返回统一的 `ExpectResult` +#### 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 需要校验响应状态码和响应头