在 objectui#3291 的只读审计中顺带发现(origin/main @ 91757a79f)。无 DOM 泄漏,所以不在 #3291 的改动集里;单独记录。
事实
packages/plugin-detail/src/index.tsx:459 把 PermissionFacetLink 裸注册进 field: 命名空间,没有经过 withFieldCarrier 包装 —— 这是全仓唯一一处这样做的 field 注册。
withFieldCarrier(objectui#3233 引入)的职责是把 SDUI 侧传来的 schema 收敛成 widget 契约里唯一的载体 field:
({ schema, field, ...props }) => <FieldWidget {...props} field={field ?? schema} />
而 PermissionFacetLink.tsx:66-68 读的是 props.field。
后果
- 表单宿主与内联编辑宿主下正常 —— 它们本来就传
field;
- SchemaRenderer(SDUI)路径下会静默失效 —— 该路径传的是
schema,于是 widget 拿到 field === undefined,field?.name 为空。
这正是 withFieldCarrier 存在的理由所要防的那种静默失败:不报错、不警告,只是渲染出一个内容为空的东西。
修法
一行:注册时套上 withFieldCarrier,与 packages/fields/src/index.tsx:2286 的注册循环一致。
顺带值得考虑的加固:让 field 命名空间的注册无法绕过这道封口(例如只暴露一个已内置包装的 registerField(),而不是让调用方自己决定包不包),否则下一个第三方 widget 作者会以完全相同的方式再踩一次 —— 这是「约定 vs 门禁」的老问题。
参考
Generated by Claude Code
在 objectui#3291 的只读审计中顺带发现(
origin/main@91757a79f)。无 DOM 泄漏,所以不在 #3291 的改动集里;单独记录。事实
packages/plugin-detail/src/index.tsx:459把PermissionFacetLink裸注册进field:命名空间,没有经过withFieldCarrier包装 —— 这是全仓唯一一处这样做的 field 注册。withFieldCarrier(objectui#3233 引入)的职责是把 SDUI 侧传来的schema收敛成 widget 契约里唯一的载体field:而
PermissionFacetLink.tsx:66-68读的是props.field。后果
field;schema,于是 widget 拿到field === undefined,field?.name为空。这正是
withFieldCarrier存在的理由所要防的那种静默失败:不报错、不警告,只是渲染出一个内容为空的东西。修法
一行:注册时套上
withFieldCarrier,与packages/fields/src/index.tsx:2286的注册循环一致。顺带值得考虑的加固:让 field 命名空间的注册无法绕过这道封口(例如只暴露一个已内置包装的
registerField(),而不是让调用方自己决定包不包),否则下一个第三方 widget 作者会以完全相同的方式再踩一次 —— 这是「约定 vs 门禁」的老问题。参考
field, retiringschema(#3233) #3296(单载体收敛与withFieldCarrier)Generated by Claude Code