dsh-conversation-recycle-bin 0.3.8 → 0.3.9

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 CHANGED
@@ -3,6 +3,20 @@
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.9] — 2026-10-27
7
+
8
+ 0.3.8 的紧急修复:**所有删除都会被拒绝**,返回 `409 E_ABANDONED`("The client disconnected before this request ran; nothing was changed.")。0.3.8 发布后不到一天即被用户在使用中撞到。
9
+
10
+ ### Fixed
11
+
12
+ - **`clientGone()` 把每一个正常请求都当成"客户端已断开"**:0.3.8 为了修 R-01(客户端中止后 Host 照样执行),在 `lib/host/api.js` 里加了断连检测,其中一条是 `req.destroyed === true`。但 Node 的 `IncomingMessage` 在请求体被读完后就自动 destroy 可读侧,所以**任何一次正常 POST 在读完 body 之后 `req.destroyed` 都是 `true`**(Node 24 实测:`{req.destroyed:true, req.aborted:false, req.complete:true, res.destroyed:false, res.socket.destroyed:false}`)。结果是删除、恢复、彻底删除、丢弃四条路由全部在开跑前被判为"客户端已断开"。
13
+ 现在只用**能在正常请求中存活**的信号:`res.destroyed`、`res.writableEnded`、`res.socket`/`req.socket` 的 `destroyed`。`req.destroyed` 与已废弃的 `req.aborted` 不再参与判定,代码注释里记了实测值,避免以后有人再加回去。
14
+ - 新增回归用例 **"queue: a fully read body is not a disconnect"**:构造一个 `req.destroyed/readableEnded/complete` 全为 `true` 的请求,断言删除仍然成功、会话确实进了回收站。0.3.8 的写法会让这条用例失败。
15
+
16
+ ### Maintenance
17
+
18
+ - 测试 152 → **153**;`lib/version.js` 与 `package.json` 同步到 0.3.9。
19
+
6
20
  ## [0.3.8] — 2026-10-26
7
21
 
8
22
  技术方案评审团(架构 / 安全 / 验收三方)对 0.3.7 的复核结论是 15 条问题、142 条测试全绿但 **NEEDS WORK**。本版把它们全部修掉,测试 142 → **152**。
package/lib/host/api.js CHANGED
@@ -503,11 +503,17 @@ export function createDeleteApi({ ctx, config, paths, now = Date.now, logger = c
503
503
  * delete a conversation afterwards.
504
504
  */
505
505
  function clientGone(req, res) {
506
+ // Only signals that survive a normal, fully-read request count here.
507
+ // `req.destroyed` is NOT one of them: Node's IncomingMessage auto-destroys
508
+ // its readable side once the body is consumed, so it is `true` on every
509
+ // healthy POST after the body has been read (measured on Node 24:
510
+ // `{req.destroyed:true, req.aborted:false, req.complete:true,
511
+ // res.destroyed:false, res.socket.destroyed:false}`). Using it rejected
512
+ // every deletion with E_ABANDONED in 0.3.8.
506
513
  if (res?.destroyed === true) return true
507
514
  if (res?.writableEnded === true) return true
508
- if (res?.socket && res.socket.destroyed === true) return true
509
- if (req?.destroyed === true) return true
510
- if (req?.aborted === true) return true
515
+ const socket = res?.socket ?? req?.socket
516
+ if (socket !== null && socket !== undefined && socket.destroyed === true) return true
511
517
  return false
512
518
  }
513
519
 
package/lib/version.js CHANGED
@@ -5,4 +5,4 @@
5
5
  * asserted against it by `test/manifest.test.mjs` so the two can never drift
6
6
  * (they did: the constant stayed at 0.1.0 while the package reached 0.1.4).
7
7
  */
8
- export const PLUGIN_VERSION = '0.3.8'
8
+ export const PLUGIN_VERSION = '0.3.9'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-conversation-recycle-bin",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
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",