Typert is the type-infrastructure generator: it separates source analysis, runtime storage, and Loader discovery so that type-level contracts written once in a business package can be reflected, validated, and served elsewhere in the monorepo without hand-written glue code. This page cites packages/typert/*.
Package landscape
Typert is a family of four packages under packages/typert/:
| Package | Role | Cordis key / consumer |
|---|---|---|
packages/typert/protocol — @deepseek-ai/dsh-typert-protocol | compiler-independent declarations: @Remote, scopes, invocation descriptors, codecs, provider contracts | pure declarations, no service |
packages/typert/generator — @deepseek-ai/dsh-typert-generator | TypeScript project analyzer + model-driven emitters | build-time library |
packages/typert/registry — @deepseek-ai/dsh-typert-registry | runtime storage of generated reflection and schemas | provides ctx.typert |
packages/typert/loader — @deepseek-ai/dsh-typert-loader | discovers Loader entries and registers generated host artifacts | consumes ctx.loader and ctx.typert |
The pipeline is registry → generator → output in the data sense, but the modules form a triangle: the analyzer reads source and builds a model, the emitters turn that model into artifacts, the loader discovers those artifacts at runtime and hands them to the registry, and everything speaks the protocol's types.
source (/packages/*) build time runtime
-------------------- ---------- -------
*.ts -----> WorkspaceAnalyzer --> FaceModel / TypeGraph
(check or write mode) |
v
emitters (FaceModelEmitter,
WorkspaceTypertGenerator)
|
lib/typert.host.{js,d.ts} (package/typert export)
lib/typert.client.{js,d.ts} (package/client/typert)
|
runtime <-- dsh-typert-loader <-- ctx.loader discovers package.json ./typert
validates TYPERT --> ctx.typert (TypertRegistry) stores <package>#<face>The analyzer: WorkspaceAnalyzer
packages/typert/generator/src/analyzer.ts converts the developer-authored source type tree into a compiler-independent data model before any artifact is rendered. Key facts:
- Uses its own
ts.Programinstances seeded fromtsconfig.host.jsonortsconfig.client.json. - Two modes (
AnalysisMode = 'check' | 'write').check(default) fails on syntax/semantic diagnostics, missing reachable public annotations, private cross-package references, and unreachable declaration merges the model cannot retain losslessly.writeinserts checker-derived annotations, rebuilds, and returns a clean check-mode model. tsconfig.base.{host,client}.jsonfaces; direct project references establish compiler-face membership;package.json#exportsestablishes every cross-package public boundary; source imports/re-exports are the only allowed cross-face edges.- Types owned by NPM dependencies (including
@typesglobals) remainexternalreferences instead of being expanded. WorkspaceCachesretains work across runs.
WorkspaceTypertGenerator (src/workspace.ts) is the higher-level driver: it discovers contributors by walking package public exports reachable from Cordis Context and Events augmentations plus explicit @typert declarations, and emits artifacts.
What the generator emits
FaceModelEmitter (src/emitter.ts) consumes only the model (no AST, no checker). It emits executable JavaScript containing supported Zod schemas and a TYPERT contribution, plus a declaration file whose schemas are typed as z.ZodType<SourceType> through the package's public export. Unsupported Zod projections fail instead of weakening the source type.
Artifact landing spots (opt-in publication):
| Face | Artifact | package.json#exports entry |
|---|---|---|
| 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) |
Generated declarations expose TYPERT as unknown, so contributing business packages do not depend on the runtime registry. Business packages without the corresponding public entry simply do not need Typert artifacts.
How dsh invokes it: the tsdown plugin
The root tsdown.config.ts wires Typert into the ordinary build via typertPlugin (packages/typert/generator/src/tsdown-plugin.ts):
// tsdown.config.ts (abridged)
import { typertPlugin } from './packages/typert/generator/lib/types/tsdown-plugin.js'
// host pass:
plugins: [typertPlugin({ mode: 'workspace', faces: ['host'] })]The root scripts that drive it (package.json):
| Script | Command |
|---|---|
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 then typecheck:contracts-ready |
The plugin does two things on the host pass: (1) before bundling it lowers standard decorators in TypeScript dependencies (via ts.transpileModule targeting ES2024/ESNext), and (2) on writeBundle it runs workspace Typert generation seeded from tsconfig.host.json as the only program, producing both Host reflection artifacts and the typert.remote-client.* projection of Host Remote contracts for the Client. The subsequent Client tsdown neither starts Typert nor analyzes tsconfig.client.json.
The runtime registry: ctx.typert
packages/typert/registry/src/service.ts provides TypertRegistry, the default plugin behind ctx.typert. It stores generated reflection and optional live Zod schemas, registers/withdraws them atomically with the calling Cordis fiber.
- Keys: package reflection keyed
<package>#<face>; schemas keyed<package>#<name>(they omit the face because host and client run in separate contexts). register(contribution)rejects malformed identities and duplicate keys before committing anything, then returns the exact Cordis effect disposer.- Queries:
get(key),resolve(key),list(filter?),getPackage(packageName, face?),listPackages(...), andtoJSONSchema(key, params?)projecting a live schema withz.toJSONSchema()on demand. - Identity helpers:
typertKey()andtypertPackageKey(). protocolpackage contribution/record contracts live in the@deepseek-ai/dsh-typert-registry/typessubpath. The registry stores reflection but does not merge host/client graphs or resolve TypeScript references — those are analyzer/emitter concerns.
Loader discovery
packages/typert/loader is Node-only. It requires ctx.loader and ctx.typert (it does not provide the registry). During activation it scans existing Loader entries, then follows Cordis internal/plugin lifecycle notifications, resolves each entry package's package.json, imports ./typert when exported, validates the TYPERT manifest, and registers the contribution until the entry or this plugin unmounts. A packages config lists extra artifacts for plugins nested behind another Loader entry. Package resolution and imported manifests are cached for the process lifetime (adding an export requires a restart). It imports only the host face today.
The protocol package: @deepseek-ai/dsh-typert-protocol
packages/typert/protocol/src/types.ts holds the compiler-independent vocabulary every other piece shares — this is the closest thing Typert has to a wire contract with the Host Gateway and Client API:
@Remote/@RemoteScope(key)decorators mark public instance methods for direct invocation on a registered Cordis Service.TypertRemoteService,bindTypertRemote,remoteMethods.InvocationDescriptor— the shared runtime form consumed by registry, Gateway, and Client Remote;AbortSignalas a final parameter opts into cooperative cancellation (the injected signal never becomes a JSON parameter).- Merge-extensible maps:
TypertLookupMap,TypertContextMap,TypertRemoteMap,TypertRemoteScopeMap,TypertRemoteNamespaceMap. - Strict codecs carry generated schemas;
src-jsoncodecs identify the weaker source-launch path.
Typert and the doc catalogs — what is and is not generated
A common confusion: docs/config-catalog.md is generated, but not by Typert. It is produced by scripts/gen-config-catalog.ts (pnpm run gen-config-catalog), which walks package entry points, config types, and static Schemastery schemas with a direct TypeScript AST traversal and a hard error on unknown type names.
Typert does power the Cordis API/catalog projections. scripts/gen-cordis-catalog.ts imports WorkspaceAnalyzer / CordisCatalogProjector / projectCordisCatalog from @deepseek-ai/dsh-typert-generator (see packages/typert/generator/src/cordis-catalog.ts), which re-project the compiler-independent FaceModel into docs/cordis-api/* and the runtime API catalog packages/extensions/tool-cordis/src/api-catalog.ts. So: Typert's analyzer is the shared extraction core; config-catalog's generator is an independent, schema-focused tool.
Key generator exports
From packages/typert/generator/src/index.ts:
| Export | Kind | Purpose |
|---|---|---|
WorkspaceAnalyzer, WorkspaceCaches, TypertAnalysisError | class | source analysis into model |
AnalysisMode, DiscoveredTypertPackage, WorkspaceAnalyzerOptions | type | analyzer options |
FaceModelEmitter, TypertEmitError | class | model → JS + d.ts + schemas |
WorkspaceTypertGenerator | class | contributor discovery + emission driver |
TypeGraphRenderer | class | deterministic text renderer |
CordisCatalogProjector, projectCordisCatalog, collectEvents, collectServices | class/function | repository doc projections |
Known limitations
| Limitation | Where |
|---|---|
| Package export patterns are skipped; contributors need concrete export targets | generator |
| Cross-face namespace re-exports fail | generator |
| Zod emitter supports a deliberate subset of the model (conditional/mapped roots fail until a schema-factory policy) | emitter |
| Registry does not merge host/client graphs or resolve TS references | registry |
| Loader imports only the host face | loader |
| Decorator markers carry method name and invocation mode only; parameter/schema reflection requires the build pipeline | protocol |
Package version table
| Package | name | version |
|---|---|---|
| Protocol | @deepseek-ai/dsh-typert-protocol | |
| Generator | @deepseek-ai/dsh-typert-generator | |
| Registry | @deepseek-ai/dsh-typert-registry | |
| Loader | @deepseek-ai/dsh-typert-loader |
Further reading
- SDK Protocol, SDK Client, SDK Server — the SDK chapter Typert shares; note they are unrelated systems (Typert deals in type/schema reflection, the SDK in JSON-RPC over stdio).
- Repository build wiring:
tsdown.config.tsand thebuild:lib:host|clientscripts in the rootpackage.json. - The Cordis catalog projection:
scripts/gen-cordis-catalog.tsandpackages/typert/generator/src/cordis-catalog.ts. - Config-catalog independence:
scripts/gen-config-catalog.ts(docs/config-catalog.md). - The runtime registry API:
packages/typert/registry/src/service.ts. - The protocol declarations:
packages/typert/protocol/src/types.ts.