Skip to content

App — 应用容器

App 是 Aalis 的顶层容器,负责初始化核心子系统、管理插件生命周期和启动消息路由。指令、权限、工具等能力由插件提供。

源码: packages/core/src/orchestration/app.ts

构造函数

typescript
const app = new App(options: AppOptions);
// 推荐:
const app = createApp(options: AppOptions);

core 不感知"文件系统 / 进程 / 终端"等任何 I/O 概念——core 自身不读取任何 YAML 文件。 配置由宿主从任意来源(文件/URL/远端)加载好,作为快照传进 config;文件读写、watch、 重启、插件发现等 I/O 全部通过 provider 注入。

AppOptions 中只有 config 是必填,其余皆可选:

字段类型说明
configAalisConfig | ConfigManager必填。配置快照(如 { name, logLevel, plugins }),或已构造的 ConfigManager
configProviderConfigProvider配置持久化与外部变更监听;缺省=只读内存模式
dataDirstring业务数据目录(plugin 用作相对路径基准)
pluginLoaderPluginLoader插件加载器;缺省=autoLoadPlugins() 为 no-op,须手动 app.plugin(mod)
restartStrategyRestartStrategy重启策略;缺省=restart() 抛错
eventsEventBus自定义事件总线
servicesServiceContainer自定义服务容器
hooksHookRegistry自定义钩子注册表
contributionsContributionRegistry自定义贡献点注册表
logHubLogHub自定义日志通道;缺省=LogHub.default(进程级共享)
loggerLogger自定义 Logger 实现;缺省=DefaultLogger(写入 logHub)
devModeboolean传给根 Context,决定 provide 是否跑一致性校验;默认 true

构造时:

  • config(快照或现成 ConfigManager)规范为 ConfigManager
  • 初始化 events / services / hooks / contributions / logger 及根 Context(注入或自建)
  • 创建 PluginManager,并 provide('app', this) / provide('plugins', …)
  • 应用配置中已有的服务偏好(preferService

关键属性

属性类型说明
ctxContext根执行上下文
pluginsPluginManager插件管理器
loggerLogger日志器
eventsEventBus事件总线
servicesServiceContainer服务容器
hooksHookRegistry钩子注册表
contributionsContributionRegistry贡献点注册表

核心方法

app.start()

  1. 发出 app:starting 事件
  2. 发出 app:ready 事件(sticky)
  3. 发出 app:started 事件(sticky)

每一步都等前一个事件的监听器全部返回后才推进。配置外部变更的热重载编排属宿主政策,由宿主自行 app.ctx.config.watch(cb) 接管,start() 不做。

app.stop()

  1. ctx.config.unwatch() 停止监听配置变更
  2. 发出 app:stopping 事件(知会用;清理一律走 ctx.onDispose
  3. plugins.idle():等在飞的 recompute 排干,否则下一步的 shutdown 请求会被排队、拓扑逆序落空
  4. plugins.stopAll():按拓扑逆序 disposeAsync 所有 active 插件(消费者先关、提供者后关,逐项等待ctx.onDispose 的异步清理完成——落盘/关连接真正结束才轮到下一个)
  5. 清空 sticky 缓存(app:ready / app:started
  6. disposeAsync 根 Context(同样等待异步清理)

单个异步清理项的等待上限由 AppOptions.disposeTimeoutMs 控制(默认 5000ms;0=不设限):超时放弃该项、继续后续清理并 warn 点名,防网络类关闭卡死停机。

app.plugin(module, config?, instanceId?)

注册单个插件,返回值同 plugins.register(false = 重名或未声明 reusable 的多实例)。instanceId 缺省用 module.name。配置合并优先级:代码传入 > 配置文件 > 宿主派生默认值(默认值经 AppOptions.pluginDefaults 注入,core 不认识任何配置词汇;缺省注入 = 无默认值)。三层是逐层深合并:同一路径上双方都是纯对象则递归合并,否则后者整体覆盖;数组与非纯对象(Date / Map / 类实例)是原子值,只覆盖不逐元素合并。所以配置文件里只写了嵌套组中的一个键(只写 server.port),同组其余默认值(server.host)在插件首次 apply 时依然在位。

app.autoLoadPlugins()

通过注入的 pluginLoader 自动发现并注册所有插件;未注入 loader 时为 no-op。流程: discover() 发现插件 → 逐个 load()app.plugin(mod) 注册 → 扫描配置中的多实例条目 (name:suffix,要求模块声明 reusable)。

app.rescanPlugins()

重新扫描插件源,加载新发现的插件(已注册的跳过),返回新加载的插件名列表。优先调用 pluginLoader.reload(desc) 做热重载,未实现时退化为 load(desc);未注入 loader 时返回 []

app.saveConfig()

委托给 configProvider 持久化当前配置,返回 Promise<void>:Promise 兑现时保存已完成,provider 失败以拒绝传出,调用方应 await;无 provider 时立即完成。并发保存的先后与外部编辑的合并不在此契约内。

app.restart()

委托给注入的 restartStrategy:清空 sticky 缓存 → 发出 app:restarting 事件 → 调用 strategy.restart({ stop })(stop / restart 时序由策略决定)。未注入 restartStrategy 时抛错, 自身不保存任何数据、也不直接 spawn 进程。

基础指令

App 本身不注册指令。基础指令由插件提供,例如 @aalis/plugin-commands 提供 /help/status/clear/shutdown/restart@aalis/plugin-authority 提供 /authority/level/auto

指令可见性说明
/helppublic列出所有已注册指令
/statuspublic显示系统状态(服务可用性、工具数、指令数)
/clear [--type/-t <type>]public清空当前会话指定类型记忆
/clear listpublic列出可清理的记忆类型
/clear all [--type/-t <type>]restricted全局清空指定类型记忆
/shutdownrestricted关闭应用
/restartrestricted重启应用
/authority [target]public查看自己或指定用户的权限等级(owner 显示 ∞)
/level <target> <level>restrictedowner 给某外部身份设置权限等级(整数;0 默认,负数封禁;仅 owner 可用,防自授)
/auto [<分钟>|on|off]restrictedowner 临时免 dangerous 二次确认(仅 owner 本人)

配置同步(宿主政策,不在 core)

默认值回填、按 configSchema 裁剪未知字段、配置外部变更的热重载编排均属宿主政策, 由 @aalis/runtime 的 config-sync 模块提供(syncPluginDefaults / installConfigHotReloadstartAalis 默认接线;configSync.trimUnknownFields=false 可保留未知字段)。 core 只持有机制:配置快照 get/set、config.watch 透传、updateConfig。 不经 runtime 的嵌入式宿主需要时用这些公开 API 自行编排。