1
0
Files
DiAL/openspec/specs/single-executable-packaging/spec.md
lanyuanxiaoyao cfca03b4d6 refactor: 规范审查与重组,合并细粒度规范,清理过时内容
- 合并 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 审查提示文档
2026-05-22 18:55:18 +08:00

7.8 KiB
Raw Blame History

Purpose

定义将 Vite 构建的前端资源通过 code generation 嵌入 Bun 后端、静态资源服务与 Content-Type 处理、打包为单个 standalone executable 的生产构建、运行配置和验证要求。

Requirements

Requirement: 生产构建顺序

生产构建 MUST 通过三步流水线完成Vite 前端构建 → code generation → Bun compile。

构建步骤 1-2Vite 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.tsrelease.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/meta SHALL 仍返回构建时固化的 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: Blobfiles: 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 返回 indexHtmlContent-Type 为 text/html; charset=utf-8Cache-Control 为 no-cache

Scenario: 请求已知静态资源

  • WHEN 请求路径匹配 files 中的某个 key
  • THEN 系统 SHALL 返回对应 BlobContent-Type 根据文件扩展名推断Cache-Control 为 public, max-age=31536000, immutable

Scenario: 请求未知带扩展名路径

  • WHEN 请求路径包含文件扩展名但未匹配任何已知资源
  • THEN 系统 SHALL 返回 404 响应

Scenario: SPA Fallback

  • WHEN 请求路径不包含文件扩展名且不以 /api/ 开头
  • THEN 系统 SHALL 返回 indexHtmlSPA 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