@jayyuen66/dsh-plugin-shared 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -91
- package/README.zh-CN.md +186 -93
- package/config/tsconfig.client.base.json +1 -1
- package/dist/canonicalize-region-paths.js +1 -1
- package/dist/card-apply.js +1 -1
- package/dist/card-diff.js +1 -1
- package/dist/config/oxlint.base.js +1 -1
- package/dist/config/vitest.base.js +1 -1
- package/dist/errors.js +1 -1
- package/dist/index.js +1 -1
- package/dist/job-outcome.js +1 -1
- package/dist/jsonl.js +2 -3
- package/dist/lesson-bus.js +1 -1
- package/dist/project-key.js +1 -1
- package/dist/record.js +1 -1
- package/dist/text.js +1 -1
- package/dist/tool-events.js +1 -1
- package/dist/types/canonicalize-region-paths.d.ts +4 -3
- package/dist/types/card-apply.d.ts +12 -1
- package/dist/types/card-diff.d.ts +2 -2
- package/dist/types/config/oxlint.base.d.ts +22 -7
- package/dist/types/config/vitest.base.d.ts +11 -1
- package/dist/types/errors.d.ts +2 -1
- package/dist/types/job-outcome.d.ts +8 -4
- package/dist/types/lesson-bus.d.ts +3 -1
- package/dist/types/locale.d.ts +3 -2
- package/dist/types/record.d.ts +8 -0
- package/dist/types/text.d.ts +2 -1
- package/dist/types/tool-events.d.ts +0 -13
- package/package.json +7 -7
package/README.zh-CN.md
CHANGED
|
@@ -1,18 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://cdn.jsdelivr.net/gh/JayYuen666/dsh-plugin-shared@main/icon.svg" width="96" height="96" alt="dsh-plugin-shared logo" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">dsh-plugin-shared</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center"><em>dsh 插件家族共用的库包:一份 tool 事件台账、一套卡片骨架、一个项目键与一套 locale 面——只被插件 import,不由用户启用。</em></p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://www.npmjs.com/package/@jayyuen66/dsh-plugin-shared"><img src="https://img.shields.io/npm/v/@jayyuen66/dsh-plugin-shared?style=flat-square&logo=npm&logoColor=white" alt="npm version" /></a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/@jayyuen66/dsh-plugin-shared"><img src="https://img.shields.io/npm/dm/@jayyuen66/dsh-plugin-shared?style=flat-square" alt="npm downloads" /></a>
|
|
12
|
+
<a href="https://github.com/JayYuen666/dsh-plugin-shared/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/JayYuen666/dsh-plugin-shared/ci.yml?branch=main&style=flat-square&label=ci" alt="ci" /></a>
|
|
13
|
+
<img src="https://img.shields.io/badge/license-MIT-4c1?style=flat-square" alt="MIT" />
|
|
14
|
+
<img src="https://img.shields.io/badge/node-%3E%3D22.12-4c1?style=flat-square&logo=nodedotjs&logoColor=white" alt="node" />
|
|
15
|
+
<img src="https://img.shields.io/badge/dsh-%5E0.2.1--rc.2-658EFF?style=flat-square" alt="dsh" />
|
|
16
|
+
<a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/powered_by_dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white" alt="powered by dsh" /></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="https://skillicons.dev/icons?i=typescript,nodejs,vitest,githubactions" alt="tech stack" />
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<a href="https://github.com/JayYuen666"><img src="https://img.shields.io/badge/GitHub-JayYuen666-181717?style=flat-square&logo=github&logoColor=white" alt="GitHub" /></a>
|
|
25
|
+
<a href="mailto:cqwkkj@gmail.com"><img src="https://img.shields.io/badge/Email-cqwkkj@gmail.com-EA4335?style=flat-square&logo=gmail&logoColor=white" alt="Email" /></a>
|
|
26
|
+
<a href="https://twitter.com/JayYuen666"><img src="https://img.shields.io/badge/Twitter-X%2FJayYuen666-000000?style=flat-square&logo=x&logoColor=white" alt="Twitter/X" /></a>
|
|
27
|
+
<a href="./README.md"><img src="https://img.shields.io/badge/lang-English-658EFF?style=flat-square" alt="English" /></a>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
**一个 [dsh](https://github.com/deepseek-ai/deepseek-harness) 库包:同一作者的插件家族共用的运行期切面与 lint/test 基线。它只被插件包 import,用户不会启用它。**
|
|
31
|
+
|
|
32
|
+
## <img src="https://api.iconify.design/lucide/file-text.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 目录
|
|
33
|
+
|
|
34
|
+
- [它做什么](#它做什么)
|
|
35
|
+
- [安装](#安装)
|
|
36
|
+
- [确认它真的在工作](#确认它真的在工作)
|
|
37
|
+
- [它不提供什么](#它不提供什么)
|
|
38
|
+
- [模块清单](#模块清单)
|
|
39
|
+
- [config 面](#config-面)
|
|
40
|
+
- [已知边界](#已知边界)
|
|
41
|
+
- [数据与隐私](#数据与隐私)
|
|
42
|
+
- [版本与兼容](#版本与兼容)
|
|
43
|
+
- [常见问题](#常见问题)
|
|
44
|
+
- [开发](#开发)
|
|
45
|
+
- [许可](#许可)
|
|
46
|
+
|
|
47
|
+
## <img src="https://api.iconify.design/lucide/shield.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 它做什么
|
|
48
|
+
|
|
49
|
+
- **它是库,不是插件。** `package.json` 里没有 `dsh` 字段,包目录下也没有 `cordis.patch.yml`,所以躺在 `node_modules` 里的副本不会被宿主登记成插件。它没有设置卡、没有 client 半,也没有自己的路由。
|
|
50
|
+
- **它收的是各插件逐字重复的部分:**
|
|
51
|
+
- tool 事件台账(`./lib/tool-events`):一次扫描把 `SessionEvent[]` 变成调用表与结果表,PTC 子调用一并归并;`editPathOf` 负责台账看得见的编辑目标。
|
|
52
|
+
- 客户端卡片骨架(`./lib/card-apply`、`./lib/card-diff`):跨 HMR 的 apply 幂等守卫,以及把 `null`、`undefined` 与键缺失当成同一种值的表单差异比较。
|
|
53
|
+
- 运行期管道(`./lib/project-key`、`./lib/locale`、`./lib/lesson-bus`、`./lib/text`、`./lib/record`、`./lib/errors`、`./lib/jsonl`、`./lib/job-outcome`):那些每个插件都会重写一遍的小决定。
|
|
54
|
+
- 构建期与门禁基线(`./lib/canonicalize-region-paths`、`./config/*`):产物正典化,加上给每个消费方的 oxlint、vitest 与 tsconfig 起点。
|
|
55
|
+
- **它只做标记,不做决策。** 哪些工具算编辑、路径怎么过滤、预算多大、门禁何时该响,都留在各插件。共享层对交到它手里的领域没有意见。
|
|
56
|
+
|
|
57
|
+
### 收录边界
|
|
58
|
+
|
|
59
|
+
- **逐字同构是收录判据。** 同一份函数体在两处以上出现才收。看起来相似、但失败语义、缓存方式或覆盖形状不同的,留在原地。
|
|
60
|
+
- **领域判定留在外面。** 依赖某个插件自己的工具清单、策略或面向用户的措辞的谓词,不是样板,即使它出现了两次。
|
|
61
|
+
- **发布面就是契约。** 新增切面要同时加子路径、barrel 转出(构建期那一枚除外)、构建脚本入口、构建 `include` 与用例;漏一处会被「开发」一节的 parity 门判红。
|
|
62
|
+
|
|
63
|
+
## <img src="https://api.iconify.design/lucide/package.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 安装
|
|
16
64
|
|
|
17
65
|
```sh
|
|
18
66
|
npm install @jayyuen66/dsh-plugin-shared
|
|
@@ -26,109 +74,154 @@ pnpm add @jayyuen66/dsh-plugin-shared
|
|
|
26
74
|
import { isRecord, fieldOf } from "@jayyuen66/dsh-plugin-shared/lib/record";
|
|
27
75
|
```
|
|
28
76
|
|
|
29
|
-
终端用户不需要单独安装或启用它:任一消费包都会顺带把它带进去。它缺位时的失败是 `ERR_MODULE_NOT_FOUND`(说明符指向 `@jayyuen66/dsh-plugin-shared
|
|
30
|
-
|
|
31
|
-
## 前置要求
|
|
77
|
+
终端用户不需要单独安装或启用它:任一消费包都会顺带把它带进去。它缺位时的失败是 `ERR_MODULE_NOT_FOUND`(说明符指向 `@jayyuen66/dsh-plugin-shared/...`),不会静默降级。本包没有 default 导出;请从 barrel 或子路径用具名符号。
|
|
32
78
|
|
|
33
|
-
| | |
|
|
79
|
+
| 要求 | 值 |
|
|
34
80
|
| --- | --- |
|
|
35
|
-
| Node.js | `>=22.12.0
|
|
36
|
-
| dsh | `^0.2.1-rc.2`,声明为 **optional** peer
|
|
81
|
+
| Node.js | `>=22.12.0`——插件家族共用的发布侧下限,声明同一枚 `engines.node` 的包不会因为这个库而被拒 |
|
|
82
|
+
| dsh | `^0.2.1-rc.2`,声明为 **optional** peer(import 本包不需要宿主在场) |
|
|
37
83
|
| 模块形态 | 仅 ESM。`require()` 只在支持 `require(esm)` 的 Node 上可用 |
|
|
38
84
|
| Tree shaking | `"sideEffects": false` |
|
|
39
85
|
|
|
40
|
-
###
|
|
86
|
+
### 依赖契约
|
|
41
87
|
|
|
42
|
-
|
|
43
|
-
| --- | --- | --- |
|
|
44
|
-
| 经 `realpathSync` 读一个目录项 | `lib/project-key.ts` | 只读传给 `deriveProjectKey` 的那条路径;失败(ENOENT/EACCES)退回 `path.resolve` 的结果,不外抛 |
|
|
45
|
-
| 其余一概不碰 | — | `lib/**` 里没有别的文件访问、没有任何写入、不读环境变量、不发外部请求、没有 `eval`/`new Function`/动态 import |
|
|
88
|
+
`dependencies` 为空;所有 `@deepseek-ai/*` 只出现在 `peerDependencies` 与 `devDependencies` 里。`test/publish-manifest.test.ts` 钉住这条,理由是一次启动期故障,不是风格偏好:
|
|
46
89
|
|
|
47
|
-
`
|
|
90
|
+
- **宿主包写进 `dependencies` 会让消费方的宿主瘫掉。** profile 用 `nodeLinker: hoisted` 与 `autoInstallPeers: false` 安装,于是列在 `dependencies` 里的宿主包会被落成真实目录,与宿主进程已加载的那份并存。`@deepseek-ai/dsh-tools` 的工具运行期调度键是普通 `Symbol`(不是 `Symbol.for`),符号身份按模块实例计算:宿主从副本上读那个键得到 `undefined`,于是每一次原生工具调用都抛错。构建、类型检查与测试在此之前全部照绿;这条不变式只能落在 manifest 上。
|
|
91
|
+
- **`@deepseek-ai/dsh-output-retention` 是唯一必需 peer。** 它是值导入(`truncateEnd`),运行期由宿主提供;在 `peerDependenciesMeta` 里明写成 `"optional": false`,不靠"没写就等于必需",因为没写进那张表对 npm 7+ 就是硬要求。
|
|
92
|
+
- **其余 peer 都是 optional。** 除裸 `@deepseek-ai/dsh`(只声明宿主版本线)外,它们只承托类型面。放在 `peerDependencies` 里让发布出去的 `.d.ts` 保持诚实;`devDependencies` 让本仓能编译与测试。profile 不装 peer,所以不会产生副本。
|
|
93
|
+
- **版本口径跟着宿主走。** `dsh-*` 与 `cordis` 精确钉在宿主声明的版本上,保证复用同一份。`test/publish-manifest.test.ts` 还要求每条 `dsh-*` 钉版与 `@deepseek-ai/dsh` peer 的版本一致。
|
|
94
|
+
- 已知上游限制:`@deepseek-ai/dsh-llm` 的声明文件引用了它自己没声明的 `@deepseek-ai/dsh-attachment`。它经 `dsh-session` 传递进来,所以开 `skipLibCheck: false` 的消费方会看到来自那个包的 `TS2307`,不是来自本包。
|
|
48
95
|
|
|
49
|
-
|
|
96
|
+
### shared 的版本下限
|
|
50
97
|
|
|
51
|
-
|
|
98
|
+
消费者的 profile 可以用 pnpm `overrides` 把本包钉住。钉得低于某个 `./lib/...` 子路径被引入的那个版本,宿主起来时就会报 `N entries did not activate` 与 `does not provide an export named ...`:包装得上、永远不加载,而任何单仓 CI 都看不见。要钉版本请先对照本包的 `exports` 与 CHANGELOG。
|
|
52
99
|
|
|
53
|
-
|
|
54
|
-
| --- | --- |
|
|
55
|
-
| `.` | 下面列出的运行期切面的 barrel。`canonicalize-region-paths` 刻意不进,运行期消费方不必为它付一次导入 |
|
|
56
|
-
| `./lib/tool-events` | `scanToolEvents` → `calls`/`results` 两张表、`toolEventRowsOf`(它的单事件那一步,同一份实现)、`parseToolArguments` 与 `toolArgumentsBad`(自建折叠单元的消费方靠这一对,不必自己再读一遍 arguments 字段)、`editPathOf` 与 `EditTarget`(按 `kind` 判别,`write`/`read-view` 两支;路径取不到时 `path` 为 `undefined`);台账三个接口字段一律 `readonly`;并转出官方 `SessionEvent` 与两枚 PTC 事件类型 |
|
|
57
|
-
| `./lib/card-apply` | `claimApply` + `CardApplyCtx`:apply 幂等守卫(`globalThis` 标记 + `ctx.effect` 卸载清理) |
|
|
58
|
-
| `./lib/card-diff` | `diffTouched`:草稿层里哪些字段真的与快照不同(按值比较,所以 `undefined`、`null` 与键缺失是同一种值) |
|
|
59
|
-
| `./lib/project-key` | `deriveProjectKey` + `ProjectKeyOptions`:cwd → `尾目录名-<sha256(norm) 前 8 位 hex>`,兜底桶 `default` |
|
|
60
|
-
| `./lib/locale` | `Locale`(官方 `BuiltInLocaleId` 的别名)、`DEFAULT_LOCALE`、`resolveLocale`、`resolveLocalePreference`、`messagesFor`、`MessagesCatalog`,加 `LOCALE_SETTINGS_NAMESPACE` 与 `LOCALE_PREFERENCE_FIELD` |
|
|
61
|
-
| `./lib/lesson-bus` | `settleLessonCall`:同步抛错与异步 rejection 归到同一个 `onFailure` 出口;函数型 thenable 也算可等待(漏判会留下未处理拒绝);`onFailure` 自己抛错时被统一收口,不会分叉成"抛给调用方"或"未处理拒绝" |
|
|
62
|
-
| `./lib/text` | `truncateEnd`(转交官方 `truncateWithoutSplittingSurrogatePair`)与 `truncateStart`(后切;官方导出面没有后切形态) |
|
|
63
|
-
| `./lib/record` | `isRecord`、`fieldOf`、`hasMethods`(服务面校验:值必须是 Record,**且**点名的每个方法都可调用) |
|
|
64
|
-
| `./lib/errors` | `errorText`:语义对齐官方 `@deepseek-ai/dsh-llm` 的 `errorChain`(cause 链、`AggregateError` 成员、空 message 回退 name、跨 realm 取自有 message、敌意 getter 降级),本地零依赖实现;`isSettingsConflict` 由 `code` / `name` 认出宿主的设置冲突形状,跨 realm 抛来的错误也认得 |
|
|
65
|
-
| `./lib/jsonl` | `shrinkJsonlTail`:JSONL 尾部收缩的纯决策件;触发策略与错误出口留给调用方 |
|
|
66
|
-
| `./lib/job-outcome` | `jobOutcomeOf` + `SettledProcess` + `JobOutcome`:把落定的子进程映射成官方作业注册表的结局;`killProc` + `KillableProcess`:收掉子进程句柄,对已回收进程抛出的 `kill()` 忽略 |
|
|
67
|
-
| `./lib/canonicalize-region-paths` | 构建期字符串件:把 rolldown 写的 `//#region <路径>` 折成与 `process.cwd()` 无关的形态。之所以只给子路径,就是为了让运行期消费方不必为它付一次导入 |
|
|
68
|
-
|
|
69
|
-
## `config/` 面
|
|
70
|
-
|
|
71
|
-
这些是给插件作者用的 lint/test 基线,不是运行期代码,它们要吃你本包自备的工具链:
|
|
72
|
-
|
|
73
|
-
| 子路径 | 需要装什么 |
|
|
74
|
-
| --- | --- |
|
|
75
|
-
| `./config/oxlint` | `oxlint`;要挂 sonarjs 规则面还要 `eslint-plugin-sonarjs` |
|
|
76
|
-
| `./config/vitest.base` | `vitest`,以及 `@vitest/coverage-v8`(基线把 `coverage.provider` 钉成 `"v8"`) |
|
|
77
|
-
| `./config/tsconfig.base.json`、`./config/tsconfig.client.base.json` | 什么都不用——给 `extends` 用的纯 JSON |
|
|
100
|
+
### 随包发什么
|
|
78
101
|
|
|
79
|
-
|
|
102
|
+
- `main` 与 `exports` 只指 `dist/`,不存在第二套源码形态的 manifest。Node 对 `node_modules` 里的 `.ts` 直接抛 `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`,而发布出去的包正是躺在那里被载入的,所以产物是必需的。
|
|
103
|
+
- `files` 是 `icon.svg`、`dist`、`config/*.json` 与 `README.zh-CN.md`。npm 永远附带 `package.json`、`LICENSE` 与 `README.md`;中文版显式列出才会一起发。
|
|
104
|
+
- `publishConfig` 只带 `access` 与 `registry`。把入口字段放在那里是陷阱:pnpm 发布时会把它合并到顶层,npm 不会——换一个发布器就会发出一个入口指向 tarball 里不存在文件的包。
|
|
105
|
+
- `build` 先删除 `dist/`,再产出运行期产物、配置面产物、类型声明与生成的 barrel 声明。两个构建脚本都压缩;`test/dist-size.test.ts` 把运行期面与配置面各自守在预算内。
|
|
106
|
+
- `prepare` 挂 `build`。消费方从 registry 安装时它不执行;它存在是为了让打包、发布与 workspace link 拿到的都是新鲜 `dist/`。
|
|
107
|
+
- 新增切面要同时改五处:`lib/index.ts` 的 barrel、`exports`、`build-host.mjs` 的 `facets` 数组、`tsconfig.build.json` 的 `include`,外加一份 `test/<模块>.test.ts`。漏一处会被「开发」一节的 parity 门判红。
|
|
80
108
|
|
|
81
|
-
|
|
109
|
+
## <img src="https://api.iconify.design/lucide/circle-check.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 确认它真的在工作
|
|
82
110
|
|
|
83
|
-
`
|
|
111
|
+
1. **子路径能解析。** 在消费包里运行 `node -e 'import("@jayyuen66/dsh-plugin-shared/lib/record").then((m) => console.log(typeof m.isRecord))'`,输出 `function`。`ERR_MODULE_NOT_FOUND` 表示依赖没装上;`does not provide an export named ...` 表示装到的那份比你 import 的子路径旧。
|
|
112
|
+
2. **barrel 能解析。** 换成裸包名做同一件事,应能取到运行期切面。`canonicalize-region-paths` 是唯一刻意不在 barrel 里的切面;构建脚本需要时按子路径取。
|
|
113
|
+
3. **消费方宿主能起来。** 启动声明该插件的 profile。若启动日志出现 `N entries did not activate` 与 `does not provide an export named ...`,说明 profile 的 `overrides` 钉得比包含该子路径的版本低——抬高钉版,不要改插件。
|
|
114
|
+
4. **本地源码改动能到消费方。** 消费方载入的是 `dist/`,不是 `lib/`。改完先跑 `pnpm run build`;`test/build-host.test.ts` 会把磁盘上的运行期产物与内存构建逐字节比对,产物过期即红。"代码改了却没生效"多半是产物过期,不是缓存问题。
|
|
84
115
|
|
|
85
|
-
##
|
|
116
|
+
## <img src="https://api.iconify.design/lucide/circle-slash.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 它不提供什么
|
|
86
117
|
|
|
87
|
-
|
|
118
|
+
- **不注册插件。** 没有 `dsh` 字段,没有 `cordis.patch.yml`,`--dump-config` 里没有它这一层;`dsh plugin add` 是错误动作。
|
|
119
|
+
- **没有设置卡,也没有 client 半。** 本包没有浏览器入口,也没有客户端状态。
|
|
120
|
+
- **没有 HTTP 路由,也没有信任闸门。** 真正注册路由的插件由宿主的 `/api` 网关负责认证、Host / Origin 判据与 body 上限。
|
|
121
|
+
- **没有 CLI 子命令,也没有模型工具。**
|
|
122
|
+
- **不做领域判定。** 它不知道哪些工具算编辑、哪条路径是密钥、预算多大、门禁何时该响。这些属于调用方的插件。
|
|
88
123
|
|
|
89
|
-
|
|
90
|
-
- `@deepseek-ai/dsh-output-retention` 是**值导入**(`truncateEnd` 用它),运行期由宿主提供,因此是唯一那条必需 peer——它在 `peerDependenciesMeta` 里明写成 `"optional": false`,不靠"没写就等于必需",因为没写对 npm 7+ 是 `ERESOLVE` 硬失败,不是一条警告。
|
|
91
|
-
- 其余那些只被发布出去的 `.d.ts` 引用,声明成 optional peer 加 dev:peer 保住类型面不静默降级——`lib/job-outcome` 的 `ShellProcess`/`JobOutcome` 曾经来自消费方根本不保证存在的包,于是在 `skipLibCheck: true` 下写错 `status` 或 `exitCode` 一条都不报,返回值类型直接塌成 `any`;dev 让本包自己的编译与测试解析得到。profile 不装 peer,所以不产生副本。
|
|
92
|
-
- 版本口径跟着宿主走:`dsh-*` 家族内一律精确钉,`cordis` 也精确钉,与宿主本体自己声明的一致,这样是复用同一份而不是装出第二份。
|
|
124
|
+
## <img src="https://api.iconify.design/lucide/package.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 模块清单
|
|
93
125
|
|
|
94
|
-
|
|
126
|
+
「子路径」是包内说明符,值 import 要带裸包名前缀。`exports` 另含 `./package.json`,供宿主读展示元数据。
|
|
95
127
|
|
|
96
|
-
|
|
128
|
+
| 子路径 | 提供什么 |
|
|
129
|
+
| --- | --- |
|
|
130
|
+
| `.` | 下面列出的运行期切面的 barrel。`canonicalize-region-paths` 刻意不进,运行期消费方不必为它付一次导入 |
|
|
131
|
+
| `./lib/tool-events` | `scanToolEvents` 返回 `calls` / `results` 两张表,PTC 子调用一并归并;`toolEventRowsOf` 是单事件的那一步,交回的记录已带归一后的 `arguments` 与 `badArguments`。`editPathOf` 返回按 `kind` 判别的 `EditTarget`,只有 `write` / `read-view` 两支;路径取不到时为 `undefined`。台账字段一律 `readonly`。并转出官方 `SessionEvent` 与两枚 PTC 事件类型 |
|
|
132
|
+
| `./lib/card-apply` | `claimApply` + `CardApplyCtx`:apply 幂等守卫,建立在 `globalThis` 上的**自有**标记(symbol,或外部写入的 `true`)加 `ctx.effect` 清理。标记是进程级的,所以同一页面的第二个 ctx 会被挡住——这是设计 |
|
|
133
|
+
| `./lib/card-diff` | `diffTouched`:touched 层里哪些字段真的与快照不同,按表单值比较。`undefined`、`null` 与键缺失是同一种值;数组与对象逐元素比较;循环引用会收敛 |
|
|
134
|
+
| `./lib/project-key` | `deriveProjectKey` + `ProjectKeyOptions`:cwd → `尾目录名-<sha256(norm) 前 8 位 hex>`;非字符串、空串或归一后的根目录落到 `default`。`realpathSync` 会折叠软链,失败则退回解析后的路径。Windows 的 `\` 与 `/` 折进同一个桶 |
|
|
135
|
+
| `./lib/locale` | `Locale`(官方 `BuiltInLocaleId` 的别名)、`DEFAULT_LOCALE`、`resolveLocale`、`resolveLocalePreference`、`messagesFor`、`MessagesCatalog`,加 `LOCALE_SETTINGS_NAMESPACE` 与 `LOCALE_PREFERENCE_FIELD`。只管 host 侧字典;client 用官方 locale 包 |
|
|
136
|
+
| `./lib/lesson-bus` | `settleLessonCall`:同步抛错与异步 rejection 归到同一个 `onFailure` 出口。函数型 thenable 也算可等待;`onFailure` 自己抛错或拒绝时被统一收口,不会分叉成"抛给调用方"或"未处理拒绝" |
|
|
137
|
+
| `./lib/text` | `truncateEnd` 转交官方 `truncateWithoutSplittingSurrogatePair`;`truncateStart` 是官方导出面没有的后切形态。两者都按代理对安全切;预算不足一枚码元(含 `<= 0` 与 NaN)返回空串 |
|
|
138
|
+
| `./lib/record` | `isRecord`、`fieldOf`、`hasMethods`。`fieldOf` 按设计走原型链(cordis 的服务方法挂在那里);敌意 getter 或 Proxy 陷阱降级为 `undefined`。`hasMethods` 要求点名的每个方法都可调用,并且不把 `Object.prototype` 的成员当成服务实现 |
|
|
139
|
+
| `./lib/errors` | `errorText` 对齐官方 `@deepseek-ai/dsh-llm` 的 `errorChain` 语义(cause 链、`AggregateError` 成员、空 message 回退 `name`、跨 realm 取 `message`、敌意 getter 降级),本地零依赖实现。`isSettingsConflict` 由 `code` / `name` 认出宿主的设置冲突形状,跨 realm 抛来的错误也认得 |
|
|
140
|
+
| `./lib/jsonl` | `shrinkJsonlTail`:JSONL 尾部收缩的纯决策件。触发策略与错误出口留给调用方;字节预算是否有限由调用方保证 |
|
|
141
|
+
| `./lib/job-outcome` | `jobOutcomeOf` + `SettledProcess` + `JobOutcome` 把落定的子进程映射成官方作业注册表的结局;`killProc` + `KillableProcess` 收掉子进程句柄,对抛错或拒绝的 `kill()` 一律忽略(例如进程已被回收) |
|
|
142
|
+
| `./lib/canonicalize-region-paths` | 构建期字符串件:把 rolldown 写的 `//#region <路径>` 折成与 `process.cwd()` 无关的形态。只给子路径,是为了让构建脚本取得到它,同时不把运行期 barrel 撑大 |
|
|
143
|
+
|
|
144
|
+
## <img src="https://api.iconify.design/lucide/sliders-horizontal.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> config 面
|
|
145
|
+
|
|
146
|
+
这些是给插件作者用的 lint/test 基线,不是运行期代码。它们依赖你的包自己提供的工具链:
|
|
147
|
+
|
|
148
|
+
| 子路径 | 提供什么 | 你需要装什么 |
|
|
149
|
+
| --- | --- | --- |
|
|
150
|
+
| `./config/oxlint` | `definePluginConfig`、`makeTestFileOverrides`、`OFF_JUSTIFICATIONS` 与共享规则基线 | `oxlint`;要挂 sonarjs 规则面还要 `eslint-plugin-sonarjs` |
|
|
151
|
+
| `./config/vitest.base` | `definePackageConfig`:四项 100% 覆盖率阈值、20 秒测试超时、共享覆盖率排除表与用例匹配范围 | `vitest` 与 `@vitest/coverage-v8`(基线把 `coverage.provider` 钉成 `"v8"`) |
|
|
152
|
+
| `./config/tsconfig.base.json`、`./config/tsconfig.client.base.json` | 给 `extends` 用的纯 JSON | 什么都不用 |
|
|
97
153
|
|
|
98
|
-
`
|
|
154
|
+
这些工具链包**不**写进 `peerDependencies`:npm 7+ 连 optional peer 都会去满足,而 `oxlint` 自己的 `peerOptional vite-plus` 把 `vitest` 钉在一个与 `>=5.0.2` 无交集的版本上,结果是本包的普通 `npm install` 直接 `ERESOLVE` 失败。这条约束因此写在文档里,而不是 manifest 里。
|
|
99
155
|
|
|
100
|
-
-
|
|
101
|
-
- `
|
|
102
|
-
- `
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
- 新增切面要同时改三处:`exports`、`build-host.mjs` 的 `facets` 数组、`tsconfig.build.json` 的 `include`,外加 `test/<模块>.test.ts`。
|
|
156
|
+
- sonarjs 的入口在 `definePluginConfig()` 内部惰性解析。没装 sonarjs 时导入 `./config/oxlint` 仍然成功;既没装 sonarjs、`jsPlugins` 里也没带 sonarjs 入口时,调用 `definePluginConfig()` 才抛错,消息里点名试过哪两个说明符,以及那条自备入口的出路。
|
|
157
|
+
- 基线里每条被关成 `off` 的规则都要在 `OFF_JUSTIFICATIONS` 里有 `kind` + `measured` + `note`,否则配置加载即抛。包级例外写进那个包自己的 `offReasons`。
|
|
158
|
+
- `definePackageConfig` 拒绝空 `coverageInclude`,也拒绝 `test.coverage` 块:两者任一都会让四项阈值或统计面静默消失。要例外就在需要例外的包自己的 `coverage` 覆盖项旁边写明理由。
|
|
159
|
+
|
|
160
|
+
## <img src="https://api.iconify.design/lucide/triangle-alert.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 已知边界
|
|
106
161
|
|
|
107
|
-
|
|
162
|
+
按类登记,每类一条;每条都由测试按现状钉住。
|
|
108
163
|
|
|
109
|
-
-
|
|
110
|
-
- `
|
|
111
|
-
-
|
|
112
|
-
- `
|
|
113
|
-
- **`
|
|
114
|
-
- `editPathOf` 的返回类型收成按 `kind` 判别的 `EditTarget`,只保留 `write` / `read-view` 两支——`"skip"` 从来不会被产出,留在类型里只会逼调用方写永不执行的分支。路径取不到时仍返回所属那一支、`path` 为 `undefined`。
|
|
115
|
-
- **`./lib/http` 与 `./lib/trust` 已删除。** 插件家族的设置卡数据面改跑官方 Typert Remote,认证、Host / DNS 重绑定 / Origin 判据与 body 上限由宿主的 `/api` 网关承担;那两条切面留着的是一份手写的、与宿主不同口径的"这个请求准不准"。没有任何消费包再 import 它们。还要自建 `webServer` 路由的插件得自己写闸门——这是刻意的:发布面不再提供第二套答案。
|
|
164
|
+
- **`fieldOf` 刻意走原型链。** 改成 `Object.hasOwn` 会让所有 cordis 服务方法都取不到。读用户设置值的代码必须改用自有属性判据;`resolveLocalePreference` 就是例子,两个方向都有用例钉住。
|
|
165
|
+
- **`claimApply` 是进程级标记,不是 per-context 锁。** 同一页面上第二个 ctx 挂同一张卡会被挡住。需要多 ctx 并存的消费方应自己按 ctx 记账,而不是"修"这道守卫。
|
|
166
|
+
- **`shrinkJsonlTail` 不归一预算。** `NaN` 与 `+Infinity` 会整段关掉收缩;`0`、负数与 `-Infinity` 仍会收缩到最多两行。触发策略(包括"0 表示永不裁剪")与错误出口都归调用方。
|
|
167
|
+
- **`jobOutcomeOf` 抄的是宿主 bash 工具的文案。** 测试钉的是 `exit code: N` / `signal: NAME` 这些字面量,不是与上游函数逐字相等——那个函数无法从已发布包里 import。上游哪天改措辞,本包不会红,显示文本仍是本包自己的。
|
|
168
|
+
- **`editPathOf` 只认一种只读写法。** 只有 `str_replace_editor` 且 `command: "view"` 才是 `read-view`;其余带 `file_path` 的工具一律按 `write` 处理,不再往下看。
|
|
116
169
|
|
|
117
|
-
##
|
|
170
|
+
## <img src="https://api.iconify.design/lucide/lock.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 数据与隐私
|
|
118
171
|
|
|
119
|
-
|
|
120
|
-
- `test/publish-manifest.test.ts` 是发布形态门:`exports` 的每个目标都必须在 `files` 真会发出去的范围内;入口必须指编译产物而不是源码;`publishConfig` 不得携带入口字段。它还钉着**切面与 `exports` 的双向 parity**:每个 `lib/*.ts` 都要有对应子路径(少一条 = 该切面对任何消费包都不可 import,而构建走的是 builder 自己的 entries 数组、不会报错),反过来也不许留悬空子路径。它还钉 **peer 的 optional 表态**:每一条 `peerDependencies` 都必须在 `peerDependenciesMeta` 里出现——没写进那张表的 peer 对 npm 7+ 的消费方就是硬要求,而类型检查、构建、用例与 parity 在那种状态下全部照绿。`./lib/canonicalize-region-paths` 正是"在 `exports` 里、不在 barrel 里"的那一枚,且这条子路径是承重的(除 Python 桥接包以外,各兄弟包的 rolldown `build-*.mjs` 都靠它),删它会一次性打断它们的构建。同一组"内部指涉"判据扫两个平面:**产物**不得出现绝对路径、家目录指涉、日期或兄弟包标识符;**源码与文档**不得出现日期与"这一轮/下一轮"这类只在协作当时成立的措辞——产物面那条看不见注释,所以必须单独扫源码,否则清理只是一次性动作、没有回归保护。
|
|
121
|
-
- `test/build-host.test.ts` 里的指纹用例比较磁盘产物与内存构建的字节,所以改了 `lib/*.ts` 忘了重建会直接红。
|
|
122
|
-
- `test/setup-logs.ts`(vitest setupFile)是算子日志的账本:它接管 `console.*`,于是 `lib/` 里留下的 `console.error` 既不会漏进测试报告(那条栈是纯噪点),又必须被用例认领——每条 `[shared/*]` 日志都要对得上 `test/log-templates.ts` 里的模板片段,否则当场红。别拿 vitest 的 `silent` 顶替:那只是不显示,新飘出来的日志照样没人看。
|
|
123
|
-
- `.github/workflows/ci.yml` 在 push 与 PR 上跨 Node 版本矩阵、并在 Windows 上跑同一道门;那边的安装矩阵还会打包、在干净目录里不带任何 flag 安装、逐子路径 import、并对消费方探针文件做类型检查。
|
|
172
|
+
本包被 import 进宿主进程;下表描述的是运行期。
|
|
124
173
|
|
|
125
|
-
|
|
174
|
+
| 口径 | 内容 |
|
|
175
|
+
| --- | --- |
|
|
176
|
+
| 文件访问 | `deriveProjectKey` 只对你交给它的那条路径调用 `realpathSync`。失败(`ENOENT` / `EACCES`)退回 `path.resolve`,不外抛。`lib/**` 里没有别的文件访问 |
|
|
177
|
+
| 写盘 | 没有。本包不写文件、不写缓存、不写日志 |
|
|
178
|
+
| 环境变量 | 不读任何环境变量 |
|
|
179
|
+
| 网络 | 没有出站请求,没有 HTTP 服务,没有子进程 |
|
|
180
|
+
| 动态代码 | 没有 `eval`,没有 `new Function`,没有动态 `import()` |
|
|
181
|
+
| 进程内状态 | 唯一的跨实例可变状态是 `claimApply` 在 `globalThis` 上的占位,effect 卸载或占位失败时恢复 |
|
|
182
|
+
| 日志 | 只有 `lesson-bus` 里的 `onFailure` 自己抛错时才会 `console.error`;测试 setup 会把每条 `[shared/*]` 日志拿去和模板账本对账 |
|
|
183
|
+
|
|
184
|
+
## <img src="https://api.iconify.design/lucide/layers.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 版本与兼容
|
|
185
|
+
|
|
186
|
+
- **公共面一律按语义化版本**:子路径、导出的值符号、导出的类型、错误/原因枚举。`0.x` 的区间只放行 patch 位,所以动这些就是消费方区间里的破坏性变更。
|
|
187
|
+
- **`deriveProjectKey` 的哈希算法、8 位切片与尾段空白清洗是冻结的。** 动一位等于把存量教训与记忆从原桶里搬走。
|
|
188
|
+
- **Windows 路径归一改变了桶键形态。** 同一目录无论写成 `\` 还是 `/` 都落到 `尾目录名-<哈希>`,盘符根与 UNC 共享根落进 `default`。归一之前写在 Windows 上的桶键不再匹配;POSIX 的桶键逐字节不变,并由字面量断言钉住。
|
|
189
|
+
- **`Locale` 是官方 `BuiltInLocaleId` 的别名**,因此跟着宿主的 `LOCALE_IDS` 走,不再是手写的字面量联合。
|
|
190
|
+
- **`errorText` 返回的是展示文本,不是可解析格式。** 它对齐官方 `errorChain` 的语义:cause 链拼成 `外: 内`、`AggregateError` 附 `[e1; e2]`、空 message 回退 `Error.name`、跨 realm 的 Error 取其自带 `message` 而不是 `String()` 的 `"Error: ..."`。拿它做正则匹配的调用方在契约之外。
|
|
191
|
+
- **`editPathOf` 返回按 `kind` 判别的 `EditTarget`,只有 `write` / `read-view` 两支。** `"skip"` 从来不会被产出,留在类型里只会逼调用方写永不执行的分支。路径取不到时仍返回所属那一支,`path` 为 `undefined`。
|
|
192
|
+
- **`./lib/http` 与 `./lib/trust` 已删除。** 插件家族的设置卡数据面跑在官方 Typert Remote 上,认证、Host / DNS 重绑定 / Origin 判据与 body 上限由宿主的 `/api` 网关承担。还要自建 `webServer` 路由的插件得自己写闸门——发布面不再提供第二套"这个请求准不准"的答案。
|
|
193
|
+
- **声明文件与宿主钉版是耦合的。** 升级宿主意味着同步移动本包里精确的 `@deepseek-ai/*` 钉版并重新发布;放宽成范围会让消费方载入一份本包没有对着它构建过的类型面。
|
|
194
|
+
|
|
195
|
+
## <img src="https://api.iconify.design/lucide/circle-help.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 常见问题
|
|
196
|
+
|
|
197
|
+
| 问题 | 回答 |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| 要不要 `dsh plugin add` 它? | 不要。它没有配置层也没有 client 半,启用不了任何东西。正确姿势是声明成插件包的依赖。 |
|
|
200
|
+
| 消费方启动时报 `does not provide an export named ...` 与 `N entries did not activate`。 | profile 提供的那份本包比消费方 import 的版本旧。把 profile 的 `overrides` 钉版抬到包含该子路径的版本;对照本包的 `exports` 与 CHANGELOG。任何单仓 CI 都看不见这个状态。 |
|
|
201
|
+
| 改了 `lib/*.ts`,消费方还是旧行为。 | 消费方载入的是 `dist/`。先跑 `pnpm run build`;`test/build-host.test.ts` 会在运行期产物过期时判红。workspace link 不会绕过产物。 |
|
|
202
|
+
| 为什么 `@deepseek-ai/dsh-output-retention` 是必需 peer,其余都是 optional? | 它是唯一按值导入的宿主包(`truncateEnd`)。其余都是类型面,唯一的例外是裸 `@deepseek-ai/dsh` peer——它只声明宿主版本线。 |
|
|
203
|
+
| 为什么 `oxlint` 不写成 `peerDependency`? | npm 7+ 会去满足它,而 `oxlint` 的 optional `vite-plus` peer 把 `vitest` 钉在基线所需范围之外,普通安装会得到 `ERESOLVE`。这条要求因此写在文档里。 |
|
|
204
|
+
| 想把某个 helper 收进来? | 先确认那份函数体在两个以上包里逐字重复。带领域判定的部分留在插件里。 |
|
|
205
|
+
|
|
206
|
+
## <img src="https://api.iconify.design/lucide/wrench.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 开发
|
|
207
|
+
|
|
208
|
+
```sh
|
|
209
|
+
pnpm install
|
|
210
|
+
pnpm run check # typecheck → lint → build → test → 格式检查
|
|
211
|
+
pnpm test # vitest,带覆盖率
|
|
212
|
+
pnpm run build # 运行期产物、配置面产物、声明文件
|
|
213
|
+
```
|
|
126
214
|
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
215
|
+
- 测试按「桩看不见的那类缺陷」分层:
|
|
216
|
+
- **切面行为**:每个运行期切面一份 `test/<模块>.test.ts`。
|
|
217
|
+
- **surface parity**:`test/index.test.ts`(barrel ↔ `lib/` 的值导出)、`test/publish-manifest.test.ts`(`exports` ↔ `lib/` 双向)与 `test/build-host.test.ts`(`build-host.mjs` 的 `facets` ↔ `lib/`)。三条各抓一种另外两条看不见的漏项。
|
|
218
|
+
- **产物门**:`test/build-host.test.ts` 把磁盘上的运行期产物与内存构建逐字节比对;`test/dist-size.test.ts` 把运行期面与配置面各自守在字节预算内;同一份测试还钉住官方值导入保持裸说明符,并拒绝为 `dsh-jobs` / `dsh-shell` 留下运行期说明符。
|
|
219
|
+
- **配置门**:`test/config-oxlint.test.ts` 把 vitest 规则快照与 `oxlint --rules` 现量对照,并要求每条 `off` 都有依据;`test/vitest-base.test.ts` 钉住合并契约,保证四项阈值与统计面不会被局部改配抹掉。
|
|
220
|
+
- **日志账本**:`test/setup-logs.ts` 接管 `console.*`;每条 `[shared/*]` 日志都要对得上 `test/log-templates.ts` 里的模板片段,否则当场红。`test/log-templates.test.ts` 双向校验,增删日志却不改账本都会失败。
|
|
221
|
+
- **发布形态**:`test/publish-manifest.test.ts` 要求入口目标都在 `files` 覆盖内、入口指向产物而不是源码、`publishConfig` 不带入口字段、每条 peer 都在 `peerDependenciesMeta` 里表态、`dsh-*` 钉版精确且统一,并要求源码、文档与产物里没有日期、绝对路径、家目录指涉或只在协作当时成立的措辞。
|
|
222
|
+
- 覆盖率四项阈值都是 **100%**(lines / statements / functions / branches),所以新增分支必须有能走到它的用例。`test/vitest-base.test.ts` 拒绝空 `coverageInclude`,因为那会让四项阈值对着空统计面全绿。
|
|
223
|
+
- CI 在 Linux、Windows 与 macOS 上跑同一道门,Node 版本矩阵覆盖 22 与 24,并有一周一次的定时运行抓上游漂移。安装矩阵会分别用 npm 与 pnpm 打包,在干净目录里不带 peer 解析旗标安装,逐子路径 import,并在两种 `skipLibCheck` 档位下对消费方探针做类型检查。
|
|
131
224
|
|
|
132
|
-
## 许可
|
|
225
|
+
## <img src="https://api.iconify.design/lucide/scale.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 许可
|
|
133
226
|
|
|
134
|
-
MIT。`LICENSE` 随包发布。
|
|
227
|
+
MIT。源码在 [github.com/JayYuen666/dsh-plugin-shared](https://github.com/JayYuen666/dsh-plugin-shared)。`LICENSE` 随包发布。
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"//": "target 不覆盖:与官方一致(deepseek-harness/tsconfig.base.json 是 es2024,client 层只加 lib)。本仓的 client 产物由 rolldown 打,构建脚本里没有 target,也没有读 tsconfig ⇒ 这里的 target 只影响类型面,不影响产物语法。旧基线把它钉在 ES2020 是继承来的写法,实测无产物后果,只把 Promise.withResolvers 一类 ES2024 API 判成 TS2550,逼出包级 lint 例外。",
|
|
2
|
+
"//": "target 不覆盖:与官方一致(deepseek-harness/tsconfig.base.json 是 es2024,client 层只加 lib)。本仓的 client 产物由 rolldown 打,构建脚本里没有 target,也没有读 tsconfig ⇒ 这里的 target 只影响类型面,不影响产物语法。旧基线把它钉在 ES2020 是继承来的写法,实测无产物后果,只把 Promise.withResolvers 一类 ES2024 API 判成 TS2550,逼出包级 lint 例外。types 置空刻意关掉父基线的 node @types 自动加载:纯浏览器 client 是对的;下游 client 源码若用到 Node 全局或需要 vite/client 的声明,须在自己的 tsconfig 显式补 types,报错点(TS2588/TS2304 一类)离这里很远,别把 node 加回这份基线。",
|
|
3
3
|
"extends": "./tsconfig.base.json",
|
|
4
4
|
"compilerOptions": {
|
|
5
5
|
"module": "ESNext",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=/^\/\/#region .*$/gmu,t=/^(?:\.\.?\/)+/u;function
|
|
1
|
+
const e=/^\/\/#region .*$/gmu,t=/^[^\s'"`]+$/u,n=/^(?:\.\.?\/)+/u,r=/[/\\]+/u;function i(e){let t=e.replace(/[/\\]+$/u,``),n=Math.max(t.lastIndexOf(`/`),t.lastIndexOf(`\\`));return n===-1?t:t.slice(n+1)}function a(e){let t=e.replace(/[/\\]+$/u,``),n=Math.max(t.lastIndexOf(`/`),t.lastIndexOf(`\\`));return n===-1?t:t.slice(0,n)}function o(e){let t=i(e);return[i(a(e)),t].filter(e=>e.length>0)}function s(e,t){let n=e;for(let e of t)n.startsWith(`${e}/`)&&(n=n.slice(e.length+1));return n}function c(e){return e.split(r)}function l(e){let t=[...e];for(;t.length>1&&t.at(-1)===``;)t.pop();return t}function u(e){return e.startsWith(`/`)||e.startsWith(`\\`)||/^[A-Za-z]:[\\/]/u.test(e)}function d(e,t){let n=u(e);if(n!==u(t))return null;let r=l(c(e)),i=l(c(t));if(n&&r[0]!==i[0])return null;let a=0;for(;a<r.length&&a<i.length&&r[a]===i[a];)a+=1;return[...Array.from({length:r.length-a},()=>`..`),...i.slice(a)]}function f(e){if(e===null||e.length===0)return``;let t=0;for(;t<e.length&&e[t]===`..`;)t+=1;return t===e.length?``:e.slice(t).join(`/`)}function p(e,t){return{own:f(d(e,t)),parent:f(d(e,a(t)))}}function m(e,t){return t.own.length>0&&e.startsWith(`${t.own}/`)?e.slice(t.own.length+1):t.parent.length>0&&e.startsWith(`${t.parent}/`)?e.slice(t.parent.length+1):e}function h(e,r,i){let a=e.slice(10);if(!t.test(a))return e;let o=a.replace(n,``);return`//#region ${i===null?s(o,r):m(o,i)}`}function g(t,n,r){let i=o(n);if(r===void 0)return i.length===0?t:t.replaceAll(e,e=>h(e,i,null));let a=p(r,n);return t.replaceAll(e,e=>h(e,i,a))}export{g as canonicalizeRegionPaths};
|
package/dist/card-apply.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e,t,n){let
|
|
1
|
+
const e=e=>e.length>0&&!(e in Object.prototype);function t(e,t,n){n===void 0?e[t]=void 0:Object.defineProperty(e,t,n)}function n(n,r,i){if(!e(r))throw Error(`claimApply 的 flag 不得为空、也不得命中 Object.prototype 的成员(__proto__ 一类会让占位静默失灵或遮蔽全局成员):${r}`);let a=globalThis,o=Object.hasOwn(a,r)?a[r]:void 0;if(o===!0||typeof o==`symbol`)return!1;let s=Object.getOwnPropertyDescriptor(a,r),c=Symbol(r);a[r]=c;try{n.effect(()=>()=>{a[r]===c&&t(a,r,s)},i)}catch(e){throw a[r]===c&&t(a,r,s),e}return!0}export{n as claimApply};
|
package/dist/card-diff.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e,t){let
|
|
1
|
+
function e(t,n,r){if(t==null||n==null)return(t??null)===(n??null);if(Object.is(t,n))return!0;if(typeof t!=`object`||typeof n!=`object`)return!1;if(r.get(t)===n)return!0;if(r.set(t,n),Array.isArray(t)||Array.isArray(n))return!Array.isArray(t)||!Array.isArray(n)||t.length!==n.length?!1:t.every((t,i)=>e(t,n[i],r));let i=Object.keys(t),a=Object.keys(n);return i.length===a.length&&i.every(i=>Object.hasOwn(n,i)&&e(Reflect.get(t,i),Reflect.get(n,i),r))}function t(t,n){let r=[];for(let i of Object.keys(t))e(t[i],n[i],/* @__PURE__ */ new WeakMap)||r.push(i);return r}export{t as diffTouched};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire as e}from"node:module";import{defineConfig as t}from"oxlint";const n=`同 no-unsafe-member-access`,r={"vitest/*":{kind:`文体作用域`,measured:7,note:`用例结构类判据在非用例文件上无从计算`},"vitest/no-hooks":{kind:`上游不推荐`,measured:181,note:`禁用 hook 本身;本仓按官方 vitest 用法大量使用 before*/after*`},"vitest/no-conditional-in-test":{kind:`文体作用域`,measured:523,note:`平台/宿主版本分支是刻意的(macOS 与 Linux 的家目录形状、0.1.7 与 0.1.6 的 API 面)`},"vitest/max-expects":{kind:`上游不推荐`,measured:163,note:`给单条用例的断言条数设上限属口味,不指向缺陷`},"vitest/prefer-expect-assertions":{kind:`上游不推荐`,measured:2902,note:`要求每个 async 用例手写 expect.assertions(n),是与断言强度无关的计数仪式`},"vitest/require-test-timeout":{kind:`上游不推荐`,measured:2892,note:`要求每条用例显式 timeout;本仓用泵式假时钟,超时由 13 份 vitest 配置统一给`},"vitest/prefer-importing-vitest-globals":{kind:`实测否证`,measured:0,note:`autofix 方向是删掉显式 import 改回裸全局;13 份 vitest 配置都没开 globals:true,应用后当场 3605 处 TS2593、测试全废`},"vitest/no-importing-vitest-globals":{kind:`实测否证`,measured:133,note:`与上一条同族、方向相反,两条同时开就是把两种写法都判死`},"vitest/prefer-import-in-mock":{kind:`实测否证`,measured:6,note:"动态形态按完整模块面校验工厂返回值,而 @types/node 的 os、@types/react 都是 `export =` 形状、类型面上没有 default;实测三处 TS2769+TS2740/2741,react 那处无论怎么 spread 都补不出 default"},"vitest/prefer-lowercase-title":{kind:`上游不推荐`,measured:171,note:`只在用例文件里生效:它带 autofix,实测一轮 oxlint --fix 静默小写化了 260 条标题(含 nO_VERIFY 这种坏名)。包级 titlePrefixes 只留真正的技术名(GET/CSRF/组件名…):非技术名(工单号一类)不该借这条豁免留在标题里,而中文开头的标题天然满足判据(无大小写)⇒ 免检面能收多小收多小。实测 oxlint 前缀一命中就把整条标题免检,所以陈旧条目由配置基线自检按『免检面未被使用』点名,不靠人工巡检`},"vitest/prefer-called-times":{kind:`对侧互斥`,measured:0,note:`探针实测(同文件四种写法各跑一次):toHaveBeenCalledTimes(1) 被 prefer-called-once 判死、toHaveBeenCalledOnce() 被本条判死、expect(spy.mock.calls).toHaveLength(1) 被 prefer-to-have-been-called-times 判死,只有 n>=2 能同时满足三条 ⇒ 三条同开等于「单次调用」这条断言无法书写(实测逼得消费方改用 assert.equal(spy.mock.calls.length, 1) 绕行)。留 prefer-called-once 与 prefer-to-have-been-called-times 两条方向一致的组合,关本条`},"vitest/prefer-describe-function-title":{kind:`对侧互斥`,measured:48,note:"要求 `describe(fn)`,而 valid-title 要求字符串字面量(oxlint 1.85 的 valid-title 是 DummyRule、无任何选项);上游 recommended 收的是 valid-title,取它,26 处 `describe(fn)` 按代码改成同名字符串"},"vitest/prefer-to-be-truthy":{kind:`实测否证`,measured:117,note:`把 toStrictEqual(true) 放宽成 toTruthy(),接受任意真值是削弱断言`},"vitest/prefer-to-be-falsy":{kind:`实测否证`,measured:89,note:`同族,放宽成 toFalsy() 同样是削弱断言`},"import/no-default-export":{kind:`契约冲突`,measured:38,note:`官方 cordis 入口就是 default export(vendor/loader/src/index.ts:201 先 unwrap default),12 条正是 12 个 bundle 入口`},"import/no-named-export":{kind:`契约冲突`,measured:895,note:`13 个包与上游 deepseek-harness 一样以具名导出 + 子路径说明符对外暴露`},"import/prefer-default-export":{kind:`契约冲突`,measured:58,note:`与 no-default-export 同族、方向与 cordis 入口相反`},"import/no-nodejs-modules":{kind:`契约冲突`,measured:448,note:"host 半就跑在 node 上,`node:` 内置模块是运行环境本身而不是越界"},"import/no-named-as-default-member":{kind:`契约冲突`,measured:47,note:`对具名导出的默认对象做成员访问是本仓消费官方声明的常态`},"import/no-anonymous-default-export":{kind:`契约冲突`,measured:12,note:`官方 cordis 入口是匿名对象 default export(registry.ts:92 读 name/Config/apply),具名化等于改官方形态`},"eslint/no-duplicate-imports":{kind:`实测否证`,measured:166,note:`ESLint 官方已废弃(不区分 import type),由 import/no-duplicates 取代`},"import/group-exports":{kind:`上游不推荐`,measured:766,note:`要求单点导出,与具名导出面冲突`},"import/exports-last":{kind:`上游不推荐`,measured:528,note:`要求导出语句在文件末尾`},"import/no-relative-parent-imports":{kind:`上游不推荐`,measured:277,note:`包内 lib/ 与 test/ 的相对父级引用是既有结构`},"import/max-dependencies":{kind:`上游不推荐`,measured:25,note:`给 import 条数设上限;host 半消费官方服务面天然依赖多`},"node/no-process-env":{kind:`上游不推荐`,measured:51,note:`环境变量是本仓的输入面(DSH_HOME、profile 目录、构建期开关);eslint-plugin-n 的 recommended 也不含本条`},"eslint/no-magic-numbers":{kind:`上游不推荐`,measured:5774,note:`把 HTTP 状态码/毫秒阈值抽成常量的计数规则`},"eslint/sort-keys":{kind:`上游不推荐`,measured:4101,note:`对象键排序;成对字面量与 locale 字典的可读性会一起牺牲`},"eslint/sort-imports":{kind:`上游不推荐`,measured:675,note:`字母序 import,与 oxfmt 的分组形态冲突`},"eslint/capitalized-comments":{kind:`上游不推荐`,measured:2388,note:`本仓注释以中文为主,「首字母大写」对中文注释无从判定`},"eslint/no-ternary":{kind:`上游不推荐`,measured:1024,note:`禁三元;改写成 if/else 并不会更正确`},"eslint/no-undefined":{kind:`上游不推荐`,measured:2009,note:"禁 `undefined` 字面量,与本仓大量显式可选返回形态冲突"},"eslint/no-void":{kind:`上游不推荐`,measured:172,note:"禁 `void` 前缀,而 `void promise` 正是消解 no-floating-promises 的官方写法"},"unicorn/no-null":{kind:`上游不推荐`,measured:788,note:`禁 null;官方声明与 JSON 边界大量用 null,改成 undefined 会改变序列化形状`},"typescript/prefer-readonly-parameter-types":{kind:`上游不推荐`,measured:2433,note:`要求每个入参类型全 readonly;官方声明面(SessionEvent/ConfigForm 等)是可变的,改不动也不该改`},"oxc/no-async-await":{kind:`对侧互斥`,measured:2160,note:`禁 async/await,而同表里 promise-function-async、require-await、no-floating-promises 都在推 async`},"oxc/no-optional-chaining":{kind:`对侧互斥`,measured:1354,note:"禁 `?.`,而 typescript/prefer-optional-chain 正在要求把 && 链改成 `?.`"},"oxc/no-rest-spread-properties":{kind:`对侧互斥`,measured:550,note:`禁对象 spread,而 ES2024 目标与官方声明的投影写法都靠它`},"typescript/no-explicit-any":{kind:`文体作用域`,measured:1,note:`用例里的桩件面(mini-react 之类)需要 any 才塞得进官方签名`},"typescript/no-non-null-assertion":{kind:`文体作用域`,measured:536,note:"用例里 `!` 是「前驱条件已成立」的最短写法"},"typescript/no-unsafe-type-assertion":{kind:`文体作用域`,measured:844,note:"桩件需要 `as never` / `as unknown as` 跨官方名义类型"},"typescript/require-await":{kind:`文体作用域`,measured:467,note:`官方 async 契约的同步实现与测试替身撑不住这条;prod 侧的条数按代码修,不进豁免`},"eslint/require-await":{kind:`对侧互斥`,measured:481,note:`它与 typescript/require-await 是同一条判据的两份实现,不是两条判据:探针实测(一段无 await 的 async 函数、--type-aware)单开各自都报、同开报两次 ⇒ 2 errors / 1 defect。按 typescript-eslint 官方的 extension-rule 口径裁决:留带类型信息的扩展那条、关基线那条。覆盖面没有因此变小:全仓逐行去重实测 514 行两点同报、typescript 独有 0 行、eslint 独有只落在用例面与配置自身的中间态;而 --type-aware 实测对 .mjs 也会由扩展那条报出,所以 scripts/*.mjs 面不需要基线那条补位。用例文件由 TEST_OVERRIDES 整族豁免`},"eslint/max-statements":{kind:`文体作用域`,measured:549,note:`用例体天然长(given/when/then 展开)`},"eslint/max-lines-per-function":{kind:`文体作用域`,measured:365,note:`同上`},"eslint/max-lines":{kind:`文体作用域`,measured:54,note:`单个用例文件的长度不作为缺陷信号`},"node/no-sync":{kind:`文体作用域`,measured:299,note:`构建脚本与门禁脚本按设计同步读;生产代码的写侧由每包 syncWrites 逐项登记,不在豁免里`},"node/no-top-level-await":{kind:`文体作用域`,measured:53,note:`脚本以 node 直接执行,顶层 await 是 ESM 常态;prod 侧 0 条`},"typescript/strict-boolean-expressions":{kind:`文体作用域`,measured:0,note:`scripts/*.mjs 无类型面,隐式 any 上无从判定`},"typescript/no-implied-eval":{kind:`文体作用域`,measured:0,note:`同上:这条要类型信息才能判`},"typescript/no-unsafe-member-access":{kind:`文体作用域`,measured:0,note:`无类型标注的脚本里每个值都落进隐式 any`},"typescript/no-unsafe-call":{kind:`文体作用域`,measured:0,note:n},"typescript/no-unsafe-assignment":{kind:`文体作用域`,measured:0,note:n},"typescript/no-unsafe-return":{kind:`文体作用域`,measured:0,note:n},"typescript/no-unsafe-argument":{kind:`文体作用域`,measured:0,note:n},"typescript/require-array-sort-compare":{kind:`文体作用域`,measured:0,note:`无类型脚本里「比较器缺失」判不出来`},"unicorn/no-process-exit":{kind:`文体作用域`,measured:2,note:`CLI 脚本的 process.exit 就是它的返回方式`},"eslint/no-continue":{kind:`文体作用域`,measured:53,note:`只对 scripts/ 豁免:TS 侧实测 0 条(包代码本来就不用 continue),908 行的门禁脚本改成嵌套 if 只会更难读`},"unicorn/max-nested-calls":{kind:`文体作用域`,measured:59,note:`只对 scripts/ 豁免:TS 侧实测 0 条;CLI 脚本里链式 map/filter/join 的深度由数据形状决定,不指向缺陷`},"unicorn/no-useless-undefined":{kind:`文体作用域`,measured:0,note:`脚本侧同上调;prod 仍按配置项 checkArguments:false 生效`},"react/set-state-in-effect":{kind:`契约冲突`,measured:16,note:`卡片用 effect 同步宿主态到 store 是挂载适配器形态;Compiler 建议的 render 期推导要改写 12 份适配器的状态流`},"react/no-deriving-state-in-effects":{kind:`契约冲突`,measured:7,note:`适配器形态里「props/state 派生值在 effect 对齐宿主」是挂载动作,不是可下沉到 render 的纯派生`},"react/exhaustive-effect-dependencies":{kind:`契约冲突`,measured:8,note:`Compiler 版依赖审计对适配器的内联闭包全量追问;与 react-hooks/exhaustive-deps(保留并已修)形成双门,留后者`},"react/hooks":{kind:`契约冲突`,measured:13,note:`「同一 hook 函数跨渲染同引用」在适配器的内联闭包形态下整体不成立;收敛批次把闭包提升后再回来开`},"react/refs":{kind:`契约冲突`,measured:1,note:`挂载适配器在 render 期读 ref 对齐宿主布局;收敛批次改成挂载后读`},"react/todo":{kind:`上游不推荐`,measured:6,note:`报的是 React 编译器 BuildHIR 自己不支持 try/catch(-finally) 的上游 TODO,不是代码缺陷`},"react/rules-of-hooks":{kind:`文体作用域`,measured:3,note:`只对用例文件豁免(TEST_FILE_OVERRIDES):测试架在组件树外直接驱动卡片适配 hook(useCardOf / useSyncExternalStore 桩),调用次数顺序由桩件固定。键用表载前缀 react/(诊断文案显示 react-hooks(...) 是显示名,配置键以 --rules 的 Source 列为准)`}},i=[`typescript/no-empty-object-type`],a=[`existsSync`,`readFileSync`,`readdirSync`,`readSync`,`realpathSync`,`statSync`],o=[`eslint`,`typescript`,`unicorn`,`oxc`,`node`,`promise`,`vitest`,`import`,`react`,`jsx-a11y`],s=[`eslint-plugin-sonarjs`,`eslint-plugin-sonarjs/cjs/plugin.js`];function c(){let t=e(import.meta.url),n=[];for(let e of s)try{return t.resolve(e)}catch{n.push(e)}throw Error(`oxlint 基线要挂 eslint-plugin-sonarjs,但从本包解析不到它(试过 ${n.join(`、`)})。要么在本包 devDependencies 里装 eslint-plugin-sonarjs,要么把自己解析到的入口经 definePluginConfig({ jsPlugins: [...] }) 传进来——带了入口就不会再解析(那条提示才真正兑现)。`)}function l(e,t=c){let n=e??[],r=null;try{r=t()}catch(e){if(n.length===0)throw e}return r===null?[...n]:[r,...n.filter(e=>e!==r)]}const u={"sonarjs/no-identical-functions":`error`,"sonarjs/no-duplicated-branches":`error`,"sonarjs/no-all-duplicated-branches":`error`,"sonarjs/no-collapsible-if":`error`,"sonarjs/no-identical-conditions":`error`,"sonarjs/no-identical-expressions":`error`,"sonarjs/no-duplicate-string":`error`,"sonarjs/cognitive-complexity":`error`,"sonarjs/max-switch-cases":`error`,"sonarjs/no-inverted-boolean-check":`error`,"sonarjs/no-duplicate-in-composite":`error`,"sonarjs/duplicates-in-character-class":`error`,"sonarjs/no-useless-catch":`error`,"sonarjs/no-extra-arguments":`error`,"sonarjs/no-redundant-assignments":`error`,"sonarjs/prefer-single-boolean-return":`error`,"sonarjs/label-position":`error`},d={correctness:`error`,suspicious:`error`,style:`error`,perf:`error`,restriction:`error`,pedantic:`error`,nursery:`error`},f={typeAware:!0,typeCheck:!0,denyWarnings:!0,maxWarnings:0,reportUnusedDisableDirectives:`error`,respectEslintDisableDirectives:!1},p=[`**/node_modules/**`,`.toolchain/**`,`coverage/**`,`dist/**`,`client.js`,`host.js`],m=/* @__PURE__ */ `vitest/consistent-each-for.vitest/consistent-test-filename.vitest/consistent-test-it.vitest/consistent-vitest-vi.vitest/expect-expect.vitest/hoisted-apis-on-top.vitest/max-expects.vitest/max-nested-describe.vitest/no-alias-methods.vitest/no-commented-out-tests.vitest/no-conditional-expect.vitest/no-conditional-in-test.vitest/no-conditional-tests.vitest/no-disabled-tests.vitest/no-duplicate-hooks.vitest/no-focused-tests.vitest/no-hooks.vitest/no-identical-title.vitest/no-import-node-test.vitest/no-importing-vitest-globals.vitest/no-interpolation-in-snapshots.vitest/no-large-snapshots.vitest/no-mocks-import.vitest/no-restricted-matchers.vitest/no-restricted-vi-methods.vitest/no-standalone-expect.vitest/no-test-prefixes.vitest/no-test-return-statement.vitest/no-unneeded-async-expect-function.vitest/padding-around-after-all-blocks.vitest/padding-around-test-blocks.vitest/prefer-called-exactly-once-with.vitest/prefer-called-once.vitest/prefer-called-times.vitest/prefer-called-with.vitest/prefer-comparison-matcher.vitest/prefer-describe-function-title.vitest/prefer-each.vitest/prefer-equality-matcher.vitest/prefer-expect-assertions.vitest/prefer-expect-resolves.vitest/prefer-expect-type-of.vitest/prefer-hooks-in-order.vitest/prefer-hooks-on-top.vitest/prefer-import-in-mock.vitest/prefer-importing-vitest-globals.vitest/prefer-lowercase-title.vitest/prefer-mock-promise-shorthand.vitest/prefer-mock-return-shorthand.vitest/prefer-snapshot-hint.vitest/prefer-spy-on.vitest/prefer-strict-boolean-matchers.vitest/prefer-strict-equal.vitest/prefer-to-be.vitest/prefer-to-be-falsy.vitest/prefer-to-be-object.vitest/prefer-to-be-truthy.vitest/prefer-to-contain.vitest/prefer-to-have-been-called-times.vitest/prefer-to-have-length.vitest/prefer-todo.vitest/require-awaited-expect-poll.vitest/require-hook.vitest/require-local-test-context-for-concurrent-snapshots.vitest/require-mock-type-parameters.vitest/require-test-timeout.vitest/require-to-throw-message.vitest/require-top-level-describe.vitest/valid-describe-callback.vitest/valid-expect.vitest/valid-expect-in-promise.vitest/valid-title.vitest/warn-todo`.split(`.`),h=/* @__PURE__ */ new Set([`vitest/max-expects`,`vitest/no-conditional-in-test`,`vitest/no-hooks`,`vitest/no-importing-vitest-globals`,`vitest/prefer-called-times`,`vitest/prefer-describe-function-title`,`vitest/prefer-expect-assertions`,`vitest/prefer-import-in-mock`,`vitest/prefer-importing-vitest-globals`,`vitest/prefer-to-be-falsy`,`vitest/prefer-to-be-truthy`,`vitest/require-test-timeout`]),g={"eslint/one-var":[`error`,`never`],"eslint/func-style":[`error`,`declaration`,{allowArrowFunctions:!0,overrides:{namedExports:`ignore`}}],"eslint/id-length":[`error`,{exceptions:[`a`,`i`,`t`,`x`,`y`,`z`]}],"eslint/init-declarations":[`error`,`never`,{ignoreForLoopInit:!0}],"eslint/max-lines":[`error`,{max:2e3,skipBlankLines:!0,skipComments:!0}],"eslint/max-lines-per-function":[`error`,{max:120,skipBlankLines:!0,skipComments:!0}],"eslint/complexity":`error`,"eslint/max-params":[`error`,{max:5}],"eslint/max-statements":[`error`,50],"eslint/no-console":[`error`,{allow:[`warn`,`error`,`info`]}],"eslint/no-warning-comments":[`error`,{terms:[`todo`,`fixme`,`xxx`,`hack`],location:`start`}],"eslint/no-param-reassign":`error`,"eslint/no-underscore-dangle":[`error`,{allow:[`__memoryInsightCardApplied`,`__memoryTdaiCardApplied`,`__sessionRescueApplied`,`__wukilDevToolsCardApplied`]}],"eslint/no-magic-numbers":`off`,"eslint/sort-keys":`off`,"eslint/sort-imports":`off`,"eslint/capitalized-comments":`off`,"eslint/no-ternary":`off`,"eslint/no-undefined":`off`,"eslint/no-void":`off`,"eslint/no-duplicate-imports":`off`,"eslint/require-await":`off`,"import/no-named-export":`off`,"import/group-exports":`off`,"import/exports-last":`off`,"import/prefer-default-export":`off`,"import/no-nodejs-modules":`off`,"import/no-relative-parent-imports":`off`,"import/max-dependencies":`off`,"import/no-default-export":`off`,"import/no-anonymous-default-export":`off`,"import/no-named-as-default-member":`off`,"import/consistent-type-specifier-style":[`error`,`prefer-top-level`],"import/no-duplicates":`error`,"import/no-cycle":`error`,"import/named":`error`,"import/first":`error`,"import/newline-after-import":`error`,"node/no-process-env":`off`,"node/no-sync":[`error`,{ignores:a}],"node/no-top-level-await":`error`,"oxc/no-async-await":`off`,"oxc/no-optional-chaining":`off`,"oxc/no-rest-spread-properties":`off`,"promise/always-return":`error`,"promise/no-promise-in-callback":[`error`,{exemptDeclarations:!0}],"promise/no-return-wrap":`error`,"promise/param-names":`error`,"promise/prefer-await-to-then":`error`,"typescript/consistent-type-imports":[`error`,{disallowTypeAnnotations:!1}],"typescript/explicit-function-return-type":[`error`,{allowDirectConstAssertionInArrowFunctions:!0,allowExpressions:!0,allowHigherOrderFunctions:!0,allowIIFEs:!0,allowTypedFunctionExpressions:!0,allowedNames:[`handler`,`callback`]}],"typescript/no-explicit-any":`error`,"typescript/no-floating-promises":`error`,"typescript/no-unsafe-assignment":`error`,"typescript/no-unsafe-type-assertion":`error`,"typescript/no-non-null-assertion":`error`,"typescript/require-await":`error`,"typescript/prefer-readonly-parameter-types":`off`,"typescript/promise-function-async":[`error`,{checkArrowFunctions:!1}],"typescript/consistent-return":[`error`,{treatUndefinedAsUnspecified:!0}],"typescript/only-throw-error":`error`,"unicorn/no-array-reduce":[`error`,{allowSimpleOperations:!0}],"unicorn/no-null":`off`,"unicorn/no-useless-undefined":[`error`,{checkArguments:!1}],"react/set-state-in-effect":`off`,"react/no-deriving-state-in-effects":`off`,"react/exhaustive-effect-dependencies":`off`,"react/hooks":`off`,"react/refs":`off`,"react/todo":`off`,...Object.fromEntries(m.map(e=>[e,`off`]))},_={files:[`**/*.test.ts`,`**/*.test.tsx`,`**/test/**`,`**/__tests__/**`],rules:{"typescript/no-non-null-assertion":`off`,"typescript/no-unsafe-type-assertion":`off`,"typescript/no-explicit-any":`off`,"typescript/require-await":`off`,"eslint/max-statements":`off`,"eslint/max-lines":`off`,"eslint/max-lines-per-function":`off`,"node/no-top-level-await":`off`}},v=[`expect`,`expectTypeOf`,`assert`,`assertType`],y=/^[A-Za-z_$][\w$]*$/u,b={files:[`**/*.test.ts`,`**/*.test.tsx`,`**/__tests__/**/*.spec.ts`],rules:{...Object.fromEntries(m.filter(e=>!h.has(e)).map(e=>[e,`error`])),"vitest/expect-expect":[`error`,{assertFunctionNames:v}],"react/rules-of-hooks":`off`}};function x(e={}){let t=e.assertionHelpers??[],n=e.titlePrefixes??[],r=[...t,...n].filter(e=>!y.test(e));if(r.length>0)throw Error(`assertionHelpers / titlePrefixes 只收裸标识符,不接受通配或带点形式(前者等于把 vitest/expect-expect 掏空):${r.join(`, `)}`);let i=[.../* @__PURE__ */ new Set([...v,...t])];return{...b,rules:{...b.rules,"vitest/expect-expect":[`error`,{assertFunctionNames:i}],...n.length===0?{}:{"vitest/prefer-lowercase-title":[`error`,{allowedPrefixes:[...new Set(n)]}]}}}}const S={files:[`scripts/**`,`*.mjs`,`*.mts`],rules:{"eslint/no-continue":`off`,"unicorn/max-nested-calls":`off`,"eslint/id-length":[`error`,{exceptions:[`a`,`i`,`t`,`x`,`y`,`z`,`n`,`p`,`s`,`v`,`e`,`f`,`ok`,`fs`,`id`]}],"unicorn/no-process-exit":`off`,"unicorn/no-useless-undefined":`off`,"typescript/no-non-null-assertion":`off`,"node/no-sync":`off`,"node/no-top-level-await":`off`,"typescript/no-unsafe-member-access":`off`,"typescript/no-unsafe-call":`off`,"typescript/no-unsafe-assignment":`off`,"typescript/no-unsafe-return":`off`,"typescript/no-unsafe-argument":`off`,"typescript/strict-boolean-expressions":`off`,"typescript/no-implied-eval":`off`,"typescript/require-array-sort-compare":`off`}};function C(e){let t={...r,...e};return e=>{let n=t[e];if(n!==void 0)return n;let r=e.indexOf(`/`);return r===-1?void 0:t[`${e.slice(0,r)}/*`]}}function w(e,t,n){if(t===void 0)return;let r=[];for(let[e,i]of Object.entries(t))if((Array.isArray(i)?i[0]:i)===`off`){let t=n(e);(t===void 0||t.note.trim().length===0)&&r.push(e)}if(r.length>0)throw Error(`${e} 里这些规则被关成 off 但没有依据(OFF_JUSTIFICATIONS 查不到,或 note 为空):${r.join(`, `)}。要么在代码里修,要么给它补一条 kind + measured + note。`)}function T(e={}){let{syncWrites:n=[],extraRules:r={},extraOverrides:i=[],extraIgnorePatterns:s=[],jsPlugins:c,offReasons:m={},assertionHelpers:h=[],titlePrefixes:v=[]}=e,y=C(m),b={...g,...u,...n.length>0?{"node/no-sync":[`error`,{ignores:[.../* @__PURE__ */ new Set([...a,...n])]}]}:{},...r},T=[_,x({assertionHelpers:h,titlePrefixes:v}),S,...i];w(`rules`,b,y);for(let[e,t]of T.entries())w(`overrides[${e}]`,t.rules,y);return t({plugins:o,env:{node:!0,es2024:!0,browser:!0},options:f,categories:d,ignorePatterns:[...p,...s],jsPlugins:l(c),rules:b,overrides:T})}export{g as BASE_RULES,r as OFF_JUSTIFICATIONS,a as READONLY_SYNC_ALLOWLIST,i as RETIRED_OFFS,S as SCRIPTS_OVERRIDES,b as TEST_FILE_OVERRIDES,_ as TEST_OVERRIDES,v as VITEST_ASSERTION_DEFAULTS,T as definePluginConfig,x as makeTestFileOverrides,l as resolveJsPlugins};
|
|
1
|
+
import{createRequire as e}from"node:module";import{defineConfig as t}from"oxlint";function n(e){return(typeof e==`string`?e:`${e.name} ${e.specifier}`).includes(`sonarjs`)}const r=`同 no-unsafe-member-access`,i={"vitest/*":{kind:`文体作用域`,measured:7,note:`用例结构类判据在非用例文件上无从计算`},"vitest/no-hooks":{kind:`上游不推荐`,measured:181,note:`禁用 hook 本身;本仓按官方 vitest 用法大量使用 before*/after*`},"vitest/no-conditional-in-test":{kind:`文体作用域`,measured:523,note:`平台/宿主版本分支是刻意的(macOS 与 Linux 的家目录形状、0.1.7 与 0.1.6 的 API 面)`},"vitest/max-expects":{kind:`上游不推荐`,measured:163,note:`给单条用例的断言条数设上限属口味,不指向缺陷`},"vitest/prefer-expect-assertions":{kind:`上游不推荐`,measured:2902,note:`要求每个 async 用例手写 expect.assertions(n),是与断言强度无关的计数仪式`},"vitest/require-test-timeout":{kind:`上游不推荐`,measured:2892,note:`要求每条用例显式 timeout;本仓用泵式假时钟,超时由 13 份 vitest 配置统一给`},"vitest/prefer-importing-vitest-globals":{kind:`实测否证`,measured:0,note:`autofix 方向是删掉显式 import 改回裸全局;13 份 vitest 配置都没开 globals:true,应用后当场 3605 处 TS2593、测试全废`},"vitest/no-importing-vitest-globals":{kind:`实测否证`,measured:133,note:`与上一条同族、方向相反,两条同时开就是把两种写法都判死`},"vitest/prefer-import-in-mock":{kind:`实测否证`,measured:6,note:"动态形态按完整模块面校验工厂返回值,而 @types/node 的 os、@types/react 都是 `export =` 形状、类型面上没有 default;实测三处 TS2769+TS2740/2741,react 那处无论怎么 spread 都补不出 default"},"vitest/prefer-lowercase-title":{kind:`文体作用域`,measured:171,note:`只在用例文件里生效:它带 autofix,实测一轮 oxlint --fix 静默小写化了 260 条标题(含 nO_VERIFY 这种坏名)。包级 titlePrefixes 只留真正的技术名(GET/CSRF/组件名…):非技术名(工单号一类)不该借这条豁免留在标题里,而中文开头的标题天然满足判据(无大小写)⇒ 免检面能收多小收多小。实测 oxlint 前缀一命中就把整条标题免检,所以陈旧条目由配置基线自检按『免检面未被使用』点名,不靠人工巡检`},"vitest/prefer-called-times":{kind:`对侧互斥`,measured:0,note:`探针实测(同文件四种写法各跑一次):toHaveBeenCalledTimes(1) 被 prefer-called-once 判死、toHaveBeenCalledOnce() 被本条判死、expect(spy.mock.calls).toHaveLength(1) 被 prefer-to-have-been-called-times 判死,只有 n>=2 能同时满足三条 ⇒ 三条同开等于「单次调用」这条断言无法书写(实测逼得消费方改用 assert.equal(spy.mock.calls.length, 1) 绕行)。留 prefer-called-once 与 prefer-to-have-been-called-times 两条方向一致的组合,关本条`},"vitest/prefer-describe-function-title":{kind:`对侧互斥`,measured:48,note:"要求 `describe(fn)`,而 valid-title 要求字符串字面量(oxlint 1.85 的 valid-title 是 DummyRule、无任何选项);上游 recommended 收的是 valid-title,取它,26 处 `describe(fn)` 按代码改成同名字符串"},"vitest/prefer-to-be-truthy":{kind:`实测否证`,measured:117,note:`把 toStrictEqual(true) 放宽成 toTruthy(),接受任意真值是削弱断言`},"vitest/prefer-to-be-falsy":{kind:`实测否证`,measured:89,note:`同族,放宽成 toFalsy() 同样是削弱断言`},"import/no-default-export":{kind:`契约冲突`,measured:38,note:`官方 cordis 入口就是 default export(vendor/loader/src/index.ts 的 unwrapExports 先 unwrap default),12 条正是 12 个 bundle 入口`},"import/no-named-export":{kind:`契约冲突`,measured:895,note:`13 个包与上游 deepseek-harness 一样以具名导出 + 子路径说明符对外暴露`},"import/prefer-default-export":{kind:`契约冲突`,measured:58,note:`与 no-default-export 同族、方向与 cordis 入口相反`},"import/no-nodejs-modules":{kind:`契约冲突`,measured:448,note:"host 半就跑在 node 上,`node:` 内置模块是运行环境本身而不是越界"},"import/no-named-as-default-member":{kind:`契约冲突`,measured:47,note:`对具名导出的默认对象做成员访问是本仓消费官方声明的常态`},"import/no-anonymous-default-export":{kind:`契约冲突`,measured:12,note:`官方 cordis 入口是匿名对象 default export(registry.ts 的 RegistryService.plugin() 读 name/Config,isApplicable() 判 apply),具名化等于改官方形态`},"eslint/no-duplicate-imports":{kind:`实测否证`,measured:166,note:`ESLint 官方已废弃(不区分 import type),由 import/no-duplicates 取代`},"import/group-exports":{kind:`上游不推荐`,measured:766,note:`要求单点导出,与具名导出面冲突`},"import/exports-last":{kind:`上游不推荐`,measured:528,note:`要求导出语句在文件末尾`},"import/no-relative-parent-imports":{kind:`上游不推荐`,measured:277,note:`包内 lib/ 与 test/ 的相对父级引用是既有结构`},"import/max-dependencies":{kind:`上游不推荐`,measured:25,note:`给 import 条数设上限;host 半消费官方服务面天然依赖多`},"node/no-process-env":{kind:`上游不推荐`,measured:51,note:`环境变量是本仓的输入面(DSH_HOME、profile 目录、构建期开关);eslint-plugin-n 的 recommended 也不含本条`},"eslint/no-magic-numbers":{kind:`上游不推荐`,measured:5774,note:`把 HTTP 状态码/毫秒阈值抽成常量的计数规则`},"eslint/sort-keys":{kind:`上游不推荐`,measured:4101,note:`对象键排序;成对字面量与 locale 字典的可读性会一起牺牲`},"eslint/sort-imports":{kind:`上游不推荐`,measured:675,note:`字母序 import,与 oxfmt 的分组形态冲突`},"eslint/capitalized-comments":{kind:`上游不推荐`,measured:2388,note:`本仓注释以中文为主,「首字母大写」对中文注释无从判定`},"eslint/no-ternary":{kind:`上游不推荐`,measured:1024,note:`禁三元;改写成 if/else 并不会更正确`},"eslint/no-undefined":{kind:`上游不推荐`,measured:2009,note:"禁 `undefined` 字面量,与本仓大量显式可选返回形态冲突"},"eslint/no-void":{kind:`上游不推荐`,measured:172,note:"禁 `void` 前缀,而 `void promise` 正是消解 no-floating-promises 的官方写法"},"unicorn/no-null":{kind:`上游不推荐`,measured:788,note:`禁 null;官方声明与 JSON 边界大量用 null,改成 undefined 会改变序列化形状`},"typescript/prefer-readonly-parameter-types":{kind:`上游不推荐`,measured:2433,note:`要求每个入参类型全 readonly;官方声明面(SessionEvent/ConfigForm 等)是可变的,改不动也不该改`},"oxc/no-async-await":{kind:`对侧互斥`,measured:2160,note:`禁 async/await,而同表里 promise-function-async、require-await、no-floating-promises 都在推 async`},"oxc/no-optional-chaining":{kind:`对侧互斥`,measured:1354,note:"禁 `?.`,而 typescript/prefer-optional-chain 正在要求把 && 链改成 `?.`"},"oxc/no-rest-spread-properties":{kind:`对侧互斥`,measured:550,note:`禁对象 spread,而 ES2024 目标与官方声明的投影写法都靠它`},"typescript/no-explicit-any":{kind:`文体作用域`,measured:1,note:`用例里的桩件面(mini-react 之类)需要 any 才塞得进官方签名`},"typescript/no-non-null-assertion":{kind:`文体作用域`,measured:536,note:"用例里 `!` 是「前驱条件已成立」的最短写法"},"typescript/no-unsafe-type-assertion":{kind:`文体作用域`,measured:844,note:"桩件需要 `as never` / `as unknown as` 跨官方名义类型"},"typescript/require-await":{kind:`文体作用域`,measured:467,note:`官方 async 契约的同步实现与测试替身撑不住这条;prod 侧的条数按代码修,不进豁免`},"eslint/require-await":{kind:`对侧互斥`,measured:481,note:`它与 typescript/require-await 是同一条判据的两份实现,不是两条判据:探针实测(一段无 await 的 async 函数、--type-aware)单开各自都报、同开报两次 ⇒ 2 errors / 1 defect。按 typescript-eslint 官方的 extension-rule 口径裁决:留带类型信息的扩展那条、关基线那条。覆盖面没有因此变小:全仓逐行去重实测 514 行两点同报、typescript 独有 0 行、eslint 独有只落在用例面与配置自身的中间态;而 --type-aware 实测对 .mjs 也会由扩展那条报出,所以 scripts/*.mjs 面不需要基线那条补位。用例文件由 TEST_OVERRIDES 整族豁免`},"eslint/max-statements":{kind:`文体作用域`,measured:549,note:`用例体天然长(given/when/then 展开)`},"eslint/max-lines-per-function":{kind:`文体作用域`,measured:365,note:`同上`},"eslint/max-lines":{kind:`文体作用域`,measured:54,note:`单个用例文件的长度不作为缺陷信号`},"node/no-sync":{kind:`文体作用域`,measured:299,note:`构建脚本与门禁脚本按设计同步读;生产代码的写侧由每包 syncWrites 逐项登记,不在豁免里`},"node/no-top-level-await":{kind:`文体作用域`,measured:53,note:`脚本以 node 直接执行,顶层 await 是 ESM 常态;prod 侧 0 条`},"typescript/strict-boolean-expressions":{kind:`文体作用域`,measured:0,note:`scripts/*.mjs 无类型面,隐式 any 上无从判定`},"typescript/no-implied-eval":{kind:`文体作用域`,measured:0,note:`同上:这条要类型信息才能判`},"typescript/no-unsafe-member-access":{kind:`文体作用域`,measured:0,note:`无类型标注的脚本里每个值都落进隐式 any`},"typescript/no-unsafe-call":{kind:`文体作用域`,measured:0,note:r},"typescript/no-unsafe-assignment":{kind:`文体作用域`,measured:0,note:r},"typescript/no-unsafe-return":{kind:`文体作用域`,measured:0,note:r},"typescript/no-unsafe-argument":{kind:`文体作用域`,measured:0,note:r},"typescript/require-array-sort-compare":{kind:`文体作用域`,measured:0,note:`无类型脚本里「比较器缺失」判不出来`},"unicorn/no-process-exit":{kind:`文体作用域`,measured:2,note:`CLI 脚本的 process.exit 就是它的返回方式`},"eslint/no-continue":{kind:`文体作用域`,measured:53,note:`只对 scripts/ 豁免:TS 侧实测 0 条(包代码本来就不用 continue),908 行的门禁脚本改成嵌套 if 只会更难读`},"unicorn/max-nested-calls":{kind:`文体作用域`,measured:59,note:`只对 scripts/ 豁免:TS 侧实测 0 条;CLI 脚本里链式 map/filter/join 的深度由数据形状决定,不指向缺陷`},"unicorn/no-useless-undefined":{kind:`文体作用域`,measured:0,note:`脚本侧同上调;prod 仍按配置项 checkArguments:false 生效`},"react/set-state-in-effect":{kind:`契约冲突`,measured:16,note:`卡片用 effect 同步宿主态到 store 是挂载适配器形态;Compiler 建议的 render 期推导要改写 12 份适配器的状态流`},"react/no-deriving-state-in-effects":{kind:`契约冲突`,measured:7,note:`适配器形态里「props/state 派生值在 effect 对齐宿主」是挂载动作,不是可下沉到 render 的纯派生`},"react/exhaustive-effect-dependencies":{kind:`契约冲突`,measured:8,note:`Compiler 版依赖审计对适配器的内联闭包全量追问;与 react-hooks/exhaustive-deps(保留并已修)形成双门,留后者`},"react/hooks":{kind:`契约冲突`,measured:13,note:`「同一 hook 函数跨渲染同引用」在适配器的内联闭包形态下整体不成立;收敛批次把闭包提升后再回来开`},"react/refs":{kind:`契约冲突`,measured:1,note:`挂载适配器在 render 期读 ref 对齐宿主布局;收敛批次改成挂载后读`},"react/todo":{kind:`上游不推荐`,measured:6,note:`报的是 React 编译器 BuildHIR 自己不支持 try/catch(-finally) 的上游 TODO,不是代码缺陷`},"react/rules-of-hooks":{kind:`文体作用域`,measured:3,note:`只对用例文件豁免(TEST_FILE_OVERRIDES):测试架在组件树外直接驱动卡片适配 hook(useCardOf / useSyncExternalStore 桩),调用次数顺序由桩件固定。键用表载前缀 react/(诊断文案显示 react-hooks(...) 是显示名,配置键以 --rules 的 Source 列为准)`}},a=[`typescript/no-empty-object-type`],o=[`existsSync`,`readFileSync`,`readdirSync`,`readSync`,`realpathSync`,`statSync`],s=[`eslint`,`typescript`,`unicorn`,`oxc`,`node`,`promise`,`vitest`,`import`,`react`,`jsx-a11y`],c=[`eslint-plugin-sonarjs`,`eslint-plugin-sonarjs/cjs/plugin.js`];function l(){let t=e(import.meta.url),n=[];for(let e of c)try{return t.resolve(e)}catch{n.push(e)}throw Error(`oxlint 基线要挂 eslint-plugin-sonarjs,但从本包解析不到它(试过 ${n.join(`、`)})。要么在本包 devDependencies 里装 eslint-plugin-sonarjs,要么把你自己解析到的 sonarjs 入口经 definePluginConfig({ jsPlugins: [...] }) 传进来(入口路径须指向 sonarjs:SONARJS_RULES 无条件引用它,别家的插件救不了这一条)。`)}function u(e,t=l){let r=e??[],i=null;try{i=t()}catch(e){if(!r.some(e=>n(e)))throw e}return i===null?[...r]:[i,...r.filter(e=>e!==i)]}const d={"sonarjs/no-identical-functions":`error`,"sonarjs/no-duplicated-branches":`error`,"sonarjs/no-all-duplicated-branches":`error`,"sonarjs/no-collapsible-if":`error`,"sonarjs/no-identical-conditions":`error`,"sonarjs/no-identical-expressions":`error`,"sonarjs/no-duplicate-string":`error`,"sonarjs/cognitive-complexity":`error`,"sonarjs/max-switch-cases":`error`,"sonarjs/no-inverted-boolean-check":`error`,"sonarjs/no-duplicate-in-composite":`error`,"sonarjs/duplicates-in-character-class":`error`,"sonarjs/no-useless-catch":`error`,"sonarjs/no-extra-arguments":`error`,"sonarjs/no-redundant-assignments":`error`,"sonarjs/prefer-single-boolean-return":`error`,"sonarjs/label-position":`error`},f={correctness:`error`,suspicious:`error`,style:`error`,perf:`error`,restriction:`error`,pedantic:`error`,nursery:`error`},p={typeAware:!0,typeCheck:!0,denyWarnings:!0,maxWarnings:0,reportUnusedDisableDirectives:`error`,respectEslintDisableDirectives:!1},m=[`**/node_modules/**`,`.toolchain/**`,`coverage/**`,`dist/**`,`client.js`,`host.js`],h=/* @__PURE__ */ `vitest/consistent-each-for.vitest/consistent-test-filename.vitest/consistent-test-it.vitest/consistent-vitest-vi.vitest/expect-expect.vitest/hoisted-apis-on-top.vitest/max-expects.vitest/max-nested-describe.vitest/no-alias-methods.vitest/no-commented-out-tests.vitest/no-conditional-expect.vitest/no-conditional-in-test.vitest/no-conditional-tests.vitest/no-disabled-tests.vitest/no-duplicate-hooks.vitest/no-focused-tests.vitest/no-hooks.vitest/no-identical-title.vitest/no-import-node-test.vitest/no-importing-vitest-globals.vitest/no-interpolation-in-snapshots.vitest/no-large-snapshots.vitest/no-mocks-import.vitest/no-restricted-matchers.vitest/no-restricted-vi-methods.vitest/no-standalone-expect.vitest/no-test-prefixes.vitest/no-test-return-statement.vitest/no-unneeded-async-expect-function.vitest/padding-around-after-all-blocks.vitest/padding-around-test-blocks.vitest/prefer-called-exactly-once-with.vitest/prefer-called-once.vitest/prefer-called-times.vitest/prefer-called-with.vitest/prefer-comparison-matcher.vitest/prefer-describe-function-title.vitest/prefer-each.vitest/prefer-equality-matcher.vitest/prefer-expect-assertions.vitest/prefer-expect-resolves.vitest/prefer-expect-type-of.vitest/prefer-hooks-in-order.vitest/prefer-hooks-on-top.vitest/prefer-import-in-mock.vitest/prefer-importing-vitest-globals.vitest/prefer-lowercase-title.vitest/prefer-mock-promise-shorthand.vitest/prefer-mock-return-shorthand.vitest/prefer-snapshot-hint.vitest/prefer-spy-on.vitest/prefer-strict-boolean-matchers.vitest/prefer-strict-equal.vitest/prefer-to-be.vitest/prefer-to-be-falsy.vitest/prefer-to-be-object.vitest/prefer-to-be-truthy.vitest/prefer-to-contain.vitest/prefer-to-have-been-called-times.vitest/prefer-to-have-length.vitest/prefer-todo.vitest/require-awaited-expect-poll.vitest/require-hook.vitest/require-local-test-context-for-concurrent-snapshots.vitest/require-mock-type-parameters.vitest/require-test-timeout.vitest/require-to-throw-message.vitest/require-top-level-describe.vitest/valid-describe-callback.vitest/valid-expect.vitest/valid-expect-in-promise.vitest/valid-title.vitest/warn-todo`.split(`.`),g=/* @__PURE__ */ new Set([`vitest/max-expects`,`vitest/no-conditional-in-test`,`vitest/no-hooks`,`vitest/no-importing-vitest-globals`,`vitest/prefer-called-times`,`vitest/prefer-describe-function-title`,`vitest/prefer-expect-assertions`,`vitest/prefer-import-in-mock`,`vitest/prefer-importing-vitest-globals`,`vitest/prefer-to-be-falsy`,`vitest/prefer-to-be-truthy`,`vitest/require-test-timeout`]),_={"eslint/one-var":[`error`,`never`],"eslint/func-style":[`error`,`declaration`,{allowArrowFunctions:!0,overrides:{namedExports:`ignore`}}],"eslint/id-length":[`error`,{exceptions:[`a`,`i`,`t`,`x`,`y`,`z`]}],"eslint/init-declarations":[`error`,`never`,{ignoreForLoopInit:!0}],"eslint/max-lines":[`error`,{max:2e3,skipBlankLines:!0,skipComments:!0}],"eslint/max-lines-per-function":[`error`,{max:120,skipBlankLines:!0,skipComments:!0}],"eslint/complexity":`error`,"eslint/max-params":[`error`,{max:5}],"eslint/max-statements":[`error`,50],"eslint/no-console":[`error`,{allow:[`warn`,`error`,`info`]}],"eslint/no-warning-comments":[`error`,{terms:[`todo`,`fixme`,`xxx`,`hack`],location:`start`}],"eslint/no-param-reassign":`error`,"eslint/no-underscore-dangle":[`error`,{allow:[`__memoryInsightCardApplied`,`__memoryTdaiCardApplied`,`__sessionRescueApplied`,`__wukilDevToolsCardApplied`]}],"eslint/no-magic-numbers":`off`,"eslint/sort-keys":`off`,"eslint/sort-imports":`off`,"eslint/capitalized-comments":`off`,"eslint/no-ternary":`off`,"eslint/no-undefined":`off`,"eslint/no-void":`off`,"eslint/no-duplicate-imports":`off`,"eslint/require-await":`off`,"import/no-named-export":`off`,"import/group-exports":`off`,"import/exports-last":`off`,"import/prefer-default-export":`off`,"import/no-nodejs-modules":`off`,"import/no-relative-parent-imports":`off`,"import/max-dependencies":`off`,"import/no-default-export":`off`,"import/no-anonymous-default-export":`off`,"import/no-named-as-default-member":`off`,"import/consistent-type-specifier-style":[`error`,`prefer-top-level`],"import/no-duplicates":`error`,"import/no-cycle":`error`,"import/named":`error`,"import/first":`error`,"import/newline-after-import":`error`,"node/no-process-env":`off`,"node/no-sync":[`error`,{ignores:o}],"node/no-top-level-await":`error`,"oxc/no-async-await":`off`,"oxc/no-optional-chaining":`off`,"oxc/no-rest-spread-properties":`off`,"promise/always-return":`error`,"promise/no-promise-in-callback":[`error`,{exemptDeclarations:!0}],"promise/no-return-wrap":`error`,"promise/param-names":`error`,"promise/prefer-await-to-then":`error`,"typescript/consistent-type-imports":[`error`,{disallowTypeAnnotations:!1}],"typescript/explicit-function-return-type":[`error`,{allowDirectConstAssertionInArrowFunctions:!0,allowExpressions:!0,allowHigherOrderFunctions:!0,allowIIFEs:!0,allowTypedFunctionExpressions:!0,allowedNames:[`handler`,`callback`]}],"typescript/no-explicit-any":`error`,"typescript/no-floating-promises":`error`,"typescript/no-unsafe-assignment":`error`,"typescript/no-unsafe-type-assertion":`error`,"typescript/no-non-null-assertion":`error`,"typescript/require-await":`error`,"typescript/prefer-readonly-parameter-types":`off`,"typescript/promise-function-async":[`error`,{checkArrowFunctions:!1}],"typescript/consistent-return":[`error`,{treatUndefinedAsUnspecified:!0}],"typescript/only-throw-error":`error`,"unicorn/no-array-reduce":[`error`,{allowSimpleOperations:!0}],"unicorn/no-null":`off`,"unicorn/no-useless-undefined":[`error`,{checkArguments:!1}],"react/set-state-in-effect":`off`,"react/no-deriving-state-in-effects":`off`,"react/exhaustive-effect-dependencies":`off`,"react/hooks":`off`,"react/refs":`off`,"react/todo":`off`,...Object.fromEntries(h.map(e=>[e,`off`]))},v=[`**/*.test.ts`,`**/*.test.tsx`],y=[`**/__tests__/**/*.spec.ts`,`**/test/**/*.spec.ts`],b={files:[...v,`**/test/**`,`**/__tests__/**`],rules:{"typescript/no-non-null-assertion":`off`,"typescript/no-unsafe-type-assertion":`off`,"typescript/no-explicit-any":`off`,"typescript/require-await":`off`,"eslint/max-statements":`off`,"eslint/max-lines":`off`,"eslint/max-lines-per-function":`off`,"node/no-top-level-await":`off`}},x=[`expect`,`expectTypeOf`,`assert`,`assertType`],S=/^[A-Za-z_$][\w$]*$/u,C={files:[...v,...y],rules:{...Object.fromEntries(h.filter(e=>!g.has(e)).map(e=>[e,`error`])),"vitest/expect-expect":[`error`,{assertFunctionNames:x}],"react/rules-of-hooks":`off`}};function w(e={}){let t=e.assertionHelpers??[],n=e.titlePrefixes??[],r=[...t,...n].filter(e=>!S.test(e));if(r.length>0)throw Error(`assertionHelpers / titlePrefixes 只收裸标识符,不接受通配或带点形式(前者等于把 vitest/expect-expect 掏空):${r.join(`, `)}`);let i=[.../* @__PURE__ */ new Set([...x,...t])];return{...C,rules:{...C.rules,"vitest/expect-expect":[`error`,{assertFunctionNames:i}],...n.length===0?{}:{"vitest/prefer-lowercase-title":[`error`,{allowedPrefixes:[...new Set(n)]}]}}}}const T={files:[`scripts/**`,`*.mjs`,`*.mts`],rules:{"eslint/no-continue":`off`,"unicorn/max-nested-calls":`off`,"eslint/id-length":[`error`,{exceptions:[`a`,`i`,`t`,`x`,`y`,`z`,`n`,`p`,`s`,`v`,`e`,`f`,`ok`,`fs`,`id`]}],"unicorn/no-process-exit":`off`,"unicorn/no-useless-undefined":`off`,"typescript/no-non-null-assertion":`off`,"node/no-sync":`off`,"node/no-top-level-await":`off`,"typescript/no-unsafe-member-access":`off`,"typescript/no-unsafe-call":`off`,"typescript/no-unsafe-assignment":`off`,"typescript/no-unsafe-return":`off`,"typescript/no-unsafe-argument":`off`,"typescript/strict-boolean-expressions":`off`,"typescript/no-implied-eval":`off`,"typescript/require-array-sort-compare":`off`}};function E(e){let t={...i,...e};return e=>{let n=t[e];if(n!==void 0)return n;let r=e.indexOf(`/`);return r===-1?void 0:t[`${e.slice(0,r)}/*`]}}function D(e,t,n){if(t===void 0)return;let r=[];for(let[e,i]of Object.entries(t))if((Array.isArray(i)?i[0]:i)===`off`){let t=n(e);(t===void 0||t.note.trim().length===0)&&r.push(e)}if(r.length>0)throw Error(`${e} 里这些规则被关成 off 但没有依据(OFF_JUSTIFICATIONS 查不到,或 note 为空):${r.join(`, `)}。要么在代码里修,要么给它补一条 kind + measured + note。`)}function O(e={}){let{syncWrites:n=[],extraRules:r={},extraOverrides:i=[],extraIgnorePatterns:a=[],jsPlugins:c,offReasons:l={},assertionHelpers:h=[],titlePrefixes:g=[]}=e,v=E(l),y={..._,...d,...n.length>0?{"node/no-sync":[`error`,{ignores:[.../* @__PURE__ */ new Set([...o,...n])]}]}:{},...r},x=[b,w({assertionHelpers:h,titlePrefixes:g}),T,...i];D(`rules`,y,v);for(let[e,t]of x.entries())D(`overrides[${e}]`,t.rules,v);return t({plugins:s,env:{node:!0,es2024:!0,browser:!0},options:p,categories:f,ignorePatterns:[...m,...a],jsPlugins:u(c),rules:y,overrides:x})}export{_ as BASE_RULES,i as OFF_JUSTIFICATIONS,o as READONLY_SYNC_ALLOWLIST,a as RETIRED_OFFS,T as SCRIPTS_OVERRIDES,C as TEST_FILE_OVERRIDES,b as TEST_OVERRIDES,h as VITEST_ALL_RULES,x as VITEST_ASSERTION_DEFAULTS,O as definePluginConfig,w as makeTestFileOverrides,u as resolveJsPlugins};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineConfig as e}from"vitest/config";
|
|
1
|
+
import{defineConfig as e}from"vitest/config";const t={provider:`v8`,exclude:[`test/**`,`node_modules/**`,`**/test/**`,`**/node_modules/**`,`build-client*`,`client.js`,`agents/**`,`scripts/**`,`**/*.d.ts`,`**/*.d.mts`],reporter:[`text`,`json-summary`,`json`],reportsDirectory:`coverage`,thresholds:{lines:100,statements:100,functions:100,branches:100}};function n(n){let{coverageInclude:r,coverage:i,test:a}=n;if(r.length===0||r.some(e=>e.trim().length===0))throw Error(`coverageInclude 不能为空数组,也不能含空串:统计面缺件后覆盖率阈值照样全绿,缺的是判据不是覆盖。`);if(a&&`coverage`in a)throw Error(`test.coverage 会整块替换公共覆盖率面(含四项 100% 阈值与排除表);覆盖率覆盖项请走 coverage 参数,逐键合并。`);return e({test:{include:[`test/**/*.{test,spec}.{ts,tsx,mts,cts}`],environment:`node`,testTimeout:2e4,coverage:{...t,...i,include:r,thresholds:{...t.thresholds,...i?.thresholds}},...a}})}export{n as definePackageConfig};
|
package/dist/errors.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fieldOf as e}from"./record.js";function t(e){let t=Object.getOwnPropertyDescriptor(e,`message`);return t!==void 0&&`value`in t&&typeof t.value==`string`?t.value:void 0}function n(e,r){if(r.has(e))return`<circular cause>`;r.add(e);try{if(!(e instanceof Error)){if(typeof e==`object`&&e){let n=t(e);if(n!==void 0)return n}return String(e)}let i=e.message===``?e.name:e.message,a=e instanceof AggregateError&&e.errors.length>0?` [${e.errors.map(e=>n(e,r)).join(`; `)}]`:``,o=e.cause===void 0||e.cause===null?``:n(e.cause,r);return`${i}${a}${o===``||o===i?``:`: ${o}`}`}catch{return`<unrenderable value>`}finally{r.delete(e)}}function r(e){return n(e,/* @__PURE__ */ new Set)}function i(t){return e(t,`code`)===`SETTINGS_CONFLICT`||e(t,`name`)===`SettingsConflictError`}export{r as errorText,i as isSettingsConflict};
|
|
1
|
+
import{fieldOf as e}from"./record.js";function t(e){let t=Object.getOwnPropertyDescriptor(e,`message`);return t!==void 0&&`value`in t&&typeof t.value==`string`?t.value:void 0}function n(e,r){if(r.has(e))return`<circular cause>`;r.add(e);try{if(!(e instanceof Error)){if(typeof e==`object`&&e){let n=t(e);if(n!==void 0&&n!==``)return n}return String(e)}let i=e.message===``?e.name:e.message,a=typeof globalThis.AggregateError==`function`&&e instanceof AggregateError&&e.errors.length>0?` [${e.errors.map(e=>n(e,r)).join(`; `)}]`:``,o=e.cause===void 0||e.cause===null?``:n(e.cause,r);return`${i}${a}${o===``||o===i?``:`: ${o}`}`}catch{return`<unrenderable value>`}finally{r.delete(e)}}function r(e){return n(e,/* @__PURE__ */ new Set)}function i(t){return e(t,`code`)===`SETTINGS_CONFLICT`||e(t,`name`)===`SettingsConflictError`}export{r as errorText,i as isSettingsConflict};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fieldOf as e,hasMethods as t,isRecord as n}from"./record.js";import{editPathOf as r,
|
|
1
|
+
import{fieldOf as e,hasMethods as t,isRecord as n}from"./record.js";import{editPathOf as r,scanToolEvents as i,toolEventRowsOf as a}from"./tool-events.js";import{claimApply as o}from"./card-apply.js";import{diffTouched as s}from"./card-diff.js";import{deriveProjectKey as c}from"./project-key.js";import{DEFAULT_LOCALE as l,LOCALE_PREFERENCE_FIELD as u,LOCALE_SETTINGS_NAMESPACE as d,messagesFor as f,resolveLocale as p,resolveLocalePreference as m}from"./locale.js";import{settleLessonCall as h}from"./lesson-bus.js";import{truncateEnd as g,truncateStart as _}from"./text.js";import{errorText as v,isSettingsConflict as y}from"./errors.js";import{shrinkJsonlTail as b}from"./jsonl.js";import{jobOutcomeOf as x,killProc as S}from"./job-outcome.js";export{l as DEFAULT_LOCALE,u as LOCALE_PREFERENCE_FIELD,d as LOCALE_SETTINGS_NAMESPACE,o as claimApply,c as deriveProjectKey,s as diffTouched,r as editPathOf,v as errorText,e as fieldOf,t as hasMethods,n as isRecord,y as isSettingsConflict,x as jobOutcomeOf,S as killProc,f as messagesFor,p as resolveLocale,m as resolveLocalePreference,i as scanToolEvents,h as settleLessonCall,b as shrinkJsonlTail,a as toolEventRowsOf,g as truncateEnd,_ as truncateStart};
|
package/dist/job-outcome.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){return e.status===`killed`?{status:`killed`,detail:e.signal===null?`killed before exit`:`signal: ${e.signal}`}:{status:`completed`,detail:`exit code: ${String(e.exitCode??0)}`}}function
|
|
1
|
+
function e(e){return e===null||typeof e!=`object`&&typeof e!=`function`?!1:typeof Reflect.get(e,`then`)==`function`}async function t(e){try{await e}catch{}}function n(e){return e.status===`killed`?{status:`killed`,detail:e.signal===null?`killed before exit`:`signal: ${e.signal}`}:{status:`completed`,detail:`exit code: ${String(e.exitCode??0)}`}}function r(n){try{let r=n.kill();e(r)&&t(r)}catch{}}export{n as jobOutcomeOf,r as killProc};
|
package/dist/jsonl.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
function e(e,t){let n=e;for(;Buffer.byteLength(n,`utf8`)>t;){let e=n.split(`
|
|
2
|
-
`);if(e.length<=2)break;
|
|
3
|
-
`)
|
|
4
|
-
`)?t.slice(1):t}return n}export{e as shrinkJsonlTail};
|
|
2
|
+
`);if(e.length<=2)break;n=e.slice(Math.floor(e.length/2)).join(`
|
|
3
|
+
`).replace(/^(?:\r?\n)+/u,``)}return n}export{e as shrinkJsonlTail};
|
package/dist/lesson-bus.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){return e===null||typeof e!=`object`&&typeof e!=`function`?!1:typeof Reflect.get(e,`then`)==`function`}function t(e,
|
|
1
|
+
function e(e){return e===null||typeof e!=`object`&&typeof e!=`function`?!1:typeof Reflect.get(e,`then`)==`function`}async function t(e){try{await e}catch(e){console.error(`[shared/lesson-bus] onFailure threw:`,e)}}function n(n,r){try{let i=r(n);e(i)&&t(i)}catch(e){console.error(`[shared/lesson-bus] onFailure threw:`,e)}}async function r(e,t){try{await e}catch(e){n(e,t)}}function i(t,i){try{let n=t();e(n)&&r(n,i)}catch(e){n(e,i)}}export{i as settleLessonCall};
|
package/dist/project-key.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{realpathSync as e}from"node:fs";import{createHash as t}from"node:crypto";import n from"node:path";const r=/[/\\]+/gu,i=/\/{2,}/gu;function a(e){return e===`\\`?r:i}const o=/^[A-Za-z]:$/u
|
|
1
|
+
import{realpathSync as e}from"node:fs";import{createHash as t}from"node:crypto";import n from"node:path";const r=/[/\\]+/gu,i=/\/{2,}/gu;function a(e){return e===`\\`?r:i}const o=/^[A-Za-z]:$/u;function s(e){if(!e.startsWith(String.raw`\\`))return!1;let t=e.replace(/^\\\\[?.]\\(?:UNC\\)?/u,``).replace(/^\\\\/u,``);return/^[^\\]+\\[^\\]+$/u.test(t)}function c(r,i){if(typeof r!=`string`)return`default`;let c=r.trim();if(c===``)return`default`;let l=i?.realpath??e,u=i?.sep??n.sep,d=(u===`\\`?n.win32:n.posix).resolve(c);try{d=l(d)}catch{}let f=d.replace(/[/\\]+$/u,``),p=d.replaceAll(a(u),`/`).replace(/\/+$/u,``);return p===``||o.test(p)||s(f)?`default`:`${p.slice(p.lastIndexOf(`/`)+1).replaceAll(/\s+/gu,`_`)}-${t(`sha256`).update(p,`utf8`).digest(`hex`).slice(0,8)}`}export{c as deriveProjectKey};
|
package/dist/record.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function t(t,
|
|
1
|
+
function e(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}const t={absent:!0};function n(e,n){try{return{absent:!1,value:e[n]}}catch{return t}}function r(t,r){if(!e(t))return!1;for(let e of r){let r=n(t,e);if(r.absent||typeof r.value!=`function`||r.value===Object.getOwnPropertyDescriptor(Object.prototype,e)?.value)return!1}return!0}function i(r,i){let a=e(r)?n(r,i):t;return a.absent?void 0:a.value}export{i as fieldOf,r as hasMethods,e as isRecord};
|
package/dist/text.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{truncateWithoutSplittingSurrogatePair as e}from"@deepseek-ai/dsh-output-retention";function t(t,n){return e(t,Math.max(0,n))}function n(e,t){if(
|
|
1
|
+
import{truncateWithoutSplittingSurrogatePair as e}from"@deepseek-ai/dsh-output-retention";function t(t,n){return e(t,Math.max(0,n))}function n(e,t){if(Number.isNaN(t)||t<1)return``;if(e.length<=t)return e;let n=e.slice(-t),r=n.codePointAt(0);return r!==void 0&&r>=56320&&r<=57343?n.slice(1):n}export{t as truncateEnd,n as truncateStart};
|