dsh-conversation-recycle-bin 0.3.20 → 0.3.21
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/CHANGELOG.md +17 -0
- package/README.md +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/test/client-module.test.mjs +43 -6
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
All notable changes to `dsh-conversation-recycle-bin` are documented here.
|
|
4
4
|
The format follows Keep a Changelog; the project uses Semantic Versioning.
|
|
5
5
|
|
|
6
|
+
## [0.3.21] — 2026-11-09
|
|
7
|
+
|
|
8
|
+
CI 第一次真正跑起来就抓到一条**只在中文机器上通过**的用例。0.3.20 发布后推送仓库,四个矩阵任务(ubuntu/windows × Node 22.15/24)全部在 `npm test` 阶段失败,失败点相同。
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`client-module.test.mjs` 把中文写进了断言**(`the background-sweep notice carries how long ago it ran`):`__formatAgo` 的「刚刚」被硬编码成字面量。`client.js` 的 `currentLocaleId()` 先读宿主的 `locale` 服务、读不到才回退 `navigator.language`,而这个用例没有安装 locale 服务,于是断言跟着**运行机器**的语言走——作者的中文 Windows 通过,所有英文 locale 的 CI 与贡献者机器都失败(`'just now' !== '刚刚'`)。这正是 0.3.17 修过的同一类缺陷:**测试没有锁定 locale,产品行为其实是对的**。
|
|
13
|
+
- 现在这条用例对 `en` 与 `zh-CN` **各跑一遍**,每次通过新加的 `loadClientWithLocale()` 显式安装 locale 服务,并逐段断言「刚刚 / 30 分钟前 / 3 小时前 / 2 天前」与英文文案,顺手把两套字典的相对时间文案一起钉住。
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **CI 把失败摘要发布成 workflow annotation**:失败任务的原始日志需要仓库凭据才能读,只看到 `exit code 1` 无法区分「locale 相关的断言断错」和「夹具缺失」。`test` 步骤现在捕获输出,失败时把失败用例的名称、`expected`/`actual` 与所在文件行号以 `::error::` 注释发出,无需凭据即可读(本条缺陷就是这样定位的)。
|
|
18
|
+
|
|
19
|
+
### Tests
|
|
20
|
+
|
|
21
|
+
- 用例总数不变(221),`client-module.test.mjs` 32/32。
|
|
22
|
+
|
|
6
23
|
## [0.3.20] — 2026-11-09
|
|
7
24
|
|
|
8
25
|
一次只改工程效率、不改删除语义的整理:把测试墙钟从 **60.5s 压到 4.6s**,堵住「跑过测试再打包」会把本机测试残留塞进 npm 包的路径,删掉三处零引用导出,并把两个确认前的**读**循环改成有界并发。生产路径的锁预算、超时与策略判定全部不变。
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
给 DeepSeek Harness (DSH) Web 增加「删除对话」:会话列表的「…」菜单与会话详情页头部各有一个删除入口;确认框内可勾选「同时删除关联记忆」(默认不勾选),并实时显示将被影响的记忆条数。删除默认进入插件回收站,可恢复;每次操作写入审计日志;批量删除有上限与限流,批量软删除需输入数量,彻底删除一键执行;事务任一步失败即回滚。
|
|
4
4
|
|
|
5
|
-
> 状态:`0.3.
|
|
5
|
+
> 状态:`0.3.21`,Host + Web Client 双半,零构建(plain JS ESM),只依赖宿主已发布的公开扩展点。
|
|
6
6
|
> 运行要求:**Node.js ≥ 22.15**(Host 侧用到 `readdir(..., { recursive: true })`、`AbortSignal` 等 22.x 行为),DSH `>=0.2.1-rc.2 <0.3.0-0`(`package.json` 的 `engines.dsh` 已如实声明,DSH 安装校验会据此拒绝范围外版本;上界是刻意的:0.2.2 与 0.3.x 之间没有承诺兼容)。
|
|
7
7
|
> 包名:`dsh-conversation-recycle-bin`(原 `dsh-delete-conversation` 因 npm 被他人占用而改名,见 CHANGELOG 0.2.0)。
|
|
8
8
|
> 本 README 同时是设计说明与验收依据;安装/回滚步骤见 [INSTALL.md](INSTALL.md);变更见 [CHANGELOG.md](CHANGELOG.md)。
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-conversation-recycle-bin",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.21",
|
|
4
4
|
"description": "DSH conversation deletion with an optional, previewed cascade into conversation-associated memory: soft delete + recycle bin, audit trail, batch rate limits and per-session rollback. 删除对话(可选一并删除关联记忆):软删除与回收站、审计留痕、批量限流、失败回滚。",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -81,6 +81,32 @@ async function loadClient({ withReactDom = false } = {}) {
|
|
|
81
81
|
return { exported, registrations, style, moduleId, body, portalCalls }
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Load the Client with an explicit active locale.
|
|
86
|
+
*
|
|
87
|
+
* `client.js` resolves the locale from the host's `locale` service and only
|
|
88
|
+
* then falls back to `navigator.language`, so a suite that just calls
|
|
89
|
+
* `loadClient()` silently follows whatever language the MACHINE is set to.
|
|
90
|
+
* That is how a Chinese literal in an assertion passed on a zh-CN laptop and
|
|
91
|
+
* failed on every English-locale CI runner. Anything that asserts user-visible
|
|
92
|
+
* text must pin the locale here instead.
|
|
93
|
+
*/
|
|
94
|
+
async function loadClientWithLocale(localeId) {
|
|
95
|
+
const { exported } = await loadClient()
|
|
96
|
+
exported.apply({
|
|
97
|
+
slots: {
|
|
98
|
+
inject(key, callback) { return callback() ?? (() => {}) },
|
|
99
|
+
register() { return () => {} },
|
|
100
|
+
},
|
|
101
|
+
effect(fn) { fn() },
|
|
102
|
+
get(name) {
|
|
103
|
+
if (name === 'locale') return { getSnapshot: () => ({ active: localeId }) }
|
|
104
|
+
return undefined
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
return exported
|
|
108
|
+
}
|
|
109
|
+
|
|
84
110
|
test('client: module id equals the package name the boot graph keys rows by', async () => {
|
|
85
111
|
const { moduleId } = await loadClient()
|
|
86
112
|
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
|
|
@@ -1092,11 +1118,22 @@ test('client module: an incomplete entry offers purge when the Host allows it',
|
|
|
1092
1118
|
})
|
|
1093
1119
|
|
|
1094
1120
|
test('client module: the background-sweep notice carries how long ago it ran', async () => {
|
|
1095
|
-
const { exported } = await loadClient()
|
|
1096
1121
|
const now = Date.now()
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1122
|
+
// Both languages, each with an explicitly installed locale service: a single
|
|
1123
|
+
// Chinese literal here (`'刚刚'`) made this assertion depend on the machine's
|
|
1124
|
+
// `navigator.language`, so it failed on every English-locale runner while
|
|
1125
|
+
// passing on the author's zh-CN laptop. Pinning both tables also proves the
|
|
1126
|
+
// relative-time wording exists on both sides.
|
|
1127
|
+
const cases = [
|
|
1128
|
+
['en', { justNow: 'just now', minutes: /^30 min ago$/, hours: /^3 h ago$/, days: /^2 d ago$/ }],
|
|
1129
|
+
['zh-CN', { justNow: '刚刚', minutes: /^30 分钟前$/, hours: /^3 小时前$/, days: /^2 天前$/ }],
|
|
1130
|
+
]
|
|
1131
|
+
for (const [localeId, expected] of cases) {
|
|
1132
|
+
const client = await loadClientWithLocale(localeId)
|
|
1133
|
+
assert.equal(client.__formatAgo(undefined, now), '', `${localeId}: no timestamp renders nothing`)
|
|
1134
|
+
assert.equal(client.__formatAgo(now - 5_000, now), expected.justNow, `${localeId}: seconds`)
|
|
1135
|
+
assert.match(client.__formatAgo(now - 30 * 60_000, now), expected.minutes, `${localeId}: minutes`)
|
|
1136
|
+
assert.match(client.__formatAgo(now - 3 * 3_600_000, now), expected.hours, `${localeId}: hours`)
|
|
1137
|
+
assert.match(client.__formatAgo(now - 2 * 86_400_000, now), expected.days, `${localeId}: days`)
|
|
1138
|
+
}
|
|
1102
1139
|
})
|