浅念 8003de336b feat(import): 全链编排与导入报告
Task 6(Plan 3):四家写入器汇流为默认编排管道。

- import.service:注入 events/pipeline 缺席时走真实事件流(runWxrAdapter
  惰性 async generator,mapConfig.locale=site.default_locale 前置事实 8);
  分派严格串行 content→term→media→route(禁 Promise.all);finish 硬序
  term(sync)→content(sync)→media(await)→route(await);counts 由 ⊕ 覆盖
  改为逐 key 求和聚合;新增 stale 回收(同站点 queued/running >24h 一条
  SQL 置 stale);报告新增 paths/productsPath+newsPath、issueGroups 六组、
  sanitize 清洗统计(事件流 changedRows/byDiffType 聚合)、adapterTiming。
- content-writer:NULL-entry 收编(快照行在而 content_entry 空,五列先验
  三分支:无先验新建挂接/同 checksum 挂接 skipped/异 checksum 挂接新版本)。
- route-writer:attachment 降级汇总按去重 sourceId 计(重跑仍必现);
  normalizeListPath 导出;writeAttachmentRoute 返回 written/resolved/blocked。
- 四家写入器 identity 收敛共享基类型 WriterContext =
  Pick<ImportJobContext,'db'|'siteId'|'connectionId'>(type-only import,
  无运行时环依赖)。
- CLI import 增 --products-path/--news-path(前置事实 9,缺省 /products/
  /news/)记入 job 报告。
- 测试:tests/integration/import-full.test.ts 6 例(全链 zh-CN 接线证明
  +published 全 NULL/报告四域计数+sanitize+分组/幂等重跑零写语义/
  无 media 快照域缺失不失败/NULL-entry 收编/stale 回收/REPORT_WRITE_FAILED
  遗留补测);import-job 契约测试钉死 counts 求和(100⊕3=103)。
  全量 323 通过;typecheck/lint 干净。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-21 20:08:06 +08:00

SiteForge

WordPress 站点到静态站点迁移引擎:读取 WXR/Sitemap/媒体归档 → 清洗入不可变内容库 → 主题化构建为自包含静态站点(Astro),全程可审计、可复现、契约冻结。

快速开始

前置:Node.js >= 22.12、Docker、corepack。

corepack enable                      # 激活 pnpm(或自行安装 pnpm@10)
pnpm install                         # 安装依赖
pnpm db:up                           # 启动 PostgreSQL 16(宿主端口 5434,等 healthcheck 就绪)
pnpm db:migrate                      # 开发库全量迁移(apps/api/drizzle)
pnpm db:test                         # 一键验收:起库 + 跑全部测试(测试用临时库,开发库零写入)

WXR 适配器(阶段 2)速览——pnpm sf CLI(tsx 直吃 TS,无需构建):

pnpm sf wxr:probe --wxr path/to/export.xml   # 只读诊断:流式解析→映射→清洗→计数报告(不入库)
pnpm sf source:register --site <key> --wxr <path> [--sitemap <path>] [--media <dir>]   # 快照登记(content-addressed、幂等)

日常质量门(等价于 CI):

pnpm lint && pnpm typecheck && pnpm test

说明:集成测试由 vitest globalSetup 自动创建临时库 sf_test_<ts> 并注入 DATABASE_URL_TEST,结束即 DROP——勿在 .env 手动设置该变量。

目录结构

apps/api/            NestJS standalone 骨架 + Drizzle schema/迁移(21 表,数据库约束即规格)+ source 快照登记
packages/contracts/  UniversalRow / Route / Render / Theme 跨源契约(冻结,变更须升版)
packages/adapter-wordpress/  WXR 适配器(saxes 流式解析 + PHP unserialize + 映射 + HTML 白名单清洗,纯函数无 DB)
tests/               单元 + 集成测试(fixtures:WXR/Sitemap/媒体样例)
infra/               docker-compose:PostgreSQL 16(端口 5434,避开 cms 项目 5433)
scripts/             migrate-test.sh 等运维脚本 · sf.ts(source:register / wxr:probe CLI)
docs/                adr/ 决策记录 · evidence/ 基线证据 · superpowers/ 规格与计划

文档导航

  • 设计基线(表结构/技术决策/阶段划分):docs/superpowers/specs/2026-10-20-siteforge-vertical-slice-design-v2.md
  • 阶段 0+1 实施计划:docs/superpowers/plans/2026-10-20-slice-foundation-phase0-1.md
  • 架构决策记录(ADR-001~016):docs/adr/
  • 基线证据(环境/真实 WXR):docs/evidence/

当前状态

  • 阶段 1 已完成:契约冻结、21 表数据模型全量落地(不可变触发器/唯一约束/CHECK 均落数据库并被 75 项测试锁定)、空环境一键启动+迁移验收通过。
  • 阶段 2 已完成:WXR 流式适配器(saxes 流式解析 → PHP unserialize → UniversalRow 映射 → HTML 白名单清洗 → 导入前报告)、source:register 快照登记(content-addressed、幂等)与 pnpm sf CLI;263 测试全绿,真实 hesupipe WXR(5.5MB)冒烟 18 项计数对照全过(峰值堆 62MiB < 100MiB,证据见 docs/evidence/phase2-smoke.md)。
  • 下一步:阶段 3——内容/分类/媒体入库(source_records 落库、稳定身份 upsert、checksum skip、route_registry 生成);输入清单见 Plan 2 文末"移交阶段 3 清单"。
S
Description
No description provided
Readme 203 KiB