- 合并 20+ 细粒度 spec 为粗粒度主题规范:dashboard、data-store、probe-engine、probe-api、probe-config 等 - 删除完全冗余规范:data-retention(被 probe-engine+data-store 覆盖)、backend-code-quality(DEVELOPMENT.md 已记录) - 补充 http-checker 规范至完整标准(配置+执行+expect+校验+observation),匹配代码 440 行实现 - 清理 tcp/udp/llm checker 规范中已废弃 defaults 配置段的残留 Scenario - 清理 checker-cohesion-structure 中的实现路径引用(src/server/...) - 统一所有 spec 格式(## Purpose 开头,去除 # Capability/Title 形式) - 更新 prompt-spec-review.md 审查提示文档
7.8 KiB
Purpose
定义将 Vite 构建的前端资源通过 code generation 嵌入 Bun 后端、静态资源服务与 Content-Type 处理、打包为单个 standalone executable 的生产构建、运行配置和验证要求。
Requirements
Requirement: 生产构建顺序
生产构建 MUST 通过三步流水线完成:Vite 前端构建 → code generation → Bun compile。
构建步骤 1-2(Vite build、code generation)的逻辑 SHALL 从 scripts/build-common.ts 导入,scripts/build.ts 只保留当前平台编译和编排逻辑。
Scenario: 运行生产构建
- WHEN 开发者运行生产构建命令
- THEN 系统 MUST 依次执行 Vite build、资源导入 code generation、Bun.build compile,最终输出单可执行文件
Scenario: Vite 构建失败
- WHEN Vite build 步骤失败
- THEN 系统 MUST 停止后续步骤,不生成 code generation 文件或 executable
Scenario: Bun compile 失败
- WHEN Bun.build compile 步骤失败
- THEN 系统 MUST 清理
.build/临时目录,不保留 stale executable
Scenario: 重构后 build 行为不变
- WHEN
scripts/build.ts改为从scripts/build-common.ts导入共享构建函数 - THEN
bun run build的产出文件路径(dist/dial-server)、构建步骤顺序和错误处理行为 SHALL 与重构前完全一致
Requirement: 单 executable 输出
生产构建 SHALL 输出一个 standalone executable,其中包含 Bun 后端和通过 import with { type: "file" } 嵌入的 Vite 前端产出。
Scenario: 在目标机器运行 executable
- WHEN 生成的 executable 在兼容目标平台上运行
- THEN 它 SHALL 启动全栈应用,且不要求目标机器安装 Node.js、Bun 或
node_modules
Scenario: 服务嵌入的前端
- WHEN executable 收到前端根路径请求
- THEN 它 SHALL 通过内嵌的 Vite 构建产出服务前端资源,且不需要外部
dist/目录
Scenario: 服务嵌入 API 和页面
- WHEN 生成的 executable 启动,且浏览器打开前端根路径
- THEN 页面 SHALL 展示同一个 executable 进程中
/api/summary和/api/targets返回的数据
Requirement: 构建中间产物管理
构建流程 SHALL 使用 .build/ 临时目录存放 code generation 产物,构建完成后清理。
清理逻辑 SHALL 定义在 scripts/build-common.ts 中,供 build.ts 和 release.ts 共用。
Scenario: 构建成功后清理中间产物
- WHEN 生产构建成功完成并输出 executable
- THEN 系统 SHALL 通过
build-common.ts中的cleanup()函数删除.build/临时目录
Scenario: 构建失败时清理中间产物
- WHEN 生产构建在 Bun compile 步骤失败
- THEN 系统 SHALL 通过
build-common.ts中的cleanup()函数删除.build/临时目录和 stale executable
Requirement: 外部运行时配置
executable MUST 将环境相关运行时配置保留在嵌入的前端和 server bundle 之外。
Scenario: 修改监听端口
- WHEN 操作者修改受支持的 port 配置
- THEN 同一个 executable SHALL 在不重新构建的情况下监听新端口
Scenario: 缺少可选配置
- WHEN 可选运行时配置被省略
- THEN executable SHALL 使用文档化的默认值
Requirement: 构建验证
项目 SHALL 提供 verify 命令执行质量检查和生产构建;原 smoke test 暂时移除,executable 路由验证由后续变更重新设计。
Scenario: 完整验证重新构建 executable
- WHEN 开发者运行完整验证命令
- THEN 系统 MUST 先执行质量检查,再基于当前源码执行生产构建
Scenario: 验证失败
- WHEN 质量检查或构建阶段失败
- THEN 验证 SHALL 使命令失败
Requirement: 生产构建版本固化
生产构建 SHALL 在 code generation 阶段读取 package.json.version,并将该版本号固化到生成的 production server entry 中,使 standalone executable 能在运行时返回构建时版本。
Scenario: 构建时注入版本号
- WHEN 开发者运行生产构建命令
- THEN 构建脚本 SHALL 在生成
.build/server-entry.ts时写入当前package.json.version对应的版本字面量
Scenario: executable 不依赖外部 package.json 返回版本
- WHEN 生成的 standalone executable 在目标机器运行且外部不存在项目根目录
package.json - THEN
GET /api/metaSHALL 仍返回构建时固化的version
Scenario: 升迁后重新构建
- WHEN 开发者先升迁
package.json.version再运行生产构建命令 - THEN 新生成的 standalone executable SHALL 返回升迁后的版本号
Requirement: 构建时资源扫描与 Code Generation
构建脚本 SHALL 在 Vite build 完成后扫描 dist/web/ 目录,自动生成 TypeScript 文件,为每个静态资源创建 import ... with { type: "file" } 声明。
Scenario: 生成资源导入文件
- WHEN 构建脚本扫描
dist/web/目录 - THEN 系统 SHALL 在
.build/static-assets.ts中为每个文件生成import fN from "<path>" with { type: "file" }语句,并导出StaticAssets对象
Scenario: StaticAssets 对象结构
- WHEN
static-assets.ts被生成 - THEN 导出的对象 SHALL 包含
indexHtml: Blob和files: Record<string, Blob>两个字段,其中 files 的 key 为 URL 路径(如/assets/index-a1b2c3.js)
Scenario: 生成 production server entry
- WHEN 构建脚本生成资源导入文件后
- THEN 系统 SHALL 在
.build/server-entry.ts中生成 production 入口,import bootstrap、config 和 staticAssets 并调用 bootstrap
Requirement: 运行时静态资源服务
系统 SHALL 提供 serveStaticAsset 函数,根据请求路径从 StaticAssets 中查找并返回对应资源。
Scenario: 请求根路径
- WHEN 请求路径为
/ - THEN 系统 SHALL 返回
indexHtml,Content-Type 为text/html; charset=utf-8,Cache-Control 为no-cache
Scenario: 请求已知静态资源
- WHEN 请求路径匹配
files中的某个 key - THEN 系统 SHALL 返回对应 Blob,Content-Type 根据文件扩展名推断,Cache-Control 为
public, max-age=31536000, immutable
Scenario: 请求未知带扩展名路径
- WHEN 请求路径包含文件扩展名但未匹配任何已知资源
- THEN 系统 SHALL 返回 404 响应
Scenario: SPA Fallback
- WHEN 请求路径不包含文件扩展名且不以
/api/开头 - THEN 系统 SHALL 返回
indexHtml(SPA fallback)
Requirement: Content-Type 推断
系统 SHALL 根据文件扩展名推断正确的 Content-Type header。
Scenario: JavaScript 文件
- WHEN 请求路径以
.js或.mjs结尾 - THEN Content-Type SHALL 为
text/javascript; charset=utf-8
Scenario: CSS 文件
- WHEN 请求路径以
.css结尾 - THEN Content-Type SHALL 为
text/css; charset=utf-8
Scenario: SVG 文件
- WHEN 请求路径以
.svg结尾 - THEN Content-Type SHALL 为
image/svg+xml
Requirement: 静态资源 import specifier SHALL 使用平台无关分隔符
构建时静态资源 code generation SHALL 将文件系统相对路径转换为 ESM import specifier,并确保生成的 import 路径在 Windows、macOS、Linux 开发环境下都使用 / 作为分隔符。
Scenario: Windows 相对路径转换为 import specifier
- WHEN code generation 将 Windows 文件系统相对路径
..\\dist\\web\\assets\\app.js转换为静态资源 import specifier - THEN 生成的 import specifier SHALL 为
../dist/web/assets/app.js,且 MUST NOT 包含\\
Scenario: POSIX 相对路径保持 import specifier 形式
- WHEN code generation 将 POSIX 文件系统相对路径
../dist/web/assets/app.js转换为静态资源 import specifier - THEN 生成的 import specifier SHALL 保持为
../dist/web/assets/app.js