English
# dsh-zotero 开发指南
## 仓库结构
```
src/
index.ts # 插件入口(纯 re-export)
service.ts # ZoteroService(Cordis 服务)
local/provider.ts # LocalApiProvider(Zotero Local API)
local/*-domain.ts # 领域管线(search/detail/retrieve/attachment/export/changes/browse)+ scope-directory/pagination/limits
http-client.ts # HTTP 传输层(loopback fetch)
config.ts # Config schema 与校验
types.ts # 领域类型(DTOs)
contract.ts # Remote wire 契约(descriptor + 严格 codec)
errors.ts # 错误类与错误码
json.ts # 无损 JSON 读取helper
concurrency.ts # 有界并发
evidence.ts # BM25 排名
attachments.ts # 附件选择
local/children-wire.ts # Local API 子对象契约:裸 /children(笔记/附件)与 ?itemType=annotation(批注)
normalize.ts # Zotero 条目 → 领域 DTO 归一化
presentation-meta.ts # 工具结果的展示投影
refs.ts # Zotero 对象引用语法
ref-grammar.ts # 引用文本模式
export-items.ts # 逐文档导出解析
export-mapping.ts # 引用 → 批量条目映射
ask.ts # 连接失败时的 user-question 交互(每类故障一次提问,并行调用共享同一张卡)
prompt.ts # 面向模型的 policy section
command.ts # /zotero status 命令
remote.ts # Web tab 的 Remote 服务
typert.ts # Typert manifest
settings-namespace.ts # 设置命名空间常量
tools/ # 8 个模型工具(search/get/children/attachment/retrieve/export/browse/changes)+ present/validate 共享件
client/ # 浏览器端(设置页、Sources tab、sources 归约、workspace 视图)
tests/ # 单元测试(mock Zotero server + 浏览器设置页测试)
```
## 安装与构建
```sh
npm install # 本仓库与 deepseek-harness 并列,仅嵌套副本才加 --no-workspaces
npm test # 单元测试(mock Zotero server + browser card tests)
npm run typecheck # 上游依赖状态检查 + tsc --noEmit(node/test/client projects)
npm run build # tsc + esbuild(node lib/ + browser lib/client.js)
npm run build:client # 仅重新构建浏览器端
npm run test:coverage # 覆盖率门禁(97 语句 / 95 分支 / 98 函数 / 97 行)
npm run harness:check # 上游版本钉与声明新鲜度(typecheck 已内置这一步)
npm run harness:pin -- # 把版本钉整体移到 (devDeps/overrides/peers/engines/README/AGENTS)
npm run verify:pack # 打包产物门禁(tarball 必含入口与 cordis.patch.yml)
npm run format # prettier --write
npm run format:check # 格式化检查
```
> 本仓库与 deepseek-harness 并列为 sibling 目录(见 AGENTS.md),直接 `npm install`;只有把它嵌套进 harness workspace 副本时才加 `--no-workspaces`。
>
> 上游**类型**来自 `node_modules/@deepseek-ai/*` 符号链接所指向的 sibling checkout 的 `lib/types` 构建产物(与发布版消费者的读取方式一致)。sibling 的 `git pull` 不会重建它们,因此 `npm run typecheck` 先跑 `node scripts/harness-state.mjs`:某个被导入的包其 `src` 比声明文件更新时,它会给出需要执行的构建命令。`npm run harness:check -- --strict`(发布检查用)把这一项从提示升级为失败。
## 集成测试
```sh
npm run test:integration
# 或: ZOTERO_INTEGRATION=1 npx vitest run tests/integration/zotero.integration.spec.ts
```
需要本地 Zotero 运行在 `127.0.0.1:23119`。
## 两部分构建
- **Node 端**(lib/):tsc 从 TypeScript 生成,包含 service、tools、provider、transport。
- **浏览器端**(lib/client.js):esbuild 生成,包含设置页和 Sources tab 视图。
## 本地开发
### 从 dsh 源码构建
```sh
pnpm install && pnpm run build # 先构建 dsh
pnpm dsh web --patch ./dsh-zotero/dev.cordis.yml
```
### 使用 npm 安装的 dsh
三种方式:
1. **Tarball 安装验证**:
```sh
npm pack
dsh plugin --profile add ./dsh-zotero-*.tgz
cd ~/.dsh/profiles/
node --input-type=module < /path/to/dsh-zotero/scripts/smoke.mjs
```
2. **Node 端热替换**:
```sh
npm run dev & # tsc --watch
dsh web --patch ./dev-lib.cordis.yml --port 3307
```
3. **浏览器端开发**:
```sh
npm run dev:client # esbuild watch
# 需先将 checkout 安装到 profile 中浏览器端才会加载
```
## 测试
- 单元测试使用 MockZotero(mock HTTP server)
- 浏览器设置页测试使用 jsdom + @testing-library/react
- 覆盖率门禁见 `vitest.config.ts`(97 语句 / 95 分支 / 98 函数 / 97 行;`src/index.ts`、`src/types.ts`、`css-modules.d.ts`、`sources/model.ts` 为纯类型/重导出除外项)
- 集成测试运行在真实 Zotero 上,默认跳过
## 发布检查清单
- `npm run harness:check -- --strict` 通过(版本钉一致,且上游声明不落后于 sibling 源码)
- `npm run verify:pack` 通过(tarball 含 `lib/index.js`、`lib/index.d.ts`、`lib/client.js`、`cordis.patch.yml`)
- `npm test` 通过
- `npm run typecheck` 通过
- `npm run test:coverage` 通过(门禁见上)
- `npm run format:check` 通过
- `npm run build` 成功
- tarball 安装后 smoke.mjs 通过
- 有 Zotero 时集成测试通过