继承接口面

本页汇总每个插件天然可见、但不属于某个 Tether 产品能力的框架接口:Cordis 核心 Context、 生命周期事件,以及 dsh 中由 loader / hmr / timer 混入的成员。它也是 TS 声明合并与 C# Type-keyed seam 的对照表。

对照基准是 dsh dsh-v0.1.1-rc.1 的 Inherited Cordis API。 该上游页面由 vendored Cordis、loader、hmr 和 timer 源码生成;本页记录当前 C# 实际公开面,不伪造同名成员。 对应的固定源码包括 events.ts、 loader/index.ts、 hmr/index.ts 与 timer/index.ts。

Inherited ctx members

dsh / TypeScriptTether / C#状态与边界
ctx.on / ctx.oncectx.On / ctx.Once对应。C# 返回 IDisposable,且通过 Context 注册时随当前 scope 回收。
ctx.emit / parallel / serial / bail / waterfallEmitAsync / ParallelAsync / SerialAsync / BailAsync / WaterfallAsync对应。C# listener 统一允许异步,普通异常向调用方传播。
ctx.plugin / ctx.injectctx.Plugin / ctx.Inject对应。返回 PluginHandle,使用 AwaitSettledAsync() 等待稳定。
ctx.effectctx.Effect / ctx.Scope.OnDispose近似。C# 登记 disposer,不运行并收集一个 effect body。
ctx.get / set / provideGet<T> / SetAsync<T> / Provide<T>对应。键为 Type;修改受 exact providing scope 约束。
ctx.accessor / ctx.mixin无TS-only。C# 不动态扩展 Context 属性。
ctx.extendctx.Fork()近似。只派生 effect scope,不继承任意 metadata。
ctx.isolate / ctx.intercept无TS-only。当前没有 label isolation 或 intercept config。
ctx.rootctx.Root对应。
ctx.scopectx.Scope对应。C# 类型为 EffectScope。
ctx.fiberctx.Scope + 挂载方的 PluginHandle职责拆分。
ctx.registryctx.Registry近似。Type-keyed store + activation graph。
ctx.reflect无TS-only。显式泛型 API 取代 proxy reflection。
ctx.eventsctx.Events对应。整棵 Context 树共享。
ctx.logger无 Cordis 核心成员TS-only。日志由宿主 / 产品 seam 显式提供。
ctx.timer无 ambient serviceTS-only。使用 .NET timer,并把取消 / dispose 登记到当前 scope。
ctx.interval / timeout / throttle / debounce无 Cordis helperTS-only。没有全局混入;按需要使用 PeriodicTimer、Timer 或产品层封装。
ctx.loader显式 CompositionLoader / MountedComposition职责拆分。Loader 不是 Context 属性。
ctx.hmr显式 HotReloadWatcher职责拆分。HMR watcher 不是 Context 属性。

各核心成员的完整签名见 Context、Events、 Fiber 与 Plugin Registry。

C# Context 默认公开面

下面是插件收到的 Context 上可直接调用的类别级清单;泛型 overload 在所属参考页逐项列出。

类别成员
根与 scopeRoot、IsRoot、Registry、Events、Scope、IsReady
应用生命周期StartAsync()(仅根)、DisposeAsync()
服务读取Get<T>()、Require<T>()、Has<T>()
服务变更Provide<T>() / Provide(Type, ...)、SetAsync<T>()、RemoveAsync<T>()
effectEffect(...)、OnDispose(...)
事件On(...)、OnWaterfall(...)、Once(...)、Off(...)、五种派发方法
scopeFork()
插件Plugin(...)、Plugin<TPlugin>()、Inject(...)

这些不是从多个服务动态 mixin 出来的代理属性,而是 Context 的真实公开成员。直接使用 ctx.Events.On(...) 或其它底层对象时,要重新承担生命周期所有权;插件日常代码应优先调用 Context 转发。

Inherited events:TS → C#

dsh inherited 页面列出的 core / loader / hmr 事件,与 C# 当前事件面并非一一同名:

