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:
浅念
2026-10-22 13:29:30 +08:00
parent 0efec4673b
commit 7194d1bcef
6 changed files with 842 additions and 6 deletions
+6 -4
View File
@@ -27,10 +27,10 @@ export default tseslint.config(
},
},
{
// Node 直跑的 .mjs 工具脚本(如 scripts/smoke-real-wxr.mjs):no-undef 对 TS 文件
// 由 TS 接管而关闭,但 .mjs 仍生效且无 node 全局——此处显式声明 node 全局,
// 否则 process/setInterval 等误报 no-undef。
files: ['scripts/**/*.mjs'],
// Node 直跑的 .mjs 工具脚本(如 scripts/smoke-real-wxr.mjs、apps/preview-server/
// server.mjs):no-undef 对 TS 文件由 TS 接管而关闭,但 .mjs 仍生效且无 node
// 全局——此处显式声明 node 全局,否则 process/setInterval/URL 等误报 no-undef。
files: ['scripts/**/*.mjs', 'apps/preview-server/**/*.mjs'],
languageOptions: {
globals: {
process: 'readonly',
@@ -41,6 +41,8 @@ export default tseslint.config(
console: 'readonly',
performance: 'readonly',
Buffer: 'readonly',
URL: 'readonly',
URLSearchParams: 'readonly',
},
},
},