1
0
Files
DiAL/openspec/changes/archive/2026-05-09-harden-fullstack-packaging-foundation/design.md

5.8 KiB
Raw Blame History

Context

当前项目是 Bun + TypeScript 的前后端一体化 demo开发期由 Vite React 提供前端 HMRBun 提供 /api/*/health;生产期先构建 Vite 静态资源,再通过 Bun file import 将资源和后端编译为单 executable。

现有能力已经通过 typecheck、单元测试和 executable smoke test 验证,但真实业务开发尚未开始。此变更聚焦平台基础设施硬化,目标是在业务 API、数据模型和前端业务页面扩展之前先把开发联调、代码质量、格式一致性、HTTP 契约和生产验证闭环固化下来。

Goals / Non-Goals

Goals:

  • 引入 ESLint 审查代码质量、React Hooks 规则和前后端边界。
  • 引入 Prettier 统一代码风格,但不格式化 openspec/,避免影响 OpenSpec 文档和 tasks 一行一个任务的规则。
  • 提供快速 check 和完整 verify 两层验证命令。
  • 让开发期 Vite proxy 目标端口和 Bun server 监听端口保持一致。
  • 补齐 HTTP method、JSON 404/405、静态资源缓存和低风险安全头的运行时契约。
  • 增强生产 executable smoke test确保验证的是当前源码构建出的生产产物。
  • 同步 README使文档描述与脚本、构建中间产物和验证流程一致。

Non-Goals:

  • 不开发 gateway checker 真实业务能力。
  • 不引入数据库、持久化、认证、React Router 或 UI 组件库。
  • 不新增 CI 配置;本次仅提供本地 checkverify 命令CI 接入留给后续仓库托管策略。
  • 不引入 CSP本次只加入低风险安全响应头避免提前约束未来前端资源策略。
  • 不做大规模目录重构或业务框架抽象。

Decisions

ESLint 和 Prettier 分工

ESLint 只承担质量审查和边界约束不承担缩进、换行、引号等格式职责。Prettier 专门负责代码风格,避免 ESLint stylistic 规则和格式化器重复工作。

备选方案是只引入 ESLint 并启用 stylistic 规则,但后续维护成本更高,且容易和编辑器格式化行为冲突。另一个备选方案是只引入 Prettier但它无法检查 React Hooks、未处理 Promise 或前端误导入后端实现等质量问题。

本次采用的最小依赖集合为 eslint@eslint/jstypescript-eslinteslint-plugin-react-hookseslint-plugin-react-refreshprettier。暂不引入 eslint-config-prettier,除非实现阶段引入会与 Prettier 冲突的 ESLint preset 或 stylistic 规则。

验证命令分层

新增 checkverify 两层命令:

check
├─ typecheck
├─ lint
├─ format:check
└─ test

verify
├─ check
├─ build
└─ test:smoke

check 面向日常开发,反馈快;verify 面向提交前或发布前验证,包含生产构建和 executable smoke test。备选方案是只提供 verify,但每次都构建 executable 会降低日常迭代速度。

Prettier 忽略范围

Prettier SHALL 忽略 openspec/dist/.build/node_modules/bun.lock 和临时构建产物。openspec/ 排除是显式决策,因为 OpenSpec tasks 要求一行一个任务Markdown 自动折行可能破坏审阅体验和规则遵循。

开发期端口配置

文档化的全栈开发命令以 PORT 作为后端端口的唯一对外配置。Vite proxy 使用的 BACKEND_PORT 应由开发脚本从 PORT 派生,或者明确作为内部变量,避免用户只改 BACKEND_PORT 导致 proxy 与 server 分叉。直接运行 Bun server 或生产 executable 时仍可继续使用现有 CLI 参数覆盖 host 和 port。

运行配置校验

运行配置继续保持 CLI 参数优先于环境变量,缺省时使用 README 文档化默认值。端口配置必须拒绝非整数、小于 0 或大于 65535 的值,并通过单元测试覆盖默认值、优先级、非法输入和边界值,避免开发期和生产期配置行为分叉。

HTTP method 和错误契约

现有 demo 端点按路径匹配,后续业务扩展前需要先固化 method 语义。/health/api/demoGET 为主,并支持 HEAD 返回相同状态和 headers 但无响应体;不支持的 method 返回 JSON 405并带 Allow header。未知 /api/* 继续返回 JSON 404不能落入前端 HTML fallback。

生产响应头策略

生产 HTML 使用 Cache-Control: no-cacheVite hash 静态资源使用长缓存 public, max-age=31536000, immutable。所有生产 HTTP 响应增加低风险安全头,例如 X-Content-Type-Options: nosniffReferrer-Policy。CSP 暂不纳入本次变更,避免后续业务页面接入外部资源时产生过早约束。

构建确定性

生成 .build/static-assets.ts 时,嵌入资源列表应按稳定顺序输出。这样可以减少重复构建时的无意义差异,也方便 smoke test 和后续审查定位问题。

Smoke test 增强

test:smoke SHALL 针对当前构建出的 executable 验证生产行为,包括 /health/api/demo、未知 API、根 HTML、SPA fallback、静态资源、未知静态资源、生产 runtime mode、缓存头和低风险安全头。verify 必须先执行 build 再 smoke避免验证旧产物。

Risks / Trade-offs

  • 新增 ESLint 和 Prettier 会增加开发依赖与初次配置成本 → 采用最小依赖集合,只启用与当前项目直接相关的规则。
  • 现有代码可能被 Prettier 产生格式化改动 → 本次作为平台硬化变更集中处理,后续业务变更减少格式噪音。
  • 405 和 HEAD 行为会让 HTTP handler 稍复杂 → 在业务 API 扩展前处理,避免未来每个端点重复补语义。
  • 安全头不包含 CSP安全强度有限 → 先采用低风险头CSP 在前端资源来源稳定后单独设计。
  • verify 包含构建和 smoke运行更慢 → 保留快速 check 作为日常反馈通道。