项目架构:包结构、依赖关系与设计约束
ConnProof 是一个 pnpm workspace 单仓,TypeScript 编写,MIT 许可。架构的核心决策只有一条:把"规则"从"工具"和"文档"中独立出来,让两者共享同一份数据。其余结构都是这条决策的展开。
包结构一览
text
packages/
shared-types 共享类型模型(零依赖,被所有包引用)
diagnostic-rules 规则数据库:32 条稳定错误码及其元数据
diagnostic-core 真实网络检测:DNS/TCP/TLS/HTTP/WS/订阅/doctor
privacy-redactor 报告脱敏模块
report-generator 报告组装与 text/JSON/Markdown 渲染
test-fixtures 本地测试服务器与固定样本(仅测试用)
apps/
cli connproof 命令行工具
web-diagnostic 浏览器安全检测(文档站的 BrowserCheck 组件使用)
docs/ VitePress 文档站(connproof.com)
scripts/ 规则/文档/链接/构建四类校验脚本1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
依赖方向
依赖是单向的,从上层流向基础:
shared-types不依赖任何包——它定义 DiagnosticStatus、Severity、DiagnosticFinding、DiagnosticReport 等全部共享类型;diagnostic-rules只依赖 shared-types——规则是纯数据加少量查询函数(getRuleByCode、docUrlFor);diagnostic-core依赖 rules 与 types——检测器产出证据后,用规则库把证据映射为带错误码的 finding;report-generator与privacy-redactor彼此独立,都只依赖基础包;cli组装以上全部;web-diagnostic刻意保持独立——浏览器检测不引用 Node 专用代码。
工具与文档如何共用规则
这是架构里最值得说明的机制:
- CLI 侧:检测失败时通过
findingFromRule(code, ...)从规则库取出错误码、原因列表、建议操作和文档路径,docUrl 由规范域名 + docPath 推导,任何地方不写死链接; - 文档站侧:VitePress 配置直接 import 规则库——错误库侧边栏由规则数据生成,首页高频错误列表来自同一数据源;
- 校验层:
validate-rules脚本强制规则与docs/errors/*.md一一对应(slug 与文件名、errorCode 与 frontmatter 逐字段比对),CI 中任何一侧的漂移都会失败。
结果是"工具说 TLS-002"与"网站讲 TLS-002"在构建层面就不可能不一致。
设计约束(不是偶然,是承诺)
- CLI 零运行时第三方依赖:只用 Node 内置模块(net/tls/dns/http/crypto),把供应链攻击面压到最低;
- 检测单目标:所有检测器只接受一个用户指定的目标;本地端口检查用固定清单且仅限回环地址。不存在网段或端口范围参数;
- 测试不碰公网:test-fixtures 提供本地 TCP/TLS/HTTP/WebSocket 服务器与自签证书,全部测试可离线运行;
- 诚实状态:检测不到就返回 unsupported/inconclusive,这是类型系统层面的一等状态而非补丁。
构建管线
pnpm build 串起完整链条:lint → typecheck → test → 规则校验 → 文档校验 → 链接校验 → 包构建 → CLI 构建 → 站点构建 → Pagefind 索引 → 部署准备 → 构建产物校验 → 打包。任何一环失败即中止——发布物必然通过了全部检查。