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-generator | TypeScript 工程分析器 + 模型驱动发射器 | 构建期库 |
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 在运行时发现这些工件并交给注册表,而所有一切都以协议的类型对话。
源码(/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 条目 |
|---|---|---|
| host | lib/typert.host.js / lib/typert.host.d.ts | ./typert(package/typert) |
| client | lib/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 接进普通构建:
// tsdown.config.ts(节选)
import { typertPlugin } from './packages/typert/generator/lib/types/tsdown-plugin.js'
// host 通道:
plugins: [typertPlugin({ mode: 'workspace', faces: ['host'] })]驱动它的根脚本(package.json):
| 脚本 | 命令 |
|---|---|
build | build:lib → build:web |
build:lib:host | tsc -b tsconfig.host.json && tsdown --env.DSH_BUILD_FACE host |
build:lib:client | tsc -b tsconfig.client.json && tsdown --env.DSH_BUILD_FACE client |
typecheck | build: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()。 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 上直接调用。TypertRemoteService、bindTypertRemote、remoteMethods。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 反射需要构建流水线 | 协议 |
包
| 包 | name | version |
|---|---|---|
| 协议 | @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。