![[DeepSeek Harness插件内核-13]ReistryServices是如何注册插件的?](http://pic.xiahunao.cn/yaotu/[DeepSeek Harness插件内核-13]ReistryServices是如何注册插件的?)
Cordis 中万物皆插件但是插件的定义简单至极用来调用插件执行和管理器周期的Fiber才是核心所在。在通过DeepSeek Harness插件内核-10Fiber是如何管理插件生命周期的 完成了针对插件和Fiber的深度介绍之后我们介绍一下用来注册插件的RegistryService服务。1. 注册的插件如何存储通过前面系列文章的介绍我们知道通过ReigstryService的plugin方法可以注册如下三种类型的插件并会调用resolve方法将她们统一转换成一个函数。exportclassRegistryService{resolve(plugin:Plugin):Function|undefined{try{if(typeofpluginfunction)returnpluginif(isApplicable(plugin))returnplugin.apply}catch{}}}exportnamespacePlugin{exportinterfaceBaseTany{name?:stringConfig?:StandardSchemaV1any,Tinject?:Inject provide?:string|string[]intercept?:Dictboolean}exportinterfaceFunctionTanyextendsBaseT{(ctx:Context,config:T):any}exportinterfaceConstructorTanyextendsBaseT{new(ctx:Context,config:T):any}exportinterfaceObjectTanyextendsBaseT{apply(ctx:Context,config:T):any}}exporttypeInjectMDict(keyofM)[]|{[KinkeyofM]?:M[K]}针对同一个插件函数表示最终通过resolve方法生成的Function的第一次注册将会伴随着Plugin.Runtime的创建后者的callback属性返回的就是此函数。除此之外如果注册是指定了插件名称它会体现在name属性上另一个Config属性返回的StandardSchemaV1可以用来检验提供的插件配置的结构。注册的每个插件实例都会对应一个Fiber对象针对当前函数注册的实例会被存储在fiber属性返回的DisposableListFiber列表中。exportnamespacePlugin{exportinterfaceRuntime{name?:stringfibers:DisposableListFibercallback:globalThis.FunctionConfig?:StandardSchemaV1}}exportclassRegistryService{private_counter0private_internalnewMapFunction,Plugin.Runtime()constructor(publicctx:Context){defineProperty(this,symbols.tracker,{property:ctx,noShadow:true,})}}RegistryService会使用_internal属性返回的字典来存储注册的插件该字典的Key和Value分别是插件函数和对应的Runtime对象。除此之外它还有一个通过_counter表示的插件注册计数器。该计数器将会作为注册插件对应Fiber的uid对于每一次插件注册该计数器都会自增。上面的代码一并给出了RegistryService的构造函数的定义通过为symbols.tracker属性设置一个Tracker对象并将其property属性设置为ctx意味着当前的Context将会绑定到它的公共属性ctx上具体原理可以参考我的文章DeepSeek Harness插件内核-07:Context全面解析。Tacker的noShadow设置为True意味着在调用其方法的时候提供的Context不会附加影子上下文。2. 两种插件注册方式插件的常规注册指的是调用Plugin方法来注册插件。从如下的代码可知调用该方法除了必须提供三种形式之一的插件之外我们还可以提供插件的配置以及一个用来获取或者构建当前调用堆栈的的函数默认提供的是如下定义的buildOuterStack函数。exportclassRegistryService{plugin(plugin:Plugin,config?:any,getOuterStackbuildOuterStack()){constcallbackthis.resolve(plugin)if(!callback)thrownewError(invalid plugin, expect function or object with an apply method, received typeofplugin)this.ctx.fiber.assertActive()letruntimethis._internal.get(callback)if(!runtime){letnameplugin.nameif(nameapply)nameundefinedruntime{name,callback,fibers:newDisposableList(),Config:plugin.Config}this._internal.set(callback,runtime)}constfibernewFiber(this.ctx,config,Inject.resolve(plugin.inject),runtime,getOuterStack)constwrappedObject.create(fiber)asFiberPromiseLikeFiberwrapped.then(onFulfilled,onRejected){returnfiber.await().then(onFulfilled,onRejected)}returnwrapped}}exportfunctionbuildOuterStack(offset0){constouterErrornewError()return()outerError.stack!.split(\n).slice(3offset)}在将提供的原始插件通过resolve方法转换成通过的插件函数并确认当前Fiber为激活状态之后plugin方法会试着利用该返回从_internal字典中提取对应的Runtime对象如果不存在就创建一个。接下来针对Fiber的创建是plugin方法的核心操作调用Fiber构造函数传入的参数包括当前Context;提供的可选配置注入服务这里为空创建的Runtime构建或者获取当前调用堆栈的函数我们在DeepSeek Harness插件内核-10Fiber是如何管理插件生命周期的逐行分析过Fiber构造函数的实现。如果满足插件执行的要求即插件对象的inject属性提供的注入服务全部就绪创建的Fiber会提供它绑定的Context和配置执行插件函数。否则会进入Fiber构建的状态机流转循环。这最后四行代码的核心意图非常巧妙它是一种经典的双态Dual-state代理设计模式。它的核心目的是让 plugin方法的返回值既是一个可以立即同步使用的Fiber实例同步态又是一个可以通过await或.then()异步等待插件加载就绪的 Promise 对象异步态。DeepSeek Harness插件内核-12这算Cordis一个大BUG吗?(续) 我们介绍了Cordis针对先注入声明后调用消费的依赖注入策略如果调用plugin方法并希望同时提供依赖服务的注入声明注册的插件只能采用对象形式。如果希望采用最简单的函数形式可以调用如下这个inject方法。exportclassRegistryService{inject(inject:Inject,callback:Plugin.Functionvoid){returnthis.plugin({inject,apply:callback,name:callback.name})}}exporttypeInjectMDict(keyofM)[]|{[KinkeyofM]?:M[K]}inject方法提供的服务注入声明可以是以来服务名称列表也可以是包含服务配置的字典。方法内部会插件回调函数和提供的服务注入声明转换成对象形式的产检最终还是调用plugin方法实施注册。3. 其他方法除了最为核心的用来注册插件的plugin和inject方法,以及将三种形式的插件统一转换成函数的resolve方法外RegistryService还提供了一系列额外的方法这些方法基本是围绕针对_counter和_internal这两个字段的读写实现。exportclassRegistryService{private_counter0private_internalnewMapFunction,Plugin.Runtime()getcounter(){returnthis._counter}getsize(){returnthis._internal.size}get(plugin:Plugin){constkeythis.resolve(plugin)returnkeythis._internal.get(key)}has(plugin:Plugin){constkeythis.resolve(plugin)return!!keythis._internal.has(key)}delete(plugin:Plugin){constkeythis.resolve(plugin)construntimekeythis._internal.get(key)if(!runtime)returnthis._internal.delete(key)for(constfiberofruntime.fibers){fiber.dispose()}returnruntime}keys(){returnthis._internal.keys()}values(){returnthis._internal.values()}entries(){returnthis._internal.entries()}forEach(callback:(value:Plugin.Runtime,key:Function)void){returnthis._internal.forEach(callback)}counter 在注册插件之前被调用利用自证的计数器作为插件绑定Fiber对象的uidsize回去当前注册插件函数非插件实例的数量get 根据指定的插件提取对应的Runtimehas确定指定的插件是否被注册delete借助指定插件所有实例的注册它不仅仅是将对应的Runtime从_internal字典中移除还会调用每个插件实例对应Fiber对象的dispose方法将销毁同时实施存续期间收集的清理工作keys/values/entries 针对_internal的Key、Value和KV的迭代forEach 针对每个Runtime和插件函数执行指定的回调。