项目:vitest-playground | 版本:v1.2 | 生成日期:2026-10-06 | 依据:源码分析
本篇是全套文档(00-20,共 21 篇)的"最后一页":术语表负责统一全文档与源码注释里的用语口径,反向索引负责"手里拿着一个源文件、想知道该读哪几篇"和"读完某篇、想知道代码在哪"的双向检索,阅读路径则把 21 篇按真实任务重组——因为没有人会按编号顺序读完全套文档,大家都是带着任务来的。本篇不引入任何新事实,所有条目都可以通过"相关篇目"回溯到源码证据;与《00-项目总览》§7 早期术语表的关系是收录其全部条目并扩充到当前篇目规模,两处如有出入以本篇为准。
术语按"机制域"分组排列(执行与沙箱、判题与课程、协议与契约、前端与持久化、部署与工程),组内按逻辑顺序而非字母序——因为多数术语只有在机制链上才能理解。每条给出:定义、源码锚点(文件与关键位置)、详述篇目。
| 术语 | 定义 | 源码锚点 | 详述篇目 |
|---|---|---|---|
| 运行器 | 对 vitestServerRunner.ts 整体的统称,集 HTTP 处理器、装配器、执行器、解析器四重职责于一身 | 项目根 vitestServerRunner.ts(485 行) | 07 §1、18 走读一 |
| 中间件 | vite.config.ts 中 vitestRunnerMiddleware() 插件,把运行器挂到 dev/preview 服务器的 /api/run-vitest | vite.config.ts 第 6-21 行 | 01 §1、16 ADR-001 |
| 沙箱(runDir) | 每次运行临时创建的目录 .vitest-runs/run-<mkdtemp 随机>/,内含配置与课时文件,跑完即删 | vitestServerRunner.ts 第 21、90-93 行 | 01 §3.3、16 ADR-005 |
| RUNS_ROOT | 沙箱目录根常量(.vitest-runs/),必须在项目根之下使 vitest 能向上解析 node_modules | 第 20-21 行注释 | 01 §3.3、05 流程一 |
.vitest-sandbox/ | 历史共享沙箱方案的遗留目录(已 gitignore,非运行时依赖),与 RUNS_ROOT 只差一个词 | 仓库根目录(残留样例) | 00 §7、13 问题 3 |
| env 白名单 | 子进程环境变量只含 NODE_ENV/HOME/LANG/PATH(Windows 另加两项),绝不继承 process.env | buildChildEnv 第 96-108 行 | 08 §3-L5、16 ADR-003 |
| 进程组整杀 | detached: true 建独立进程组,超时以 process.kill(-pid, 'SIGKILL') 整组击杀,不留 vitest worker 孤儿 | 第 355-379 行 | 01 §3.4、18 §1.6 |
| 快速失败链 | handleRunVitest 中"越廉价的检查越靠前"的校验顺序:405 → 400(body/字段)→ 400(课时)→ 429 → 执行 | 第 437-485 行 | 03 §5.3、18 §1.3 |
| 所有权转移 | 信号量 release 时若有等待者,槽位直接移交且 active 不减,消除微任务间隙超订 | createRunLimiter 第 320-342 行 | 07 §1.6、16 ADR-011 |
| 零残留不变量 | 任何路径退出后 RUNS_ROOT 下无残留目录(finally rm + 死循环测试断言) | 第 311-313 行;tests/runner-security.test.ts | 05 不变量 1、08 §4 |
| 结果文件主路径 | 结果优先读 --outputFile.json=result.json,stdout 仅为回退与排障(免疫 console.log 污染) | finishRun 第 402-420 行 | 16 ADR-004、18 §1.7 |
| output 裁剪 | stdout 累计上限 1MB、失败消息取前 6 行、marker 截 200 字符、异常 output 附尾部 2000 字符 | 第 189-199、363-369 行 | 08 §3-L7 |
| 术语 | 定义 | 源码锚点 | 详述篇目 |
|---|---|---|---|
| 装配 | 服务端把用户代码与课时数据合成为沙箱文件集的过程 | buildLessonFiles 第 72-88 行 | 07 §1.3、18 走读二 |
| 防篡改契约 | 请求只许 chapterKey/lessonKey/code 三字段,grader/hiddenGrader/extraFiles/环境/开关全由服务端推导 | RunBody 第 49-53 行;runner.ts 第 4-8 行注释 | 08 §3-L2、16 ADR-006 |
| grader | TDD 课时的可见主校验测试(lesson.spec.ts),前端只读展示 | data.ts 第 13 行(M4 注释) | 04 §1.2、07 §4 |
| hiddenGrader | 隐藏校验测试(lesson.hidden.spec.ts),不进入任何 HTTP 响应,防硬编码蒙混 | data.ts 第 15 行;tdd-fizzbuzz 实例 | 05 流程二、19 §4.2 |
| 判题器 | 项目中无独立模块,指"数据侧定义 + 服务端装配执行 + 结果侧信息过滤"三角色协作体系 | data.ts + buildLessonFiles + parseVitestJson | 07 §4 |
| 课时形态 | 普通 playground / TDD / jsdom / bench 四种学习者可感知形态,由 Lesson 字段组合推导,无显式 mode 字段 | data.ts 第 6-25 行 Lesson 接口 | 00 §2.1、16 ADR-007 |
| TDD 模式 | 有 grader(非纯空白)即进入:用户编辑被测实现(lesson.ts),课程给定测试 | chapter.tsx 第 63 行 isTDD 推导 | 05 流程二、19 §4.2 |
| pragma | 写在 spec 文件首行的环境指令 // @vitest-environment happy-dom,文件级切换执行环境 | buildLessonFiles 第 75 行 | 05 流程三、19 §4.3 |
| bench(基准模式) | benchmark: true 课时以 vitest bench 运行,无 JSON reporter,解析默认表格输出 | 第 219-243 行 parseBenchTable | 05 流程五、19 §3.6 |
| extraFiles | 随课时写入沙箱的附加源文件(如 api.ts),供 vi.mock 解析真实模块;文件名同样过白名单 | data.ts vi-mock 课时;第 86 行 | 04 §1.2、19 §4.5 |
| trim 守卫 | 纯空白的 grader/hiddenGrader 视为不存在(与旧前端语义等价,测试锁定) | 第 77-81 行注释 | 07 §1.3 |
| 通关 | 课时运行 success === true 后写入的进度状态(status:'passed');看答案运行也记通关(backlog 未拦截) | chapter.tsx 第 151-154 行 | 04 §3、13 问题 5 |
| check:lessons | 课程质检脚本:逐课时实测 solution 必须通过、code 允许教学性失败、esbuild 语法门禁 | scripts/check-lessons.ts(258 行) | 05 流程六、09 §3 |
| 术语 | 定义 | 源码锚点 | 详述篇目 |
|---|---|---|---|
| RunResult | 接口传输模型:{success, output, passed, failed, pending?, error?, tests?, markers?, coverage?} | types.ts 第 17-27 行 | 03 §3、15 §4、19 §5 |
| TestResult | 前端页面状态模型:status 四态(idle/running/success/error)+ RunResult 字段投影 | types.ts 第 1-15 行 | 04 §2.2、02 §5.1 |
| marker | 失败用例的行级定位 {line, message},普通课时下发 Monaco 画红色波浪线,TDD 课时丢弃 | ErrorMarker types.ts 第 62-65 行 | 02 §5.3、18 §3.3 |
| CoverageSummary | 覆盖率摘要 {total, files[]},四项 0-100 裸百分比,来自 json-summary 报告 | readCoverage 第 130-149 行 | 04 §2.1 |
| 错误四象限 | 400/429/500 ="没运行",200+success:false ="运行了但失败";前端统一折叠成失败结果 | handleRunVitest;runner.ts 第 28-44 行 | 03 §4、19 §3 |
| ProgressMap | localStorage 进度模型,键为 ${chapterKey}:${lessonKey},含 code/status/lastRunAt | progress.ts 第 6-16 行 | 04 §3、08 §6 |
| computeBadges | 五枚成就徽章的纯函数计算(first/ten/half/chapter/master,零课时特例) | progress.ts 第 73-89 行 | 04 §3.2 |
| 术语 | 定义 | 源码锚点 | 详述篇目 |
|---|---|---|---|
| COEP/COOP | Cross-Origin-Embedder-Policy: require-corp 与 Cross-Origin-Opener-Policy: same-origin 响应头,WebContainer 时代遗留,现服务于 Monaco Worker 同源加载 | vite.config.ts 第 43-57 行 | 16 ADR-012、03 §8 |
| Monaco 本地打包 | 绕过 CDN loader,仅引入编辑器内核 + TS 语言贡献,worker 以 ?worker 同源加载 | monacoSetup.ts 全文 | 16 ADR-008、10 §5 |
| vitestGlobalsDts | 手写的精简 Vitest/RTL 全局类型声明,经 addExtraLib 注入 Monaco(只管编辑器诊断,不管运行) | vitestGlobalsDts.ts(125 行) | 07 §5.4 |
| markers effect | CodeEditor 中把 errorMarkers 写入 Monaco 模型的 useEffect,空数组显式复位(owner 'vitest') | CodeEditor.tsx 第 59-82 行 | 18 §3.3、02 §5.3 |
| isReady 垫片 | useRealVitest 返回的恒真值,WebContainer 时代异步就绪检测的兼容残留 | runner.ts 第 52 行 | 13 问题 6、16 ADR-002 |
| 自动重跑 | 课时页开关,代码停止输入 1.2s 后自动运行(watch 体验的轻量替代) | chapter.tsx 第 179-186 行 | 02 §5.2 |
| manualChunks | vite 构建的手动分包:monaco / antd / react-vendor 三块,配 chunkSizeWarningLimit 2500 | vite.config.ts 第 25-36 行 | 10 §5、17 §2.2 |
| WebContainer(历史) | 早期方案的浏览器内 Node 运行时(@webcontainer/api),已整体移除,残迹见 isReady/文案/COEP | README.md 第 114 行 | 16 ADR-002、00 §8 |
| 术语 | 定义 | 源码锚点 | 详述篇目 |
|---|---|---|---|
| 生产形态 | NAS Docker 容器(整应用)→ frpc 隧道 → VPS nginx 反代(10r/m 限流)→ pnpm preview :8000 | docs/deploy/runbook.md | 06 §4、14 §9 |
| 双层限流 | 应用内信号量(2 并发/8 排队,429)+ nginx 按 IP 限流(10r/m + burst 3,429),各自独立生效 | 第 316-318 行;runbook nginx 配置 | 16 ADR-011、03 §8 |
| 铁律 | AGENTS.md 中对改动者的强制约束:不删中间件/运行器、不削弱超时与白名单、只用 pnpm | AGENTS.md 铁律 1-3 | 08 §7、11 §7 |
| 四绿门禁 | 改动验收基线:pnpm tsc && pnpm test && pnpm build && pnpm check:lessons 全过 | .github/workflows/ci.yml 双 job | 06 §7、09 §4 |
| 容器化隔离(计划) | `VITEST_RUNNER_BACKEND=local | docker` 双后端方案(--network none、非 root、只读根),仅有方案文档未实现 | docs/superpowers/plans/2026-09-28-container-isolation.md |
两个特别容易混淆的对照组值得在正文里点破。第一组:"运行器"与"runner.ts"。vitestServerRunner.ts 是服务端运行器(Node 侧,真正执行测试);src/pages/vitest-learn/runner.ts 是浏览器侧的请求客户端(fetch 封装 + Hook)。两个文件名共享 "runner" 一词纯属历史巧合,读到"运行器"三个字时默认指前者。第二组:.vitest-runs 与 .vitest-sandbox。前者是现行运行时目录根(RUNS_ROOT),后者是早期共享沙箱方案的遗留样例(已 gitignore、非运行时依赖)——《13》问题 3 建议本地删除它,接手者若看到两个目录并存,不要把后者当参考实现。另有三组"近义不同义":grader(可见测试)与 hiddenGrader(隐藏测试)的区别在于是否进入 HTTP 响应;environment: 'jsdom'(字段名)与 happy-dom(实际执行的模拟 DOM 实现)是"声明类别"与"执行实现"的错位;RunResult(接口契约)与 TestResult(页面状态)的分界在于 status 字段的归属。
使用这张术语表还有一个正确姿势:不要把它当字典逐条背,而是当"歧义探测器"用。读其它篇目或源码注释时,凡是遇到一个词与你的理解有微妙不符(最常见的是"装配"到底包含不含配置写入、"判题器"到底是不是一个模块),先回到这里对一次口径,再决定是否继续往下读——本项目的多数术语都有一条"定义 → 源码锚点 → 详述篇目"的三级追溯链,任何一处存疑都可以在两分钟内落到具体代码行。反过来,如果你在源码注释里发现了一个本表未收录的行话(例如某次新提交引入的新概念),按第 4 节的维护约定把它补录进来,注明出处与首次出现的位置,让后来者不必重走你的理解弯路。
使用方法:改某个文件前,先在左列找到它,把右列篇目当作"改动影响面阅读清单";反过来,读完某篇想知道代码落点,也可以从右往左查。标注 ★ 的是改动频率与影响半径最大的文件。
| 源码/工程文件 | 主要讲述篇目(★ 为核心篇) |
|---|---|
★ vitestServerRunner.ts | 01 §3 ★、03、05、07 §1 ★、08 ★、12、13(问题 10)、15、16(ADR-003/004/005/011)、18 走读一/二 ★、19 §3 |
★ vite.config.ts | 01 §1、02、07 §2、14 §2、16(ADR-001/012)、17 §2.5 |
★ src/pages/vitest-learn/data.ts | 00 §2、04 §1、07 §3、11(SOP-1/2/3)、13(问题 9)、15 §7、19 §4 |
src/pages/vitest-learn/chapter.tsx | 02 §2.2、04 §8、05 流程一/二、07 §5.2、18 走读三、19 §4 |
src/pages/vitest-learn/CodeEditor.tsx | 02 §4.1/§5.3、07 §5.3、13(问题 7)、18 §3.3 |
src/pages/vitest-learn/runner.ts | 03 §4、07 §5.1、13(问题 6)、18 §3.2/§3.4 |
src/pages/vitest-learn/types.ts | 03 §3、04 §2、15 §4、19 §5 |
src/pages/vitest-learn/progress.ts | 04 §3、07 §5.5、08 §6(问题 1)、13(问题 11) |
src/pages/vitest-learn/monacoSetup.ts / vitestGlobalsDts.ts | 02 §5.4、07 §5.4、10 §5、16 ADR-008、17 §2.2 |
src/pages/vitest-learn/index.tsx / progressPage.tsx / Quiz.tsx | 02 §2.1/§2.3、07 §5.5、04 §3 |
src/app.tsx / main.tsx / theme.tsx / usePageMeta.ts / AppLayout.tsx | 02 §1/§3、07 §5.5、13(问题 8) |
tests/runner-security.test.ts | 06 §5.2、08 §4 ★、09 §2.1、03 §9 |
tests/lessons.test.ts / progress.test.ts / setupTests.ts / __mocks__/ | 09 §2.2-2.5、13(问题 2) |
scripts/check-lessons.ts | 05 流程六、06 §5.3、09 §3、11、17 §2.7 |
vitest.config.ts | 14 §3、17 §2.1 |
package.json / pnpm-lock.yaml / .npmrc / pnpm-workspace.yaml | 14 §4/§8、17 全篇 ★ |
tsconfig.json | 14 §5 |
.github/workflows/ci.yml | 06 §7、09 §4 |
docs/deploy/runbook.md | 06 §4.3、08 §5、14 §9 |
docs/superpowers/plans/(两份方案) | 01 §6、08 §2、13、16 全篇(考古出处) |
AGENTS.md / README.md | 00、08 §1/§7、11 §7、16(ADR-002 证据)、13(问题 1) |
index.html / public/(SEO 资源) | 02 §6、00 §4 |
从这张表能读出一个事实:篇目覆盖在 vitestServerRunner.ts 与 data.ts 两个文件上高度重叠(前者 11 篇提及、后者 7 篇),这与《07》§6 依赖图的结论一致——它们就是全项目的两个枢纽。反向的提醒是:CodeEditor.tsx、progressPage.tsx 等前端文件的篇目覆盖较薄(多为 02/07 的段落级描述),若要大改前端,建议以《02-页面设计》的组件树为起点补一次现场走读,不要假设文档已覆盖所有行级细节。
维护这张索引时有三条操作规则。其一,新增文件即登记:任何新提交引入的源文件(或被删除的文件)都应在这张表里增删对应行,引用篇目写"实际讲过它的篇目"而非"理论上应该讲的篇目"——空挂的引用比没有引用更误导。其二,篇目号是稳定标识:文件可以改名,篇目编号不回收复用;若某篇被拆分或合并,在原编号位置留一行指向新篇目的说明。其三,重叠度是健康信号:枢纽文件被多篇覆盖是正常的(多视角才能覆盖多职责),反而要警惕的是某个源文件在右列完全消失——那通常意味着文档集出现了盲区,应该在下次修订时补写或并入邻近篇目,而不是放任"代码先行、文档永久滞后"。
路径的设计原则是"最短闭环":每个任务给出首读篇(建立正确心智)、操作篇(动手时对照)、验收篇(收尾门禁),以及一条"别踩"的提醒。所有路径的公共前置是《00-项目总览》——它足够短,且术语与本篇对齐。
首读《11-扩展开发指南》SOP-1/SOP-2(操作步骤与 grader 编写约定)与《04-数据模型》§1.2(Lesson 字段语义);动手时对照《19-数据契约与示例集》§4——四种形态各有一份真实课时实例,新课时照着最近形态的实例抄字段结构最不容易漏;收尾跑 pnpm check:lessons(solution 必须实测通过)与 pnpm test(lessons.test.ts 校验 key 唯一性与 code||grader 非空)。两个提醒:其一,纯空白 grader 等于没有 grader(trim 守卫),不要用空白字符串"占位";其二,extraFiles 文件名过白名单,扁平名以外一律被拒。若要加的是新形态(而非新课时),升级为先读《16》ADR-007 理解形态推导哲学,再走《11》SOP-4 的扩展三道门。
首读《07-模块详解》§4(判题器三角色协作)与《05-核心流程》流程二(TDD 批改时序,理解 hidden 失败事实会出现在 output 但源码不会泄露);操作时对照《19》§4.2 的 tdd-fizzbuzz 范本——hiddenGrader 的写法范式(全量遍历防查表、边界补充用例)都在这个实例里;收尾四绿门禁,特别注意 pnpm check:lessons 会用新 grader 实测原 solution,若把规则调严到 solution 都过不了,CI 会替你拦下。提醒:TDD 课时改判题不影响前端(markers 本就被丢弃),但会改变结果面板逐用例列表的行数——有 UI 截图类验收(gui-test-screenshots)时需同步。
首读《12-FAQ与故障排查》§2 排查决策树与 §3 高频报错逐条拆解(它是按"用户看到的文案"索引的,从报错文案直接定位);然后按错误类型分流:HTTP 4xx/5xx 查《15》§3 错误码总表与《19》§3 的对应形态示例(含"非法文件名实为 500"的口径说明);200+success:false 查《03》§4 的错误约定(理解为什么 200 也算失败);怀疑防线误伤查《08》§3 找到是哪一层拦截(L1-L10 各有触发条件与测试锚点)。维护者级别的深挖(卡死进程、残留目录、stdout 尾部信息损失)补读《12》§4-§5 与《08》§6 的已知隐患清单。提醒:所有失败形态的 output 文案都是源码原文,grep 文案是定位代码的最快方式。
首读《06-部署运行》§10 发布核对清单(逐项含验收方式);操作时对照《14》§9(NAS Docker → frpc → VPS nginx 三级链路的配置级展开)与 docs/deploy/runbook.md 原文(nginx 限流配置、env 泄露面检查、故障处理命令);安全项逐条过《08》§7 安全检查清单(超时/白名单/env 键集合/上限常量未被削弱);涉及依赖变更的(升级、换包)加读《17-依赖全景》的健康度评估与升级风险列——特别是 vitest 三件套必须同版本联动、@types/node 与 engines 的对齐问题。提醒:/api/run-vitest 依赖 Vite 进程,纯静态托管不可用(《00》§5),任何"搬到 Vercel/GH Pages"的想法可以直接放弃。
首读《18-代码走读实录》三条走读(逐段带码,建立"为什么每行长这样"的直觉);补齐《16》ADR-003/004/005/011 四条决策(知道哪些行为是决策而非巧合,才不会"顺手优化"掉防线);操作时全程对照《08》§7 安全清单与《01》§3.8 的"新增防线正确姿势"(新纯函数 + 导出 + 独立 describe,挂快速失败链);收尾除四绿外必须跑 runner-security 全量用例(集成用例 30-60s 预算,含真 spawn)。提醒:AGENTS.md 铁律 2 是硬约束——不得削弱超时与 kill 兜底、不得放宽白名单与并发限制;timeoutMs 的 Math.min 钳制与 env 白名单的精确集合断言,任何"放宽"都会被 CI 用例当场拦截。
顺序读法:00(定位与现状)→ 01(架构与决策表)→ 05(五个核心流程)→ 07(模块落地层)→ 16(为什么是这样)→ 13(还有什么欠账);配合本篇 §2 反向索引随时跳源码。做评审或排期时,《13》的三阶段路线图给出改动排序逻辑,《16》结尾的复盘给出"迁移残迹要有意识清理、暂不做要留档"两条过程经验;评估任何新方案前先查 docs/superpowers/plans/ 是否已有存档方案(容器化就是"已有方案待落地"的实例)。
21 篇文档自身也需要防漂移,以下约定沿袭《13》结尾的条款并扩展到新五篇。第一,版本与日期:每篇头部元信息的版本号与生成日期表示"该篇最后一次对源码核实的时间",源码行为变更时必须修订对应篇目并更新版本——历史上已发生过一次口径漂移(《12》§3.6 曾称非法文件名为 400 变体,源码实为 500 出口,已于 2026-10-06 按《19》§3.5 的源码口径回刷修正),这类不一致一经发现应立即在两处同时修正。第二,测试是最高事实源:文档、测试断言、实际行为三者冲突时,以测试为准修订文档,并以测试名为引用锚点(如"「旧 files 形态请求被 400 拒绝」用例")。第三,重复内容用引用不用复制:跨篇的机制描述只在一篇展开(如并发语义只在 07 §1.6、防篡改只在 08 §3-L2),其余篇目用"见《0X》"——新五篇严格遵循了这一分工(16 只写"为什么"、17 只写"依赖评估"、18 只写"逐行走读"、19 只写"实例"、20 只写"索引")。第四,无法确认的事实标注 ⚠️ 待确认,禁止用推测填充(现有标注集中在 bench 单位、TS6 行为、antd 主题接入等处),接手者处理完一条就应删除对应标注。最后,本篇作为索引,其阅读路径与术语条目应随篇目增删同步维护——当第 22 篇出现时,先来这里登记,再动笔写正文。