dsh-maoying 0.1.22 → 0.1.23

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.
@@ -29,6 +29,7 @@ webServer.register({ kind: 'prefix', path: '/api/maoying', handler: async (req,
29
29
 
30
30
  - **信任围栏**:isTrustedApiRequest 信任围栏(Host 回环或 webRuntime.trustedHosts + 拒 cross-site + origin 同源校验),403 走 `{ ok: false, error: 'forbidden' }`(错误形状与全局契约同形,客户端形状检查不必对这一条网开一面)。读取 trustedHosts 每请求实时 `ctx.get('webRuntime')?.trustedHosts ?? []`,自捕不抛。
31
31
  - duplicate prefix route 短重试(8 次 × 30ms)。
32
+ - **路由随纤维注销**(0.1.23):register 返回的 disposer 由 apply 挂接 `ctx.effect`——宿主对 `<home>/cordis.patch.yml` 与 profile 级 patch 都有 HMR 热重载(版本升级重写托管块即触发),插件纤维整树重建时路由必须随之出表;否则残留死 handler(inactive context)且新注册被 duplicate 挡住,面板全接口 500。注册完成时纤维已销毁(effect 抛 INACTIVE_EFFECT)则立即手动注销堵竞态窗口。
32
33
  - 所有响应 JSON;错误形状统一 `{ ok: false, error }`(读路由可省 ok,直接 `{ error }` + 状态码)。
33
34
  - **路径安全**:凡是带 `path` 参数的请求,必须 resolve 后与 workspaceRegistry 列出的某个 workspace root **严格相等**(不做子路径、不做 ~ 展开);novel 目录恒为 `<root>/novel`,file 参数 basename 化 + 规范化后必须落在 novel 根内(防穿越)。不满足一律 403。
34
35
  - 全程 try/catch,路由崩溃不弄死宿主;注册失败只 warn(安装器语义不变)。
package/index.mjs CHANGED
@@ -49,7 +49,9 @@
49
49
  * 注册点(inject webServer + workspaceRegistry,见 registerCreatorRoutes)。
50
50
  * 路由段与安装段彼此独立、各失败各的:路由注册/请求处理失败只 warn,
51
51
  * 安装器语义分毫不动。要点:信任围栏、duplicate prefix route 短重试、
52
- * 限额 JSON 读体、client.css 现读下发。
52
+ * 限额 JSON 读体、client.css 现读下发、disposer 挂 ctx.effect(宿主对
53
+ * cordis.patch.yml 的 HMR 热重载会重建插件纤维,路由必须随纤维注销,
54
+ * 否则残留死 handler 报 inactive context,见 apply 内注释)。
53
55
  */
54
56
 
55
57
  import { closeSync, copyFileSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs'
@@ -1005,8 +1007,9 @@ export async function registerPrefixRouteWithRetry(webServer, route, { attempts
1005
1007
  }
1006
1008
 
1007
1009
  /**
1008
- * 注册创作台全部路由(docs/creator-panel.md §2 的 23 条)。返回宿主 disposer;
1009
- * webServer 缺席(非 web 部署)时抛错,由 apply 侧统一降级为 warn。
1010
+ * 注册创作台全部路由(docs/creator-panel.md §2 的 23 条)。返回宿主 disposer
1011
+ *(由 apply 挂接 ctx.effect——纤维销毁时注销路由,防 HMR 热重载后残留死
1012
+ * handler);webServer 缺席(非 web 部署)时抛错,由 apply 侧统一降级为 warn。
1010
1013
  */
1011
1014
  export async function registerCreatorRoutes(ctx) {
1012
1015
  const webServer = ctx?.webServer
@@ -1481,11 +1484,46 @@ export function apply(ctx = {}, _config = {}) {
1481
1484
  // 注:0.2.1-rc.2+ 的内联委派行守卫已由 v2 AgentPreset 形态适配(composeManagedRows
1482
1485
  // 按宿主版本自动选形状,所选形态带在安装日志里),不再需要降级提示。
1483
1486
 
1487
+ // 路由 disposer 挂接宿主生命周期(0.1.23,修「inactive context」面板瘫):
1488
+ // 宿主 dsh CLI 的 runProfile 对 <home>/cordis.patch.yml 与 profile 级
1489
+ // cordis.patch.yml 都挂了 HMR 热重载(dsh-app-boot watchUserPatches),本插件
1490
+ // 安装器恰会在版本变化时重写 home patch 托管块——插件纤维随即被整树重建。
1491
+ // 若 disposer 不挂 ctx.effect,旧纤维销毁后 /api/maoying 路由残留在
1492
+ // webServer 表里(handler 闭包攥着死 ctx),新纤维 apply 又被 duplicate
1493
+ // prefix route 挡住,面板请求全落死 handler,报
1494
+ // `cannot get required service "workspaceRegistry" in inactive context`。
1495
+ // 挂接语义:ctx.effect 可用则挂(纤维销毁时 cordis 自动注销路由);注册
1496
+ // 完成时纤维已销毁(effect 抛 INACTIVE_EFFECT 等)则立即手动注销,堵住
1497
+ // 「注册与销毁竞态」的残留窗口;宿主连 effect/on 都不提供时只 warn 提示
1498
+ // 路由可能残留(不主动注销——此时纤维活着,路由该留)。注销本身自吞错:
1499
+ // 卸载路径的失败不能炸宿主。
1500
+ const wireRouteDisposal = (dispose) => {
1501
+ const removeRoute = () => {
1502
+ try { dispose() } catch { /* 卸载路径绝不抛 */ }
1503
+ }
1504
+ try {
1505
+ if (typeof ctx.effect === 'function') {
1506
+ ctx.effect(() => removeRoute)
1507
+ return
1508
+ }
1509
+ if (typeof ctx.on === 'function') {
1510
+ ctx.on('dispose', removeRoute)
1511
+ return
1512
+ }
1513
+ } catch {
1514
+ // 注册完成时纤维已销毁(cordis effect 对 inactive context 抛
1515
+ // INACTIVE_EFFECT):路由立即手动注销,不留残留。
1516
+ removeRoute()
1517
+ return
1518
+ }
1519
+ logger.warn?.('maoying-installer: 宿主 ctx 无 effect/on 生命周期接口,创作台路由在插件卸载后可能残留')
1520
+ }
1521
+
1484
1522
  // 创作台路由(三期):与安装段彼此独立——注册失败只 warn,面板不可用不
1485
1523
  // 拖累安装器语义。registerCreatorRoutes 是 async 函数,同步阶段不可能 throw
1486
1524
  //(异常一律进 rejected promise),外层 try 对它是永假防御——只挂 .catch
1487
1525
  // 一条兜底通道即可,异常不会冒出 apply。
1488
- registerCreatorRoutes(ctx).catch((error) => {
1526
+ registerCreatorRoutes(ctx).then(wireRouteDisposal, (error) => {
1489
1527
  logger.warn?.(`毛颖创作台路由注册失败(面板不可用,其余功能不受影响): ${error?.message ?? error}`)
1490
1528
  })
1491
1529
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-maoying",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "毛颖 — DSH 网文创作辅助插件:主编 + 五帮手编排(大纲/写手/审校/设定/考据),novel/ 结构化文件为数据库",
5
5
  "type": "module",
6
6
  "license": "MIT",