预览与构建链路
预览、截图与构建由 Backend 协调。Editor 发起用户操作,Runtime 提供页面渲染和构建能力;真实 Chromium 截图与页面诊断由独立 Renderer 执行。
页面预览
- Editor 保存页面源码或配置。
- Backend 校验用户权限和源码导入边界。
- Backend 创建 preview artifact,并为 Runtime 签发短期上下文令牌。
- Editor iframe 打开 Backend 返回的签名预览地址;Backend 校验票据、绑定 Runtime 版本并返回可信桥接宿主页。用户页面 HTML 在独立预览主机名的 Runtime iframe 中执行。
- Runtime 使用内部服务票据 S 回源 Backend 读取 manifest、配置包和模块源码,交给 Vite 作为远程虚拟模块编译;浏览器凭短期预览票据 P 向 Runtime 请求页面与模块。
- iframe 内的浏览器凭 P 按 artifact 资源映射从 Backend 受保护入口读取图片、字体等素材。Backend 在缓存、304、HEAD 与 Range 处理前完成授权和版本校验。
单页与整项目预览使用相同机制,分别以模块或路由作为入口;普通预览不把全部素材先复制到 Runtime 临时目录。源码及素材完整落盘发生在下述项目构建流程。当前入口、票据与续签语义见预览源站接入;生产存储与多副本待验收项见用户文件存储鉴权与缓存实施规划。
页面 iframe 使用版本 1 状态消息回传最终结果:成功发送 page-preview:ready,初始化或页面模块失败发送 page-preview:error;payload 必须包含当前 artifactId。Editor 同时校验消息窗口、origin、协议版本和 artifact,忽略旧预览迟到消息。Editor 在等待 8 秒后展示弱网提示,30 秒后进入可重试错误;超时不会销毁 iframe,因此同一 artifact 的迟到 ready 仍可恢复预览。
组件预览
组件预览依赖组件源码和 previewSchema。Backend 负责校验 previewSchema 可导入能力,Runtime 负责按 schema 渲染典型状态。previewSchema 变化时要同步 Runtime 组件预览测试和 Backend 契约测试。
组件预览沿用 component-preview:ready/error 消息,在 Editor 中采用与页面预览一致的 8 秒弱网提示和 30 秒可重试超时。刷新同一预览对象时保留旧 iframe 并覆盖加载状态;切换对象时清空旧画面,避免串用 artifact。
截图
- Backend 持久化渲染请求,并按租约和并发限制调度受信 Renderer。
- Renderer 为本次执行创建独立 Chromium 和 Context,加载 Runtime 的受保护预览页面。
- Renderer 等待页面就绪,生成截图或诊断结果并回传 Backend;Backend 关联任务与产物。
Renderer 浏览器使用本次 artifact 的短期票据 P 读取受保护素材;Backend 可在授权后使用 _object-cache 缓存 S3 对象,这不是 Runtime 临时工作区。初始文档的受控请求头不能代替后续图片和字体请求各自的授权。
截图任务需要关注 viewport、超时、并发、资源加载和页面 visual-ready 状态。Backend 进程不直接启动浏览器。
项目构建
- 用户在 Editor 发起项目构建。
- Backend 创建
ProjectBuildJob持久任务(含attempt_id、租约字段和创建时即确定的绝对deadline_at)并生成 build snapshot。 runtime-build内的 Build Worker 按 project lane 并发启动等量领取消费者,通过POST /internal/runtime/build-jobs/claim以条件更新认领任务;Backend 下发 attempt 令牌,并把租约与令牌 TTL 一并裁剪到deadline_at之前。- Worker 执行期间周期调用
renew:收到 401/409 立即中止在跑构建,与 Backend 失联时也在本地租约到期(留出安全余量)前主动停手,避免失守的执行者继续消耗资源或抢写状态。 - Runtime 拉取 snapshot,生成临时入口并在隔离子进程内执行 Vite 构建与 ZIP 归档;构建子进程不继承 Worker 领取凭证。中止信号一路传到子进程本身:租约失守或 deadline 到期时先
SIGTERM,宽限 1 秒未退出再SIGKILL,并等到真实close才释放调度槽位与临时工作区。只中止主进程的fetch/sleep会让最耗 CPU 与内存的构建阶段继续跑到自然结束。 - Worker 以
POST /internal/runtime/build-jobs/{job_id}/artifact上传产物:归档按分块流直接作为请求体,入口文件与 sha256、大小通过x-runtime-build-archive-*头声明,两侧都不再把整包读进内存,Backend 超过PROJECT_BUILD_ARTIFACT_MAX_BYTES即中止写入。校验和/大小不符、attempt 围栏失守或提交异常时,Backend 会删除这个没有任何行引用的 attempt 归档,避免对象存储积累 orphan;仍被任务行引用的产物一律保留。上传成功后调用complete。Backend 只在attempt_id与有效租约同时匹配时才提升产物或写终态,失守的执行者只能留下等待恢复的running行。 complete(success)对同一attempt_id幂等:任务已是succeeded且产物已提升时返回 200 而不是 409,Runtime 侧对传输失败与 5xx 再做最多 3 次重试。否则「产物已上传、complete 响应丢失」只能等租约过期,用户会看到产物已生成但任务仍running十几分钟。4xx 是确定性拒绝,不重试。- 恢复循环按过期租约接管或收敛:已经上传产物的任务收敛为
succeeded,超过总期限仍未被领取的任务收敛为failed。 - 下载走
GET /api/projects/{project_id}/build-jobs/{job_id}/artifact:本地驱动用FileResponse,S3 驱动在head_object取到元信息后直接把get_object的响应体转发给客户端,不再整包回源到_object-cache,因此该路径不做整包 sha256 复核——完整性由第 6 步上传阶段保证。两条链路都支持单区间Range与If-Range;Editor 先用HEAD预检拿鉴权/存在性错误,再把字节传输交给浏览器原生下载,避免把产物大小与带宽的乘积压进脚本超时预算。
构建不存在第二跳执行路径:Backend 不再向 Runtime 同步派发构建,Runtime 也不暴露构建 HTTP 入口。凭证未配置时 Backend claim API fail-closed 且 Worker 不启动,构建任务停留在 pending 直到总期限把它收敛为失败。RUNTIME_ROLE=build 的容器在这种情况下直接启动失败,/__runtime_readyz 也不会就绪——「进程健康但永远不构建」比启动失败更难发现。
关键约束
- 页面源码、组件源码和 previewSchema 只能引用 Runtime Kit manifest 公开且版本化的能力。
- Runtime shell 内部能力不进入页面、组件或 AI 能力目录。
- 构建 snapshot 应固化当次构建所需上下文,避免构建过程中读取漂移状态。
- 预览 artifact 和构建心跳属于临时运行态,应有 TTL 和恢复策略。