Skip to content

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/:

PackageRoleCordis key / consumer
packages/typert/protocol@deepseek-ai/dsh-typert-protocolcompiler-independent declarations: @Remote, scopes, invocation descriptors, codecs, provider contractspure declarations, no service
packages/typert/generator@deepseek-ai/dsh-typert-generatorTypeScript project analyzer + model-driven emittersbuild-time library
packages/typert/registry@deepseek-ai/dsh-typert-registryruntime storage of generated reflection and schemasprovides ctx.typert
packages/typert/loader@deepseek-ai/dsh-typert-loaderdiscovers Loader entries and registers generated host artifactsconsumes 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.

text
 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.Program instances seeded from tsconfig.host.json or tsconfig.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. write inserts checker-derived annotations, rebuilds, and returns a clean check-mode model.
  • tsconfig.base.{host,client}.json faces; direct project references establish compiler-face membership; package.json#exports establishes every cross-package public boundary; source imports/re-exports are the only allowed cross-face edges.
  • Types owned by NPM dependencies (including @types globals) remain external references instead of being expanded.
  • WorkspaceCaches retains 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):

FaceArtifactpackage.json#exports entry
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)

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):

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):

ScriptCommand
buildbuild:libbuild: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 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(...), and toJSONSchema(key, params?) projecting a live schema with z.toJSONSchema() on demand.
  • Identity helpers: typertKey() and typertPackageKey().
  • protocol package contribution/record contracts live in the @deepseek-ai/dsh-typert-registry/types subpath. 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; AbortSignal as 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-json codecs 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:

ExportKindPurpose
WorkspaceAnalyzer, WorkspaceCaches, TypertAnalysisErrorclasssource analysis into model
AnalysisMode, DiscoveredTypertPackage, WorkspaceAnalyzerOptionstypeanalyzer options
FaceModelEmitter, TypertEmitErrorclassmodel → JS + d.ts + schemas
WorkspaceTypertGeneratorclasscontributor discovery + emission driver
TypeGraphRendererclassdeterministic text renderer
CordisCatalogProjector, projectCordisCatalog, collectEvents, collectServicesclass/functionrepository doc projections

Known limitations

LimitationWhere
Package export patterns are skipped; contributors need concrete export targetsgenerator
Cross-face namespace re-exports failgenerator
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 referencesregistry
Loader imports only the host faceloader
Decorator markers carry method name and invocation mode only; parameter/schema reflection requires the build pipelineprotocol

Package version table

Packagenameversion
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.ts and the build:lib:host|client scripts in the root package.json.
  • The Cordis catalog projection: scripts/gen-cordis-catalog.ts and packages/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.