feat(preview): 路由驱动的本地预览服务器
Plan 4 Task 7(V2 spec §12.2):apps/preview-server 零依赖 node:http 服务—— 只读 succeeded job 的 dist + route-manifest(不查库),exact/ignore 双索引匹配 (query_key 行全串优先),html/asset 文件服务(manifest content_type 优先、 扩展名 MIME 表兜底、未知扩展 octet-stream)、redirect 301+Location、gone 410、 未命中 404 走 dist/404.html 自定义页;全响应 X-Robots-Tag noindex + HTML 响应 基础 CSP;回环绑定白名单(库级即拒);穿越预检先于 URL 解析(裸/编码点段、 反斜杠)+ target_file resolve+realpath 双检在 dist 内;目录请求一律 404(禁 列表);仅 GET/HEAD。sf.ts 增 preview:start(job 定位 + succeeded 校验 + 产物 行读取后即断池;--host 外值拒绝先于 DB 访问)。集成测试 12 例:真实构建产物 全链 + 手构 gone/MIME 探针行 + CLI 拒绝与子进程 happy path 冒烟。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
// server.mjs 的 TS 声明面(NodeNext 自动拾取):实现刻意保持纯 JS(零依赖、不进 TS
|
||||
// 构建链),类型契约在此钉死。行类型是 RouteManifestRoute 的结构超集容忍面——
|
||||
// @sf/contracts 的 RouteManifest 可直接传入(字段窄化兼容)。
|
||||
import type { AddressInfo } from 'node:net'
|
||||
import type { Server } from 'node:http'
|
||||
|
||||
export interface PreviewRouteRow {
|
||||
exact_key: string
|
||||
query_policy: string
|
||||
query_key?: string | null
|
||||
kind: string
|
||||
target_file?: string
|
||||
status: number
|
||||
location?: string
|
||||
content_type?: string
|
||||
indexable: boolean
|
||||
}
|
||||
|
||||
export interface PreviewManifestLike {
|
||||
routes: PreviewRouteRow[]
|
||||
}
|
||||
|
||||
export interface PreviewServerOptions {
|
||||
/** 构建产物目录(<artifactsRoot>/jobs/<jobId>/dist) */
|
||||
distDir: string
|
||||
/** §12.1 route-manifest(已解析对象——预览只读不加载路径) */
|
||||
routeManifest: PreviewManifestLike
|
||||
/** 绑定地址;仅 127.0.0.1 / localhost(缺省 127.0.0.1)——外值构造即拒 */
|
||||
host?: string
|
||||
/** 监听端口(缺省 4173;0 = 随机可用端口) */
|
||||
port?: number
|
||||
}
|
||||
|
||||
export interface PreviewServerHandle {
|
||||
server: Server
|
||||
/** 开始监听;resolve 值 = server.address()(port=0 时含实际端口) */
|
||||
start(): Promise<AddressInfo>
|
||||
/** 关停(destroy 连接后 close——防 keep-alive 套接字吊住) */
|
||||
stop(): Promise<void>
|
||||
}
|
||||
|
||||
export declare function createPreviewServer(options: PreviewServerOptions): PreviewServerHandle
|
||||
Reference in New Issue
Block a user