mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-21 11:49:02 +08:00
feat: 实现依赖注入系统、插件架构和窗口管理
- 引入ExamAware的DI系统并适配SecScore项目 - 添加服务注册表和窗口管理器 - 实现基于Koishi的Disposable设计的插件系统 - 集成Winston日志服务 - 分离设置窗口为独立页面 - 将非核心功能改为内置插件 - 更新README文件 - 添加相关hooks和工具函数
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import type {
|
||||
ConfigureHostDelegate,
|
||||
HostBuilderContext,
|
||||
HostExposure,
|
||||
HostedService,
|
||||
PluginHostApplicationContext,
|
||||
PluginMiddleware,
|
||||
ServiceToken,
|
||||
} from "./types"
|
||||
import { ServiceProvider } from "./serviceCollection"
|
||||
|
||||
// 插件Host应用类,管理应用的生命周期和托管服务
|
||||
export class PluginHostApplication {
|
||||
private hostedInstances: HostedService[] = [] // 已启动的托管服务实例
|
||||
private hostDisposers: Array<() => void> = [] // 清理函数列表
|
||||
private started = false // 是否已启动
|
||||
|
||||
constructor(
|
||||
private readonly context: HostBuilderContext, // Host构建器上下文
|
||||
private readonly provider: ServiceProvider, // 服务提供者
|
||||
private readonly configureDelegates: ConfigureHostDelegate[], // 配置委托
|
||||
private readonly middleware: PluginMiddleware[], // 中间件
|
||||
private readonly hostedTokens: ServiceToken<HostedService>[] // 托管服务令牌
|
||||
) {}
|
||||
|
||||
// 包装应用,添加服务暴露功能
|
||||
withExposures(exposures: HostExposure[]): PluginHostApplicationWithExposures {
|
||||
return new PluginHostApplicationWithExposures(this, exposures)
|
||||
}
|
||||
|
||||
// 启动应用
|
||||
async start(): Promise<void> {
|
||||
if (this.started) return
|
||||
const appCtx = this.createApplicationContext() // 创建应用上下文
|
||||
|
||||
// 执行所有配置委托
|
||||
for (const configure of this.configureDelegates) {
|
||||
await configure(this.context, appCtx)
|
||||
}
|
||||
|
||||
// 通过中间件管道执行终端逻辑(启动托管服务)
|
||||
await this.dispatch(0, appCtx, async () => {
|
||||
await this.bootstrapHostedServices()
|
||||
})
|
||||
|
||||
this.started = true
|
||||
await this.context.lifetime.notifyStarted() // 通知生命周期已启动
|
||||
}
|
||||
|
||||
// 停止应用
|
||||
async stop(): Promise<void> {
|
||||
if (!this.started) return
|
||||
await this.context.lifetime.notifyStopping() // 通知正在停止
|
||||
await this.disposeHostedServices() // 停止托管服务
|
||||
await this.context.lifetime.notifyStopped() // 通知已停止
|
||||
this.started = false
|
||||
}
|
||||
|
||||
// 释放资源
|
||||
async dispose(): Promise<void> {
|
||||
await this.stop()
|
||||
await this.provider.dispose() // 释放服务提供者
|
||||
}
|
||||
|
||||
// 获取服务提供者
|
||||
get services(): ServiceProvider {
|
||||
return this.provider
|
||||
}
|
||||
|
||||
// 获取Host上下文
|
||||
get hostContext(): HostBuilderContext {
|
||||
return this.context
|
||||
}
|
||||
|
||||
// 创建应用上下文
|
||||
private createApplicationContext(): PluginHostApplicationContext {
|
||||
return {
|
||||
ctx: this.context.ctx,
|
||||
services: this.provider,
|
||||
host: this.context,
|
||||
}
|
||||
}
|
||||
|
||||
// 启动所有托管服务
|
||||
private async bootstrapHostedServices() {
|
||||
for (const token of this.hostedTokens) {
|
||||
const service = this.provider.get(token) // 从提供者获取服务实例
|
||||
this.hostedInstances.push(service)
|
||||
if (typeof service.start === "function") {
|
||||
await service.start() // 调用启动方法
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 停止所有托管服务
|
||||
private async disposeHostedServices() {
|
||||
while (this.hostedInstances.length) {
|
||||
const service = this.hostedInstances.pop()
|
||||
if (!service) continue
|
||||
if (typeof service.stop === "function") {
|
||||
await service.stop() // 调用停止方法
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行中间件管道
|
||||
private async dispatch(
|
||||
index: number,
|
||||
appCtx: PluginHostApplicationContext,
|
||||
terminal: () => Promise<void>
|
||||
) {
|
||||
const middleware = this.middleware[index]
|
||||
if (!middleware) {
|
||||
await terminal() // 执行终端逻辑
|
||||
return
|
||||
}
|
||||
|
||||
// 调用中间件,传入下一个中间件的执行函数
|
||||
await middleware(appCtx, () => this.dispatch(index + 1, appCtx, terminal))
|
||||
}
|
||||
}
|
||||
|
||||
// 带服务暴露的Host应用类
|
||||
export class PluginHostApplicationWithExposures {
|
||||
private readonly exposureDisposers: Array<() => void> = [] // 暴露服务的清理函数
|
||||
|
||||
constructor(
|
||||
private readonly app: PluginHostApplication,
|
||||
private readonly exposures: HostExposure[]
|
||||
) {}
|
||||
|
||||
// 启动应用,包括注册暴露服务
|
||||
async start(): Promise<void> {
|
||||
try {
|
||||
await this.registerExposures() // 先注册暴露服务
|
||||
await this.app.start() // 再启动应用
|
||||
} catch (error) {
|
||||
await this.disposeExposures() // 出错时清理暴露服务
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 停止应用,包括清理暴露服务
|
||||
async stop(): Promise<void> {
|
||||
await this.app.stop()
|
||||
await this.disposeExposures()
|
||||
}
|
||||
|
||||
// 释放资源
|
||||
async dispose(): Promise<void> {
|
||||
await this.app.dispose()
|
||||
await this.disposeExposures()
|
||||
}
|
||||
|
||||
// 获取服务提供者
|
||||
get services() {
|
||||
return this.app.services
|
||||
}
|
||||
|
||||
// 获取Host上下文
|
||||
get hostContext() {
|
||||
return this.app.hostContext
|
||||
}
|
||||
|
||||
// 注册暴露的服务到插件上下文
|
||||
private async registerExposures() {
|
||||
const ctx = this.app.hostContext.ctx
|
||||
if (!ctx.services) return
|
||||
for (const exposure of this.exposures) {
|
||||
const value = exposure.resolver(this.app.services) // 解析服务值
|
||||
const disposer = ctx.services.provide(exposure.name, value) // 注册到服务API
|
||||
this.exposureDisposers.push(disposer)
|
||||
}
|
||||
}
|
||||
|
||||
// 清理暴露的服务
|
||||
private async disposeExposures() {
|
||||
while (this.exposureDisposers.length) {
|
||||
const dispose = this.exposureDisposers.pop()
|
||||
if (!dispose) continue
|
||||
await dispose() // 调用清理函数
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import { ServiceCollection, ServiceProvider } from "./serviceCollection"
|
||||
import { PluginHostApplication, PluginHostApplicationWithExposures } from "./hostApplication"
|
||||
import {
|
||||
type ConfigureHostDelegate,
|
||||
type ConfigureServicesDelegate,
|
||||
type HostBuilderContext,
|
||||
type HostBuilderSettings,
|
||||
type HostExposure,
|
||||
type HostExposureResolver,
|
||||
type HostedService,
|
||||
type PluginMiddleware,
|
||||
type PluginRuntimeContext,
|
||||
type ServiceToken,
|
||||
} from "./types"
|
||||
|
||||
// ExamAware Host构建器,类似.NET的HostBuilder,用于配置插件的服务和生命周期
|
||||
export class ExamAwareHostBuilder {
|
||||
private readonly serviceCollection: ServiceCollection // 服务集合,管理所有注册的服务
|
||||
private readonly configureServicesDelegates: ConfigureServicesDelegate[] = [] // 配置服务的委托列表
|
||||
private readonly configureDelegates: ConfigureHostDelegate[] = [] // 配置应用的委托列表
|
||||
private readonly middleware: PluginMiddleware[] = [] // 中间件列表
|
||||
private readonly hostedServices: ServiceToken<HostedService>[] = [] // 托管服务的令牌列表
|
||||
private readonly exposures: HostExposure[] = [] // 要暴露的服务列表
|
||||
private readonly builderContext: HostBuilderContext // 构建器上下文
|
||||
|
||||
constructor(
|
||||
private readonly runtimeCtx: PluginRuntimeContext,
|
||||
settings: HostBuilderSettings = {}
|
||||
) {
|
||||
this.serviceCollection = new ServiceCollection(runtimeCtx)
|
||||
this.builderContext = {
|
||||
ctx: runtimeCtx,
|
||||
environmentName: settings.environment ?? process.env.EXAMAWARE_ENV ?? "Production",
|
||||
properties: new Map(Object.entries(settings.properties ?? {})),
|
||||
lifetime: this.serviceCollection.getLifetime(), // 获取应用生命周期管理器
|
||||
}
|
||||
}
|
||||
|
||||
// 获取构建器上下文
|
||||
get context(): HostBuilderContext {
|
||||
return this.builderContext
|
||||
}
|
||||
|
||||
// 添加服务配置委托
|
||||
configureServices(callback: ConfigureServicesDelegate): this {
|
||||
this.configureServicesDelegates.push(callback)
|
||||
return this
|
||||
}
|
||||
|
||||
// 添加应用配置委托
|
||||
configure(callback: ConfigureHostDelegate): this {
|
||||
this.configureDelegates.push(callback)
|
||||
return this
|
||||
}
|
||||
|
||||
// 添加中间件
|
||||
use(middleware: PluginMiddleware): this {
|
||||
this.middleware.push(middleware)
|
||||
return this
|
||||
}
|
||||
|
||||
// 添加托管服务
|
||||
addHostedService(token: ServiceToken<HostedService>): this {
|
||||
this.hostedServices.push(token)
|
||||
return this
|
||||
}
|
||||
|
||||
// 暴露服务到插件上下文
|
||||
exposeHostService(name: string, resolver: HostExposureResolver): this {
|
||||
const normalized = this.normalizeResolver(resolver)
|
||||
if (normalized) {
|
||||
this.exposures.push({ name, resolver: normalized })
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
// 构建Host应用
|
||||
async build(): Promise<PluginHost> {
|
||||
// 执行所有服务配置委托
|
||||
for (const configure of this.configureServicesDelegates) {
|
||||
await configure(this.builderContext, this.serviceCollection)
|
||||
}
|
||||
|
||||
const provider = this.serviceCollection.buildServiceProvider() // 构建服务提供者
|
||||
const application = new PluginHostApplication(
|
||||
this.builderContext,
|
||||
provider,
|
||||
this.configureDelegates,
|
||||
this.middleware,
|
||||
this.hostedServices
|
||||
)
|
||||
return new PluginHost(application.withExposures(this.exposures)) // 返回包装了暴露服务的Host
|
||||
}
|
||||
|
||||
// 标准化暴露解析器
|
||||
private normalizeResolver(
|
||||
resolver: HostExposureResolver
|
||||
): ((provider: ServiceProvider) => unknown) | null {
|
||||
if (resolver.token) {
|
||||
return (provider) => provider.get(resolver.token as ServiceToken) // 通过令牌获取服务
|
||||
}
|
||||
if (resolver.factory) {
|
||||
return resolver.factory // 使用工厂函数
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// 插件Host类,管理应用的启动和停止
|
||||
export class PluginHost {
|
||||
constructor(private readonly app: PluginHostApplicationWithExposures) {}
|
||||
|
||||
// 获取服务提供者
|
||||
get services() {
|
||||
return this.app.services
|
||||
}
|
||||
|
||||
// 获取Host上下文
|
||||
get hostContext() {
|
||||
return this.app.hostContext
|
||||
}
|
||||
|
||||
// 启动应用
|
||||
async start() {
|
||||
await this.app.start()
|
||||
}
|
||||
|
||||
// 停止应用
|
||||
async stop() {
|
||||
await this.app.stop()
|
||||
}
|
||||
|
||||
// 释放资源
|
||||
async dispose() {
|
||||
await this.app.dispose()
|
||||
}
|
||||
|
||||
// 运行应用,返回停止函数
|
||||
async run() {
|
||||
await this.start()
|
||||
return async () => {
|
||||
await this.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建Host构建器的工厂函数
|
||||
export function createPluginHostBuilder(ctx: PluginRuntimeContext, settings?: HostBuilderSettings) {
|
||||
return new ExamAwareHostBuilder(ctx, settings)
|
||||
}
|
||||
|
||||
// Host工具类,提供创建构建器的静态方法
|
||||
export const Host = {
|
||||
createApplicationBuilder: createPluginHostBuilder,
|
||||
}
|
||||
|
||||
// 定义插件的辅助函数,使用Host构建器配置插件
|
||||
export function defineExamAwarePlugin(
|
||||
setup: (builder: ExamAwareHostBuilder) => Promise<void> | void
|
||||
) {
|
||||
return async function examAwarePlugin(ctx: PluginRuntimeContext) {
|
||||
const builder = Host.createApplicationBuilder(ctx)
|
||||
await setup(builder)
|
||||
const host = await builder.build()
|
||||
return host.run() // 返回运行函数,插件加载时调用
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
export {
|
||||
ExamAwareHostBuilder,
|
||||
PluginHost,
|
||||
createPluginHostBuilder,
|
||||
Host,
|
||||
defineExamAwarePlugin,
|
||||
} from "./hostBuilder"
|
||||
export { ServiceCollection, ServiceProvider } from "./serviceCollection"
|
||||
export {
|
||||
PluginContextToken,
|
||||
PluginLoggerToken,
|
||||
PluginSettingsToken,
|
||||
DesktopApiToken,
|
||||
HostApplicationLifetimeToken,
|
||||
} from "./tokens"
|
||||
export type {
|
||||
PluginRuntimeContext,
|
||||
PluginLogger,
|
||||
PluginSettingsAPI,
|
||||
ServiceAPI,
|
||||
HostedService,
|
||||
PluginMiddleware,
|
||||
HostBuilderSettings,
|
||||
HostBuilderContext,
|
||||
PluginHostApplicationLifetime,
|
||||
ConfigureServicesDelegate,
|
||||
ConfigureHostDelegate,
|
||||
PluginHostApplicationContext,
|
||||
ServiceToken,
|
||||
} from "./types"
|
||||
@@ -0,0 +1,61 @@
|
||||
import type { Awaitable, Disposer, PluginHostApplicationLifetime } from "./types"
|
||||
|
||||
type LoggerLike = { error: (...args: any[]) => void }
|
||||
|
||||
// 默认的应用生命周期实现,管理启动/停止事件
|
||||
export class DefaultHostApplicationLifetime implements PluginHostApplicationLifetime {
|
||||
constructor(private readonly logger: LoggerLike = console) {}
|
||||
private readonly started = new Set<() => Awaitable<void>>() // 启动事件处理器
|
||||
private readonly stopping = new Set<() => Awaitable<void>>() // 停止事件处理器
|
||||
private readonly stopped = new Set<() => Awaitable<void>>() // 已停止事件处理器
|
||||
|
||||
// 注册启动事件处理器
|
||||
onStarted(handler: () => Awaitable<void>): Disposer {
|
||||
this.started.add(handler)
|
||||
return () => {
|
||||
this.started.delete(handler)
|
||||
} // 返回清理函数
|
||||
}
|
||||
|
||||
// 注册停止事件处理器
|
||||
onStopping(handler: () => Awaitable<void>): Disposer {
|
||||
this.stopping.add(handler)
|
||||
return () => {
|
||||
this.stopping.delete(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// 注册已停止事件处理器
|
||||
onStopped(handler: () => Awaitable<void>): Disposer {
|
||||
this.stopped.add(handler)
|
||||
return () => {
|
||||
this.stopped.delete(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// 通知所有启动事件处理器
|
||||
async notifyStarted() {
|
||||
await this.dispatch(this.started)
|
||||
}
|
||||
|
||||
// 通知所有停止事件处理器
|
||||
async notifyStopping() {
|
||||
await this.dispatch(this.stopping)
|
||||
}
|
||||
|
||||
// 通知所有已停止事件处理器
|
||||
async notifyStopped() {
|
||||
await this.dispatch(this.stopped)
|
||||
}
|
||||
|
||||
// 执行事件处理器列表
|
||||
private async dispatch(targets: Set<() => Awaitable<void>>) {
|
||||
for (const handler of Array.from(targets)) {
|
||||
try {
|
||||
await handler()
|
||||
} catch (error) {
|
||||
this.logger.error("[PluginHostLifetime] handler failed", error as Error) // 记录错误但不中断
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
import {
|
||||
DesktopApiToken,
|
||||
HostApplicationLifetimeToken,
|
||||
PluginContextToken,
|
||||
PluginLoggerToken,
|
||||
PluginSettingsToken,
|
||||
} from "./tokens"
|
||||
import { DefaultHostApplicationLifetime } from "./lifetime"
|
||||
import type {
|
||||
Disposer,
|
||||
InjectableClass,
|
||||
PluginRuntimeContext,
|
||||
ServiceDescriptor,
|
||||
ServiceFactory,
|
||||
ServiceFactoryOrValue,
|
||||
ServiceLifetime,
|
||||
ServiceToken,
|
||||
} from "./types"
|
||||
|
||||
// 检查值是否为构造函数
|
||||
function isConstructor<T>(value: ServiceFactoryOrValue<T>): value is InjectableClass<T> {
|
||||
return (
|
||||
typeof value === "function" &&
|
||||
!!(value as any).prototype &&
|
||||
(value as any).prototype.constructor === value
|
||||
)
|
||||
}
|
||||
|
||||
// 服务集合类,管理所有注册的服务描述符
|
||||
export class ServiceCollection {
|
||||
private readonly descriptors = new Map<ServiceToken, ServiceDescriptor>() // 服务描述符映射
|
||||
private readonly hostLifetime: DefaultHostApplicationLifetime // 应用生命周期管理器
|
||||
|
||||
constructor(private readonly ctx: PluginRuntimeContext) {
|
||||
this.hostLifetime = new DefaultHostApplicationLifetime((ctx as any).logger ?? console)
|
||||
// 预注册常用服务
|
||||
this.addSingleton(PluginContextToken, () => ctx)
|
||||
this.addSingleton(PluginLoggerToken, () => ctx.logger)
|
||||
this.addSingleton(PluginSettingsToken, () => ctx.settings)
|
||||
this.addSingleton(DesktopApiToken, () => ctx.desktopApi)
|
||||
this.addSingleton(HostApplicationLifetimeToken, () => this.hostLifetime)
|
||||
}
|
||||
|
||||
// 获取生命周期管理器
|
||||
getLifetime() {
|
||||
return this.hostLifetime
|
||||
}
|
||||
|
||||
// 添加单例服务
|
||||
addSingleton<T>(token: ServiceToken<T>, impl: ServiceFactoryOrValue<T>): this {
|
||||
return this.register(token, "singleton", impl)
|
||||
}
|
||||
|
||||
// 添加作用域服务
|
||||
addScoped<T>(token: ServiceToken<T>, impl: ServiceFactoryOrValue<T>): this {
|
||||
return this.register(token, "scoped", impl)
|
||||
}
|
||||
|
||||
// 添加瞬时服务
|
||||
addTransient<T>(token: ServiceToken<T>, impl: ServiceFactoryOrValue<T>): this {
|
||||
return this.register(token, "transient", impl)
|
||||
}
|
||||
|
||||
// 尝试添加单例服务(如果不存在)
|
||||
tryAddSingleton<T>(token: ServiceToken<T>, impl: ServiceFactoryOrValue<T>): this {
|
||||
if (!this.descriptors.has(token)) {
|
||||
this.addSingleton(token, impl)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
// 检查服务是否已注册
|
||||
has(token: ServiceToken): boolean {
|
||||
return this.descriptors.has(token)
|
||||
}
|
||||
|
||||
// 清空所有服务
|
||||
clear(): void {
|
||||
this.descriptors.clear()
|
||||
}
|
||||
|
||||
// 构建服务提供者
|
||||
buildServiceProvider(): ServiceProvider {
|
||||
return new ServiceProvider(this.ctx, new Map(this.descriptors))
|
||||
}
|
||||
|
||||
// 注册服务
|
||||
private register<T>(
|
||||
token: ServiceToken<T>,
|
||||
lifetime: ServiceLifetime,
|
||||
impl: ServiceFactoryOrValue<T>
|
||||
): this {
|
||||
const descriptor: ServiceDescriptor = {
|
||||
token,
|
||||
lifetime,
|
||||
factory: this.normalizeFactory(impl), // 标准化工厂函数
|
||||
}
|
||||
this.descriptors.set(token, descriptor)
|
||||
return this
|
||||
}
|
||||
|
||||
// 标准化工厂函数
|
||||
private normalizeFactory<T>(impl: ServiceFactoryOrValue<T>): ServiceFactory<T> {
|
||||
if (isConstructor(impl)) {
|
||||
return (provider) => this.instantiateClass(impl, provider) // 构造函数,实例化类
|
||||
}
|
||||
|
||||
if (typeof impl === "function") {
|
||||
return impl as ServiceFactory<T> // 已经是工厂函数
|
||||
}
|
||||
|
||||
return () => impl // 直接值,返回常量
|
||||
}
|
||||
|
||||
// 实例化类,注入依赖
|
||||
private instantiateClass<T>(Ctor: InjectableClass<T>, provider: ServiceProvider): T {
|
||||
const deps = [...(Ctor.inject ?? [])].map((token) => provider.get(token)) // 获取依赖
|
||||
return new Ctor(...deps) // 构造实例
|
||||
}
|
||||
}
|
||||
|
||||
// 服务提供者类,负责解析和提供服务实例
|
||||
export class ServiceProvider {
|
||||
private readonly singletonCache: Map<ServiceToken, unknown> // 单例缓存
|
||||
private readonly scopedCache = new Map<ServiceToken, unknown>() // 作用域缓存
|
||||
private readonly singletonCleanup: Disposer[] // 单例清理函数
|
||||
private readonly scopedCleanup: Disposer[] = [] // 作用域清理函数
|
||||
private readonly root: ServiceProvider | null // 根提供者
|
||||
|
||||
constructor(
|
||||
private readonly ctx: PluginRuntimeContext,
|
||||
private readonly descriptors: Map<ServiceToken, ServiceDescriptor>,
|
||||
root: ServiceProvider | null = null,
|
||||
private readonly isScope = false // 是否为作用域提供者
|
||||
) {
|
||||
this.root = root
|
||||
this.singletonCache = root ? root.singletonCache : new Map() // 共享单例缓存
|
||||
this.singletonCleanup = root ? root.singletonCleanup : []
|
||||
}
|
||||
|
||||
// 获取服务实例
|
||||
get<T>(token: ServiceToken<T>): T {
|
||||
const descriptor = this.descriptors.get(token) as ServiceDescriptor<T> | undefined
|
||||
if (!descriptor) {
|
||||
const hostValue = this.resolveFromHost<T>(token) // 尝试从宿主获取
|
||||
if (hostValue !== undefined) return hostValue
|
||||
throw new Error(`Service not registered for token: ${token.toString?.() ?? String(token)}`)
|
||||
}
|
||||
|
||||
switch (descriptor.lifetime) {
|
||||
case "singleton":
|
||||
return this.resolveSingleton(descriptor)
|
||||
case "scoped":
|
||||
return this.resolveScoped(descriptor)
|
||||
case "transient":
|
||||
default:
|
||||
return this.instantiate(descriptor) // 每次都新实例
|
||||
}
|
||||
}
|
||||
|
||||
// 创建作用域提供者
|
||||
createScope(): ServiceProvider {
|
||||
return new ServiceProvider(this.ctx, this.descriptors, this.root ?? this, true)
|
||||
}
|
||||
|
||||
// 释放资源
|
||||
async dispose(): Promise<void> {
|
||||
await this.flushCleanup(this.scopedCleanup) // 先清理作用域
|
||||
if (!this.isScope) {
|
||||
await this.flushCleanup(this.singletonCleanup) // 再清理单例
|
||||
this.singletonCache.clear()
|
||||
}
|
||||
this.scopedCache.clear()
|
||||
}
|
||||
|
||||
// 解析单例服务
|
||||
private resolveSingleton<T>(descriptor: ServiceDescriptor<T>): T {
|
||||
const cacheOwner = this.root ?? this
|
||||
if (cacheOwner.singletonCache.has(descriptor.token)) {
|
||||
return cacheOwner.singletonCache.get(descriptor.token) as T
|
||||
}
|
||||
const instance = this.instantiate(descriptor)
|
||||
cacheOwner.singletonCache.set(descriptor.token, instance)
|
||||
const disposer = this.extractDisposer(instance) // 提取清理函数
|
||||
if (disposer) cacheOwner.singletonCleanup.push(disposer)
|
||||
return instance
|
||||
}
|
||||
|
||||
// 解析作用域服务
|
||||
private resolveScoped<T>(descriptor: ServiceDescriptor<T>): T {
|
||||
if (this.scopedCache.has(descriptor.token)) {
|
||||
return this.scopedCache.get(descriptor.token) as T
|
||||
}
|
||||
const instance = this.instantiate(descriptor)
|
||||
this.scopedCache.set(descriptor.token, instance)
|
||||
const disposer = this.extractDisposer(instance)
|
||||
if (disposer) this.scopedCleanup.push(disposer)
|
||||
return instance
|
||||
}
|
||||
|
||||
// 实例化服务
|
||||
private instantiate<T>(descriptor: ServiceDescriptor<T>): T {
|
||||
return descriptor.factory(this)
|
||||
}
|
||||
|
||||
// 从宿主服务API解析
|
||||
private resolveFromHost<T>(token: ServiceToken<T>): T | undefined {
|
||||
if (typeof token === "string" && this.ctx.services?.has?.(token)) {
|
||||
return this.ctx.services.inject<T>(token)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
// 提取实例的清理函数
|
||||
private extractDisposer(instance: unknown): Disposer | null {
|
||||
if (!instance) return null
|
||||
if (typeof (instance as any).dispose === "function") {
|
||||
return () => (instance as any).dispose()
|
||||
}
|
||||
if (typeof (instance as any).destroy === "function") {
|
||||
return () => (instance as any).destroy()
|
||||
}
|
||||
if (typeof (instance as any).stop === "function") {
|
||||
return () => (instance as any).stop()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 执行清理函数列表
|
||||
private async flushCleanup(cleanup: Disposer[]) {
|
||||
while (cleanup.length) {
|
||||
const disposer = cleanup.pop()
|
||||
if (!disposer) continue
|
||||
await disposer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 服务令牌定义,用于依赖注入
|
||||
// 使用Symbol确保唯一性,避免字符串冲突
|
||||
|
||||
// 插件运行时上下文令牌
|
||||
export const PluginContextToken = Symbol.for("examaware.hosting.ctx")
|
||||
|
||||
// 插件日志器令牌
|
||||
export const PluginLoggerToken = Symbol.for("examaware.hosting.logger")
|
||||
|
||||
// 插件设置API令牌
|
||||
export const PluginSettingsToken = Symbol.for("examaware.hosting.settings")
|
||||
|
||||
// Desktop API令牌
|
||||
export const DesktopApiToken = Symbol.for("examaware.hosting.desktopApi")
|
||||
|
||||
// 应用生命周期管理器令牌
|
||||
export const HostApplicationLifetimeToken = Symbol.for("examaware.hosting.lifetime")
|
||||
@@ -0,0 +1,150 @@
|
||||
import type { ServiceProvideOptions, ServiceWatcherMeta } from "../../shared/services/registry"
|
||||
import type { ServiceCollection, ServiceProvider } from "./serviceCollection"
|
||||
|
||||
// 基础类型定义
|
||||
export type Awaitable<T> = T | Promise<T> // 可以是同步值或Promise
|
||||
export type Disposer = () => Awaitable<void> // 清理函数,返回void或Promise<void>
|
||||
|
||||
// 服务令牌,可以是字符串、符号或构造函数
|
||||
export type ServiceToken<T = unknown> = string | symbol | (new (...args: any[]) => T)
|
||||
|
||||
// 可注入的类,带可选的inject属性声明依赖
|
||||
export interface InjectableClass<T = unknown> {
|
||||
new (...args: any[]): T
|
||||
inject?: readonly ServiceToken[] // 依赖的令牌列表
|
||||
}
|
||||
|
||||
// 服务工厂函数,从provider获取实例
|
||||
export type ServiceFactory<T> = (provider: ServiceProvider) => T
|
||||
// 服务实现,可以是工厂函数、构造函数或直接值
|
||||
export type ServiceFactoryOrValue<T> = ServiceFactory<T> | InjectableClass<T> | T
|
||||
|
||||
// 服务生命周期:单例、作用域内单例、每次都新实例
|
||||
export type ServiceLifetime = "singleton" | "scoped" | "transient"
|
||||
|
||||
// 服务描述符,定义如何创建服务
|
||||
export interface ServiceDescriptor<T = unknown> {
|
||||
token: ServiceToken<T>
|
||||
lifetime: ServiceLifetime
|
||||
factory: ServiceFactory<T> // 创建实例的工厂函数
|
||||
}
|
||||
|
||||
// 托管服务接口,有启动和停止方法
|
||||
export interface HostedService {
|
||||
start(): Awaitable<void>
|
||||
stop(): Awaitable<void>
|
||||
}
|
||||
|
||||
// Host构建器设置
|
||||
export interface HostBuilderSettings {
|
||||
environment?: string // 环境名,如'development'
|
||||
properties?: Record<string, unknown> // 额外属性
|
||||
}
|
||||
|
||||
// Host构建器上下文,包含运行时上下文和配置
|
||||
export interface HostBuilderContext {
|
||||
ctx: PluginRuntimeContext // 插件运行时上下文
|
||||
environmentName: string // 当前环境
|
||||
properties: Map<string | symbol, unknown> // 属性映射
|
||||
lifetime: PluginHostApplicationLifetime // 应用生命周期管理
|
||||
}
|
||||
|
||||
// 插件应用上下文,包含服务提供者和上下文
|
||||
export interface PluginHostApplicationContext {
|
||||
ctx: PluginRuntimeContext
|
||||
services: ServiceProvider
|
||||
host: HostBuilderContext
|
||||
}
|
||||
|
||||
// 配置服务委托,在构建时注册服务
|
||||
export type ConfigureServicesDelegate = (
|
||||
context: HostBuilderContext,
|
||||
services: ServiceCollection
|
||||
) => Awaitable<void>
|
||||
|
||||
// 配置Host委托,在应用启动时执行
|
||||
export type ConfigureHostDelegate = (
|
||||
context: HostBuilderContext,
|
||||
app: PluginHostApplicationContext
|
||||
) => Awaitable<void>
|
||||
|
||||
// 中间件函数,包装应用逻辑
|
||||
export type PluginMiddleware = (
|
||||
app: PluginHostApplicationContext,
|
||||
next: () => Promise<void>
|
||||
) => Awaitable<void>
|
||||
|
||||
// 暴露服务解析器,可以通过令牌或工厂函数
|
||||
export interface HostExposureResolver<T = unknown> {
|
||||
token?: ServiceToken<T> // 通过令牌暴露
|
||||
factory?: (provider: ServiceProvider) => T // 通过工厂函数暴露
|
||||
}
|
||||
|
||||
// 服务API接口,插件用来注册和获取服务
|
||||
export interface ServiceAPI {
|
||||
provide: (name: string, value: unknown, options?: ServiceProvideOptions) => Disposer
|
||||
inject: <T = unknown>(name: string, owner?: string) => T
|
||||
injectAsync?: <T = unknown>(name: string, owner?: string) => Promise<T>
|
||||
when?: <T = unknown>(
|
||||
name: string,
|
||||
cb: (svc: T, owner: string, meta: ServiceWatcherMeta) => void | (() => void)
|
||||
) => Disposer
|
||||
has: (name: string, owner?: string) => boolean
|
||||
}
|
||||
|
||||
// 插件日志接口
|
||||
export interface PluginLogger {
|
||||
info: (...args: any[]) => void
|
||||
warn: (...args: any[]) => void
|
||||
error: (...args: any[]) => void
|
||||
debug?: (...args: any[]) => void
|
||||
}
|
||||
|
||||
// 插件设置API,读写配置
|
||||
export interface PluginSettingsAPI {
|
||||
all(): Record<string, any> // 获取所有配置
|
||||
get<T = unknown>(key?: string, def?: T): T // 获取单个配置项
|
||||
set<T = unknown>(key: string, value: T): Promise<void> // 设置配置项
|
||||
patch(partial: Record<string, any>): Promise<void> // 批量更新配置
|
||||
reset(): Promise<void> // 重置配置
|
||||
onChange(listener: (config: Record<string, any>) => void): Disposer // 监听配置变化
|
||||
}
|
||||
|
||||
// 插件运行时上下文,插件的核心接口
|
||||
export interface PluginRuntimeContext {
|
||||
app: "main" | "renderer" // 运行在主进程还是渲染进程
|
||||
logger: PluginLogger // 日志器
|
||||
config: Record<string, any> // 插件配置
|
||||
settings: PluginSettingsAPI // 设置API
|
||||
effect: (fn: () => void | Disposer | Promise<void | Disposer>) => void // 注册副作用清理
|
||||
services: ServiceAPI // 服务API
|
||||
windows?: {
|
||||
// 窗口操作(主进程)
|
||||
broadcast: (channel: string, payload?: any) => void
|
||||
}
|
||||
ipc?: {
|
||||
// IPC通信(主进程)
|
||||
registerChannel: (channel: string, handler: (event: unknown, ...args: any[]) => any) => Disposer
|
||||
invokeRenderer?: (channel: string, payload?: any) => void
|
||||
}
|
||||
desktopApi?: unknown // Desktop API(渲染进程)
|
||||
}
|
||||
|
||||
// 插件应用生命周期接口,管理启动/停止事件
|
||||
export interface PluginHostApplicationLifetime {
|
||||
onStarted(handler: () => Awaitable<void>): Disposer // 监听启动事件
|
||||
onStopping(handler: () => Awaitable<void>): Disposer // 监听停止事件
|
||||
onStopped(handler: () => Awaitable<void>): Disposer // 监听已停止事件
|
||||
notifyStarted(): Promise<void> // 触发启动通知
|
||||
notifyStopping(): Promise<void> // 触发停止通知
|
||||
notifyStopped(): Promise<void> // 触发已停止通知
|
||||
}
|
||||
|
||||
// 暴露的服务定义
|
||||
export type HostExposure = {
|
||||
name: string // 服务名
|
||||
resolver: (provider: ServiceProvider) => unknown // 解析函数
|
||||
}
|
||||
|
||||
// 重新导出类型,便于使用
|
||||
export type { ServiceCollection, ServiceProvider }
|
||||
Reference in New Issue
Block a user