dsh eventC# 对应状态与差异
internal/plugin无公开事件Context.Plugin() 直接返回句柄;核心不广播创建通知。
internal/status无公开事件查询 PluginHandle.IsActive / IsDisposed、EffectScope.State,或等待 settlement。
internal/serviceservice(不是同一 hook)dsh 项是 service binding interception;C# 项是服务发布、更新或移除后的 Type-keyed 收敛通知。
internal/update无组合更新走显式 MountedComposition.ReplaceAsync();没有可拦截的核心 waterfall。
internal/get无Registry.Get 不派发读取 hook。
internal/set无SetAsync 执行 owner 校验与依赖收敛,不派发写入 waterfall。
internal/listener无注册 listener 不广播内部事件。
internal/dispatch无普通派发不先经过内部 interception event。
hmr/change无 Cordis eventHotReloadWatcher 内部消费文件变更;公开控制面是方法与状态。
hmr/reload无 Cordis event调用 ReloadAsync(file) / watcher 自动 reload;失败通过 Error 与 internal/error 报告。
exit无 Cordis core event进程宿主负责信号与 root Context disposal。
loader/config-update无事件调用方渲染候选后显式 ReplaceAsync。
loader/entry-init无事件插件行初始化属于组合挂载事务内部。
loader/partial-dispose无事件句柄与组合通过 DisposeAsync / RequestDispose 显式回收。
loader/patch-context无事件C# patch 作用于 stable-id 组合行,不 patch Context proxy。

“无公开事件”表示当前 C# 调用方没有同层 hook;不应通过猜测字符串事件名来依赖内部实现。

C# 核心生命周期事件

C# 把插件作者确实需要的框架事件集中在 LifecycleEvents:

public static class LifecycleEvents
{
    public const string Ready = "ready";
    public const string Dispose = "dispose";
    public const string Fork = "fork";
    public const string Service = "service";
    public const string Error = "internal/error";
}
常量参数生产时机与派发方式
Ready根 Context根首次 StartAsync() 时顺序等待;普通 listener 失败传播。
Dispose被回收的 Contextscope 回收期间派发;listener 失败计入 cleanup outcome。
Fork新 child ContextFork() 成功后 contained 派发,不阻塞同步调用。
Service服务键 Type服务发布、实例更新或移除的依赖收敛中派发;失败参与聚合结果。
ErrorException、source string仅显式 contained 通知的错误 sink;普通派发绝不自动路由到这里。
ctx.On<Type>(LifecycleEvents.Service, key =>
{
    Console.WriteLine($"service changed: {key.FullName}");
});

ctx.On<Exception, string>(LifecycleEvents.Error, (error, source) =>
{
    logger.LogError(error, "Contained Cordis failure from {Source}", source);
});

Loader、HMR 与 timer 为什么不混入 ctx

Loader

CompositionLoader 是显式对象:宿主提供 PluginCatalog,调用 Render* / Load*, 并持有返回的 MountedComposition。组合替换、回滚与 disposal 因此有明确 owner,不依赖 ambient ctx.loader。

HMR

HotReloadWatcher 显式接收 Context、源码目录和 ScriptCompiler。调用方持有 watcher, 可查询 IsRunning / MountedFiles,调用 StartAsync、ReloadAsync、UnmountAsync、DisposeAsync。 它还有 .NET Error event,同时把失败 contained 到 Cordis internal/error;没有 ctx.hmr 或 hmr 字符串事件协议。

Timer

C# 直接使用 BCL timer,并把它绑定到 scope:

var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));
ctx.Effect(timer.Dispose);

async Task RefreshLoopAsync()
{
    try
    {
        while (await timer.WaitForNextTickAsync(ctx.Scope.Cancellation))
        {
            await RefreshAsync();
        }
    }
    catch (OperationCanceledException) when (ctx.Scope.Cancellation.IsCancellationRequested)
    {
    }
}

若长期循环由 apply 启动,插件不能在 apply 中直接 await RefreshLoopAsync();通常启动后台任务、监听 scope cancellation, 并登记一个可等待 disposer 来 join 该任务。核心不提供 interval / timeout / throttle / debounce mixin。

在 C# 中扩展插件可见接口

TS 通过 declare module 扩展 Context / Events 接口,再依靠字符串键和 proxy 注入实现。C# 使用两条显式 seam。

服务:接口 Type

public interface IIndexService
{
    ValueTask RebuildAsync(CancellationToken cancellationToken = default);
}

// provider
ctx.Provide<IIndexService>(new LocalIndexService());

// consumer
[Inject(typeof(IIndexService))]
public sealed class SearchPlugin : Plugin
{
    protected override void Apply(Context ctx)
        => _ = ctx.Require<IIndexService>();
}

Service Definition 程序集只放接口;Provider 和 Consumer 都引用它。更换 preset 时只换实现,不换消费方引用。

事件:常量 + 泛型调用点

public static class IndexEvents
{
    public const string Rebuilt = "index/rebuilt";
}

ctx.On<int>(IndexEvents.Rebuilt, documentCount => { /* ... */ });
await ctx.EmitAsync(IndexEvents.Rebuilt, 42);

事件名仍是运行时字符串,泛型只约束当前 listener 的参数形状;发布包应集中声明常量和约定,不在各处散写 literal。 需要编译期更强的契约时,优先把交互建模为 Service Definition 接口。

在 GitHub 上编辑此页