@xrn1997/dsh-session-delete 0.5.0 → 0.6.0
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/LICENSE +201 -201
- package/README.md +164 -141
- package/cordis.patch.yml +7 -3
- package/icon.svg +18 -18
- package/lib/client.js +467 -343
- package/lib/index.js +590 -320
- package/lib/types/api/attachment-orphans.d.ts +9 -3
- package/lib/types/api/attachment.d.ts +17 -7
- package/lib/types/api/connection-route.d.ts +23 -0
- package/lib/types/api/dispatch.d.ts +24 -17
- package/lib/types/api/route-table.d.ts +25 -9
- package/lib/types/client/batch-bar.d.ts +1 -1
- package/lib/types/client/batch-pick.d.ts +19 -0
- package/lib/types/client/batch-select.d.ts +7 -5
- package/lib/types/client/host-rows.d.ts +27 -32
- package/lib/types/client/main-view.d.ts +2 -1
- package/lib/types/client/pending-open.d.ts +17 -0
- package/lib/types/client/remote.d.ts +14 -15
- package/lib/types/client/store.d.ts +1 -7
- package/lib/types/client/use-trash-panel-controller.d.ts +1 -1
- package/lib/types/config.d.ts +45 -0
- package/lib/types/deps.d.ts +8 -3
- package/lib/types/domain/closure.d.ts +3 -0
- package/lib/types/index.d.ts +13 -12
- package/lib/types/ports/attachments-port.d.ts +40 -0
- package/lib/types/shared/wire.d.ts +18 -5
- package/lib/types/trash/attachments.d.ts +2 -0
- package/lib/types/verbs/orphan-subagents.d.ts +3 -2
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +117 -117
- package/lib/types/api/request-trust.d.ts +0 -45
- package/lib/types/ports/connection-port.d.ts +0 -33
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
* 孤儿附件的报告段:`GET /attachment-orphans`。
|
|
3
3
|
*
|
|
4
4
|
* 报告把扫描计划折成数量/字节 + 对象清单(`id`/`bytes`,供设置页预览缩略图;最多
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
5
|
+
* `src/config.ts` 的 `orphanPreviewLimit`(默认 200,ADR-0037),清理不受预览条数限制)。
|
|
6
|
+
* 扫描跳过(引用源或对象列表读不出)时**报告必须失败**——回一份 0 会让界面说"没有孤儿",
|
|
7
|
+
* 而这类失败恰恰是"不知道",不是"没有"。
|
|
7
8
|
* 清理是 `POST /attachment-clean`,由路由直接接到 `cleanOrphanImageAttachments`。
|
|
8
9
|
*/
|
|
9
10
|
import { type AttachmentReadDeps } from '../trash/attachments.js';
|
|
11
|
+
import type { SessionDeleteConfig } from '../config.js';
|
|
10
12
|
import type { AttachmentOrphanReportWire } from '../shared/wire.js';
|
|
11
|
-
|
|
13
|
+
/** 报告比扫描多读一只旋钮(预览条数);扫描本体仍只认 `maxJsonRefBytes`。 */
|
|
14
|
+
export interface AttachmentOrphanReportDeps extends AttachmentReadDeps {
|
|
15
|
+
config: AttachmentReadDeps['config'] & Pick<SessionDeleteConfig, 'orphanPreviewLimit'>;
|
|
16
|
+
}
|
|
17
|
+
export declare function reportAttachmentOrphans(deps: AttachmentOrphanReportDeps): Promise<AttachmentOrphanReportWire>;
|
|
@@ -2,22 +2,32 @@
|
|
|
2
2
|
* 附件原始字节(`GET /attachment`,全仓唯一的二进制响应段)。
|
|
3
3
|
*
|
|
4
4
|
* 四条硬口径:
|
|
5
|
-
* ① **寻址只认 sha256 id**:`id` 必须 `sha256:<64 位小写 hex
|
|
6
|
-
*
|
|
5
|
+
* ① **寻址只认 sha256 id**:`id` 必须 `sha256:<64 位小写 hex>`,路径不来自请求里的任何一截
|
|
6
|
+
* (`name` 只做下载名;媒体元数据只给宿主 `attachments` 服务做引用校验)。
|
|
7
7
|
* ② **kind 只认 image / file**:分别对应宿主附件库的 `objects`(图片)与 `file-objects`(普通文件);
|
|
8
8
|
* 不认 v2、tmp、cache,也不给"读任意文件"留入口。
|
|
9
|
-
* ③ **单文件 ≤ 20 MiB
|
|
10
|
-
*
|
|
11
|
-
* ④
|
|
12
|
-
* `attachment
|
|
9
|
+
* ③ **单文件 ≤ `maxAttachmentBytes`(默认 20 MiB,可配,ADR-0037)**:所有路径共用同一道
|
|
10
|
+
* `assertWithinLimit`;服务路径先按引用字节拦一道、再按实际字节复核,流式读在累计处停下。
|
|
11
|
+
* ④ **直接 fs 路径**按**魔数**给图片类型(PNG / JPEG / GIF / WebP),认不出就按下载处理
|
|
12
|
+
* (`application/octet-stream` + `attachment`),不猜、也不拿请求里的名字当类型来源;服务路径用
|
|
13
|
+
* 宿主归一化后的 `mediaType`(ADR-0035)。
|
|
14
|
+
*
|
|
15
|
+
* **读取优先走宿主 `attachments` 服务**(ADR-0035):`readImage` / `readFileStream` 会按记录里的
|
|
16
|
+
* 引用校验字节;`attachments` 缺席或请求里没有足够的引用元数据时,才退回直接 fs(旧宿主)。
|
|
17
|
+
* 两种"接不了这次读"(服务缺席 / 元数据不够)在调用方看来是同一个 `undefined`,不各写一条 fallback。
|
|
18
|
+
* 直接 fs 的路径仍只由 `attachmentPathOf(home, kind, id)` 推导。
|
|
13
19
|
*
|
|
14
20
|
* 请求本身不对(id/kind 形状)⇒ 400;对象不在 / 超限 / 读失败 ⇒ 200 + 信封(与其余段同一条错误面)。
|
|
15
21
|
*/
|
|
16
22
|
import type { FsPort } from '../ports/fs-port.js';
|
|
23
|
+
import type { SessionDeleteConfig } from '../config.js';
|
|
24
|
+
import type { AttachmentsPort } from '../ports/attachments-port.js';
|
|
17
25
|
import type { BinaryResponse } from './route-table.js';
|
|
18
|
-
/** 读附件只需要 stat + read
|
|
26
|
+
/** 读附件只需要 stat + read、宿主 `attachments` 读面与响应上限;执行面(removeDir/removeFile)不给。 */
|
|
19
27
|
export interface AttachmentReadPorts {
|
|
20
28
|
fs: Pick<FsPort, 'statFile' | 'readFile'>;
|
|
21
29
|
home: string;
|
|
30
|
+
attachments: AttachmentsPort;
|
|
31
|
+
config: Pick<SessionDeleteConfig, 'maxAttachmentBytes'>;
|
|
22
32
|
}
|
|
23
33
|
export declare function readAttachmentResponse(deps: AttachmentReadPorts, query: URLSearchParams): Promise<BinaryResponse>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Dispatch } from './dispatch.js';
|
|
2
|
+
/** 请求体字节上限:这条通道只收几个 id,1MiB 是"够用且不给自己开一条内存放大器"的量级。 */
|
|
3
|
+
export declare const JSON_BODY_MAX_BYTES: number;
|
|
4
|
+
/** 宿主 `connection.fetch.register` 的入参形状(只声明本适配器用到的成员)。 */
|
|
5
|
+
export interface ConnectionFetchRouteLike {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly methods: readonly ('GET' | 'HEAD' | 'POST')[];
|
|
8
|
+
readonly requestBody: 'buffered' | 'streaming';
|
|
9
|
+
readonly fetch: (request: Request) => Promise<Response>;
|
|
10
|
+
}
|
|
11
|
+
/** 宿主 `connection` 服务的窄镜像:只要求 exact Fetch 注册点。 */
|
|
12
|
+
export interface ConnectionFetchServiceLike {
|
|
13
|
+
readonly fetch?: {
|
|
14
|
+
register(route: ConnectionFetchRouteLike): () => Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 注册九段 exact Fetch 路由。返回一个组合 disposer;调用方把它挂在自己的 effect 上。
|
|
19
|
+
* `connection` 是插件的硬依赖(web UI / 桌面端都装 `client-connection`);`fetch.register`
|
|
20
|
+
* 缺席属于宿主形状漂移,直接抛——静默返回空 disposer 会让插件「在但没有入口」。
|
|
21
|
+
* `dispatch` 与分发器同源。
|
|
22
|
+
*/
|
|
23
|
+
export declare function registerConnectionRoutes(service: ConnectionFetchServiceLike, dispatch: Dispatch): () => Promise<void>;
|
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* /dsh-session-delete-api 的前缀路由分发:只负责传输。
|
|
3
|
-
*
|
|
4
|
-
* 四件事:请求栅栏 → 查路由表 → 按段注入窄面 → 回信封。纯度、body 预读与读面的判据都在
|
|
5
|
-
* src/api/route-table.ts(方法也从那张表读,字面量家在 `shared/wire.ts` 的段名表;这里只按判据执行);
|
|
6
|
-
* 行投影在 src/api/trash-rows.ts。
|
|
7
|
-
*
|
|
8
|
-
* - 请求本身不对用 4xx(400 请求体 / 405 方法 / 404 未知路由 / 403 栅栏拒绝);
|
|
9
|
-
* 操作失败回 200 + {ok:false,error}。浏览器半先解信封,再看状态。
|
|
10
|
-
* - 请求栅栏(归属 / 身份)归 `src/api/request-trust.ts`,这里只按它的裁决写 403。
|
|
11
|
-
* - purity 为 chained 的请求串行执行,避免读清册/动文件/写清册交错。
|
|
12
|
-
*/
|
|
13
|
-
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
14
1
|
import type { DeleteDeps } from '../deps.js';
|
|
15
|
-
import { type
|
|
16
|
-
|
|
17
|
-
|
|
2
|
+
import { type BinaryResponse, type JsonBody, type RouteSpec, type SliceDeps } from './route-table.js';
|
|
3
|
+
/** 适配器归一后的一次请求:`rest` 是插件前缀之后的段名(如 `/list`)。 */
|
|
4
|
+
export interface ApiInput {
|
|
5
|
+
readonly method: string;
|
|
6
|
+
readonly rest: string;
|
|
7
|
+
readonly search: URLSearchParams;
|
|
8
|
+
readonly body: JsonBody;
|
|
9
|
+
}
|
|
10
|
+
/** 适配器归一后的一次响应:JSON 信封或二进制字节,状态码与头由适配器写。 */
|
|
11
|
+
export type ApiResult = {
|
|
12
|
+
readonly kind: 'json';
|
|
13
|
+
readonly status: number;
|
|
14
|
+
readonly body: unknown;
|
|
15
|
+
} | {
|
|
16
|
+
readonly kind: 'binary';
|
|
17
|
+
readonly body: BinaryResponse;
|
|
18
|
+
};
|
|
19
|
+
/** 段名的 404 与方法的 405 只在这里判一次;空段名按插件前缀显示,避免 404 原文出现一个空串。 */
|
|
20
|
+
export declare function resolveRoute(method: string, rest: string): RouteSpec;
|
|
21
|
+
/** 四格只建一次:收窄对象在 dispatcher 构造期固定,请求路径只做引用选择。 */
|
|
18
22
|
export declare function slicesOf(full: DeleteDeps): SliceDeps;
|
|
19
|
-
|
|
23
|
+
/** 分发函数:查段 → 方法闸 → 读面 → 处理。 */
|
|
24
|
+
export type Dispatch = (input: ApiInput) => Promise<ApiResult>;
|
|
25
|
+
/** 路由执行器:查段 → 方法闸 → 读面 → 处理;返回归一结果。组合根只建一次。 */
|
|
26
|
+
export declare function createDispatcher(deps: DeleteDeps): Dispatch;
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { type ApplyAction, type RouteName } from '../shared/wire.js';
|
|
15
15
|
import type { CoreDeps, DeleteDeps, RemovalDeps } from '../deps.js';
|
|
16
|
+
import type { AttachmentsPort } from '../ports/attachments-port.js';
|
|
16
17
|
import type { TrashEntry } from '../trash/entry.js';
|
|
17
18
|
/** 请求体:`json` 才读下来(GET 一律不读;读两遍会把流消费掉)。 */
|
|
18
19
|
export type BodyKind = 'none' | 'json';
|
|
@@ -33,29 +34,43 @@ export interface BinaryResponse {
|
|
|
33
34
|
export interface RouteRead {
|
|
34
35
|
rows(): Promise<readonly TrashEntry[]>;
|
|
35
36
|
}
|
|
36
|
-
/** deps 切片(ADR-0032
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
export type RouteSlice = 'core' | 'removal' | 'full';
|
|
37
|
+
/** deps 切片(ADR-0032):每一段只声明自己要的那一面。读与探测五段拿基础面,`attachment` 段另多一条
|
|
38
|
+
* 宿主 `attachments` 读面,两个删盘段多删除能力,`/apply` 是唯一拿全量面的行——段的 run 里长出超面
|
|
39
|
+
* 调用就是编译错,dispatch 也只按这一格注入(运行期收窄住 `src/api/dispatch.ts` 的 `slicesOf`)。 */
|
|
40
|
+
export type RouteSlice = 'core' | 'removal' | 'full' | 'attachment';
|
|
40
41
|
/** 切片名 → 依赖面:{@link RouteSpec.run} 的 deps 参数类型与 `depsFor` 的返回类型都从这一格取。 */
|
|
41
42
|
export type SliceDeps = {
|
|
42
43
|
core: CoreDeps;
|
|
43
44
|
removal: RemovalDeps;
|
|
44
45
|
full: DeleteDeps;
|
|
46
|
+
attachment: CoreDeps & {
|
|
47
|
+
readonly attachments: AttachmentsPort;
|
|
48
|
+
};
|
|
45
49
|
};
|
|
46
|
-
|
|
50
|
+
interface BaseRouteSpec<S extends RouteSlice> {
|
|
47
51
|
/** 这一行吃哪一格 deps:dispatch 按它注入,`run` 的 deps 参数类型也跟着它走。 */
|
|
48
52
|
readonly slice: S;
|
|
49
53
|
readonly method: 'GET' | 'POST';
|
|
50
54
|
readonly body: BodyKind;
|
|
51
55
|
readonly purity: Purity;
|
|
52
56
|
readonly read: ReadKind;
|
|
53
|
-
/**
|
|
54
|
-
|
|
57
|
+
/** 同源取数闸(ADR-0033):只有能被别的页面渲染消费的段需要;判据住 `src/api/connection-route.ts`。
|
|
58
|
+
* 缺省不设闸——JSON 响应跨源读不到(没有 CORS),不需要它。 */
|
|
59
|
+
readonly fetchGate?: 'same-origin';
|
|
60
|
+
}
|
|
61
|
+
/** JSON 段:处理函数拿请求体与读面;`query` 不在这里(九行里只有二进制段需要查询串)。 */
|
|
62
|
+
export interface JsonRouteSpec<S extends RouteSlice = RouteSlice> extends BaseRouteSpec<S> {
|
|
63
|
+
readonly response: 'json';
|
|
55
64
|
/** 声明成函数属性而不是方法:方法签名是双变的,显式标注更宽的 deps 能绕过切片闸;属性按逆变校验。 */
|
|
56
|
-
readonly run: (deps: SliceDeps[S], body: JsonBody, read: RouteRead
|
|
65
|
+
readonly run: (deps: SliceDeps[S], body: JsonBody, read: RouteRead) => Promise<unknown>;
|
|
66
|
+
}
|
|
67
|
+
/** 二进制段:只有 `/attachment`;处理函数直接拿查询串,交回字节与安全头。 */
|
|
68
|
+
export interface BinaryRouteSpec<S extends RouteSlice = RouteSlice> extends BaseRouteSpec<S> {
|
|
69
|
+
readonly response: 'binary';
|
|
70
|
+
readonly run: (deps: SliceDeps[S], query: URLSearchParams) => Promise<BinaryResponse>;
|
|
57
71
|
}
|
|
58
|
-
|
|
72
|
+
export type RouteSpec<S extends RouteSlice = RouteSlice> = JsonRouteSpec<S> | BinaryRouteSpec<S>;
|
|
73
|
+
/** 九行的并集(表的值类型):每行由自己的 `slice` 与 `response` 字面量定型,`run` 的 deps 面随之收窄。 */
|
|
59
74
|
export type AnyRouteSpec = {
|
|
60
75
|
[S in RouteSlice]: RouteSpec<S>;
|
|
61
76
|
}[RouteSlice];
|
|
@@ -67,3 +82,4 @@ export declare const APPLY_ACTIONS: readonly ApplyAction[];
|
|
|
67
82
|
export declare const ROUTE_TABLE: Record<RouteName, AnyRouteSpec>;
|
|
68
83
|
/** 段名 → 那一行;不认识的段名回 `undefined`(调用方抛 404,并带出原路径)。 */
|
|
69
84
|
export declare function routeOf(rest: string): RouteSpec | undefined;
|
|
85
|
+
export {};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 批量删除的浮条:**多选模式内常驻**(0 条也渲染,既显示模式又给退出入口),
|
|
3
3
|
* 汇总「已选 N 条」+(读得到时)「含 K 个子会话」;「查看已选」展开限高可滚动的标题清单
|
|
4
4
|
* (逐条可移除),回答"到底选了哪些"——清单默认收起,不把底部输入区盖住。
|
|
5
|
-
* 模式内的整行点击守卫也挂在这棵常驻宿主上(见 `
|
|
5
|
+
* 模式内的整行点击守卫也挂在这棵常驻宿主上(见 `batch-pick.ts` 的 `useSelectionRowClickGuard`)。
|
|
6
6
|
*
|
|
7
7
|
* 为什么单独一个文件而不是并进 `batch-select.tsx`:那份是"行开关控件",而浮条要点开确认框
|
|
8
8
|
* (要 import 它的现场)——两个方向合到一个文件上就是一圈。浮条住在
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type PickItem } from './batch-selection-store.js';
|
|
2
|
+
/** 全选:把当前可见可选行按登记序追加;已在名单里的由 `addPicks` 自己跳过,原有选中保持在前。 */
|
|
3
|
+
export declare function selectAllVisible(): void;
|
|
4
|
+
/**
|
|
5
|
+
* 同一个切换口该显示「全选」还是「取消全选」:
|
|
6
|
+
* - 有选中、且当前可见可选行全都选中 ⇒ 取消全选;
|
|
7
|
+
* - 有选中、但当前没有可见可选行(全折叠/过滤掉)⇒ 只能清空,也显示取消全选;
|
|
8
|
+
* - 其余(含 0 条)⇒ 全选。
|
|
9
|
+
*/
|
|
10
|
+
export declare function areAllVisibleRowsSelected(rows: readonly PickItem[], picks: readonly PickItem[]): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* 多选态内的整行点击守卫(document 捕获阶段)。
|
|
13
|
+
*
|
|
14
|
+
* 边界:行里的按钮 / 链接 / 输入 / 菜单项一律放行(否则 ⋯ 菜单、归档、置顶、单删都点不动);
|
|
15
|
+
* 双击的第二下与 dblclick 不翻转(第一下已经选中,第二下不能又反掉,也不能打开宿主的重命名)。
|
|
16
|
+
* 认行靠 `data-row-key="session:…"`——**这是本文件唯一一处 DOM 读**(ADR-0034):宿主将来改掉这个
|
|
17
|
+
* 属性,最差是模式内点行不生效,勾选框本身仍在。模式外不挂监听:这时点行必须还是宿主的"打开会话"。
|
|
18
|
+
*/
|
|
19
|
+
export declare function useSelectionRowClickGuard(): void;
|
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
*
|
|
5
5
|
* 入口在每行的「⋯」菜单里(`batch-menu-item.tsx`):**按下才进入多选模式**,模式内才渲染勾选框、
|
|
6
6
|
* 才把整行点击变成"勾/取消";退出模式清空名单。选中集只住 `batch-selection-store.ts`(模式 + 名单一份
|
|
7
|
-
* 合并快照);"
|
|
8
|
-
* store
|
|
7
|
+
* 合并快照);"哪些行现在可见"住 `host-rows.ts`,"点行算不算选中"住 `batch-pick.ts`。四处隔着
|
|
8
|
+
* 槽位边界,只有模块级 store 能让他们看见同一份名单;本文件不持有任何现场。
|
|
9
9
|
*
|
|
10
10
|
* **为什么主勾选在行首**:宿主对行尾 `.rowActions` 是 `display:none`,只有 `row:hover` / 菜单打开
|
|
11
11
|
* 才出现(app.asar 0.2.0-rc.2 的 Rows 渲染);行首 16px 格在无状态点时是空的,模式内可见。
|
|
12
12
|
* 宿主在「有状态点(运行中/待确认/未读完成)」与归档行不渲染行首格 ⇒ 这些行走行尾悬停兜底,
|
|
13
|
-
* 但模式内**点整行**也能选(`
|
|
13
|
+
* 但模式内**点整行**也能选(`batch-pick.ts` 的 `useSelectionRowClickGuard`),所以不依赖那颗小框。
|
|
14
14
|
* 两条勾选不会同时出现(`ui.css` 的 `:has` 兜底隐藏规则,靠宿主行的 `data-row-key`)。
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
16
|
+
import type { ReactElement } from 'react';
|
|
17
17
|
import { type Translate } from './copy.js';
|
|
18
18
|
import { type SessionDeleteRemote } from './remote.js';
|
|
19
19
|
/**
|
|
@@ -27,6 +27,8 @@ export interface BatchPickCheckboxProps {
|
|
|
27
27
|
sessionId: string;
|
|
28
28
|
/** 槽位只投 `sessionId`;标题由宿主侧的 root hook 读出来再传进来(见 `index.tsx` 的 `PickLeadingRow`)。 */
|
|
29
29
|
title: string;
|
|
30
|
+
/** 运行态同样由 root hook 读:登记表里的 `live` 与行尾兜底保持同一口径。 */
|
|
31
|
+
live?: boolean;
|
|
30
32
|
/** 文案(缺省 = 内置 zh,见 `copy.ts`)。 */
|
|
31
33
|
t?: Translate;
|
|
32
34
|
}
|
|
@@ -37,7 +39,7 @@ export interface BatchPickCheckboxProps {
|
|
|
37
39
|
* (行首格在宿主行那条 `onClick` 里面)。宿主对「有状态点」和归档行不渲染这一格,
|
|
38
40
|
* 那些行走 `BatchPickButton` 兜底;两条不会同时出现(见 `ui.css` 的兜底隐藏规则)。
|
|
39
41
|
*/
|
|
40
|
-
export declare function BatchPickCheckbox({ sessionId, title, t, }: BatchPickCheckboxProps): ReactElement | null;
|
|
42
|
+
export declare function BatchPickCheckbox({ sessionId, title, live, t, }: BatchPickCheckboxProps): ReactElement | null;
|
|
41
43
|
export interface BatchPickButtonProps {
|
|
42
44
|
sessionId: string;
|
|
43
45
|
title: string;
|
|
@@ -1,54 +1,49 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PickItem } from './batch-selection-store.js';
|
|
2
2
|
export interface HostRowInfo {
|
|
3
|
-
/**
|
|
3
|
+
/** 槽位宿主投影的标题(`PickLeadingRow` / `PickRow` 从 root hook 读);守卫只读登记,不读 DOM 文本。 */
|
|
4
4
|
readonly title: string;
|
|
5
5
|
/** 在跑的行点整行也不选:与置灰的勾选框同口径(级联遇在跑成员整组拒绝)。 */
|
|
6
6
|
readonly live: boolean;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* 不该把快照翻新一遍。
|
|
9
|
+
* 行槽位开关登记一行:模式内与模式外都要登记(守卫进模式的那一帧就得有标题)。
|
|
10
|
+
* 每次登记算**多一个挂载实例**;不给 info 时只占位,随后由 {@link updateHostRowInfo} 填值。
|
|
12
11
|
*/
|
|
13
|
-
export declare function registerHostRowInfo(sessionId: string, info
|
|
14
|
-
/**
|
|
12
|
+
export declare function registerHostRowInfo(sessionId: string, info?: HostRowInfo): void;
|
|
13
|
+
/**
|
|
14
|
+
* 标题/运行态变了就地更新:**不注销、不重登记**,否则这一行会被挪到登记序末尾(ADR-0034 决定 2)。
|
|
15
|
+
*/
|
|
16
|
+
export declare function updateHostRowInfo(sessionId: string, info: HostRowInfo): void;
|
|
17
|
+
/**
|
|
18
|
+
* 槽位组件共用的登记生命周期:挂载登记、卸载注销;标题/运行态变化只走更新口。
|
|
19
|
+
* 两次 effect 分工是顺序契约:先占住首次登记序,再填值。
|
|
20
|
+
*/
|
|
21
|
+
export declare function useHostRowRegistration(sessionId: string, info: HostRowInfo): void;
|
|
22
|
+
/** 行槽位开关卸载时调用:那一行已经离开渲染树,登记跟着走。
|
|
15
23
|
* 还有别的挂载实例在用同一行(重复行)就只减计数,不摘登记。 */
|
|
16
24
|
export declare function unregisterHostRowInfo(sessionId: string): void;
|
|
17
25
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
|
|
26
|
+
* 守卫与全选共用的一行读数:这一行此刻可勾选时交回登记(含标题);未登记或在跑回 `undefined`。
|
|
27
|
+
* 登记表是可见行的事实源,所以"没登记"就是"这一行此刻不在可见集合里"。
|
|
28
|
+
*/
|
|
29
|
+
export declare function pickableRowOf(sessionId: string): HostRowInfo | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* 「当前可见」的可选行:**已登记**的会话行(槽位开关还挂在渲染树上)——折叠组、归档过滤外的行
|
|
32
|
+
* 没有挂载,因此没有登记;在跑的行跳过(与置灰勾选框同口径)。标题/运行态来自登记值;
|
|
33
|
+
* 顺序是登记顺序(宿主按列表顺序挂载行)。
|
|
21
34
|
*
|
|
22
35
|
* 这是**当场读**:事件路径(点「全选」那一刻的页面)与测试用它;渲染侧读下面的快照。
|
|
23
36
|
*/
|
|
24
37
|
export declare function readVisibleSelectableRows(): PickItem[];
|
|
25
38
|
/**
|
|
26
|
-
*
|
|
39
|
+
* 渲染侧的快照:脏了才重读登记;干净时回同一个引用(React 的 `useSyncExternalStore` 契约)。
|
|
27
40
|
* 没变化时连续两次调用必须同引用,否则订阅方会被自己的快照更新推成死循环。
|
|
28
41
|
*/
|
|
29
42
|
export declare function getVisibleSelectableRowsSnapshot(): readonly PickItem[];
|
|
30
43
|
/**
|
|
31
|
-
* 渲染侧只读快照 +
|
|
32
|
-
*
|
|
44
|
+
* 渲染侧只读快照 + 登记订阅;`enabled === false` 不订阅也不读登记——行列表只随多选态存在,
|
|
45
|
+
* 模式外不必付订阅的成本。
|
|
33
46
|
*/
|
|
34
47
|
export declare function useVisibleSelectableRows(enabled: boolean): readonly PickItem[];
|
|
35
|
-
/**
|
|
36
|
-
export declare function selectAllVisible(): void;
|
|
37
|
-
/**
|
|
38
|
-
* 同一个切换口该显示「全选」还是「取消全选」:
|
|
39
|
-
* - 有选中、且当前可见可选行全都选中 ⇒ 取消全选;
|
|
40
|
-
* - 有选中、但当前没有可见可选行(全折叠/过滤掉)⇒ 只能清空,也显示取消全选;
|
|
41
|
-
* - 其余(含 0 条)⇒ 全选。
|
|
42
|
-
*/
|
|
43
|
-
export declare function areAllVisibleRowsSelected(rows: readonly PickItem[], picks: readonly PickItem[]): boolean;
|
|
44
|
-
/**
|
|
45
|
-
* 多选态内的整行点击守卫(document 捕获阶段)。
|
|
46
|
-
*
|
|
47
|
-
* 边界:行里的按钮 / 链接 / 输入 / 菜单项一律放行(否则 ⋯ 菜单、归档、置顶、单删都点不动);
|
|
48
|
-
* 双击的第二下与 dblclick 不翻转(第一下已经选中,第二下不能又反掉,也不能打开宿主的重命名)。
|
|
49
|
-
* 认行靠 `data-row-key="session:…"`——宿主将来改掉这个属性,最差是模式内点行不生效,
|
|
50
|
-
* 勾选框本身仍在。模式外不挂监听:这时点行必须还是宿主的"打开会话"。
|
|
51
|
-
*/
|
|
52
|
-
export declare function useSelectionRowClickGuard(): void;
|
|
53
|
-
/** 测试专用:登记表清空并标脏(会话 id 不跨用例复用,名字/运行态别串味);观察者归订阅者管。 */
|
|
48
|
+
/** 测试专用:登记表清空并标脏(会话 id 不跨用例复用,名字/运行态别串味)。 */
|
|
54
49
|
export declare function resetHostRows(): void;
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* 所以关闭/重开是**一段生命周期**,不是一个 id:删除成功后 `afterDelete` 关掉当前
|
|
12
12
|
* 主视图并记下"这一条是我们关掉的";撤销成功后 `afterRestore` 只重开自己记过的那些。调用方不再
|
|
13
13
|
* 各自判定该不该关、也不再把会话 id 塞进撤销提示传一圈——判据全在这一处。两次重开排队时只有最新
|
|
14
|
-
*
|
|
14
|
+
* 那条会打开(等待本身住 `src/client/pending-open.ts` 的票与订阅),`resetMainView()` 清掉这份记忆
|
|
15
|
+
* 并取消在等的重开。
|
|
15
16
|
*
|
|
16
17
|
* **不 import 宿主客户端包**(AGENTS.md 红线):只通过 `ctx.get('uiWorkspace')` 可选读服务,
|
|
17
18
|
* 形状不符/服务缺席都安静退回,绝不影响插件激活。字段与方法名来自上面两个包的产物原文;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface PendingOpenInput {
|
|
2
|
+
/** 订阅「清单变了」;返回退订。旧 store 可能没有——那时只有"已经出现"这一支能开。 */
|
|
3
|
+
readonly subscribe?: ((listener: () => void) => () => void) | undefined;
|
|
4
|
+
/** 目标是否已经出现在清单里。 */
|
|
5
|
+
readonly isReady: () => boolean;
|
|
6
|
+
/** 出现的这一拍还该不该抢焦点(用户可能已经选了别的会话)。 */
|
|
7
|
+
readonly canOpen: () => boolean;
|
|
8
|
+
/** 打开目标;调用方自己收宿主抛错。 */
|
|
9
|
+
readonly open: () => void;
|
|
10
|
+
}
|
|
11
|
+
export interface PendingOpen {
|
|
12
|
+
/** 取消在等的等待(没有也安全);后一次 `schedule` 会先取消前一次。 */
|
|
13
|
+
cancel(): void;
|
|
14
|
+
/** 等目标出现再打开;已经在清单里就立即打开。 */
|
|
15
|
+
schedule(input: PendingOpenInput): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function createPendingOpen(giveUpMs?: number): PendingOpen;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 浏览器半的取数通道:同源 fetch
|
|
2
|
+
* 浏览器半的取数通道:同源 fetch 宿主 `connection.fetch` 的 exact Fetch 路由(`/api` + 插件前缀)。
|
|
3
|
+
* 信封与失败原文在这一层收口。
|
|
3
4
|
*
|
|
4
|
-
* -
|
|
5
|
-
*
|
|
5
|
+
* - 九段注册在宿主 `connection.fetch.register` 上,鉴权归宿主 `/api` bridge(ADR-0033);
|
|
6
|
+
* 没有旧前缀 fallback——支持面是 web UI 与桌面端,二者的标准组合都装 `client-connection`。
|
|
7
|
+
* 官方 remote.<namespace> 仍不可用(第三方拿不到 Typert 描述符的动态发现挂载点)。
|
|
6
8
|
* - 九段的**段名与方法**住 `src/shared/wire.ts` 的结构化段名表(`ROUTES` 每项 `{ path, method }`),
|
|
7
9
|
* 本文件只投影路径(`prefix + ROUTES.x.path`)与方法(GET 段直接取 `ROUTES.x.method`,写段把它
|
|
8
10
|
* 交给 `post()`)——方法字面量不在本文件出现第二份;
|
|
@@ -75,20 +77,17 @@ export interface RawSessionDeleteRemote {
|
|
|
75
77
|
empty(): Promise<RemoteResult<PurgeResultLike>>;
|
|
76
78
|
}
|
|
77
79
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* `dsh web` 打印的 URL 打开过的页面成立(凭据是那行 URL 换来的 cookie,不是插件自己的秘密)。
|
|
80
|
+
* exact Fetch 路由的 fetch 实现。**相对路径**:不拼 host、不认 origin——页面本身就是从宿主页面
|
|
81
|
+
* 加载的,同源是这条通道的全部前提;鉴权归宿主 `/api` bridge(ADR-0033)。所以这条通道**只对**
|
|
82
|
+
* 宿主页面打开过、带宿主身份的客户端成立(凭据是那次访问换来的 cookie,不是插件自己的秘密)。
|
|
82
83
|
*/
|
|
83
|
-
export declare function createHttpRemote(
|
|
84
|
+
export declare function createHttpRemote(): RawSessionDeleteRemote;
|
|
84
85
|
/**
|
|
85
|
-
*
|
|
86
|
+
* 探测唯一通道:一次 `GET /ping` 回成功信封(`pong`)才注册入口。
|
|
86
87
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* 探测用一次零成本的 `GET /ping`,不读清单,避免每次页面加载都跑全表对账。
|
|
88
|
+
* - 401/403 是宿主 `/api` 的纯文本身份/归属失败:入口不出现,不装禁用态。
|
|
89
|
+
* - 失败信封 / 404 / 非信封同样按不可用处理:`/api` 网关对未知路径也可能回错误形状,
|
|
90
|
+
* 不能据此把入口摆出来让每个动作都失败。
|
|
92
91
|
*/
|
|
93
92
|
export declare function probeChannel(raw: RawSessionDeleteRemote): Promise<boolean>;
|
|
94
93
|
/** `shared/wire.ts` 的 `ApplyOutcome`、扫描报告与附件回执从这里转出去——浏览器半只认 `remote.js` 这一扇门。 */
|
|
@@ -117,7 +116,7 @@ export interface SessionDeleteRemote {
|
|
|
117
116
|
/** 回收站之外的无主子会话报告(纯读)。读不到就是 throw —— 界面上那是**一次失败的检查**,
|
|
118
117
|
* 不是"零条残留"(把 `io` 折成空报告等于把"不知道"说成"没有")。 */
|
|
119
118
|
scanOrphans(): Promise<OrphanReportWire>;
|
|
120
|
-
/** `.dsh`
|
|
119
|
+
/** `.dsh` 内无引用图片对象的报告(纯读;预览清单条数由 Node 半配置,默认 200)。读不到就是 throw,同样不许折成 0。 */
|
|
121
120
|
scanAttachmentOrphans(): Promise<AttachmentOrphanReportWire>;
|
|
122
121
|
/** 清掉报告那一刻无引用的图片副本(聚合回执)。 */
|
|
123
122
|
cleanAttachmentOrphans(): Promise<AttachmentCleanupWire>;
|
|
@@ -24,13 +24,7 @@ export interface LazyStore<T> {
|
|
|
24
24
|
invalidate: () => void;
|
|
25
25
|
subscribe: (listener: () => void) => () => void;
|
|
26
26
|
}
|
|
27
|
-
export
|
|
28
|
-
/** 第一个订阅者到来时跑(观察者在这里接上)。 */
|
|
29
|
-
onFirstSubscribe?(): void;
|
|
30
|
-
/** 最后一个订阅者退订时跑(观察者在这里断开)。 */
|
|
31
|
-
onLastUnsubscribe?(): void;
|
|
32
|
-
}
|
|
33
|
-
export declare function createLazyStore<T>(initial: T, read: () => T, lifecycle?: LazyStoreLifecycle): LazyStore<T>;
|
|
27
|
+
export declare function createLazyStore<T>(initial: T, read: () => T): LazyStore<T>;
|
|
34
28
|
/**
|
|
35
29
|
* 最新票句柄:每张票记着签发时的序号,只有序号仍等于当前的那张才 `current`。
|
|
36
30
|
* 调用方在发起异步读时 `issue()` 一张票,落地前问 `ticket.current`——旧读因此不会盖掉新状态。
|
|
@@ -43,7 +43,7 @@ export interface TrashPanelController {
|
|
|
43
43
|
readonly detailNav: DetailNav;
|
|
44
44
|
/** 打开一个孩子(父详情子会话段点进来):读它的详情,但清单选中不动。 */
|
|
45
45
|
openChild(child: TrashEntryWire): Promise<void>;
|
|
46
|
-
/**
|
|
46
|
+
/** 回到父详情(详情顶部那颗「返回父会话」;窄档 back/Esc 的第一程也走它)。 */
|
|
47
47
|
closeChild(): void;
|
|
48
48
|
readonly drilled: boolean;
|
|
49
49
|
readonly detail: TrashDetailState;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 可调项(Config,ADR-0037):三只旋钮 + 本地 Standard Schema。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不用 `@deepseek-ai/schemastery` 写 schema:那是宿主包,值 import 会破本仓「跨插件一律
|
|
5
|
+
* `import type`」的硬约束;Cordis 的 Config 接受 Standard Schema(`@standard-schema/spec`),
|
|
6
|
+
* 手写这一小段即可,不需要把宿主 schema 库搬进插件。代价是宿主配置页拿不到字段级 JSON Schema
|
|
7
|
+
* ——配置以 profile 的 `cordis.patch.yml` 为准(README「可调项」有示例)。
|
|
8
|
+
*
|
|
9
|
+
* 这里只做**默认值、归一与 schema**,不持有运行期现场:`apply` 归一一次,结果作为
|
|
10
|
+
* `CoreDeps.config` 注入;读方从自己的 deps 读,不 import 模块级单例。
|
|
11
|
+
* 客户端 6 秒撤销时长不在这里:它是浏览器半的产品决定,不跨半传。
|
|
12
|
+
*/
|
|
13
|
+
export interface SessionDeleteConfig {
|
|
14
|
+
/** 设置页维护卡「孤儿附件」预览清单最多列多少条(数量与字节仍按全量报告)。 */
|
|
15
|
+
readonly orphanPreviewLimit: number;
|
|
16
|
+
/** `GET /attachment` 单次响应字节上限(服务路径与直接 fs fallback 共用)。 */
|
|
17
|
+
readonly maxAttachmentBytes: number;
|
|
18
|
+
/** 附件引用扫描里单个 JSON 引用源的字节上限;超过就按「读不出」fail closed。 */
|
|
19
|
+
readonly maxJsonRefBytes: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const DEFAULT_CONFIG: SessionDeleteConfig;
|
|
22
|
+
/**
|
|
23
|
+
* 把 patch 里的 `config` 归一成完整配置:缺省取默认值,未知键 / 非整数 / 越界都抛。
|
|
24
|
+
* 抛错会让插件激活失败(响亮),而不是悄悄用半套配置跑。
|
|
25
|
+
*/
|
|
26
|
+
export declare function normalizeConfig(value: unknown): SessionDeleteConfig;
|
|
27
|
+
/**
|
|
28
|
+
* 插件 Config:Cordis 接受 Standard Schema,`~standard.validate` 交回归一后的配置。
|
|
29
|
+
* 宿主在激活前校验;`apply` 再归一一次(同一份事实,不会漂)。
|
|
30
|
+
*/
|
|
31
|
+
export declare const Config: {
|
|
32
|
+
readonly '~standard': {
|
|
33
|
+
readonly version: 1;
|
|
34
|
+
readonly vendor: "dsh-session-delete";
|
|
35
|
+
readonly validate: (value: unknown) => {
|
|
36
|
+
value: SessionDeleteConfig;
|
|
37
|
+
issues?: never;
|
|
38
|
+
} | {
|
|
39
|
+
issues: {
|
|
40
|
+
message: string;
|
|
41
|
+
}[];
|
|
42
|
+
value?: never;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
};
|
package/lib/types/deps.d.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { CoreFsPort, FsPort } from './ports/fs-port.js';
|
|
2
2
|
import type { HostCorePort } from './ports/host-port.js';
|
|
3
|
+
import type { AttachmentsPort } from './ports/attachments-port.js';
|
|
3
4
|
import type { PresenceCapability } from './ports/presence-port.js';
|
|
5
|
+
import type { SessionDeleteConfig } from './config.js';
|
|
4
6
|
import type { TrashLedger } from './trash/ledger.js';
|
|
5
7
|
/** Node 半的基础依赖面:组合根只装配这一颗;动词之间不互相 import 依赖面 interface。
|
|
6
8
|
* `fs` 不含 `removeDir` / `removeFile`,`host` 不含在场三件——两条危险能力各走 {@link RemovalDeps} 与
|
|
7
|
-
* {@link CoreDeps.presence}
|
|
9
|
+
* {@link CoreDeps.presence}。`config` 是 `apply` 归一后的只读快照:读方从 deps 读,不 import 模块级单例。 */
|
|
8
10
|
export interface CoreDeps {
|
|
9
11
|
fs: CoreFsPort;
|
|
10
12
|
host: HostCorePort;
|
|
11
13
|
ledger: TrashLedger;
|
|
12
14
|
home: string;
|
|
13
15
|
presence: PresenceCapability;
|
|
16
|
+
config: SessionDeleteConfig;
|
|
14
17
|
}
|
|
15
18
|
/** 会话删除动词的依赖面:只多读时钟;它不删目录(回滚只把搬走的搬回)。 */
|
|
16
19
|
export type DeleteSessionDeps = CoreDeps & {
|
|
@@ -21,5 +24,7 @@ export type RemovalDeps = CoreDeps & {
|
|
|
21
24
|
fs: CoreFsPort & Pick<FsPort, 'removeDir' | 'removeFile'>;
|
|
22
25
|
};
|
|
23
26
|
/** 组合根装出的全量面:`/apply` 的分发同时够到删除的时钟与永久删除的 `removeDir` / `removeFile`
|
|
24
|
-
*
|
|
25
|
-
export type DeleteDeps = DeleteSessionDeps & RemovalDeps
|
|
27
|
+
* (后者也供孤儿附件清理段使用)。`attachments` 只给 `attachment` 读段(ADR-0035);动词不拿它。 */
|
|
28
|
+
export type DeleteDeps = DeleteSessionDeps & RemovalDeps & {
|
|
29
|
+
readonly attachments: AttachmentsPort;
|
|
30
|
+
};
|
|
@@ -33,6 +33,9 @@ export declare function firstByBare<N extends ClosureNode>(members: readonly N[]
|
|
|
33
33
|
* 已被前一组收走的那条,连同它整支子树都不再出现在这一组里——后代在那一组已被全部认领,所以
|
|
34
34
|
* 「丢掉已认领的」与「不走进已认领的那支」交回的是同一个集合。环上的代价是「少数一条」,
|
|
35
35
|
* 不是把一次扫描挂死。组序与成员序都按语料原序。
|
|
36
|
+
*
|
|
37
|
+
* 后代也必须是 subagent 起源:普通 fork 的 `parentSession` 只是来源记录,它不进组、也不穿过它收孩子
|
|
38
|
+
* (否则从 subagent 分出去的分支会被当成孤儿永久删掉)。
|
|
36
39
|
*/
|
|
37
40
|
export declare function orphanRootsOf<N extends ClosureNode & {
|
|
38
41
|
readonly parentSessionId?: string;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import { Config } from './config.js';
|
|
2
3
|
export declare const name = "@xrn1997/dsh-session-delete";
|
|
4
|
+
/** 可调项的 Config(Standard Schema);为什么不用宿主 schema 库见 ADR-0037。 */
|
|
5
|
+
export { Config };
|
|
3
6
|
/**
|
|
4
7
|
* 插件依赖:**只写字符串数组**(隔离数据根上实测:写成 `{ required, optional }` 会让插件停在
|
|
5
8
|
* `pending (waiting for services: required, optional)`)。
|
|
@@ -14,24 +17,22 @@ export declare const name = "@xrn1997/dsh-session-delete";
|
|
|
14
17
|
* - `agents`:只给 handle 捕获用(`ctx.get('agents')`;缺席时 `ctx.inject(['agents'])` 等服务就绪再装)。
|
|
15
18
|
* 它同样可能晚于本插件激活,写进插件 inject 只会让整个插件 pending。
|
|
16
19
|
*
|
|
17
|
-
* `
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* `
|
|
21
|
-
*
|
|
22
|
-
* `connection` 只喂请求栅栏的远程支(`src/api/request-trust.ts` ②),按 `ctx.get` 可选读:
|
|
23
|
-
* 服务缺席时插件照常激活,远程客户端 fail-closed。它不进 `inject`——写进去会让没有 web 组成的
|
|
24
|
-
* profile 整个停在 pending。
|
|
20
|
+
* `connection` 是浏览器半取数通道的唯一承载者:九段注册到 `connection.fetch.register` 的
|
|
21
|
+
* `/api/dsh-session-delete-api/*`,Host/Origin/浏览器认证由宿主 `/api` bridge 完成(ADR-0033)。
|
|
22
|
+
* 支持面是 web UI 与桌面端,二者的标准组合都装 `client-connection`;服务缺席时插件停在 pending
|
|
23
|
+
* ——可见的激活失败,而不是静默地没有入口。`webServer` 不再直接使用:exact route 仍由宿主的
|
|
24
|
+
* `connection` 挂在其 `/api` 前缀上,插件不需要自己碰物理服务器。
|
|
25
25
|
*
|
|
26
26
|
* 清册的三层各有各的家:raw 绑定 `src/trash/storage-manifest.ts`、读面(对账 + 读)`src/trash/ledger.ts`、
|
|
27
27
|
* 数据根 `src/ports/home.ts`。这里只把它们接起来,任何一层都不在组合根里长判据。
|
|
28
28
|
*/
|
|
29
29
|
export declare const inject: string[];
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* 装配顺序:**配置归一 → 清单 → 端口 → 在场能力 → 动词依赖 → 分发器 → exact Fetch 路由**
|
|
32
|
+
* (`DeleteDeps` 把这些依赖面合成一颗;`config` 是归一后的只读快照)。
|
|
32
33
|
*
|
|
33
34
|
* 路由用 `ctx.effect` 挂、disposer 归装载它的那根 fiber:插件卸载即摘路由(`register` 返回的
|
|
34
|
-
* disposer 是官方契约)。**同一进程里只注册一次**这件事由宿主把住:重复的 `(
|
|
35
|
-
* 在 `register`
|
|
35
|
+
* disposer 是官方契约)。**同一进程里只注册一次**这件事由宿主把住:重复的 `(path, method)`
|
|
36
|
+
* 在 `connection.fetch.register` 里直接抛,响亮失败而不是悄悄覆盖。
|
|
36
37
|
*/
|
|
37
|
-
export declare function apply(ctx: Context): void;
|
|
38
|
+
export declare function apply(ctx: Context, rawConfig?: unknown): void;
|