@yunzai-ng/core 0.1.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 +661 -0
- package/README.md +51 -0
- package/dist/adapter/accounts.d.ts +174 -0
- package/dist/adapter/accounts.d.ts.map +1 -0
- package/dist/adapter/accounts.js +653 -0
- package/dist/adapter/accounts.js.map +1 -0
- package/dist/adapter/bots.d.ts +142 -0
- package/dist/adapter/bots.d.ts.map +1 -0
- package/dist/adapter/bots.js +454 -0
- package/dist/adapter/bots.js.map +1 -0
- package/dist/adapter/host.d.ts +114 -0
- package/dist/adapter/host.d.ts.map +1 -0
- package/dist/adapter/host.js +90 -0
- package/dist/adapter/host.js.map +1 -0
- package/dist/adapter/login.d.ts +174 -0
- package/dist/adapter/login.d.ts.map +1 -0
- package/dist/adapter/login.js +345 -0
- package/dist/adapter/login.js.map +1 -0
- package/dist/adapter/registry.d.ts +102 -0
- package/dist/adapter/registry.d.ts.map +1 -0
- package/dist/adapter/registry.js +150 -0
- package/dist/adapter/registry.js.map +1 -0
- package/dist/config/core-config.d.ts +151 -0
- package/dist/config/core-config.d.ts.map +1 -0
- package/dist/config/core-config.js +338 -0
- package/dist/config/core-config.js.map +1 -0
- package/dist/config/schema.d.ts +449 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +871 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/store.d.ts +184 -0
- package/dist/config/store.d.ts.map +1 -0
- package/dist/config/store.js +428 -0
- package/dist/config/store.js.map +1 -0
- package/dist/config/yaml.d.ts +23 -0
- package/dist/config/yaml.d.ts.map +1 -0
- package/dist/config/yaml.js +108 -0
- package/dist/config/yaml.js.map +1 -0
- package/dist/http/client.d.ts +82 -0
- package/dist/http/client.d.ts.map +1 -0
- package/dist/http/client.js +658 -0
- package/dist/http/client.js.map +1 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +94 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel/app.d.ts +244 -0
- package/dist/kernel/app.d.ts.map +1 -0
- package/dist/kernel/app.js +654 -0
- package/dist/kernel/app.js.map +1 -0
- package/dist/kernel/kv-sink.d.ts +34 -0
- package/dist/kernel/kv-sink.d.ts.map +1 -0
- package/dist/kernel/kv-sink.js +25 -0
- package/dist/kernel/kv-sink.js.map +1 -0
- package/dist/kernel/policy.d.ts +77 -0
- package/dist/kernel/policy.d.ts.map +1 -0
- package/dist/kernel/policy.js +99 -0
- package/dist/kernel/policy.js.map +1 -0
- package/dist/kernel/runtime.d.ts +117 -0
- package/dist/kernel/runtime.d.ts.map +1 -0
- package/dist/kernel/runtime.js +197 -0
- package/dist/kernel/runtime.js.map +1 -0
- package/dist/kernel/sql-sink.d.ts +36 -0
- package/dist/kernel/sql-sink.d.ts.map +1 -0
- package/dist/kernel/sql-sink.js +114 -0
- package/dist/kernel/sql-sink.js.map +1 -0
- package/dist/logger/format.d.ts +67 -0
- package/dist/logger/format.d.ts.map +1 -0
- package/dist/logger/format.js +218 -0
- package/dist/logger/format.js.map +1 -0
- package/dist/logger/index.d.ts +97 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +363 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/rotate.d.ts +47 -0
- package/dist/logger/rotate.d.ts.map +1 -0
- package/dist/logger/rotate.js +242 -0
- package/dist/logger/rotate.js.map +1 -0
- package/dist/message/segment.d.ts +255 -0
- package/dist/message/segment.d.ts.map +1 -0
- package/dist/message/segment.js +510 -0
- package/dist/message/segment.js.map +1 -0
- package/dist/message/split.d.ts +34 -0
- package/dist/message/split.d.ts.map +1 -0
- package/dist/message/split.js +79 -0
- package/dist/message/split.js.map +1 -0
- package/dist/message/target.d.ts +29 -0
- package/dist/message/target.d.ts.map +1 -0
- package/dist/message/target.js +34 -0
- package/dist/message/target.js.map +1 -0
- package/dist/pipeline/cooldown.d.ts +84 -0
- package/dist/pipeline/cooldown.d.ts.map +1 -0
- package/dist/pipeline/cooldown.js +94 -0
- package/dist/pipeline/cooldown.js.map +1 -0
- package/dist/pipeline/dispatch.d.ts +98 -0
- package/dist/pipeline/dispatch.d.ts.map +1 -0
- package/dist/pipeline/dispatch.js +323 -0
- package/dist/pipeline/dispatch.js.map +1 -0
- package/dist/pipeline/event.d.ts +130 -0
- package/dist/pipeline/event.d.ts.map +1 -0
- package/dist/pipeline/event.js +538 -0
- package/dist/pipeline/event.js.map +1 -0
- package/dist/pipeline/middleware.d.ts +63 -0
- package/dist/pipeline/middleware.d.ts.map +1 -0
- package/dist/pipeline/middleware.js +174 -0
- package/dist/pipeline/middleware.js.map +1 -0
- package/dist/pipeline/prompt.d.ts +80 -0
- package/dist/pipeline/prompt.d.ts.map +1 -0
- package/dist/pipeline/prompt.js +155 -0
- package/dist/pipeline/prompt.js.map +1 -0
- package/dist/pipeline/router.d.ts +98 -0
- package/dist/pipeline/router.d.ts.map +1 -0
- package/dist/pipeline/router.js +489 -0
- package/dist/pipeline/router.js.map +1 -0
- package/dist/platform/detect.d.ts +24 -0
- package/dist/platform/detect.d.ts.map +1 -0
- package/dist/platform/detect.js +178 -0
- package/dist/platform/detect.js.map +1 -0
- package/dist/platform/paths.d.ts +38 -0
- package/dist/platform/paths.d.ts.map +1 -0
- package/dist/platform/paths.js +129 -0
- package/dist/platform/paths.js.map +1 -0
- package/dist/platform/system.d.ts +71 -0
- package/dist/platform/system.d.ts.map +1 -0
- package/dist/platform/system.js +242 -0
- package/dist/platform/system.js.map +1 -0
- package/dist/plugin/context.d.ts +92 -0
- package/dist/plugin/context.d.ts.map +1 -0
- package/dist/plugin/context.js +573 -0
- package/dist/plugin/context.js.map +1 -0
- package/dist/plugin/define.d.ts +92 -0
- package/dist/plugin/define.d.ts.map +1 -0
- package/dist/plugin/define.js +82 -0
- package/dist/plugin/define.js.map +1 -0
- package/dist/plugin/discover.d.ts +106 -0
- package/dist/plugin/discover.d.ts.map +1 -0
- package/dist/plugin/discover.js +254 -0
- package/dist/plugin/discover.js.map +1 -0
- package/dist/plugin/events.d.ts +109 -0
- package/dist/plugin/events.d.ts.map +1 -0
- package/dist/plugin/events.js +209 -0
- package/dist/plugin/events.js.map +1 -0
- package/dist/plugin/hooks.d.ts +266 -0
- package/dist/plugin/hooks.d.ts.map +1 -0
- package/dist/plugin/hooks.js +84 -0
- package/dist/plugin/hooks.js.map +1 -0
- package/dist/plugin/host.d.ts +160 -0
- package/dist/plugin/host.d.ts.map +1 -0
- package/dist/plugin/host.js +546 -0
- package/dist/plugin/host.js.map +1 -0
- package/dist/plugin/market.d.ts +264 -0
- package/dist/plugin/market.d.ts.map +1 -0
- package/dist/plugin/market.js +700 -0
- package/dist/plugin/market.js.map +1 -0
- package/dist/plugin/services.d.ts +116 -0
- package/dist/plugin/services.d.ts.map +1 -0
- package/dist/plugin/services.js +213 -0
- package/dist/plugin/services.js.map +1 -0
- package/dist/plugin/tar.d.ts +65 -0
- package/dist/plugin/tar.d.ts.map +1 -0
- package/dist/plugin/tar.js +277 -0
- package/dist/plugin/tar.js.map +1 -0
- package/dist/render/registry.d.ts +127 -0
- package/dist/render/registry.d.ts.map +1 -0
- package/dist/render/registry.js +306 -0
- package/dist/render/registry.js.map +1 -0
- package/dist/scheduler/index.d.ts +67 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +279 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/server/api.d.ts +179 -0
- package/dist/server/api.d.ts.map +1 -0
- package/dist/server/api.js +602 -0
- package/dist/server/api.js.map +1 -0
- package/dist/server/auth.d.ts +83 -0
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/auth.js +178 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/browse.d.ts +60 -0
- package/dist/server/browse.d.ts.map +1 -0
- package/dist/server/browse.js +187 -0
- package/dist/server/browse.js.map +1 -0
- package/dist/server/files.d.ts +27 -0
- package/dist/server/files.d.ts.map +1 -0
- package/dist/server/files.js +76 -0
- package/dist/server/files.js.map +1 -0
- package/dist/server/index.d.ts +189 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +1123 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/table.d.ts +105 -0
- package/dist/server/table.d.ts.map +1 -0
- package/dist/server/table.js +289 -0
- package/dist/server/table.js.map +1 -0
- package/dist/store/index.d.ts +50 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +120 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/json.d.ts +73 -0
- package/dist/store/json.d.ts.map +1 -0
- package/dist/store/json.js +174 -0
- package/dist/store/json.js.map +1 -0
- package/dist/store/kv.d.ts +126 -0
- package/dist/store/kv.d.ts.map +1 -0
- package/dist/store/kv.js +232 -0
- package/dist/store/kv.js.map +1 -0
- package/dist/store/level.d.ts +103 -0
- package/dist/store/level.d.ts.map +1 -0
- package/dist/store/level.js +119 -0
- package/dist/store/level.js.map +1 -0
- package/dist/store/memory.d.ts +61 -0
- package/dist/store/memory.d.ts.map +1 -0
- package/dist/store/memory.js +94 -0
- package/dist/store/memory.js.map +1 -0
- package/dist/store/sql.d.ts +142 -0
- package/dist/store/sql.d.ts.map +1 -0
- package/dist/store/sql.js +246 -0
- package/dist/store/sql.js.map +1 -0
- package/dist/testing/fake.d.ts +109 -0
- package/dist/testing/fake.d.ts.map +1 -0
- package/dist/testing/fake.js +196 -0
- package/dist/testing/fake.js.map +1 -0
- package/dist/testing/index.d.ts +16 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +16 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-adapter.d.ts +205 -0
- package/dist/testing/mock-adapter.d.ts.map +1 -0
- package/dist/testing/mock-adapter.js +309 -0
- package/dist/testing/mock-adapter.js.map +1 -0
- package/dist/util/deep.d.ts +108 -0
- package/dist/util/deep.d.ts.map +1 -0
- package/dist/util/deep.js +275 -0
- package/dist/util/deep.js.map +1 -0
- package/dist/util/defer.d.ts +107 -0
- package/dist/util/defer.d.ts.map +1 -0
- package/dist/util/defer.js +163 -0
- package/dist/util/defer.js.map +1 -0
- package/dist/util/dispose.d.ts +88 -0
- package/dist/util/dispose.d.ts.map +1 -0
- package/dist/util/dispose.js +143 -0
- package/dist/util/dispose.js.map +1 -0
- package/dist/util/duration.d.ts +28 -0
- package/dist/util/duration.d.ts.map +1 -0
- package/dist/util/duration.js +77 -0
- package/dist/util/duration.js.map +1 -0
- package/dist/util/fs.d.ts +110 -0
- package/dist/util/fs.d.ts.map +1 -0
- package/dist/util/fs.js +269 -0
- package/dist/util/fs.js.map +1 -0
- package/dist/util/id.d.ts +49 -0
- package/dist/util/id.d.ts.map +1 -0
- package/dist/util/id.js +87 -0
- package/dist/util/id.js.map +1 -0
- package/dist/util/lru.d.ts +74 -0
- package/dist/util/lru.d.ts.map +1 -0
- package/dist/util/lru.js +156 -0
- package/dist/util/lru.js.map +1 -0
- package/dist/util/queue.d.ts +64 -0
- package/dist/util/queue.d.ts.map +1 -0
- package/dist/util/queue.js +147 -0
- package/dist/util/queue.js.map +1 -0
- package/dist/util/text.d.ts +86 -0
- package/dist/util/text.d.ts.map +1 -0
- package/dist/util/text.js +182 -0
- package/dist/util/text.js.map +1 -0
- package/package.json +44 -0
- package/src/adapter/accounts.ts +758 -0
- package/src/adapter/bots.ts +582 -0
- package/src/adapter/host.ts +224 -0
- package/src/adapter/login.ts +481 -0
- package/src/adapter/registry.ts +202 -0
- package/src/config/core-config.test.ts +60 -0
- package/src/config/core-config.ts +401 -0
- package/src/config/schema.test.ts +179 -0
- package/src/config/schema.ts +1036 -0
- package/src/config/store.test.ts +245 -0
- package/src/config/store.ts +506 -0
- package/src/config/yaml.ts +119 -0
- package/src/http/client.test.ts +568 -0
- package/src/http/client.ts +777 -0
- package/src/index.ts +106 -0
- package/src/kernel/app.test.ts +267 -0
- package/src/kernel/app.ts +843 -0
- package/src/kernel/kv-sink.ts +58 -0
- package/src/kernel/policy.ts +120 -0
- package/src/kernel/runtime.test.ts +542 -0
- package/src/kernel/runtime.ts +307 -0
- package/src/kernel/sql-sink.ts +154 -0
- package/src/logger/format.ts +259 -0
- package/src/logger/index.ts +427 -0
- package/src/logger/rotate.ts +265 -0
- package/src/message/segment.test.ts +335 -0
- package/src/message/segment.ts +569 -0
- package/src/message/split.ts +97 -0
- package/src/message/target.ts +48 -0
- package/src/pipeline/cooldown.ts +136 -0
- package/src/pipeline/dispatch.ts +419 -0
- package/src/pipeline/event.ts +735 -0
- package/src/pipeline/middleware.test.ts +426 -0
- package/src/pipeline/middleware.ts +199 -0
- package/src/pipeline/prompt.ts +212 -0
- package/src/pipeline/router.test.ts +432 -0
- package/src/pipeline/router.ts +575 -0
- package/src/platform/detect.ts +178 -0
- package/src/platform/paths.ts +147 -0
- package/src/platform/system.test.ts +102 -0
- package/src/platform/system.ts +289 -0
- package/src/plugin/context.test.ts +459 -0
- package/src/plugin/context.ts +757 -0
- package/src/plugin/define.test.ts +120 -0
- package/src/plugin/define.ts +142 -0
- package/src/plugin/discover.test.ts +253 -0
- package/src/plugin/discover.ts +353 -0
- package/src/plugin/events.test.ts +162 -0
- package/src/plugin/events.ts +266 -0
- package/src/plugin/hooks.ts +373 -0
- package/src/plugin/host.test.ts +696 -0
- package/src/plugin/host.ts +738 -0
- package/src/plugin/market.test.ts +781 -0
- package/src/plugin/market.ts +870 -0
- package/src/plugin/services.test.ts +135 -0
- package/src/plugin/services.ts +261 -0
- package/src/plugin/tar.test.ts +158 -0
- package/src/plugin/tar.ts +309 -0
- package/src/render/registry.test.ts +127 -0
- package/src/render/registry.ts +439 -0
- package/src/scheduler/index.ts +357 -0
- package/src/server/api.test.ts +802 -0
- package/src/server/api.ts +869 -0
- package/src/server/auth.ts +201 -0
- package/src/server/browse.test.ts +193 -0
- package/src/server/browse.ts +240 -0
- package/src/server/files.ts +97 -0
- package/src/server/index.test.ts +566 -0
- package/src/server/index.ts +1386 -0
- package/src/server/table.ts +351 -0
- package/src/store/index.ts +156 -0
- package/src/store/json.ts +198 -0
- package/src/store/kv.test.ts +307 -0
- package/src/store/kv.ts +256 -0
- package/src/store/level.ts +167 -0
- package/src/store/memory.ts +109 -0
- package/src/store/sql.test.ts +204 -0
- package/src/store/sql.ts +324 -0
- package/src/testing/fake.ts +297 -0
- package/src/testing/index.ts +15 -0
- package/src/testing/mock-adapter.ts +588 -0
- package/src/util/deep.ts +272 -0
- package/src/util/defer.ts +211 -0
- package/src/util/dispose.ts +169 -0
- package/src/util/duration.ts +82 -0
- package/src/util/fs.ts +279 -0
- package/src/util/id.ts +94 -0
- package/src/util/lru.ts +185 -0
- package/src/util/queue.ts +167 -0
- package/src/util/text.ts +189 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模块职责:中间件管线的单元测试 —— 洋葱顺序、优先级、事件大类分桶、拦截语义
|
|
3
|
+
* 依赖方向:测试文件,依赖 pipeline/middleware 与 testing/fake
|
|
4
|
+
* 生命周期:每个用例新建一条管线
|
|
5
|
+
* 注意事项:这里也不建真事件 —— 管线只读 `kind`/`stopped`/`id` 三个字段。
|
|
6
|
+
* 真事件那一侧(中间件改写 message 后路由按改写结果匹配)由
|
|
7
|
+
* kernel/runtime.test.ts 覆盖。
|
|
8
|
+
*
|
|
9
|
+
* 顺序断言一律用一个 `trace: string[]`,进出各记一笔(`a>` / `a<`)。
|
|
10
|
+
* 仅断言"哪些中间件被执行"并不充分:中间件最易出错之处为 `next()` 之后的收尾代码
|
|
11
|
+
* 跑在了内层之前,而那只有进出成对记录才看得出来。
|
|
12
|
+
*/
|
|
13
|
+
import { describe, expect, it } from "vitest"
|
|
14
|
+
import type { AnyEvent, EventKind, Middleware, MiddlewareOptions } from "@yunzai-ng/types"
|
|
15
|
+
import type { MiddlewareRegistration } from "../plugin/hooks.js"
|
|
16
|
+
import { fakeLogger, type FakeLogger } from "../testing/fake.js"
|
|
17
|
+
import { MiddlewarePipeline } from "./middleware.js"
|
|
18
|
+
|
|
19
|
+
/** 够管线用的假事件 */
|
|
20
|
+
interface FakeEvent {
|
|
21
|
+
/** 事件大类 */
|
|
22
|
+
kind: EventKind
|
|
23
|
+
/** 事件 id */
|
|
24
|
+
id: string
|
|
25
|
+
/** 是否已被阻断 */
|
|
26
|
+
stopped: boolean
|
|
27
|
+
/** 阻断 */
|
|
28
|
+
stop: () => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 造一个假事件
|
|
33
|
+
* @param kind 事件大类,缺省消息
|
|
34
|
+
* @returns 假事件
|
|
35
|
+
*/
|
|
36
|
+
function ev(kind: EventKind = "message"): FakeEvent {
|
|
37
|
+
const e: FakeEvent = {
|
|
38
|
+
kind,
|
|
39
|
+
id: `e-${kind}`,
|
|
40
|
+
stopped: false,
|
|
41
|
+
stop: () => {
|
|
42
|
+
e.stopped = true
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return e
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 造一条中间件登记
|
|
50
|
+
* @param plugin 插件名
|
|
51
|
+
* @param fn 中间件函数
|
|
52
|
+
* @param options 注册选项
|
|
53
|
+
* @returns 登记内容
|
|
54
|
+
*/
|
|
55
|
+
function reg(plugin: string, fn: Middleware, options: MiddlewareOptions = {}): MiddlewareRegistration {
|
|
56
|
+
return { plugin, fn, options }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 造一条只在 trace 里留下进出记录的中间件
|
|
61
|
+
* @param tag 标记
|
|
62
|
+
* @param trace 记录数组
|
|
63
|
+
* @returns 中间件登记用的函数
|
|
64
|
+
*/
|
|
65
|
+
function tracer(tag: string, trace: string[]): Middleware {
|
|
66
|
+
return async (_e, next) => {
|
|
67
|
+
trace.push(`${tag}>`)
|
|
68
|
+
await next()
|
|
69
|
+
trace.push(`${tag}<`)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 建一条管线与它的日志器 */
|
|
74
|
+
function make(): { pipe: MiddlewarePipeline; logger: FakeLogger } {
|
|
75
|
+
const logger = fakeLogger()
|
|
76
|
+
return { pipe: new MiddlewarePipeline(logger), logger }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 执行一遍管线
|
|
81
|
+
*
|
|
82
|
+
* 将 `as unknown as AnyEvent` 收束于此一处,以使用例中的 `e` 保持 FakeEvent 类型、
|
|
83
|
+
* 可直接断言 `e.stopped`。
|
|
84
|
+
* @param pipe 管线
|
|
85
|
+
* @param e 假事件
|
|
86
|
+
* @param core 最内层
|
|
87
|
+
*/
|
|
88
|
+
async function run(pipe: MiddlewarePipeline, e: FakeEvent, core: () => Promise<void>): Promise<void> {
|
|
89
|
+
await pipe.run(e as unknown as AnyEvent, core)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 造一个往 trace 里记一笔的最内层
|
|
94
|
+
* @param trace 记录数组
|
|
95
|
+
* @returns 最内层函数
|
|
96
|
+
*/
|
|
97
|
+
function core(trace: string[]): () => Promise<void> {
|
|
98
|
+
return async () => {
|
|
99
|
+
trace.push("core")
|
|
100
|
+
return Promise.resolve()
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
describe("执行顺序", () => {
|
|
105
|
+
it("洋葱模型:next() 之后的收尾在内层执行完毕之后执行", async () => {
|
|
106
|
+
const { pipe } = make()
|
|
107
|
+
const trace: string[] = []
|
|
108
|
+
pipe.register(reg("甲", tracer("甲", trace)))
|
|
109
|
+
pipe.register(reg("乙", tracer("乙", trace)))
|
|
110
|
+
|
|
111
|
+
await run(pipe, ev(), core(trace))
|
|
112
|
+
|
|
113
|
+
expect(trace).toEqual(["甲>", "乙>", "core", "乙<", "甲<"])
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it("priority 小者在外层,同优先级按注册先后", async () => {
|
|
117
|
+
const { pipe } = make()
|
|
118
|
+
const trace: string[] = []
|
|
119
|
+
pipe.register(reg("先注册", tracer("先注册", trace)))
|
|
120
|
+
pipe.register(reg("最外层", tracer("最外层", trace), { priority: 10 }))
|
|
121
|
+
pipe.register(reg("后注册", tracer("后注册", trace)))
|
|
122
|
+
pipe.register(reg("最内层", tracer("最内层", trace), { priority: 900 }))
|
|
123
|
+
|
|
124
|
+
await run(pipe, ev(), core(trace))
|
|
125
|
+
|
|
126
|
+
expect(trace).toEqual([
|
|
127
|
+
"最外层>",
|
|
128
|
+
"先注册>",
|
|
129
|
+
"后注册>",
|
|
130
|
+
"最内层>",
|
|
131
|
+
"core",
|
|
132
|
+
"最内层<",
|
|
133
|
+
"后注册<",
|
|
134
|
+
"先注册<",
|
|
135
|
+
"最外层<"
|
|
136
|
+
])
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it("一条中间件都没有时也会执行最内层", async () => {
|
|
140
|
+
const { pipe } = make()
|
|
141
|
+
const trace: string[] = []
|
|
142
|
+
|
|
143
|
+
await run(pipe, ev(), core(trace))
|
|
144
|
+
|
|
145
|
+
expect(trace).toEqual(["core"])
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
describe("事件大类分桶", () => {
|
|
150
|
+
it("kind 限定生效,单值与数组两种写法都算", async () => {
|
|
151
|
+
const { pipe } = make()
|
|
152
|
+
const trace: string[] = []
|
|
153
|
+
pipe.register(reg("只管消息", tracer("只管消息", trace), { kind: "message" }))
|
|
154
|
+
pipe.register(reg("管通知与请求", tracer("管通知与请求", trace), { kind: ["notice", "request"] }))
|
|
155
|
+
pipe.register(reg("全都管", tracer("全都管", trace)))
|
|
156
|
+
|
|
157
|
+
await run(pipe, ev("message"), core(trace))
|
|
158
|
+
expect(trace).toEqual(["只管消息>", "全都管>", "core", "全都管<", "只管消息<"])
|
|
159
|
+
|
|
160
|
+
trace.length = 0
|
|
161
|
+
await run(pipe, ev("notice"), core(trace))
|
|
162
|
+
expect(trace).toEqual(["管通知与请求>", "全都管>", "core", "全都管<", "管通知与请求<"])
|
|
163
|
+
|
|
164
|
+
trace.length = 0
|
|
165
|
+
await run(pipe, ev("meta"), core(trace))
|
|
166
|
+
expect(trace).toEqual(["全都管>", "core", "全都管<"])
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it("不认识的 kind 不会让最内层被跳过", async () => {
|
|
170
|
+
const { pipe } = make()
|
|
171
|
+
const trace: string[] = []
|
|
172
|
+
pipe.register(reg("全都管", tracer("全都管", trace)))
|
|
173
|
+
const weird = ev()
|
|
174
|
+
weird.kind = "unknown" as EventKind
|
|
175
|
+
|
|
176
|
+
await run(pipe, weird, core(trace))
|
|
177
|
+
|
|
178
|
+
// 桶中不存在该大类:管线应当直接执行核心逻辑,而非丢弃该事件
|
|
179
|
+
expect(trace).toEqual(["core"])
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
describe("拦截语义", () => {
|
|
184
|
+
it("不调 next() 就拦住了后续与最内层", async () => {
|
|
185
|
+
const { pipe } = make()
|
|
186
|
+
const trace: string[] = []
|
|
187
|
+
pipe.register(reg("拦路", async () => void trace.push("拦路")))
|
|
188
|
+
pipe.register(reg("后面的", tracer("后面的", trace)))
|
|
189
|
+
|
|
190
|
+
await run(pipe, ev(), core(trace))
|
|
191
|
+
|
|
192
|
+
expect(trace).toEqual(["拦路"])
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it("不调 next() 也没 stop() 时留一条 trace 级提示", async () => {
|
|
196
|
+
const { pipe, logger } = make()
|
|
197
|
+
pipe.register(reg("忘了写 next 的插件", async () => Promise.resolve()))
|
|
198
|
+
|
|
199
|
+
await run(pipe, ev(), async () => Promise.resolve())
|
|
200
|
+
|
|
201
|
+
expect(logger.lines.some(l => l.includes("未调用 next()") && l.includes("忘了写 next 的插件"))).toBe(true)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it("显式 stop() 后不再提示未调用 next()", async () => {
|
|
205
|
+
const { pipe, logger } = make()
|
|
206
|
+
const e = ev()
|
|
207
|
+
pipe.register(reg("正当拦截", async ev0 => void ev0.stop()))
|
|
208
|
+
|
|
209
|
+
await run(pipe, e, async () => Promise.resolve())
|
|
210
|
+
|
|
211
|
+
expect(e.stopped).toBe(true)
|
|
212
|
+
expect(logger.lines.some(l => l.includes("未调用 next()"))).toBe(false)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it("stop() 之后即使调了 next(),下游与最内层也不执行", async () => {
|
|
216
|
+
const { pipe } = make()
|
|
217
|
+
const trace: string[] = []
|
|
218
|
+
pipe.register(
|
|
219
|
+
reg("先停再放行", async (e, next) => {
|
|
220
|
+
trace.push("先停再放行>")
|
|
221
|
+
e.stop()
|
|
222
|
+
await next()
|
|
223
|
+
trace.push("先停再放行<")
|
|
224
|
+
})
|
|
225
|
+
)
|
|
226
|
+
pipe.register(reg("下游", tracer("下游", trace)))
|
|
227
|
+
|
|
228
|
+
await run(pipe, ev(), core(trace))
|
|
229
|
+
|
|
230
|
+
// next() 本身不抛错,只是什么都不做:拦截路径不该逼插件多写一个 try
|
|
231
|
+
expect(trace).toEqual(["先停再放行>", "先停再放行<"])
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it("事件进来时就已 stopped 的话一条中间件都不跑", async () => {
|
|
235
|
+
const { pipe } = make()
|
|
236
|
+
const trace: string[] = []
|
|
237
|
+
pipe.register(reg("甲", tracer("甲", trace)))
|
|
238
|
+
const e = ev()
|
|
239
|
+
e.stopped = true
|
|
240
|
+
|
|
241
|
+
await run(pipe, e, core(trace))
|
|
242
|
+
|
|
243
|
+
expect(trace).toEqual([])
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it("空管线遇到已 stopped 的事件也不执行最内层", async () => {
|
|
247
|
+
const { pipe } = make()
|
|
248
|
+
const trace: string[] = []
|
|
249
|
+
const e = ev()
|
|
250
|
+
e.stopped = true
|
|
251
|
+
|
|
252
|
+
await run(pipe, e, core(trace))
|
|
253
|
+
|
|
254
|
+
expect(trace).toEqual([])
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
describe("错误处理", () => {
|
|
259
|
+
it("重复调用 next() 直接抛错,错误信息里点名插件", async () => {
|
|
260
|
+
const { pipe } = make()
|
|
261
|
+
pipe.register(
|
|
262
|
+
reg("话多的插件", async (_e, next) => {
|
|
263
|
+
await next()
|
|
264
|
+
await next()
|
|
265
|
+
})
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
await expect(run(pipe, ev(), async () => Promise.resolve())).rejects.toThrow(
|
|
269
|
+
/话多的插件.*重复调用了 next\(\)/
|
|
270
|
+
)
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it("中间件抛的错原样冒泡,交给 dispatch 统一处理", async () => {
|
|
274
|
+
const { pipe } = make()
|
|
275
|
+
pipe.register(reg("执行失败的插件", async () => Promise.reject(new Error("插件内部执行失败"))))
|
|
276
|
+
|
|
277
|
+
await expect(run(pipe, ev(), async () => Promise.resolve())).rejects.toThrow("插件内部执行失败")
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
it("最内层抛的错也原样冒泡,且外层的收尾不会被跑", async () => {
|
|
281
|
+
const { pipe } = make()
|
|
282
|
+
const trace: string[] = []
|
|
283
|
+
pipe.register(reg("甲", tracer("甲", trace)))
|
|
284
|
+
|
|
285
|
+
await expect(
|
|
286
|
+
run(pipe, ev(), async () => {
|
|
287
|
+
throw new Error("命令处理执行失败")
|
|
288
|
+
})
|
|
289
|
+
).rejects.toThrow("命令处理执行失败")
|
|
290
|
+
expect(trace).toEqual(["甲>"])
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
describe("注册表维护", () => {
|
|
295
|
+
it("执行期间新注册的中间件不影响本次执行,下一次才生效", async () => {
|
|
296
|
+
const { pipe } = make()
|
|
297
|
+
const trace: string[] = []
|
|
298
|
+
pipe.register(
|
|
299
|
+
reg("装了个新的", async (_e, next) => {
|
|
300
|
+
pipe.register(reg("半路加入", tracer("半路加入", trace)))
|
|
301
|
+
await next()
|
|
302
|
+
})
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
await run(pipe, ev(), core(trace))
|
|
306
|
+
expect(trace).toEqual(["core"])
|
|
307
|
+
|
|
308
|
+
trace.length = 0
|
|
309
|
+
await run(pipe, ev(), core(trace))
|
|
310
|
+
expect(trace).toEqual(["半路加入>", "core", "半路加入<"])
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it("注销后不再执行,size 也跟着减", async () => {
|
|
314
|
+
const { pipe } = make()
|
|
315
|
+
const trace: string[] = []
|
|
316
|
+
const off = pipe.register(reg("甲", tracer("甲", trace)))
|
|
317
|
+
expect(pipe.size).toBe(1)
|
|
318
|
+
|
|
319
|
+
off()
|
|
320
|
+
// 幂等:插件卸载时上下文与兜底清理可能都调一次
|
|
321
|
+
off()
|
|
322
|
+
|
|
323
|
+
expect(pipe.size).toBe(0)
|
|
324
|
+
await run(pipe, ev(), core(trace))
|
|
325
|
+
expect(trace).toEqual(["core"])
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
it("重复注册同一条登记是幂等的", async () => {
|
|
329
|
+
const { pipe } = make()
|
|
330
|
+
const trace: string[] = []
|
|
331
|
+
const r = reg("甲", tracer("甲", trace))
|
|
332
|
+
pipe.register(r)
|
|
333
|
+
pipe.register(r)
|
|
334
|
+
|
|
335
|
+
expect(pipe.size).toBe(1)
|
|
336
|
+
await run(pipe, ev(), core(trace))
|
|
337
|
+
expect(trace).toEqual(["甲>", "core", "甲<"])
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it("removePlugin 只摘该插件的中间件,含多大类登记", async () => {
|
|
341
|
+
const { pipe } = make()
|
|
342
|
+
const trace: string[] = []
|
|
343
|
+
pipe.register(reg("甲", tracer("甲消息", trace), { kind: "message" }))
|
|
344
|
+
pipe.register(reg("甲", tracer("甲全部", trace)))
|
|
345
|
+
pipe.register(reg("乙", tracer("乙", trace)))
|
|
346
|
+
|
|
347
|
+
expect(pipe.removePlugin("甲")).toBe(2)
|
|
348
|
+
expect(pipe.size).toBe(1)
|
|
349
|
+
|
|
350
|
+
await run(pipe, ev(), core(trace))
|
|
351
|
+
expect(trace).toEqual(["乙>", "core", "乙<"])
|
|
352
|
+
// 通知桶里也不该有残留
|
|
353
|
+
trace.length = 0
|
|
354
|
+
await run(pipe, ev("notice"), core(trace))
|
|
355
|
+
expect(trace).toEqual(["乙>", "core", "乙<"])
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
it("clear 清空全部大类的桶", async () => {
|
|
359
|
+
const { pipe } = make()
|
|
360
|
+
const trace: string[] = []
|
|
361
|
+
pipe.register(reg("甲", tracer("甲", trace), { kind: ["message", "notice"] }))
|
|
362
|
+
|
|
363
|
+
pipe.clear()
|
|
364
|
+
|
|
365
|
+
expect(pipe.size).toBe(0)
|
|
366
|
+
await run(pipe, ev(), core(trace))
|
|
367
|
+
await run(pipe, ev("notice"), core(trace))
|
|
368
|
+
expect(trace).toEqual(["core", "core"])
|
|
369
|
+
})
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
describe("对外清单", () => {
|
|
373
|
+
it("**顺序是执行顺序,不是注册顺序** —— 面板上这张表要回答「一条消息先过谁」", () => {
|
|
374
|
+
const { pipe } = make()
|
|
375
|
+
const trace: string[] = []
|
|
376
|
+
pipe.register(reg("后注册的", tracer("后", trace), { priority: 10 }))
|
|
377
|
+
pipe.register(reg("先注册的", tracer("先", trace), { priority: 200 }))
|
|
378
|
+
|
|
379
|
+
expect(pipe.list().map(item => item.plugin)).toEqual(["后注册的", "先注册的"])
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
it("未声明优先级时报出缺省值,而不是留空", () => {
|
|
383
|
+
const { pipe } = make()
|
|
384
|
+
pipe.register(reg("甲", tracer("甲", [])))
|
|
385
|
+
expect(pipe.list()[0]?.priority).toBe(100)
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
it("**未限定 kind 时列出全部大类** —— 「适用于全部」与「一类都不适用」必须分得开", () => {
|
|
389
|
+
const { pipe } = make()
|
|
390
|
+
pipe.register(reg("甲", tracer("甲", [])))
|
|
391
|
+
expect(pipe.list()[0]?.kinds).toEqual(["message", "notice", "request", "meta"])
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
it("限定了 kind 就只列那几类,单值写法也算", () => {
|
|
395
|
+
const { pipe } = make()
|
|
396
|
+
pipe.register(reg("甲", tracer("甲", []), { kind: "notice" }))
|
|
397
|
+
pipe.register(reg("乙", tracer("乙", []), { kind: ["request", "meta"] }))
|
|
398
|
+
const byPlugin = new Map(pipe.list().map(item => [item.plugin, item.kinds]))
|
|
399
|
+
expect(byPlugin.get("甲")).toEqual(["notice"])
|
|
400
|
+
expect(byPlugin.get("乙")).toEqual(["request", "meta"])
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
it("**不管消息的中间件也要列出来** —— 少列一条会让人以为插件没注册上", () => {
|
|
404
|
+
const { pipe } = make()
|
|
405
|
+
pipe.register(reg("只管通知的", tracer("甲", []), { kind: "notice" }))
|
|
406
|
+
expect(pipe.list().map(item => item.plugin)).toEqual(["只管通知的"])
|
|
407
|
+
expect(pipe.list()).toHaveLength(pipe.size)
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
it("条数与 size 始终一致,注销之后也是", () => {
|
|
411
|
+
const { pipe } = make()
|
|
412
|
+
const off = pipe.register(reg("甲", tracer("甲", [])))
|
|
413
|
+
pipe.register(reg("乙", tracer("乙", []), { kind: "meta" }))
|
|
414
|
+
expect(pipe.list()).toHaveLength(2)
|
|
415
|
+
off()
|
|
416
|
+
expect(pipe.list()).toHaveLength(1)
|
|
417
|
+
expect(pipe.list()[0]?.plugin).toBe("乙")
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it("不含中间件函数本身 —— 那是个闭包,序列化不出去", () => {
|
|
421
|
+
const { pipe } = make()
|
|
422
|
+
pipe.register(reg("甲", tracer("甲", [])))
|
|
423
|
+
expect(pipe.list()[0]).not.toHaveProperty("fn")
|
|
424
|
+
expect(() => JSON.stringify(pipe.list())).not.toThrow()
|
|
425
|
+
})
|
|
426
|
+
})
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模块职责:Koa 式中间件管线(实现 `MiddlewareSink`)
|
|
3
|
+
* 依赖方向:依赖类型包与 plugin/hooks 的登记结构
|
|
4
|
+
* 生命周期:随内核创建;每条注册返回 Disposer,插件卸载即摘除
|
|
5
|
+
* 注意事项:中间件是"把业务逻辑挡在内核之外"的关键机制。诸如 `*` → 星铁、
|
|
6
|
+
* `%` → 绝区零这类前缀识别,都是插件里的一个中间件,内核对"游戏"
|
|
7
|
+
* 一词毫不知情 —— 写进内核就再也洗不掉。
|
|
8
|
+
*
|
|
9
|
+
* 按事件大类预分桶(`#byKind`)而不是每条消息 filter 一遍:
|
|
10
|
+
* 注册是低频的,消息是高频的。
|
|
11
|
+
*
|
|
12
|
+
* `next()` 只能调一次。重复调用在 Koa 里也是硬错误 —— 它意味着
|
|
13
|
+
* 下游会被跑两遍,而"回复发了两条"这种症状极难追回到源头。
|
|
14
|
+
*/
|
|
15
|
+
import type { AnyEvent, Disposer, EventKind, Logger, MiddlewareInfo } from "@yunzai-ng/types"
|
|
16
|
+
import type { MiddlewareRegistration, MiddlewareSink } from "../plugin/hooks.js"
|
|
17
|
+
|
|
18
|
+
/** 缺省优先级 */
|
|
19
|
+
const DEFAULT_PRIORITY = 100
|
|
20
|
+
|
|
21
|
+
/** 全部事件大类 */
|
|
22
|
+
const ALL_KINDS: readonly EventKind[] = ["message", "notice", "request", "meta"]
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 比较两条中间件的执行顺序
|
|
26
|
+
* @param a 登记 a
|
|
27
|
+
* @param b 登记 b
|
|
28
|
+
* @returns 排序结果
|
|
29
|
+
*/
|
|
30
|
+
function byPriority(a: MiddlewareRegistration, b: MiddlewareRegistration): number {
|
|
31
|
+
return (a.options.priority ?? DEFAULT_PRIORITY) - (b.options.priority ?? DEFAULT_PRIORITY)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 中间件管线 */
|
|
35
|
+
export class MiddlewarePipeline implements MiddlewareSink {
|
|
36
|
+
/** 日志器 */
|
|
37
|
+
readonly #logger: Logger
|
|
38
|
+
/** 全部登记,用于 `size` 与按插件摘除 */
|
|
39
|
+
readonly #all = new Set<MiddlewareRegistration>()
|
|
40
|
+
/** 按事件大类预分桶的有序执行链 */
|
|
41
|
+
readonly #byKind = new Map<EventKind, MiddlewareRegistration[]>()
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param logger 日志器
|
|
45
|
+
*/
|
|
46
|
+
constructor(logger: Logger) {
|
|
47
|
+
this.#logger = logger
|
|
48
|
+
for (const kind of ALL_KINDS) this.#byKind.set(kind, [])
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 已注册的中间件条数 */
|
|
52
|
+
get size(): number {
|
|
53
|
+
return this.#all.size
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 列出全部中间件,顺序即实际的执行顺序
|
|
58
|
+
*
|
|
59
|
+
* **按 `message` 桶的顺序输出,不按注册顺序。** 面板上这张表要回答的是
|
|
60
|
+
* 「一条消息先过谁」,而注册顺序与执行顺序并不一致 —— `register()` 是插入排序,
|
|
61
|
+
* 后注册的高优先级中间件会排到前面去。
|
|
62
|
+
*
|
|
63
|
+
* 只对某几类**非** message 事件生效的中间件不在 message 桶里,故补在末尾:
|
|
64
|
+
* 少列一条比顺序不完美糟得多,那会让人以为某个插件没注册上。
|
|
65
|
+
* @returns 中间件描述数组
|
|
66
|
+
*/
|
|
67
|
+
list(): MiddlewareInfo[] {
|
|
68
|
+
const ordered = [...(this.#byKind.get("message") ?? [])]
|
|
69
|
+
for (const reg of this.#all) {
|
|
70
|
+
if (!ordered.includes(reg)) ordered.push(reg)
|
|
71
|
+
}
|
|
72
|
+
return ordered.map(reg => ({
|
|
73
|
+
plugin: reg.plugin,
|
|
74
|
+
priority: reg.options.priority ?? DEFAULT_PRIORITY,
|
|
75
|
+
kinds: [...this.#kindsOf(reg)]
|
|
76
|
+
}))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 登记中间件
|
|
81
|
+
* @param reg 登记内容
|
|
82
|
+
* @returns 注销句柄
|
|
83
|
+
*/
|
|
84
|
+
register(reg: MiddlewareRegistration): Disposer {
|
|
85
|
+
if (this.#all.has(reg)) return () => this.#remove(reg)
|
|
86
|
+
this.#all.add(reg)
|
|
87
|
+
|
|
88
|
+
for (const kind of this.#kindsOf(reg)) {
|
|
89
|
+
const list = this.#byKind.get(kind)
|
|
90
|
+
if (list === undefined) continue
|
|
91
|
+
// 插入排序:同优先级时后注册的排在后面,符合"先装的插件先跑"的直觉
|
|
92
|
+
let at = list.length
|
|
93
|
+
while (at > 0 && byPriority(list[at - 1]!, reg) > 0) at--
|
|
94
|
+
list.splice(at, 0, reg)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return () => this.#remove(reg)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 执行整条管线
|
|
102
|
+
*
|
|
103
|
+
* `core` 是管线最内层 —— 对消息事件而言即命令路由。中间件未调用 `next()`
|
|
104
|
+
* 或调用了 `e.stop()` 均将导致 `core` 不执行,这正是"拦截消息"的实现方式。
|
|
105
|
+
* @param e 事件
|
|
106
|
+
* @param core 最内层处理
|
|
107
|
+
* @throws 任一中间件或 core 抛错时原样冒泡(由 dispatch 统一处理)
|
|
108
|
+
*/
|
|
109
|
+
async run(e: AnyEvent, core: () => Promise<void>): Promise<void> {
|
|
110
|
+
const chain = this.#byKind.get(e.kind)
|
|
111
|
+
// 没有中间件时直接执行核心逻辑,一个闭包都不分配
|
|
112
|
+
if (chain === undefined || chain.length === 0) {
|
|
113
|
+
if (!e.stopped) await core()
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 取快照:中间件里调 ctx.middleware() 注册新中间件不该影响本次执行,
|
|
118
|
+
// 否则同一条消息的处理链会在跑的过程中变长,行为不可复现
|
|
119
|
+
const list = chain.slice()
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 递归执行第 i 层
|
|
123
|
+
* @param i 层号
|
|
124
|
+
*/
|
|
125
|
+
const dispatch = async (i: number): Promise<void> => {
|
|
126
|
+
if (e.stopped) return
|
|
127
|
+
if (i === list.length) {
|
|
128
|
+
await core()
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const reg = list[i]!
|
|
133
|
+
let called = false
|
|
134
|
+
/** 传给中间件的 next */
|
|
135
|
+
const next = async (): Promise<void> => {
|
|
136
|
+
if (called) {
|
|
137
|
+
throw new Error(`插件 ${reg.plugin} 的中间件重复调用了 next():下游会被执行多次`)
|
|
138
|
+
}
|
|
139
|
+
called = true
|
|
140
|
+
await dispatch(i + 1)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
await reg.fn(e, next)
|
|
144
|
+
|
|
145
|
+
// 中间件既没调 next 也没显式 stop:这是合法的"拦截",但很容易是忘了写。
|
|
146
|
+
// 只在 trace 级别提示,避免正常拦截也刷日志。
|
|
147
|
+
if (!called && !e.stopped && this.#logger.isLevelEnabled("trace")) {
|
|
148
|
+
this.#logger.trace(`插件 ${reg.plugin} 的中间件未调用 next(),事件 ${e.id} 到此中断`)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
await dispatch(0)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 摘除某插件的全部中间件
|
|
157
|
+
* @param plugin 插件名
|
|
158
|
+
* @returns 摘除的条数
|
|
159
|
+
*/
|
|
160
|
+
removePlugin(plugin: string): number {
|
|
161
|
+
let n = 0
|
|
162
|
+
for (const reg of [...this.#all]) {
|
|
163
|
+
if (reg.plugin === plugin) {
|
|
164
|
+
this.#remove(reg)
|
|
165
|
+
n++
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return n
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** 清空全部登记 */
|
|
172
|
+
clear(): void {
|
|
173
|
+
this.#all.clear()
|
|
174
|
+
for (const list of this.#byKind.values()) list.length = 0
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 取某条登记适用的事件大类
|
|
179
|
+
* @param reg 登记内容
|
|
180
|
+
* @returns 事件大类数组
|
|
181
|
+
*/
|
|
182
|
+
#kindsOf(reg: MiddlewareRegistration): readonly EventKind[] {
|
|
183
|
+
const want = reg.options.kind
|
|
184
|
+
if (want === undefined) return ALL_KINDS
|
|
185
|
+
return Array.isArray(want) ? want : [want]
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 注销一条登记
|
|
190
|
+
* @param reg 登记内容
|
|
191
|
+
*/
|
|
192
|
+
#remove(reg: MiddlewareRegistration): void {
|
|
193
|
+
if (!this.#all.delete(reg)) return
|
|
194
|
+
for (const list of this.#byKind.values()) {
|
|
195
|
+
const at = list.indexOf(reg)
|
|
196
|
+
if (at >= 0) list.splice(at, 1)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|