从 #5000 的核实工作里撞到的(写 CLI 侧 pin 测试时)。观察类 :今天没有用户能碰到的故障,但它让一类看起来天经地义的断言/推理不成立,值得记下来。
事实
packages/spec 的 package.json 声明了 16 个 entry(. / ./kernel / ./ui / …),tsup 按 entry 分别打包,同一个 schema 在不同 entry 的产物里是不同的对象 :
// 在已 build 的 workspace 里
const root = require ( '@objectstack/spec' ) ;
const kernel = require ( '@objectstack/spec/kernel' ) ;
const ui = require ( '@objectstack/spec/ui' ) ;
// root 的 ObjectStackDefinitionSchema.pages 的 element
const el = root . ObjectStackDefinitionSchema . _zod . def . shape . pages . _zod . def . innerType . _zod . def . element ;
el === kernel . getMetadataTypeSchema ( 'page' ) // false
ui . PageSchema === kernel . getMetadataTypeSchema ( 'page' ) // false
源码里它们是同一个模块绑定(stack.zod.ts 与 metadata-type-schemas.ts 都 import ui/page.zod.ts 的 PageSchema),只有从 src 直接跑(tsx / vitest 走源码)时 === 才成立 ;跨包消费的是 dist,就是各自一份。
为什么记下来
它把「同一道门」这件事变成不可断言。 objectstack build / validate 从不按 PageSchema 解析页面元数据:ADR-0089 D3a 早就该拒绝的键一路通过,#4001 的「三个示例应用 validate 全过」对 page 面是空证 #5000 问的正是「CLI 走的 schema 是不是写路径那一个」。最自然的证明是实例同一性;跨 entry 它恒为 false,所以只能退回到行为等价 (同一个未声明键、两边同样拒绝)。我在 PR 里就是这么写的,并把原因写进了测试注释 —— 否则下一个人会先写一遍 identity 断言、看着它红、再怀疑是自己的改动。
registerMetadataTypeSchema() 的可见范围被这条边界圈死。 运行时 overlay 存在 kernel bundle 里的 EXTRA_METADATA_TYPE_SCHEMAS。今天没事,因为注册表的读写 API 只从 ./kernel 导出 ,所有消费者拿的是同一份;但这是个巧合而不是保证 —— 哪天某个 entry 再导出一个内部会读注册表的 helper,插件注册的类型对它就是不存在的,而且这种「看得见声明、读不到注册」的失败非常难查。
顺带的:同一份 zod schema 在多个 entry 里各存一份,内存/体积上是重复的(analyze-bundle-size.ts 应该能量到)。
不建议顺手改
改动面(entry 拆分 / chunk 共享 / external 策略)属于 spec 打包契约,不是随手能定的;记在这里让后来者不必重新推一遍即可。
相关
从 #5000 的核实工作里撞到的(写 CLI 侧 pin 测试时)。观察类:今天没有用户能碰到的故障,但它让一类看起来天经地义的断言/推理不成立,值得记下来。
事实
packages/spec的package.json声明了 16 个 entry(././kernel/./ui/ …),tsup 按 entry 分别打包,同一个 schema 在不同 entry 的产物里是不同的对象:源码里它们是同一个模块绑定(
stack.zod.ts与metadata-type-schemas.ts都 importui/page.zod.ts的PageSchema),只有从 src 直接跑(tsx / vitest 走源码)时===才成立;跨包消费的是 dist,就是各自一份。为什么记下来
它把「同一道门」这件事变成不可断言。
objectstack build/validate从不按 PageSchema 解析页面元数据:ADR-0089 D3a 早就该拒绝的键一路通过,#4001 的「三个示例应用 validate 全过」对 page 面是空证 #5000 问的正是「CLI 走的 schema 是不是写路径那一个」。最自然的证明是实例同一性;跨 entry 它恒为 false,所以只能退回到行为等价(同一个未声明键、两边同样拒绝)。我在 PR 里就是这么写的,并把原因写进了测试注释 —— 否则下一个人会先写一遍 identity 断言、看着它红、再怀疑是自己的改动。registerMetadataTypeSchema()的可见范围被这条边界圈死。 运行时 overlay 存在 kernel bundle 里的EXTRA_METADATA_TYPE_SCHEMAS。今天没事,因为注册表的读写 API 只从./kernel导出,所有消费者拿的是同一份;但这是个巧合而不是保证 —— 哪天某个 entry 再导出一个内部会读注册表的 helper,插件注册的类型对它就是不存在的,而且这种「看得见声明、读不到注册」的失败非常难查。顺带的:同一份 zod schema 在多个 entry 里各存一份,内存/体积上是重复的(
analyze-bundle-size.ts应该能量到)。不建议顺手改
改动面(entry 拆分 / chunk 共享 /
external策略)属于 spec 打包契约,不是随手能定的;记在这里让后来者不必重新推一遍即可。相关
objectstack build/validate从不按 PageSchema 解析页面元数据:ADR-0089 D3a 早就该拒绝的键一路通过,#4001 的「三个示例应用 validate 全过」对 page 面是空证 #5000(核实过程中发现;PR 里的packages/cli/test/metadata-type-schema-gate.test.ts注释记了这条约束)packages/spec/package.json的exports、packages/spec/scripts/analyze-bundle-size.ts