# 参与开发 ## 环境 - Node ≥ 22.19(`^22.19.0 || >=24.0.0`) - **零依赖**:`dependencies` 是空的,**不需要 `npm install`** 就能跑测试 ## 跑测试 ```bash npm run test:no-isolation # 推荐:所有测试共享一个进程 npm test # 等价于 node --test(默认进程隔离,慢一些) ``` `test/cordis-integration.test.mjs` 需要本机装着 DSH。找不到时它**跳过而不是失败**—— 这是刻意的(开源仓库不该因为"没装 DSH"就变红)。路径按这个顺序解析: 1. 环境变量 `DSH_APP_NODE_MODULES`(指向 `resources/app/node_modules`); 2. `test/.dsh-app-path` 的第一行 —— **这个文件不入库**(已进 `.gitignore`), 建议直接放本机路径,比每次设环境变量省事: ```bash echo 'E:/DSH Desktop/resources/app/node_modules' > test/.dsh-app-path ``` 3. 桌面版的几个常见安装位置(`C:/Program Files/DSH Desktop/…` 等)。 都找不到就跳过。**别把本机路径写回测试文件**——它会让公开仓库依赖某台机器的布局。 ### 测试清单 | 测试文件 | 覆盖 | |---|---| | `test/plugin.test.mjs` | 宿主契约:导出、patch 一致性、装配、卸载、段落自我否决 | | `test/encoding.test.mjs` | 编码探测与解码(含手工构造的 GBK/UTF-16 真实字节)| | `test/chapters.test.mjs` | 章节解析与误切防护(重点在**拒绝不是章节的行**,含重复目录行合并)| | `test/library.test.mjs` | 书库往返、字节区间精确性、幂等、路径安全、`reindex` 的锚点重映射与拒绝语义 | | `test/location.test.mjs` | 笔记落点:第一种**「根之外」的路径**,以及老数据迁移 | | `test/spoiler.test.mjs` | **防剧透边界**:真实书籍 + 逐字断言后续章节不出现 | | `test/injection.test.mjs` | **真正的注入路径**:`GET /context` 与 `systemPrompt.section` 两条路必须一致 | | `test/prompt-order.test.mjs` | 注入顺序(稳定→只增→动态)、**缓存前缀稳定性**、时间感知 | | `test/notes.test.mjs` | **只追加不重写**(逐字节比较)、AI 回应默认不落盘、草稿 | | `test/categories.test.mjs` | 书架分类:主观归类、未分类置顶、旧数据迁移 | | `test/tags.test.mjs` | tag 归一化严格性与建议排序 | | `test/persona.test.mjs` | `persona.md` 三件事:不存在时不写、存在时只读、坏内容不覆盖 | | `test/discussions.test.mjs` | 讨论时间线的**向后兼容**(老书没有这个文件)| | `test/compact.test.mjs` | 背景认识的**预算分配与压缩**(保名 / 保号 / 真变小)| | `test/background.test.mjs` | 背景认识的分区层与权重表 | | `test/settings.test.mjs` | 运行期设置(联网档位不再需要改配置重启)| | `test/memory.test.mjs` | **绝不挂死**(race 而非只 abort)、联网面精确、失败语义分明 | | `test/routes.test.mjs` | HTTP 数据面(真 `node:http` + 真 `fetch`)| | `test/contract.test.mjs` | 前后端路由契约(客户端路径必须有宿主路由接住)| | `test/cordis-integration.test.mjs` | **真实 cordis 集成**:挂真宿主服务,验证 inject / 段落装配 / 工具闸 | | `test/client.test.mjs` | 浏览器契约 + 进度锚点纯函数 + **组件冒烟执行** | > `test/digest.test.mjs` 已并入 `spoiler.test.mjs` 与 `memory.test.mjs`; > `test/helpers/server.mjs` 是共享的 HTTP 夹具,不是测试文件。 `test/spoiler.test.mjs` 与 `test/notes.test.mjs` 是最重要的两份。前者造一本**每章带 唯一标记**的书,把进度停在中间,逐字断言后面几章的标记一个都不在投喂文本里;后者 先往 `notes.md` 里写一段"用户的珍贵内容",再追加两条笔记,然后**逐字节比较**原文 有没有被动过。任何一次重构让它们变红,就是剧透回归或数据丢失回归。 `test/cordis-integration.test.mjs` 是**唯一在真实宿主服务上跑的测试**:它把真的 `@deepseek-ai/cordis` + `dsh-system-prompt` + `dsh-tools` 装起来,再把本插件挂上去, 断言三个 inject 全部解析、`systemPrompt.assemble()` 真的产出了段落、 `tools.guardReason()` 真的拒了越界读取。这条测试能抓住一类别处抓不到的故障: **inject 名字写错**——它的表现是插件静默地永远等待,连日志都没有。 ## 改客户端半边(`lib/client.js`) 它是**普通脚本**、没有构建步骤,所以: - 不能用 `import` / `export` / JSX; - React 必须手写 `React.createElement`(文件里别名 `h`); - 样式是 JS 模板字符串,注入的 `