Skip to content

Typert 是类型基础设施生成器:它把源码分析、运行时存储与 Loader 发现分开,使某业务包中只写一次的类型级契约能够在 monorepo 其他地方被反射、校验与对外服务,而无需手写胶水代码。本页引用 packages/typert/*。

包地图 ​

Typert 是 packages/typert/ 下的一个四件套:

包角色Cordis 键 / 消费方
packages/typert/protocol — @deepseek-ai/dsh-typert-protocol与编译器无关的声明:@Remote、作用域、调用描述符、编解码器、provider 契约纯声明,无服务
packages/typert/generator — @deepseek-ai/dsh-typert-generatorTypeScript 工程分析器 + 模型驱动发射器构建期库
packages/typert/registry — @deepseek-ai/dsh-typert-registry生成反射与 schema 的运行时存储提供 ctx.typert
packages/typert/loader — @deepseek-ai/dsh-typert-loader发现 Loader 条目并注册生成的 host 工件消费 ctx.loader 与 ctx.typert

在数据意义上流水线是“注册表 → 生成器 → 产出”,但各模块形成一个三角:分析器读源码并构建模型,发射器把模型变成工件,loader 在运行时发现这些工件并交给注册表,而所有一切都以协议的类型对话。

text
 源码(/packages/*)            构建期                      运行时
 --------------------                  ----------           -------
 *.ts  ----->  WorkspaceAnalyzer  --> FaceModel / TypeGraph
                (check 或 write 模式)       |
                                           v
                                    发射器(FaceModelEmitter、
                                    WorkspaceTypertGenerator)
                                           |
                         lib/typert.host.{js,d.ts}   (package/typert 导出)
                         lib/typert.client.{js,d.ts} (package/client/typert)
                                           |
 运行时  <-- dsh-typert-loader <-- ctx.loader 发现 package.json ./typert
                 校验 TYPERT --> ctx.typert(TypertRegistry)存储 <package>#<face>

分析器:WorkspaceAnalyzer ​

packages/typert/generator/src/analyzer.ts 在渲染任何工件之前,先把开发者手写的源码类型树转换成与编译器无关的数据模型。要点:

  • 使用以 tsconfig.host.json 或 tsconfig.client.json 为种子的独立 ts.Program。
  • 两种模式(AnalysisMode = 'check' | 'write')。check(默认)在语法/语义诊断、缺失的可达公共注解、私有跨包引用,以及模型无法无损保留的可达声明合并上失败。write 会插入 checker 推导的注解、重建程序,并返回一个干净的 check 模式模型。
  • tsconfig.base.{host,client}.json 面;直接的工程引用确立编译器面的成员关系;package.json#exports 确立每条跨包公共边界;源码导入/再导出是唯一允许的跨面边。
  • NPM 依赖所拥有的类型(包括 @types 全局声明)以 external 引用保留,而非被展开。
  • WorkspaceCaches 跨运行保留工作成果。

WorkspaceTypertGenerator(src/workspace.ts)是更高层的驱动:它通过遍历“从 Cordis Context 与 Events 增广可达的包公共导出”以及显式 @typert 声明来发现贡献者,并发射工件。

生成器发射什么 ​

FaceModelEmitter(src/emitter.ts)只消费模型(没有 AST,没有 checker)。它发射可执行的 JavaScript,内含受支持的 Zod schema 与一个 TYPERT 贡献,外加一份声明文件,其 schema 通过包的公共导出被定义为 z.ZodType<SourceType>。不受支持的 Zod 投影会失败,而不是弱化源类型。

工件落点(选择性发布):

面工件package.json#exports 条目
hostlib/typert.host.js / lib/typert.host.d.ts./typert(package/typert)
clientlib/typert.client.js / lib/typert.client.d.ts./client/typert(package/client/typert)

生成的声明把 TYPERT 暴露为 unknown,因此贡献的业务包不依赖运行时注册表。没有相应公共条目的业务包根本不需要 Typert 工件。

dsh 如何调用它:tsdown 插件 ​

根 tsdown.config.ts 通过 typertPlugin(packages/typert/generator/src/tsdown-plugin.ts)把 Typert 接进普通构建:

ts
// tsdown.config.ts(节选)
import { typertPlugin } from './packages/typert/generator/lib/types/tsdown-plugin.js'
// host 通道:
plugins: [typertPlugin({ mode: 'workspace', faces: ['host'] })]

驱动它的根脚本(package.json):

脚本命令
buildbuild:lib → build:web
build:lib:hosttsc -b tsconfig.host.json && tsdown --env.DSH_BUILD_FACE host
build:lib:clienttsc -b tsconfig.client.json && tsdown --env.DSH_BUILD_FACE client
typecheckbuild:lib:host 再 typecheck:contracts-ready

该插件在 host 通道做两件事:(1)打包前先降低 TypeScript 依赖中的标准装饰器(经 ts.transpileModule,目标 ES2024/ESNext);(2)writeBundle 时以 tsconfig.host.json 作为唯一程序种子运行 workspace 级 Typert 生成,既产出 Host 反射工件,也产出给 Client 用的 Host Remote 契约的 typert.remote-client.* 投影。随后的 Client tsdown 既不起动 Typert,也不分析 tsconfig.client.json。

运行时注册表:ctx.typert ​

packages/typert/registry/src/service.ts 提供 TypertRegistry,即 ctx.typert 背后的默认插件。它存储生成的反射与可选的活跃 Zod schema,并在随 Cordis fiber 调用方的生命周期内原子地注册与移除。

  • 键:包反射以 <package>#<face> 为键;schema 以 <package>#<name> 为键(它们省略面,因为 host 与 client 运行在独立上下文中)。
  • register(contribution) 在提交任何东西之前就拒绝非法的身份与重复键,然后返回确切的 Cordis effect 处置器。
  • 查询:get(key)、resolve(key)、list(filter?)、getPackage(packageName, face?)、listPackages(...),以及用 z.toJSONSchema() 按需投影活跃 schema 的 toJSONSchema(key, params?)。
  • 身份辅助:typertKey() 与 typertPackageKey()。
  • 子注册表(TypertRegistry 直接暴露四个活跃视图):local(当前环境的调用定义)、remotes(消费者选定的 Remote 定义)、lookups(Host 对象查找 Provider)、contexts(Host 与 Client Context 适配器)——各自带独立的 subscribe/register 表面(packages/typert/registry/src/service.ts)。
  • protocol 包的贡献/记录契约位于 @deepseek-ai/dsh-typert-registry/types 子路径。注册表存储反射,但不做 host/client 图合并,也不解析 TypeScript 引用——这些是分析器与发射器的职责。

Loader 发现 ​

packages/typert/loader 仅限 Node。它需要 ctx.loader 与 ctx.typert(它自己不提供注册表)。激活期间扫描现有 Loader 条目,然后跟随 Cordis internal/plugin 生命周期通知,解析每个条目包的 package.json,在导出 ./typert 时导入它,校验 TYPERT 清单,并把贡献注册到该条目或本插件卸载为止。packages 配置可列出嵌在另一个 Loader 条目后面的插件的额外工件。包解析与导入的清单会在进程生命周期内缓存(新增导出需要重启)。目前它只导入 host 面。

协议包:@deepseek-ai/dsh-typert-protocol ​

packages/typert/protocol/src/types.ts 持有其他所有部分共享的、与编译器无关的词汇表——这是 Typert 与 Host Gateway 及 Client API 之间最接近“线上契约”的东西:

  • @Remote / @RemoteScope(key) 装饰器把公共实例方法标记为可在已注册的 Cordis Service 上直接调用。
  • 流模式:Remote({ mode: 'stream' }) 把某方法标记为经共享逻辑流载体逐个投递每个 Iterable 条目——流式 Remote 解析为 AsyncIterable<T> 而非单个 T。
  • 收敛的结果词汇表:每次 Remote 调用都解析为 RemoteResult<T> = { ok: true, value: T } | { ok: false, error: RemoteFailure };错误分支是 RemoteError 实例的按码判别联合(code 免 cast 收窄 details),且 throw result.error 保留 RemoteError 的抛错语义。
  • TypertRemoteService、bindTypertRemote、remoteMethods。
  • TypertClientRemote(Gateway 侧的 Client Remote 能力)新增 $mount(contribution)——在调用方 fiber 中挂载一份生成的 Host-for-Client 贡献,命名空间服务与具体方法就绪后返回一个处置器——以及 $on(event, listener) 订阅一个被转发过来的 Host 事件(作用域瀑布可返回、经 next() 委托,或拒绝 Host 分发)。
  • InvocationDescriptor——注册表、Gateway 与 Client Remote 共享的运行时形态;以 AbortSignal 作为最后一个参数会加入协作式取消(被注入的 signal 永远不会变成 JSON 参数)。
  • 可合并扩展的地图:TypertLookupMap、TypertContextMap、TypertRemoteMap、TypertRemoteScopeMap、TypertRemoteNamespaceMap。
  • 严格编解码器携带生成的 schema;src-json 编解码器标识较弱的源码启动路径。

Typert 与文档目录——什么被生成、什么没有 ​

一个常见的混淆点:docs/config-catalog.md(配置目录)是被生成的,但不是由 Typert 生成。它由 scripts/gen-config-catalog.ts(pnpm run gen-config-catalog)产出,该脚本用一次直接的 TypeScript AST 遍历来扫描包入口点、配置类型与静态 Schemastery schema,并对未知类型名硬报错。

Typert 确实驱动着 Cordis API/目录投影。scripts/gen-cordis-catalog.ts 从 @deepseek-ai/dsh-typert-generator 导入 WorkspaceAnalyzer / CordisCatalogProjector / projectCordisCatalog(见 packages/typert/generator/src/cordis-catalog.ts),把与编译器无关的 FaceModel 重新投射成 docs/cordis-api/* 以及运行时 API 目录 packages/extensions/tool-cordis/src/api-catalog.ts。换句话说:Typert 的分析器是共享的抽取核心;config-catalog 的生成器则是一个独立、聚焦于 schema 的工具。

关键生成器导出 ​

来自 packages/typert/generator/src/index.ts:

导出类别用途
WorkspaceAnalyzer、WorkspaceCaches、TypertAnalysisError类源码分析为模型
AnalysisMode、DiscoveredTypertPackage、WorkspaceAnalyzerOptions类型分析器选项
FaceModelEmitter、TypertEmitError类模型 → JS + d.ts + schemas
WorkspaceTypertGenerator类贡献者发现 + 发射驱动
TypeGraphRenderer类确定性文本渲染
CordisCatalogProjector、projectCordisCatalog、collectEvents、collectServices类/函数仓库文档投影

已知局限 ​

局限位置
跳过包导出模式;贡献者需要具体导出目标生成器
跨面 namespace 再导出会失败生成器
Zod 发射器只支持模型的限定子集(条件/映射根在出现具体 schema-factory 策略前会失败)发射器
注册表不做 host/client 图合并,也不解析 TS 引用注册表
Loader 只导入 host 面loader
装饰器标记只携带方法名与调用模式;参数/schema 反射需要构建流水线协议

包 ​

包nameversion
协议@deepseek-ai/dsh-typert-protocol
生成器@deepseek-ai/dsh-typert-generator
注册表@deepseek-ai/dsh-typert-registry
Loader@deepseek-ai/dsh-typert-loader

延伸阅读 ​

  • SDK 协议、SDK 客户端、SDK 服务端——Typert 与之同属 SDK 章;注意它们是无关的系统(Typert 处理类型/schema 反射,SDK 处理基于 stdio 的 JSON-RPC)。
  • 仓库构建接线:tsdown.config.ts 与根 package.json 中的 build:lib:host|client 脚本。
  • Cordis 目录投影:scripts/gen-cordis-catalog.ts 与 packages/typert/generator/src/cordis-catalog.ts。
  • config-catalog 的独立性:scripts/gen-config-catalog.ts(docs/config-catalog.md)。
  • 运行时注册表 API:packages/typert/registry/src/service.ts。
  • 协议声明:packages/typert/protocol/src/types.ts。