@deepseek-ai/dsh-typert-generator 0.1.5-rc.2 → 0.1.6-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +6 -4
- package/README.zh.md +11 -9
- package/lib/index.js +17 -13
- package/lib/types/analyzer.js +5 -4
- package/lib/types/cordis-catalog.d.ts +2 -0
- package/lib/types/cordis-catalog.js +7 -5
- package/lib/types/emitter.js +7 -6
- package/package.json +3 -3
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/typert/generator/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 399e4fe622a785d68452ffeae79e106246941fa1
|
|
6
|
+
README.zh.md: 1fd19fd76ab804013a8741cb90522e9fd3f608d3
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
`dsh-typert-generator` lets maintainers turn public TypeScript types into build artifacts and compiler-independent models. Packages opt in through the `./typert` and optional `./client/typert` exports, and generation rejects declarations, publish lists, Remote exports, or Zod projections that it cannot represent correctly. Repository builds
|
|
12
|
+
`dsh-typert-generator` lets maintainers turn public TypeScript types into build artifacts and compiler-independent models. Packages opt in through the `./typert` and optional `./client/typert` exports, and generation rejects declarations, publish lists, Remote exports, or Zod projections that it cannot represent correctly. Repository builds emit executable schema factories and matching declarations, while tools can call `WorkspaceAnalyzer` for inspection or catalog generation without publishing artifacts. Generation runs only at build time and never in a live agent session.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -79,14 +79,16 @@ The generator is built on one separation: extraction and emission are decoupled
|
|
|
79
79
|
|
|
80
80
|
### Analysis and faces
|
|
81
81
|
|
|
82
|
-
Host and
|
|
82
|
+
Host and Client are independent TypeScript programs. Direct project references establish compiler-face membership, while `dsh.client` package subpaths establish runtime-face contribution; `package.json#exports` marks every cross-package public boundary, and imports or re-exports are the only cross-face edges. A relative import that resolves inside the referencing package is followed through that module's re-exports until a package specifier appears, so package-local forwarding modules keep their original declaration references; a relative import that resolves into another package fails. `check` mode fails on syntax or semantic diagnostics, missing public annotations, private cross-package references, and reachable declaration merges the model cannot retain losslessly; `write` mode inserts checker-derived annotations and returns a clean check-mode model. Types owned by NPM dependencies remain `external` references instead of being expanded.
|
|
83
83
|
|
|
84
84
|
### Emission and publication contract
|
|
85
85
|
|
|
86
|
-
`FaceModelEmitter` emits executable JavaScript containing
|
|
86
|
+
`FaceModelEmitter` emits executable JavaScript containing success-cached Zod schema factories and the `TYPERT` contribution, plus a declaration file whose factories return `z.ZodType<SourceType>` through the package's public export; unsupported Zod projections fail. The Host face with Remote methods additionally emits `typert.remote-client.*` projections of Host Remote contracts for the Client. `WorkspaceTypertGenerator` validates each contributor's `package.json`: `./typert` and `./client/typert` (and `./remote` when Remote methods exist) must point at the exact generated files, and the `files` list must include them.
|
|
87
87
|
|
|
88
88
|
### Catalog projection
|
|
89
89
|
|
|
90
|
+
The runtime type closure indexes exported workspace declarations and referenced framework enums. Vendored declarations stay outside business API discovery; their enums remain available when a public result refers to them.
|
|
91
|
+
|
|
90
92
|
The root export includes the model-driven extraction, completeness checks, and deterministic text renderers used by this repository's Cordis catalogs. They accept a `CordisCatalogPolicy`; repository-owned type links, foundation and exemption classifications, and inherited Cordis entries stay in `scripts/gen-cordis-catalog.ts` and are passed in explicitly, so this package contains projection mechanics, not a hidden copy of the repository's documentation taxonomy.
|
|
91
93
|
|
|
92
94
|
</details>
|
|
@@ -138,4 +140,4 @@ None.
|
|
|
138
140
|
|
|
139
141
|
</details>
|
|
140
142
|
|
|
141
|
-
**Runtime invariant:** No companion is published.
|
|
143
|
+
**Runtime invariant:** No companion is published. The source-project analyzer and build-time emitter run outside any Cordis runtime; model snapshots, executable artifacts, and consuming-package typechecks enforce the output contract.
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-library"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
`dsh-typert-generator` 让维护者把公开的 TypeScript 类型转换为构建产物和与编译器无关的模型。包通过 `./typert` 和可选的 `./client/typert` 导出选择加入;如果声明、发布清单、Remote 导出或 Zod
|
|
12
|
+
`dsh-typert-generator` 让维护者把公开的 TypeScript 类型转换为构建产物和与编译器无关的模型。包通过 `./typert` 和可选的 `./client/typert` 导出选择加入;如果声明、发布清单、Remote 导出或 Zod 投影无法被正确表示,生成过程就会失败。仓库构建会生成可执行 schema factory 与配套声明,工具也可以调用 `WorkspaceAnalyzer` 完成检查或目录生成而不发布产物。生成过程只在构建时运行,绝不会进入实时 agent(智能体)会话。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -45,11 +45,11 @@ files:
|
|
|
45
45
|
|
|
46
46
|
### 静态分析工作区
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
静态消费方直接针对工作区的 `tsconfig.host.json` 与 `tsconfig.client.json` 聚合配置调用 `WorkspaceAnalyzer`,选择 face 与包子集,并在不生成或加载运行时产物的前提下读取生成的 `FaceModel` 与类型图。`analyzeInBatches()` 通过有界的编译器程序处理大批量包选择,模型形态保持一致;`discoverPackages()` 无需构建类型检查程序即可找出参与贡献的包。
|
|
49
49
|
|
|
50
50
|
### 在 tsdown 构建中运行生成
|
|
51
51
|
|
|
52
|
-
包的 `./tsdown` 子路径为根 tsdown 配置提供 `typertPlugin()
|
|
52
|
+
包的 `./tsdown` 子路径为根 tsdown 配置提供 `typertPlugin()`:它在打包前转换 TypeScript 依赖中的标准装饰器,并在包输出根目录生成模型驱动的 face 产物。`package` 模式只生成当前打包的包;`workspace` 模式对每个显式贡献方各生成一次。
|
|
53
53
|
|
|
54
54
|
-----
|
|
55
55
|
|
|
@@ -63,7 +63,7 @@ files:
|
|
|
63
63
|
|
|
64
64
|
### 设计理念
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
生成器遵循一项核心分离原则:提取与生成通过与编译器无关的模型解耦。`WorkspaceAnalyzer` 读取以 face aggregate tsconfig 为种子的 TypeScript 程序,产出 `FaceModel` 与 `TypeGraph` 数据;`FaceModelEmitter` 只消费该模型,绝不接收编译器节点。模型保留声明标识、泛型参数及应用、显式继承、条件类型与映射类型、导入属性、abstract 修饰符与源码 JSDoc,并排除构造函数、静态成员与非公共成员。
|
|
67
67
|
|
|
68
68
|
### 源码地图
|
|
69
69
|
|
|
@@ -74,7 +74,7 @@ files:
|
|
|
74
74
|
| [`src/model.ts`](src/model.ts) | 与编译器无关的模型类型 |
|
|
75
75
|
| [`src/emitter.ts`](src/emitter.ts) | `FaceModelEmitter`:Zod schema 与声明生成、Remote 声明 |
|
|
76
76
|
| [`src/workspace.ts`](src/workspace.ts) | `WorkspaceTypertGenerator`:发现、生成、导出与文件清单校验 |
|
|
77
|
-
| [`src/tsdown-plugin.ts`](src/tsdown-plugin.ts) | tsdown
|
|
77
|
+
| [`src/tsdown-plugin.ts`](src/tsdown-plugin.ts) | tsdown 插件面:装饰器转换与产物生成 |
|
|
78
78
|
| [`src/cordis-catalog.ts`](src/cordis-catalog.ts) | 生成 Cordis 目录所用的目录投影 |
|
|
79
79
|
|
|
80
80
|
### 分析与 face
|
|
@@ -83,10 +83,12 @@ Host 与 Client 是两个独立的 TypeScript 程序。直接项目引用确定
|
|
|
83
83
|
|
|
84
84
|
### 生成与发布约定
|
|
85
85
|
|
|
86
|
-
`FaceModelEmitter`
|
|
86
|
+
`FaceModelEmitter` 输出包含只缓存成功结果的 Zod schema factory 与 `TYPERT` 贡献的可执行 JavaScript,以及把 factory 通过包的公开导出标注为返回 `z.ZodType<SourceType>` 的声明文件;不支持的 Zod 投影会失败。含 Remote 方法的 Host face 还会额外为 Client 生成 Host Remote 约定的 `typert.remote-client.*` 投影。`WorkspaceTypertGenerator` 校验每个贡献方的 `package.json`:`./typert` 与 `./client/typert`(存在 Remote 方法时还有 `./remote`)必须指向精确的生成文件,且 `files` 清单必须包含它们。
|
|
87
87
|
|
|
88
88
|
### 目录投影
|
|
89
89
|
|
|
90
|
+
运行时类型闭包索引导出的 workspace 声明和被引用的框架 enum。Vendor 声明不参与业务 API 发现;公共结果引用其中 enum 时,目录仍提供其定义。
|
|
91
|
+
|
|
90
92
|
根导出包含本仓库 Cordis 目录使用的模型驱动提取逻辑、完整性检查与确定性文本渲染器。它们接受 `CordisCatalogPolicy`;由仓库持有的类型链接、基础类型/豁免分类与继承的 Cordis 条目仍位于 `scripts/gen-cordis-catalog.ts`,由调用方显式传入,因此本包只包含投影机制,不会隐式复制仓库的文档分类体系。
|
|
91
93
|
|
|
92
94
|
</details>
|
|
@@ -101,8 +103,8 @@ Host 与 Client 是两个独立的 TypeScript 程序。直接项目引用确定
|
|
|
101
103
|
- [Typert 子系统参考](../../../docs/subsystems/typert.zh.md)——生成器建模的 Remote 约定与注册表接口。
|
|
102
104
|
- [Typert 协议](../protocol/README.zh.md)——生成产物所扩展并消费的声明。
|
|
103
105
|
- [Typert 注册表](../registry/README.zh.md)——生成产物所供给的运行时存储。
|
|
104
|
-
- [API
|
|
105
|
-
- [
|
|
106
|
+
- [API 网关参考](../../../docs/api-gateway.zh.md)——生成的 Remote 描述符如何端到端被调用。
|
|
107
|
+
- [与编译器无关的模型 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-27-compiler-independent-typert-model.zh.md)——模型设计、备选方案与后果。
|
|
106
108
|
|
|
107
109
|
-----
|
|
108
110
|
|
|
@@ -138,4 +140,4 @@ Host 与 Client 是两个独立的 TypeScript 程序。直接项目引用确定
|
|
|
138
140
|
|
|
139
141
|
</details>
|
|
140
142
|
|
|
141
|
-
**运行时不变式:**
|
|
143
|
+
**运行时不变式:** 不发布伴生入口。源码项目分析器与构建时 emitter 均不在任何 Cordis 运行时中运行;模型快照、可执行产物与消费方包的类型检查会强制执行其输出约定。
|
package/lib/index.js
CHANGED
|
@@ -238,7 +238,7 @@ var WorkspaceAnalyzer = class WorkspaceAnalyzer {
|
|
|
238
238
|
indexSourceDeclarations() {
|
|
239
239
|
const selected = this.options.packages === void 0 ? void 0 : new Set(this.options.packages);
|
|
240
240
|
const declarations = [];
|
|
241
|
-
for (const registration of this.loadRegistrations()) {
|
|
241
|
+
for (const registration of this.loadRegistrations(true)) {
|
|
242
242
|
if (!this.options.faces.includes(registration.face) || selected !== void 0 && !selected.has(registration.name)) continue;
|
|
243
243
|
for (const file of registration.config.parsed.fileNames) {
|
|
244
244
|
const relativeFile = slash(relative(this.options.root, file));
|
|
@@ -264,8 +264,8 @@ var WorkspaceAnalyzer = class WorkspaceAnalyzer {
|
|
|
264
264
|
}
|
|
265
265
|
return uniqueBy(declarations, (declaration) => `${declaration.face}\0${declaration.location.file}\0${String(declaration.location.line)}\0${declaration.name}`).sort((left, right) => left.face.localeCompare(right.face) || left.location.file.localeCompare(right.location.file) || left.location.line - right.location.line);
|
|
266
266
|
}
|
|
267
|
-
loadRegistrations() {
|
|
268
|
-
const inventoryKey = `${this.options.root}\0${this.options.hostConfig}\0${this.options.clientConfig}`;
|
|
267
|
+
loadRegistrations(includeVendor = false) {
|
|
268
|
+
const inventoryKey = `${this.options.root}\0${this.options.hostConfig}\0${this.options.clientConfig}\0${String(includeVendor)}`;
|
|
269
269
|
const cached = this.caches.registrations.get(inventoryKey);
|
|
270
270
|
if (cached !== void 0) return cached;
|
|
271
271
|
const registrations = [];
|
|
@@ -276,7 +276,7 @@ var WorkspaceAnalyzer = class WorkspaceAnalyzer {
|
|
|
276
276
|
for (const reference of aggregate.parsed.projectReferences ?? []) {
|
|
277
277
|
const configPath = projectConfigPath(reference.path);
|
|
278
278
|
const packageRoot = dirname(configPath);
|
|
279
|
-
if (!isWithin(realPath(packageRoot), join(this.options.root, "packages"))) continue;
|
|
279
|
+
if (!isWithin(realPath(packageRoot), join(this.options.root, "packages")) && !(includeVendor && isWithin(realPath(packageRoot), join(this.options.root, "vendor")))) continue;
|
|
280
280
|
const manifestPath = join(packageRoot, "package.json");
|
|
281
281
|
if (!existsSync(manifestPath)) continue;
|
|
282
282
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
@@ -2821,7 +2821,7 @@ var FaceModelEmitter = class {
|
|
|
2821
2821
|
lines.push(` package: ${quote$1(packageModel.name)},`);
|
|
2822
2822
|
lines.push(` face: ${quote$1(this.face.face)},`);
|
|
2823
2823
|
lines.push(" schemas: [");
|
|
2824
|
-
for (const schema of schemas.exports) lines.push(` { name: ${quote$1(schema.exportName)},
|
|
2824
|
+
for (const schema of schemas.exports) lines.push(` { name: ${quote$1(schema.exportName)}, create: ${schema.exportName} },`);
|
|
2825
2825
|
lines.push(" ],");
|
|
2826
2826
|
lines.push(" invocations: [");
|
|
2827
2827
|
for (const invocation of packageModel.invocations) lines.push(`${indent(this.invocationLiteral(invocation, schemas), 4)},`);
|
|
@@ -2842,7 +2842,7 @@ var FaceModelEmitter = class {
|
|
|
2842
2842
|
if (schemas.exports.length > 0) lines.splice(1, 0, "import type { z } from 'zod'");
|
|
2843
2843
|
for (const [specifier, names] of [...imports].sort(([left], [right]) => left.localeCompare(right))) lines.push(`import type { ${names.sort().join(", ")} } from ${quote$1(specifier)}`);
|
|
2844
2844
|
lines.push("");
|
|
2845
|
-
for (const schema of schemas.exports) lines.push(`export declare const ${schema.exportName}: z.ZodType<${schema.exportName}$source>`);
|
|
2845
|
+
for (const schema of schemas.exports) lines.push(`export declare const ${schema.exportName}: () => z.ZodType<${schema.exportName}$source>`);
|
|
2846
2846
|
if (schemas.exports.length > 0) lines.push("");
|
|
2847
2847
|
lines.push("export declare const TYPERT: unknown");
|
|
2848
2848
|
return `${lines.join("\n")}\n`;
|
|
@@ -3056,7 +3056,10 @@ var SchemaEmitter = class {
|
|
|
3056
3056
|
}
|
|
3057
3057
|
emit() {
|
|
3058
3058
|
const definitions = this.declarations.map((declaration) => this.declarationDefinition(declaration));
|
|
3059
|
-
for (const boundary of this.boundaries)
|
|
3059
|
+
for (const boundary of this.boundaries) {
|
|
3060
|
+
const name = this.boundaryName(boundary.key);
|
|
3061
|
+
definitions.push(`let ${name}$value\nconst ${name} = () => (${name}$value ??= ${this.typeSchema(boundary.type)})`);
|
|
3062
|
+
}
|
|
3060
3063
|
return {
|
|
3061
3064
|
definitions,
|
|
3062
3065
|
exports: this.schemas.map((model) => ({
|
|
@@ -3069,7 +3072,7 @@ var SchemaEmitter = class {
|
|
|
3069
3072
|
}
|
|
3070
3073
|
declarationDefinition(declaration) {
|
|
3071
3074
|
const name = this.schemaName(declaration.id);
|
|
3072
|
-
if (declaration.typeParameters.length === 0) return `
|
|
3075
|
+
if (declaration.typeParameters.length === 0) return `let ${name}$value\nconst ${name} = () => (${name}$value ??= ${this.declarationSchema(declaration, /* @__PURE__ */ new Map())})`;
|
|
3073
3076
|
const parameters = declaration.typeParameters.map((parameter, index) => [`type${String(index)}$schema`, parameter.id]);
|
|
3074
3077
|
const substitutions = new Map(parameters.map(([schema, id]) => [id, schema]));
|
|
3075
3078
|
return `const ${name} = (${parameters.map(([schema]) => schema).join(", ")}) => ${this.declarationSchema(declaration, substitutions)}`;
|
|
@@ -3129,7 +3132,7 @@ var SchemaEmitter = class {
|
|
|
3129
3132
|
const declaration = this.renderer.declaration(node.target.symbol);
|
|
3130
3133
|
if (declaration.typeParameters.length === 0) {
|
|
3131
3134
|
if (node.arguments.length > 0) this.fail(node.name, `non-generic declaration received ${String(node.arguments.length)} type arguments`);
|
|
3132
|
-
return `z.lazy(() => ${name})`;
|
|
3135
|
+
return `z.lazy(() => ${name}())`;
|
|
3133
3136
|
}
|
|
3134
3137
|
return `z.lazy(() => ${name}(${this.declarationArguments(node, declaration, substitutions).join(", ")}))`;
|
|
3135
3138
|
}
|
|
@@ -3297,7 +3300,7 @@ function strictCodec(boundary, schema) {
|
|
|
3297
3300
|
"{",
|
|
3298
3301
|
" mode: 'strict',",
|
|
3299
3302
|
` typeSymbol: ${quote$1(boundary.typeSymbol)},`,
|
|
3300
|
-
`
|
|
3303
|
+
` create: ${schema},`,
|
|
3301
3304
|
"}"
|
|
3302
3305
|
].join("\n");
|
|
3303
3306
|
}
|
|
@@ -3529,13 +3532,14 @@ var CordisCatalogProjector = class {
|
|
|
3529
3532
|
runtimeTypes(services, events) {
|
|
3530
3533
|
const declarations = /* @__PURE__ */ new Map();
|
|
3531
3534
|
const ambiguous = /* @__PURE__ */ new Set();
|
|
3535
|
+
const maxDeclarationChars = this.policy.runtimeDeclarationMaxChars ?? DEFAULT_MAX_DECL_CHARS;
|
|
3532
3536
|
for (const declaration of this.sourceDeclarations) {
|
|
3533
|
-
if (declaration.face !== this.face.face || declaration.kind === "enum"
|
|
3537
|
+
if (declaration.face !== this.face.face || !/^packages\/[^/]+\/[^/]+\/src\/.+\.tsx?$/.test(declaration.location.file) && !(declaration.kind === "enum" && /^vendor\/[^/]+\/src\/.+\.ts$/.test(declaration.location.file))) continue;
|
|
3534
3538
|
if (declarations.has(declaration.name)) {
|
|
3535
3539
|
ambiguous.add(declaration.name);
|
|
3536
3540
|
continue;
|
|
3537
3541
|
}
|
|
3538
|
-
declarations.set(declaration.name, declaration.text.length >
|
|
3542
|
+
declarations.set(declaration.name, declaration.text.length > maxDeclarationChars ? `${declaration.text.slice(0, maxDeclarationChars)} /* …truncated — full shape in source */` : declaration.text);
|
|
3539
3543
|
}
|
|
3540
3544
|
for (const name of ambiguous) declarations.delete(name);
|
|
3541
3545
|
return referencedTypes([...services.flatMap((service) => service.methods.map((method) => method.signature)), ...events.map((event) => event.signature)], declarations);
|
|
@@ -3751,7 +3755,7 @@ function signatureTypeNames(renderer, signature) {
|
|
|
3751
3755
|
return [...names].sort();
|
|
3752
3756
|
}
|
|
3753
3757
|
/** Declarations longer than this render as a truncated stub. */
|
|
3754
|
-
const
|
|
3758
|
+
const DEFAULT_MAX_DECL_CHARS = 1500;
|
|
3755
3759
|
/** Render one value as a single-quoted TypeScript literal. */
|
|
3756
3760
|
function quote(value) {
|
|
3757
3761
|
return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'").replaceAll("\n", "\\n")}'`;
|
package/lib/types/analyzer.js
CHANGED
|
@@ -254,7 +254,7 @@ export class WorkspaceAnalyzer {
|
|
|
254
254
|
indexSourceDeclarations() {
|
|
255
255
|
const selected = this.options.packages === undefined ? undefined : new Set(this.options.packages);
|
|
256
256
|
const declarations = [];
|
|
257
|
-
for (const registration of this.loadRegistrations()) {
|
|
257
|
+
for (const registration of this.loadRegistrations(true)) {
|
|
258
258
|
if (!this.options.faces.includes(registration.face)
|
|
259
259
|
|| (selected !== undefined && !selected.has(registration.name)))
|
|
260
260
|
continue;
|
|
@@ -297,8 +297,8 @@ export class WorkspaceAnalyzer {
|
|
|
297
297
|
|| left.location.file.localeCompare(right.location.file)
|
|
298
298
|
|| left.location.line - right.location.line);
|
|
299
299
|
}
|
|
300
|
-
loadRegistrations() {
|
|
301
|
-
const inventoryKey = `${this.options.root}\0${this.options.hostConfig}\0${this.options.clientConfig}`;
|
|
300
|
+
loadRegistrations(includeVendor = false) {
|
|
301
|
+
const inventoryKey = `${this.options.root}\0${this.options.hostConfig}\0${this.options.clientConfig}\0${String(includeVendor)}`;
|
|
302
302
|
const cached = this.caches.registrations.get(inventoryKey);
|
|
303
303
|
if (cached !== undefined)
|
|
304
304
|
return cached;
|
|
@@ -311,7 +311,8 @@ export class WorkspaceAnalyzer {
|
|
|
311
311
|
for (const reference of aggregate.parsed.projectReferences ?? []) {
|
|
312
312
|
const configPath = projectConfigPath(reference.path);
|
|
313
313
|
const packageRoot = dirname(configPath);
|
|
314
|
-
if (!isWithin(realPath(packageRoot), join(this.options.root, 'packages'))
|
|
314
|
+
if (!isWithin(realPath(packageRoot), join(this.options.root, 'packages'))
|
|
315
|
+
&& !(includeVendor && isWithin(realPath(packageRoot), join(this.options.root, 'vendor'))))
|
|
315
316
|
continue;
|
|
316
317
|
const manifestPath = join(packageRoot, 'package.json');
|
|
317
318
|
if (!existsSync(manifestPath))
|
|
@@ -72,6 +72,8 @@ export interface CordisCatalogPolicy {
|
|
|
72
72
|
readonly inheritedEvents: readonly InheritedEntry[];
|
|
73
73
|
/** Manually curated framework context members inherited by every plugin. */
|
|
74
74
|
readonly inheritedServices: readonly InheritedEntry[];
|
|
75
|
+
/** Maximum rendered characters per runtime type declaration. */
|
|
76
|
+
readonly runtimeDeclarationMaxChars?: number;
|
|
75
77
|
}
|
|
76
78
|
/** Complete model-level Cordis projection used by every text renderer. */
|
|
77
79
|
export interface CordisCatalogModel {
|
|
@@ -218,16 +218,18 @@ export class CordisCatalogProjector {
|
|
|
218
218
|
runtimeTypes(services, events) {
|
|
219
219
|
const declarations = new Map();
|
|
220
220
|
const ambiguous = new Set();
|
|
221
|
+
const maxDeclarationChars = this.policy.runtimeDeclarationMaxChars ?? DEFAULT_MAX_DECL_CHARS;
|
|
221
222
|
for (const declaration of this.sourceDeclarations) {
|
|
222
|
-
if (declaration.face !== this.face.face
|
|
223
|
-
|| !/^packages\/[^/]+\/[^/]+\/src\/.+\.tsx?$/.test(declaration.location.file)
|
|
223
|
+
if (declaration.face !== this.face.face
|
|
224
|
+
|| (!/^packages\/[^/]+\/[^/]+\/src\/.+\.tsx?$/.test(declaration.location.file)
|
|
225
|
+
&& !(declaration.kind === 'enum' && /^vendor\/[^/]+\/src\/.+\.ts$/.test(declaration.location.file))))
|
|
224
226
|
continue;
|
|
225
227
|
if (declarations.has(declaration.name)) {
|
|
226
228
|
ambiguous.add(declaration.name);
|
|
227
229
|
continue;
|
|
228
230
|
}
|
|
229
|
-
declarations.set(declaration.name, declaration.text.length >
|
|
230
|
-
? `${declaration.text.slice(0,
|
|
231
|
+
declarations.set(declaration.name, declaration.text.length > maxDeclarationChars
|
|
232
|
+
? `${declaration.text.slice(0, maxDeclarationChars)} /* …truncated — full shape in source */`
|
|
231
233
|
: declaration.text);
|
|
232
234
|
}
|
|
233
235
|
for (const name of ambiguous)
|
|
@@ -487,7 +489,7 @@ function signatureTypeNames(renderer, signature) {
|
|
|
487
489
|
return [...names].sort();
|
|
488
490
|
}
|
|
489
491
|
/** Declarations longer than this render as a truncated stub. */
|
|
490
|
-
const
|
|
492
|
+
const DEFAULT_MAX_DECL_CHARS = 1_500;
|
|
491
493
|
/** Render one value as a single-quoted TypeScript literal. */
|
|
492
494
|
function quote(value) {
|
|
493
495
|
return `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'").replaceAll('\n', '\\n')}'`;
|
package/lib/types/emitter.js
CHANGED
|
@@ -121,7 +121,7 @@ export class FaceModelEmitter {
|
|
|
121
121
|
lines.push(` face: ${quote(this.face.face)},`);
|
|
122
122
|
lines.push(' schemas: [');
|
|
123
123
|
for (const schema of schemas.exports) {
|
|
124
|
-
lines.push(` { name: ${quote(schema.exportName)},
|
|
124
|
+
lines.push(` { name: ${quote(schema.exportName)}, create: ${schema.exportName} },`);
|
|
125
125
|
}
|
|
126
126
|
lines.push(' ],');
|
|
127
127
|
lines.push(' invocations: [');
|
|
@@ -151,7 +151,7 @@ export class FaceModelEmitter {
|
|
|
151
151
|
}
|
|
152
152
|
lines.push('');
|
|
153
153
|
for (const schema of schemas.exports) {
|
|
154
|
-
lines.push(`export declare const ${schema.exportName}: z.ZodType<${schema.exportName}$source>`);
|
|
154
|
+
lines.push(`export declare const ${schema.exportName}: () => z.ZodType<${schema.exportName}$source>`);
|
|
155
155
|
}
|
|
156
156
|
if (schemas.exports.length > 0)
|
|
157
157
|
lines.push('');
|
|
@@ -410,7 +410,8 @@ class SchemaEmitter {
|
|
|
410
410
|
emit() {
|
|
411
411
|
const definitions = this.declarations.map(declaration => this.declarationDefinition(declaration));
|
|
412
412
|
for (const boundary of this.boundaries) {
|
|
413
|
-
|
|
413
|
+
const name = this.boundaryName(boundary.key);
|
|
414
|
+
definitions.push(`let ${name}$value\nconst ${name} = () => (${name}$value ??= ${this.typeSchema(boundary.type)})`);
|
|
414
415
|
}
|
|
415
416
|
const exports = this.schemas.map((model) => ({
|
|
416
417
|
model,
|
|
@@ -426,7 +427,7 @@ class SchemaEmitter {
|
|
|
426
427
|
declarationDefinition(declaration) {
|
|
427
428
|
const name = this.schemaName(declaration.id);
|
|
428
429
|
if (declaration.typeParameters.length === 0) {
|
|
429
|
-
return `
|
|
430
|
+
return `let ${name}$value\nconst ${name} = () => (${name}$value ??= ${this.declarationSchema(declaration, new Map())})`;
|
|
430
431
|
}
|
|
431
432
|
const parameters = declaration.typeParameters.map((parameter, index) => [`type${String(index)}$schema`, parameter.id]);
|
|
432
433
|
const substitutions = new Map(parameters.map(([schema, id]) => [id, schema]));
|
|
@@ -500,7 +501,7 @@ class SchemaEmitter {
|
|
|
500
501
|
if (node.arguments.length > 0) {
|
|
501
502
|
this.fail(node.name, `non-generic declaration received ${String(node.arguments.length)} type arguments`);
|
|
502
503
|
}
|
|
503
|
-
return `z.lazy(() => ${name})`;
|
|
504
|
+
return `z.lazy(() => ${name}())`;
|
|
504
505
|
}
|
|
505
506
|
const arguments_ = this.declarationArguments(node, declaration, substitutions);
|
|
506
507
|
return `z.lazy(() => ${name}(${arguments_.join(', ')}))`;
|
|
@@ -694,7 +695,7 @@ function strictCodec(boundary, schema) {
|
|
|
694
695
|
'{',
|
|
695
696
|
' mode: \'strict\',',
|
|
696
697
|
` typeSymbol: ${quote(boundary.typeSymbol)},`,
|
|
697
|
-
`
|
|
698
|
+
` create: ${schema},`,
|
|
698
699
|
'}',
|
|
699
700
|
].join('\n');
|
|
700
701
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-typert-generator",
|
|
3
3
|
"description": "TypeScript project analyzer and model-driven Typert artifact generator",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.6-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"zod": "^4.4.3",
|
|
43
|
-
"@deepseek-ai/dsh-tool-cordis": "^0.1.
|
|
44
|
-
"@deepseek-ai/dsh-typert-registry": "^0.1.
|
|
43
|
+
"@deepseek-ai/dsh-tool-cordis": "^0.1.6-alpha.2",
|
|
44
|
+
"@deepseek-ai/dsh-typert-registry": "^0.1.6-alpha.2",
|
|
45
45
|
"@deepseek-ai/cordis": "^4.0.2"
|
|
46
46
|
}
|
|
47
47
|
}
|