## Context 当前项目是 Bun + TypeScript 的最小工程,入口文件只有 `index.ts`,尚未形成前端、后端、共享类型、测试和构建的边界。目标是在保持 Bun 单文件部署优势的同时,引入完整的 Vite + React 前端开发体验。 业界成熟实践通常不是让后端参与前端 HMR,而是开发期让 Vite dev server 独立承载前端,使用 proxy 将 `/api/*` 转发给后端;生产期由前端构建生成静态资源,再由后端服务这些资源。Go/Rust 生态常用类似 `embed` 的方式把 Vite `dist/` 打入单个二进制,Bun 可通过 `bun build --compile` 与 embedded files/full-stack asset 能力实现同类目标。 ``` 开发期 Browser | v Vite dev server :5173 |-- React HMR |-- /api/* proxy | v Bun API server :3000 生产期 Browser | v gateway-checker executable |-- /api/* Bun API |-- /health 健康检查 |-- /assets/* Vite 静态资源 |-- /* React SPA fallback ``` ## Goals / Non-Goals **Goals:** - 建立 Vite + React + TypeScript 前端应用结构,保留开发期 HMR。 - 建立 Bun 后端服务结构,统一承载 API、健康检查和生产前端资源。 - 提供一个可运行 demo,前端页面通过 `/api/demo` 调用后端并展示响应。 - 建立前后端共享类型边界,避免重复定义基础接口类型。 - 建立生产构建链路,输出单个 Bun standalone executable。 - 保持前端可拆离:前端只通过 HTTP `/api/*` 依赖后端,不直接 import 后端实现。 - 更新 README,记录结构、命令、测试、构建和运行方式。 **Non-Goals:** - 不引入 SSR、React Server Components、Next.js 或其他全栈框架。 - 不引入数据库、认证、用户系统或业务功能。 - 不要求一次性完成多平台发布矩阵,只定义可扩展的 target 机制。 - 不把运行期配置、日志或可变数据嵌入 executable。 - 不在开发期强制单端口访问;开发期可以使用 Vite 端口作为浏览器入口。 ## Decisions ### Decision: 使用 Vite + React 作为前端开发框架 采用 Vite + React + TypeScript,开发期由 Vite 提供 HMR,生产期由 `vite build` 输出静态资源。React 适合后续构建复杂管理界面、状态页、图表和交互式检测视图。 替代方案:使用 Bun 原生 HTML imports。该方案更简单、依赖更少,但前端生态、插件、测试和组件体系弱于 Vite。 替代方案:使用 Next.js。该方案能力更强,但 SSR/路由/部署模型与“Bun 单 executable”目标存在额外摩擦。 ### Decision: 开发期 Vite proxy `/api/*` 到 Bun 后端 浏览器开发入口默认使用 Vite dev server,前端请求统一使用相对路径 `/api/*`。Vite 负责把这些请求代理到 Bun 后端服务,从而保持同源开发体验,避免 CORS 和硬编码后端地址。 替代方案:Bun 后端反向代理 Vite dev server。该方案可以让开发期也统一一个端口,但会增加胶水代码,并且容易干扰 Vite HMR 行为。 ### Decision: 生产期由 Bun 服务 Vite `dist/` 生产构建先执行 Vite build,再让 Bun 后端服务 `index.html`、`assets/*` 和其他静态资源。非 API、非静态资源路径 fallback 到 `index.html`,用于支持 React SPA 路由刷新。 替代方案:前端独立部署到 CDN。该方案扩展性更好,但不满足当前“一个可执行程序包含前后端”的目标。 ### Decision: 单 executable 是发布形态,不是代码耦合方式 前端和后端在源码层保持清晰边界,只通过 HTTP API 和共享类型协作。打包层负责将 Vite 产物嵌入 Bun executable。这样未来若需要 CDN、独立前端部署或多客户端复用 API,不需要重写后端业务代码。 替代方案:后端源码直接 import 前端源码或前端模块。该方案短期简单,但会模糊运行时边界,增加后续拆离成本。 ### Decision: API 路径统一保留在 `/api/*` 所有业务 API 使用 `/api/*` 前缀,健康检查使用 `/health`。demo API 使用 `/api/demo`,返回前端可展示的 JSON 响应。生产期路由优先级为 API、健康检查、静态资源、SPA fallback。未命中的 `/api/*` 必须返回 JSON 404,不能 fallback 到前端页面。 替代方案:API 与页面路径混排。该方案不利于 Vite proxy、生产 fallback 和未来前端独立部署。 ### Decision: 运行配置使用环境变量或 CLI 参数 host、port、日志级别等运行期配置不嵌入 executable,优先从 CLI 参数或环境变量读取。executable 内只包含只读程序代码和前端静态资源。 替代方案:构建期写死配置。该方案部署简单,但不同环境需要重新构建,且不利于发布同一个二进制。 ### Decision: demo 是验收基线而不是业务功能 demo 只证明前端开发、后端 API、生产静态服务和 executable 打包链路能跑通。页面应展示来自 `/api/demo` 的后端响应,并在 README 中记录开发期访问方式和 executable 运行后的验证方式。 替代方案:只搭建空白 React 页面和空 API。该方案能证明结构存在,但不能证明前后端开发和打包后联通链路真实可用。 ## Risks / Trade-offs - [Risk] Bun standalone executable 与 Vite `dist/` 嵌入方式相对 Go `embed` 更年轻。→ Mitigation: 先实现最小静态资源嵌入和端到端构建测试,再扩展多平台构建。 - [Risk] Vite hashed assets、SPA fallback 和 Bun 静态路由可能出现路径映射问题。→ Mitigation: 对 `/`, `/assets/*`, 前端路由刷新和 `/api/*` 404 编写测试或构建后验证。 - [Risk] 依赖数量会明显增加。→ Mitigation: 初期只引入 Vite、React、React DOM 和必要类型,不引入 UI 组件库、状态管理或路由库,除非后续需求明确。 - [Risk] 单 executable 会把前端资源大小计入二进制。→ Mitigation: 保留 Vite 产物压缩能力,后续可按需启用分离部署或 CDN。 - [Risk] 开发期前端和后端是两个进程,启动命令更复杂。→ Mitigation: 提供 `dev:web`、`dev:server` 和 `dev` 聚合脚本,并在 README 中说明。 ## Migration Plan 1. 保留当前最小入口语义,重构为新的 server 入口。 2. 新增 web、server、shared 和 scripts 目录结构。 3. 引入最小 Vite + React 依赖并配置开发代理。 4. 实现 Bun API、健康检查和生产静态资源服务。 5. 增加测试和构建验证。 6. 更新 README 作为项目结构和命令的权威说明。 回滚策略:如果 Vite 集成阻塞,可以保留 Bun 后端结构,移除 web 目录和前端构建脚本,退回 Bun 原生 HTML imports 或后端-only 形态。 ## Open Questions - 前端是否需要路由库,例如 React Router,还是先保持单页面组件状态? - 是否需要 UI 组件库,例如 TDesign、shadcn/ui 或保持纯 CSS? - 生产 executable 首期目标平台是当前 macOS,还是同时需要 Linux x64/arm64?