@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,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模块职责:内核事件总线(`ctx.on` 的实现)
|
|
3
|
+
* 依赖方向:依赖 util/defer、类型包
|
|
4
|
+
* 生命周期:应用级单例
|
|
5
|
+
* 注意事项:刻意不使用 Node 的 EventEmitter,原因有三 ——
|
|
6
|
+
*
|
|
7
|
+
* 1) **错误隔离**。EventEmitter 中一个监听器抛错即中断整条 emit 链,
|
|
8
|
+
* 其后的监听器收不到事件;异步监听器抛错更会转为
|
|
9
|
+
* unhandledRejection 直接终止进程 —— 一个插件的缺陷便影响全部插件。
|
|
10
|
+
* 2) **可等待异步监听器**。`app/stopping` 必须等待插件保存完状态方可继续
|
|
11
|
+
* 停机,而 EventEmitter 不 await 返回的 Promise。
|
|
12
|
+
* 3) **可按插件归属批量摘除**,且每次订阅均返回 Disposer,
|
|
13
|
+
* 热重载不残留悬空监听器。
|
|
14
|
+
*
|
|
15
|
+
* 单个监听器超时(缺省 10s)会被**放弃等待**并记录警告:无法真正终止它,
|
|
16
|
+
* 但绝不使一个阻塞的插件永久阻止停机流程完成。
|
|
17
|
+
*/
|
|
18
|
+
import type { Awaitable, Disposer, Logger } from "@yunzai-ng/types"
|
|
19
|
+
import type { CoreEventMap } from "@yunzai-ng/types"
|
|
20
|
+
import { TimeoutError, withTimeout } from "../util/defer.js"
|
|
21
|
+
|
|
22
|
+
/** 单个监听器的默认等待上限 */
|
|
23
|
+
const DEFAULT_HANDLER_TIMEOUT = 10_000
|
|
24
|
+
|
|
25
|
+
/** 监听器记录 */
|
|
26
|
+
interface Listener {
|
|
27
|
+
/** 处理函数 */
|
|
28
|
+
fn: (...args: never[]) => Awaitable<void>
|
|
29
|
+
/** 归属插件名,用于日志与批量摘除 */
|
|
30
|
+
owner: string
|
|
31
|
+
/** 是否只触发一次 */
|
|
32
|
+
once: boolean
|
|
33
|
+
/** 已触发次数(诊断用) */
|
|
34
|
+
calls: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** 事件总线参数 */
|
|
38
|
+
export interface EventBusOptions {
|
|
39
|
+
/** 日志器 */
|
|
40
|
+
logger: Logger
|
|
41
|
+
/** 单个监听器等待上限毫秒,缺省 10000;<=0 表示不限时 */
|
|
42
|
+
handlerTimeout?: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 监听器的对外描述 */
|
|
46
|
+
export interface ListenerInfo {
|
|
47
|
+
/** 事件名 */
|
|
48
|
+
event: string
|
|
49
|
+
/** 归属插件 */
|
|
50
|
+
owner: string
|
|
51
|
+
/** 触发次数 */
|
|
52
|
+
calls: number
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 内核事件总线
|
|
57
|
+
*
|
|
58
|
+
* 键类型来自 `CoreEventMap`,因此 `ctx.on("bot/online", bot => ...)` 里
|
|
59
|
+
* `bot` 的类型是自动推出来的,写错事件名编译期就报错。
|
|
60
|
+
*/
|
|
61
|
+
export class CoreEventBus {
|
|
62
|
+
/** 事件名 → 监听器列表(保持注册顺序) */
|
|
63
|
+
readonly #listeners = new Map<string, Listener[]>()
|
|
64
|
+
/** 日志器 */
|
|
65
|
+
readonly #logger: Logger
|
|
66
|
+
/** 单监听器超时毫秒 */
|
|
67
|
+
readonly #timeout: number
|
|
68
|
+
/** 是否已停用(停机后仍到达的事件直接丢弃) */
|
|
69
|
+
#closed = false
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param opts 参数
|
|
73
|
+
*/
|
|
74
|
+
constructor(opts: EventBusOptions) {
|
|
75
|
+
this.#logger = opts.logger.child({ scope: "event" })
|
|
76
|
+
this.#timeout = opts.handlerTimeout ?? DEFAULT_HANDLER_TIMEOUT
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 订阅事件
|
|
81
|
+
* @param event 事件名
|
|
82
|
+
* @param fn 处理函数
|
|
83
|
+
* @param owner 归属插件名
|
|
84
|
+
* @returns 取消订阅句柄;重复调用无副作用
|
|
85
|
+
*/
|
|
86
|
+
on<K extends keyof CoreEventMap>(
|
|
87
|
+
event: K,
|
|
88
|
+
fn: (...args: CoreEventMap[K]) => Awaitable<void>,
|
|
89
|
+
owner = "core"
|
|
90
|
+
): Disposer {
|
|
91
|
+
return this.#add(event as string, fn as Listener["fn"], owner, false)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 订阅一次
|
|
96
|
+
* @param event 事件名
|
|
97
|
+
* @param fn 处理函数
|
|
98
|
+
* @param owner 归属插件名
|
|
99
|
+
* @returns 取消订阅句柄
|
|
100
|
+
*/
|
|
101
|
+
once<K extends keyof CoreEventMap>(
|
|
102
|
+
event: K,
|
|
103
|
+
fn: (...args: CoreEventMap[K]) => Awaitable<void>,
|
|
104
|
+
owner = "core"
|
|
105
|
+
): Disposer {
|
|
106
|
+
return this.#add(event as string, fn as Listener["fn"], owner, true)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 触发事件并等待全部监听器结束
|
|
111
|
+
*
|
|
112
|
+
* 监听器按注册顺序**同时启动**(而非串行),因为它们之间不应有依赖关系;
|
|
113
|
+
* 谁想要顺序保证就该用服务依赖或中间件优先级表达,而不是偷偷依赖注册顺序。
|
|
114
|
+
* @param event 事件名
|
|
115
|
+
* @param args 事件参数
|
|
116
|
+
* @returns 全部监听器结束(或超时被放弃)后兑现;本身永不拒绝
|
|
117
|
+
*/
|
|
118
|
+
async emit<K extends keyof CoreEventMap>(event: K, ...args: CoreEventMap[K]): Promise<void> {
|
|
119
|
+
const listeners = this.#take(event as string)
|
|
120
|
+
if (listeners.length === 0) return
|
|
121
|
+
|
|
122
|
+
await Promise.all(listeners.map(listener => this.#invoke(event as string, listener, args)))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 触发事件但不等待
|
|
127
|
+
*
|
|
128
|
+
* 用于消息统计一类"通知即可"的场景:绝不能让某个插件的统计逻辑拖慢
|
|
129
|
+
* 消息处理主链路。错误照样被记录。
|
|
130
|
+
* @param event 事件名
|
|
131
|
+
* @param args 事件参数
|
|
132
|
+
*/
|
|
133
|
+
emitDetached<K extends keyof CoreEventMap>(event: K, ...args: CoreEventMap[K]): void {
|
|
134
|
+
const listeners = this.#take(event as string)
|
|
135
|
+
if (listeners.length === 0) return
|
|
136
|
+
for (const listener of listeners) {
|
|
137
|
+
// void 显式表明"刻意不等待",同时 #invoke 内部已捕获全部错误,
|
|
138
|
+
// 不会产生 unhandledRejection
|
|
139
|
+
void this.#invoke(event as string, listener, args)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 某事件当前的监听器数量
|
|
145
|
+
* @param event 事件名
|
|
146
|
+
* @returns 数量
|
|
147
|
+
*/
|
|
148
|
+
count<K extends keyof CoreEventMap>(event: K): number {
|
|
149
|
+
return this.#listeners.get(event as string)?.length ?? 0
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 摘除某插件的全部监听器
|
|
154
|
+
* @param owner 插件名
|
|
155
|
+
* @returns 摘除的数量
|
|
156
|
+
*/
|
|
157
|
+
removeByOwner(owner: string): number {
|
|
158
|
+
let removed = 0
|
|
159
|
+
for (const [event, list] of this.#listeners) {
|
|
160
|
+
const kept = list.filter(listener => listener.owner !== owner)
|
|
161
|
+
removed += list.length - kept.length
|
|
162
|
+
if (kept.length === 0) this.#listeners.delete(event)
|
|
163
|
+
else this.#listeners.set(event, kept)
|
|
164
|
+
}
|
|
165
|
+
return removed
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 列出全部监听器
|
|
170
|
+
* @returns 描述数组
|
|
171
|
+
*/
|
|
172
|
+
list(): ListenerInfo[] {
|
|
173
|
+
const out: ListenerInfo[] = []
|
|
174
|
+
for (const [event, list] of this.#listeners) {
|
|
175
|
+
for (const listener of list) out.push({ event, owner: listener.owner, calls: listener.calls })
|
|
176
|
+
}
|
|
177
|
+
return out
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** 清空全部监听器并停止接受事件 */
|
|
181
|
+
close(): void {
|
|
182
|
+
this.#closed = true
|
|
183
|
+
this.#listeners.clear()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 登记监听器
|
|
188
|
+
* @param event 事件名
|
|
189
|
+
* @param fn 处理函数
|
|
190
|
+
* @param owner 归属插件
|
|
191
|
+
* @param once 是否只触发一次
|
|
192
|
+
* @returns 取消订阅句柄
|
|
193
|
+
*/
|
|
194
|
+
#add(event: string, fn: Listener["fn"], owner: string, once: boolean): Disposer {
|
|
195
|
+
const listener: Listener = { fn, owner, once, calls: 0 }
|
|
196
|
+
const list = this.#listeners.get(event)
|
|
197
|
+
if (list) list.push(listener)
|
|
198
|
+
else this.#listeners.set(event, [listener])
|
|
199
|
+
|
|
200
|
+
let disposed = false
|
|
201
|
+
return () => {
|
|
202
|
+
if (disposed) return
|
|
203
|
+
disposed = true
|
|
204
|
+
const current = this.#listeners.get(event)
|
|
205
|
+
if (!current) return
|
|
206
|
+
const index = current.indexOf(listener)
|
|
207
|
+
if (index >= 0) current.splice(index, 1)
|
|
208
|
+
if (current.length === 0) this.#listeners.delete(event)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 取出本次要触发的监听器快照
|
|
214
|
+
*
|
|
215
|
+
* 必须是快照:监听器在处理过程中订阅/退订是完全合法的(`once` 自己就会退订),
|
|
216
|
+
* 直接遍历原数组会漏触发或重复触发。
|
|
217
|
+
* @param event 事件名
|
|
218
|
+
* @returns 监听器数组;已关闭或无监听器时为空数组
|
|
219
|
+
*/
|
|
220
|
+
#take(event: string): Listener[] {
|
|
221
|
+
if (this.#closed) return []
|
|
222
|
+
const list = this.#listeners.get(event)
|
|
223
|
+
if (!list || list.length === 0) return []
|
|
224
|
+
|
|
225
|
+
const snapshot = [...list]
|
|
226
|
+
// once 监听器在派发前即摘除,避免其自身抛错导致永久无法摘除
|
|
227
|
+
if (snapshot.some(listener => listener.once)) {
|
|
228
|
+
const kept = list.filter(listener => !listener.once)
|
|
229
|
+
if (kept.length === 0) this.#listeners.delete(event)
|
|
230
|
+
else this.#listeners.set(event, kept)
|
|
231
|
+
}
|
|
232
|
+
return snapshot
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 调用单个监听器,捕获错误并仅记日志
|
|
237
|
+
* @param event 事件名
|
|
238
|
+
* @param listener 监听器
|
|
239
|
+
* @param args 事件参数
|
|
240
|
+
* @returns 结束后兑现;永不拒绝
|
|
241
|
+
*/
|
|
242
|
+
async #invoke(event: string, listener: Listener, args: readonly unknown[]): Promise<void> {
|
|
243
|
+
listener.calls++
|
|
244
|
+
try {
|
|
245
|
+
const result = listener.fn(...(args as never[]))
|
|
246
|
+
if (result instanceof Promise) {
|
|
247
|
+
await withTimeout(result, this.#timeout, `事件 ${event} 的监听器超过 ${this.#timeout}ms 未结束`)
|
|
248
|
+
}
|
|
249
|
+
} catch (err) {
|
|
250
|
+
if (err instanceof TimeoutError) {
|
|
251
|
+
this.#logger.warn(`插件 ${listener.owner} 处理事件 ${event} 超时,已放弃等待`, err.message)
|
|
252
|
+
} else {
|
|
253
|
+
this.#logger.error(`插件 ${listener.owner} 处理事件 ${event} 出错`, err)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* 创建事件总线
|
|
261
|
+
* @param opts 参数
|
|
262
|
+
* @returns 总线实例
|
|
263
|
+
*/
|
|
264
|
+
export function createEventBus(opts: EventBusOptions): CoreEventBus {
|
|
265
|
+
return new CoreEventBus(opts)
|
|
266
|
+
}
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模块职责:插件上下文与各子系统之间的接缝(sink 接口)
|
|
3
|
+
* 依赖方向:只依赖类型包
|
|
4
|
+
* 生命周期:纯接口 + 一组"子系统不可用"的占位实现
|
|
5
|
+
* 注意事项:`ctx.command` / `ctx.cron` / `ctx.route` / `ctx.render` 的**语义**属于插件上下文,
|
|
6
|
+
* **实现**属于各子系统。让 context.ts 直接 import 那些子系统会绕成
|
|
7
|
+
* context → router → context 的环,且没有服务器时上下文就编译不过。
|
|
8
|
+
*
|
|
9
|
+
* 故此处定义一组窄接口,内核启动时把真实子系统插进来。这有当下的现实需求:
|
|
10
|
+
* 服务器可被用户关掉(`server.enable: false`),渲染器可能没装任何插件 ——
|
|
11
|
+
* 那时 `ctx.route` / `ctx.render` 须给出一句人能看懂的话,而不是
|
|
12
|
+
* `undefined is not a function`。`unavailable*()` 是这条路径的正式实现,不是临时桩。
|
|
13
|
+
*/
|
|
14
|
+
import type {
|
|
15
|
+
AdapterProvider,
|
|
16
|
+
BotApi,
|
|
17
|
+
CommandHandler,
|
|
18
|
+
CommandOptions,
|
|
19
|
+
CommandPattern,
|
|
20
|
+
Disposer,
|
|
21
|
+
HttpMethod,
|
|
22
|
+
KvDriver,
|
|
23
|
+
Middleware,
|
|
24
|
+
MiddlewareOptions,
|
|
25
|
+
RenderRequest,
|
|
26
|
+
RenderResult,
|
|
27
|
+
RendererProvider,
|
|
28
|
+
RouteHandler,
|
|
29
|
+
RouteOptions,
|
|
30
|
+
ServerInfo,
|
|
31
|
+
SqlHandle,
|
|
32
|
+
TaskFn,
|
|
33
|
+
TaskOptions,
|
|
34
|
+
WebSocketHandler,
|
|
35
|
+
WebSocketOptions
|
|
36
|
+
} from "@yunzai-ng/types"
|
|
37
|
+
|
|
38
|
+
/* ────────────────────────────── 命令 ────────────────────────────── */
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 一条命令的登记内容
|
|
42
|
+
*
|
|
43
|
+
* 刻意设计成**可变对象**:`ctx.command(p).desc(x).master().action(fn)` 是链式的,
|
|
44
|
+
* 每一环都在修改这份登记。路由器持有同一个对象引用,因此无须采用"先累积完毕再注册"
|
|
45
|
+
* 这类依赖微任务时序的做法;变更索引键(模式)时链式构造器会调用 `reindex`。
|
|
46
|
+
*/
|
|
47
|
+
export interface CommandRegistration {
|
|
48
|
+
/** 所属插件名 */
|
|
49
|
+
readonly plugin: string
|
|
50
|
+
/** 全部触发模式(主模式 + 别名),至少一个 */
|
|
51
|
+
patterns: CommandPattern[]
|
|
52
|
+
/** 命令选项 */
|
|
53
|
+
options: CommandOptions
|
|
54
|
+
/** 处理函数;`action()` 之前为 undefined,路由器应跳过并告警 */
|
|
55
|
+
handler: CommandHandler | undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 命令注册目标(由命令路由器实现) */
|
|
59
|
+
export interface CommandSink {
|
|
60
|
+
/**
|
|
61
|
+
* 登记一条命令
|
|
62
|
+
* @param reg 登记内容
|
|
63
|
+
* @returns 注销句柄
|
|
64
|
+
*/
|
|
65
|
+
register(reg: CommandRegistration): Disposer
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 通知索引键已变化(追加了别名等)
|
|
69
|
+
* @param reg 登记内容
|
|
70
|
+
*/
|
|
71
|
+
reindex(reg: CommandRegistration): void
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* ────────────────────────────── 中间件 ────────────────────────────── */
|
|
75
|
+
|
|
76
|
+
/** 一个中间件的登记内容 */
|
|
77
|
+
export interface MiddlewareRegistration {
|
|
78
|
+
/** 所属插件名 */
|
|
79
|
+
readonly plugin: string
|
|
80
|
+
/** 中间件函数 */
|
|
81
|
+
readonly fn: Middleware
|
|
82
|
+
/** 注册选项 */
|
|
83
|
+
readonly options: MiddlewareOptions
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** 中间件注册目标(由管线实现) */
|
|
87
|
+
export interface MiddlewareSink {
|
|
88
|
+
/**
|
|
89
|
+
* 登记中间件
|
|
90
|
+
* @param reg 登记内容
|
|
91
|
+
* @returns 注销句柄
|
|
92
|
+
*/
|
|
93
|
+
register(reg: MiddlewareRegistration): Disposer
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ────────────────────────────── 定时任务 ────────────────────────────── */
|
|
97
|
+
|
|
98
|
+
/** 一个定时任务的登记内容 */
|
|
99
|
+
export interface TaskRegistration {
|
|
100
|
+
/** 所属插件名 */
|
|
101
|
+
readonly plugin: string
|
|
102
|
+
/** `"cron"` 为表达式,`"interval"` 为固定间隔毫秒 */
|
|
103
|
+
readonly kind: "cron" | "interval"
|
|
104
|
+
/** cron 表达式或间隔毫秒 */
|
|
105
|
+
readonly schedule: string | number
|
|
106
|
+
/** 任务体 */
|
|
107
|
+
readonly fn: TaskFn
|
|
108
|
+
/** 任务选项 */
|
|
109
|
+
readonly options: TaskOptions
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** 定时任务注册目标(由调度器实现) */
|
|
113
|
+
export interface TaskSink {
|
|
114
|
+
/**
|
|
115
|
+
* 登记任务
|
|
116
|
+
* @param reg 登记内容
|
|
117
|
+
* @returns 注销句柄
|
|
118
|
+
* @throws cron 表达式非法时
|
|
119
|
+
*/
|
|
120
|
+
register(reg: TaskRegistration): Disposer
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ────────────────────────────── HTTP 服务器 ────────────────────────────── */
|
|
124
|
+
|
|
125
|
+
/** HTTP 注册目标(由共享服务器实现) */
|
|
126
|
+
export interface ServerSink {
|
|
127
|
+
/** 服务器信息,用于插件拼回调地址 */
|
|
128
|
+
readonly info: ServerInfo
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 注册路由
|
|
132
|
+
* @param scope URL 前缀(如 `/plugin/mhy-game`)
|
|
133
|
+
* @param method HTTP 方法
|
|
134
|
+
* @param path 相对 scope 的路径
|
|
135
|
+
* @param handler 处理函数
|
|
136
|
+
* @param opts 选项
|
|
137
|
+
* @returns 注销句柄
|
|
138
|
+
*/
|
|
139
|
+
route(scope: string, method: HttpMethod, path: string, handler: RouteHandler, opts?: RouteOptions): Disposer
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 注册 WebSocket 路径
|
|
143
|
+
* @param scope URL 前缀
|
|
144
|
+
* @param path 相对 scope 的路径
|
|
145
|
+
* @param handler 连接处理函数
|
|
146
|
+
* @param opts 选项
|
|
147
|
+
* @returns 注销句柄
|
|
148
|
+
*/
|
|
149
|
+
websocket(scope: string, path: string, handler: WebSocketHandler, opts?: WebSocketOptions): Disposer
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 挂载静态目录
|
|
153
|
+
* @param scope URL 前缀
|
|
154
|
+
* @param urlPath 相对 scope 的路径
|
|
155
|
+
* @param dir 本地目录绝对路径
|
|
156
|
+
* @returns 注销句柄
|
|
157
|
+
*/
|
|
158
|
+
static(scope: string, urlPath: string, dir: string): Disposer
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 挂载单页应用目录并接管站点根路径
|
|
162
|
+
*
|
|
163
|
+
* 内置面板与替换它的插件走同一个入口,因此"谁在提供面板"只有一个判定依据。
|
|
164
|
+
* 根路径已被占用时抛错,不静默覆盖。
|
|
165
|
+
* @param owner 归属标识,仅用于归属查询与日志,如 `core` 或 `plugin:webui-next`
|
|
166
|
+
* @param dir 单页应用产物目录(绝对路径)
|
|
167
|
+
* @returns 注销句柄
|
|
168
|
+
*/
|
|
169
|
+
panel(owner: string, dir: string): Disposer
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 查询某路径当前由谁提供
|
|
173
|
+
*
|
|
174
|
+
* 内置面板据此判断根路径是否已被插件接管:内核自带面板是兜底实现,
|
|
175
|
+
* 已有提供方时不再挂载,避免重复注册直接抛错。
|
|
176
|
+
* @param path URL 路径,如 `/`
|
|
177
|
+
* @returns 提供方标识;无人提供时 undefined
|
|
178
|
+
*/
|
|
179
|
+
claimant(path: string): string | undefined
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ────────────────────────────── 渲染 ────────────────────────────── */
|
|
183
|
+
|
|
184
|
+
/** 渲染目标(由渲染注册表实现) */
|
|
185
|
+
export interface RenderSink {
|
|
186
|
+
/**
|
|
187
|
+
* 注册渲染器
|
|
188
|
+
* @param provider 渲染器实现
|
|
189
|
+
* @param owner 提供方插件名
|
|
190
|
+
* @returns 注销句柄
|
|
191
|
+
*/
|
|
192
|
+
register(provider: RendererProvider, owner: string): Disposer
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 执行渲染
|
|
196
|
+
* @param req 渲染请求(路径已由上下文补全为绝对路径)
|
|
197
|
+
* @returns 渲染结果
|
|
198
|
+
* @throws 无可用渲染器或渲染失败时
|
|
199
|
+
*/
|
|
200
|
+
render(req: RenderRequest): Promise<RenderResult>
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ────────────────────────────── 适配器与存储 ────────────────────────────── */
|
|
204
|
+
|
|
205
|
+
/** 适配器注册目标(由适配器注册表实现) */
|
|
206
|
+
export interface AdapterSink {
|
|
207
|
+
/**
|
|
208
|
+
* 注册适配器
|
|
209
|
+
* @param provider 适配器实现
|
|
210
|
+
* @param owner 提供方插件名
|
|
211
|
+
* @returns 注销句柄;注销时应先断开该适配器的全部账号
|
|
212
|
+
*/
|
|
213
|
+
register(provider: AdapterProvider, owner: string): Disposer
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** KV 驱动注册目标(由存储层实现) */
|
|
217
|
+
export interface KvDriverSink {
|
|
218
|
+
/**
|
|
219
|
+
* 注册 KV 驱动
|
|
220
|
+
* @param driver 驱动实现
|
|
221
|
+
* @param owner 提供方插件名
|
|
222
|
+
* @returns 注销句柄
|
|
223
|
+
*/
|
|
224
|
+
register(driver: KvDriver, owner: string): Disposer
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** SQL 打开目标(由存储层实现) */
|
|
228
|
+
export interface SqlSink {
|
|
229
|
+
/**
|
|
230
|
+
* 打开某插件专属的库
|
|
231
|
+
* @param plugin 插件名(决定文件落在哪个子目录)
|
|
232
|
+
* @param name 库名
|
|
233
|
+
* @returns SQL 句柄
|
|
234
|
+
* @throws better-sqlite3 不可用时
|
|
235
|
+
*/
|
|
236
|
+
open(plugin: string, name: string): Promise<SqlHandle>
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* 关闭某插件打开的全部库
|
|
240
|
+
*
|
|
241
|
+
* `SqlHandle` 上刻意没有 `close()`:句柄会被插件到处传递,谁都能关就意味着
|
|
242
|
+
* 谁都可能在别人还在用时关掉。连接的生死由存储层按插件归属统一管理,
|
|
243
|
+
* 插件卸载时由上下文调用这里。
|
|
244
|
+
* @param plugin 插件名
|
|
245
|
+
*/
|
|
246
|
+
close(plugin: string): Promise<void>
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Bot 选取目标(由账号管理器实现) */
|
|
250
|
+
export interface BotSink {
|
|
251
|
+
/**
|
|
252
|
+
* 选一个可用账号
|
|
253
|
+
* @param id 账号记录 id 或平台 selfId;省略时取第一个在线账号
|
|
254
|
+
* @returns Bot;无可用账号时 undefined
|
|
255
|
+
*/
|
|
256
|
+
pick(id?: string): BotApi | undefined
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* ────────────────────────────── 汇总 ────────────────────────────── */
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* 插件上下文依赖的全部子系统
|
|
263
|
+
*
|
|
264
|
+
* 内核在 `create()` 阶段逐个填好;未启用的子系统填 `unavailable*()`。
|
|
265
|
+
*/
|
|
266
|
+
export interface KernelHooks {
|
|
267
|
+
/** 命令路由 */
|
|
268
|
+
commands: CommandSink
|
|
269
|
+
/** 中间件管线 */
|
|
270
|
+
middlewares: MiddlewareSink
|
|
271
|
+
/** 调度器 */
|
|
272
|
+
tasks: TaskSink
|
|
273
|
+
/** HTTP 服务器;关闭时为不可用实现 */
|
|
274
|
+
server: ServerSink
|
|
275
|
+
/** 渲染 */
|
|
276
|
+
render: RenderSink
|
|
277
|
+
/** 适配器注册表 */
|
|
278
|
+
adapters: AdapterSink
|
|
279
|
+
/** KV 驱动注册表 */
|
|
280
|
+
kvDrivers: KvDriverSink
|
|
281
|
+
/** SQL */
|
|
282
|
+
sql: SqlSink
|
|
283
|
+
/** Bot 选取 */
|
|
284
|
+
bots: BotSink
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 子系统不可用的统一错误
|
|
289
|
+
*
|
|
290
|
+
* 信息中必须写明"为何不可用"与"如何使其可用",因为看到该错误的通常是插件使用者
|
|
291
|
+
* 而非插件作者。
|
|
292
|
+
*/
|
|
293
|
+
export class SubsystemUnavailableError extends Error {
|
|
294
|
+
/** 错误名 */
|
|
295
|
+
override readonly name = "SubsystemUnavailableError"
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* @param subsystem 子系统名
|
|
299
|
+
* @param hint 启用办法
|
|
300
|
+
*/
|
|
301
|
+
constructor(subsystem: string, hint: string) {
|
|
302
|
+
super(`${subsystem}当前不可用:${hint}`)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 构造"服务器未启用"的实现
|
|
308
|
+
* @param reason 不可用原因
|
|
309
|
+
* @returns ServerSink
|
|
310
|
+
*/
|
|
311
|
+
export function unavailableServer(reason = "配置项 server.enable 为 false"): ServerSink {
|
|
312
|
+
/** 统一抛错 */
|
|
313
|
+
const fail = (): never => {
|
|
314
|
+
throw new SubsystemUnavailableError("HTTP 服务器", `${reason}。在 WebUI 或 config/yunzai.yaml 中开启后重启即可`)
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
info: { enabled: false, host: "", port: 0, publicUrl: "" },
|
|
318
|
+
route: fail,
|
|
319
|
+
websocket: fail,
|
|
320
|
+
static: fail,
|
|
321
|
+
panel: fail,
|
|
322
|
+
// 查询不抛错:调用方问的是"有没有人提供该路径",服务器整体不可用时
|
|
323
|
+
// 答案就是"没有"。为一次查询抛错会迫使每个调用点包一层 try
|
|
324
|
+
claimant: () => undefined
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* 构造"没有渲染器"的实现
|
|
330
|
+
*
|
|
331
|
+
* 注意 `register` 是可用的:渲染器插件正是通过它把自己装进来的,
|
|
332
|
+
* 只有 `render` 在没有任何渲染器时才失败。真实实现由 render 子系统提供,
|
|
333
|
+
* 这里只服务于"渲染子系统整体未初始化"的极早期阶段。
|
|
334
|
+
* @returns RenderSink
|
|
335
|
+
*/
|
|
336
|
+
export function unavailableRender(): RenderSink {
|
|
337
|
+
return {
|
|
338
|
+
register: () => () => undefined,
|
|
339
|
+
render: () => {
|
|
340
|
+
throw new SubsystemUnavailableError("渲染", "没有安装任何渲染器插件,建议安装 renderer-puppeteer")
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* 构造一组全部不可用的子系统
|
|
347
|
+
*
|
|
348
|
+
* 供单元测试与"内核只想加载插件、不跑消息"的诊断模式使用。
|
|
349
|
+
* @returns KernelHooks
|
|
350
|
+
*/
|
|
351
|
+
export function unavailableHooks(): KernelHooks {
|
|
352
|
+
/** 生成一个抛错的注册函数 */
|
|
353
|
+
const failing = (subsystem: string, hint: string) => () => {
|
|
354
|
+
throw new SubsystemUnavailableError(subsystem, hint)
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
commands: {
|
|
358
|
+
register: failing("命令路由", "消息管线未初始化"),
|
|
359
|
+
reindex: () => undefined
|
|
360
|
+
},
|
|
361
|
+
middlewares: { register: failing("中间件管线", "消息管线未初始化") },
|
|
362
|
+
tasks: { register: failing("调度器", "调度子系统未初始化") },
|
|
363
|
+
server: unavailableServer("服务器子系统未初始化"),
|
|
364
|
+
render: unavailableRender(),
|
|
365
|
+
adapters: { register: failing("适配器注册表", "适配器子系统未初始化") },
|
|
366
|
+
kvDrivers: { register: failing("KV 驱动注册表", "存储子系统未初始化") },
|
|
367
|
+
sql: {
|
|
368
|
+
open: () => Promise.reject(new SubsystemUnavailableError("SQL", "better-sqlite3 未安装或存储子系统未初始化")),
|
|
369
|
+
close: () => Promise.resolve()
|
|
370
|
+
},
|
|
371
|
+
bots: { pick: () => undefined }
|
|
372
|
+
}
|
|
373
|
+
}
|