现象
packages/spec/scripts/build-docs.ts 生成每个 reference 页的 TypeScript Usage 段时,import type { X } 行是从 schema const 名机械剥掉 Schema 后缀推导的:
// scripts/build-docs.ts:461
const typeNames = schemas.map(s => s.name.replace(/Schema$/, '')).join(', ');
// :466
md += `import type { ${typeNames} } from '@objectstack/spec/${category}';\n\n`;
它假设每个 XSchema 都存在且导出了同名推断类型 X。这个假设没有任何 gate 验证:一旦某个 schema 没有配套的 export type X(或别名被删/改名),生成出的文档会引用一个不存在的导出 —— check:docs 依然全绿,因为它只对比「生成器现在的输出」和「已提交的文档」,不验证 import 行能否编译。
触发实例
#4539 处理 shared TransformType 双源行时,首选方案本来是只删除零消费者的类型别名、保留 TransformTypeSchema 不动,结果发现这样会让生成文档持续宣传 import type { TransformType } from '@objectstack/spec/shared'(该导出已不存在,且与 ./data 的同名 enum 混淆)。最终被迫改成整对重命名(FieldMappingTransformSchema / FieldMappingTransform)来绕开生成器的这个假设 —— 结果是对的,但决策被生成器缺陷绑架了。
建议
生成 import 示例时对照真实导出面(api-surface.json 已按 entry 记录了每个名字,或直接解析入口 d.ts),只列真实存在的名字;或者加一个校验步骤:剥后缀推导出的每个类型名必须出现在对应 entry 的导出里,否则 check:docs 变红。机器可读表面不能撒谎(AGENTS.md「Route & surface ownership」#4)—— 文档里的 import 示例正是 AI 编写元数据应用时最常被复制的行。
关联:#4539(触发场景)、#2978(schema 静默下架删文档的老问题,同一层生成器)。
现象
packages/spec/scripts/build-docs.ts生成每个 reference 页的 TypeScript Usage 段时,import type { X }行是从 schema const 名机械剥掉Schema后缀推导的:它假设每个
XSchema都存在且导出了同名推断类型X。这个假设没有任何 gate 验证:一旦某个 schema 没有配套的export type X(或别名被删/改名),生成出的文档会引用一个不存在的导出 ——check:docs依然全绿,因为它只对比「生成器现在的输出」和「已提交的文档」,不验证 import 行能否编译。触发实例
#4539 处理 shared
TransformType双源行时,首选方案本来是只删除零消费者的类型别名、保留TransformTypeSchema不动,结果发现这样会让生成文档持续宣传import type { TransformType } from '@objectstack/spec/shared'(该导出已不存在,且与./data的同名 enum 混淆)。最终被迫改成整对重命名(FieldMappingTransformSchema/FieldMappingTransform)来绕开生成器的这个假设 —— 结果是对的,但决策被生成器缺陷绑架了。建议
生成 import 示例时对照真实导出面(api-surface.json 已按 entry 记录了每个名字,或直接解析入口 d.ts),只列真实存在的名字;或者加一个校验步骤:剥后缀推导出的每个类型名必须出现在对应 entry 的导出里,否则
check:docs变红。机器可读表面不能撒谎(AGENTS.md「Route & surface ownership」#4)—— 文档里的 import 示例正是 AI 编写元数据应用时最常被复制的行。关联:#4539(触发场景)、#2978(schema 静默下架删文档的老问题,同一层生成器)。