Skip to content

fix(fields): close the widget DOM prop leak with a whitelist toDomProps - #3313

Merged
xuyushun441-sys merged 2 commits into
mainfrom
claude/issue-3291-todomprops
Aug 3, 2026
Merged

fix(fields): close the widget DOM prop leak with a whitelist toDomProps#3313
xuyushun441-sys merged 2 commits into
mainfrom
claude/issue-3291-todomprops

Conversation

@xuyushun441-sys

@xuyushun441-sys xuyushun441-sys commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #3291

范围以 #3291 上「审计结果」那条评论为准(正文已过时):14 个 widget + 2 个新文件,form.tsx 未改动,SelectFieldaria-invalid 缺失留给 #3306

病根:不是某个具名 prop,是「作者键的开放尾巴」

widget 用裸展开把剩余 props 转发给控件,宿主给什么就落什么到 DOM 上(下方标签的 < 后统一加了空格,否则会被 GitHub 正文净化器当成 HTML 标签吃掉):

< input placeholder="PH-f" zzcanary="CANARY-STR" zzcanaryobj="[object Object]"
        zzcanarynum="42" id="…" type="text" value="" name="f" >

zzcanaryobj="[object Object]" —— 本单命名由来的那个 [object Object],换了个键仍然完整复现。它是作者写在字段配置上的普通额外键被 String() 到属性上。React 19 对全小写未知属性完全静默,所以它安静地活了很久。

因此必须是白名单

黑名单(枚举 error / emptyHint / dataSource / dependentValues / dependsOn / options / inputType / …)会放行上面每一个 canary,也挡不住 SDUI 路径上的 label。两条路径的形状决定了这一点:

  • 表单渲染器只解构固定一组已知键,其余原样转发;
  • SchemaRenderer 把整个作者节点摊成 props,没有任何 strip 层 —— 该路径上 widget 自己的展开是唯一防线。

改动

  • 新增 toDomProps(props),从 @object-ui/fields 导出(理由同已导出的 withFieldCarrier:仓外 widget 作者要够得着,否则会重新长出裸展开)。
  • 14 个 widget 走它:text / textarea / number / boolean / date / datetime / time / email / phone / url / password / currency / percent / select
  • 11 个 widget 开头那行 const { inputType, ...domProps } = props as any;(注释写着 "Filter out non-DOM props")连同注释一起删除 —— inputType 早在宿主侧就被 strip 掉了,这行什么也没过滤,注释是主动误导

SelectField 两处展开只收口了一处:Select.Root 那处收口;< MultiSelectField {...props} / >widget 到 widget 的委托,不是 DOM 展开,narrow 掉会把它变成空组件 —— 代码里写明了这个边界。

白名单与 types.ts 声明的差异(需要 review 的一点)

toDomPropsFieldWidgetComponentProps「DOM pass-through」段落的运行时执行体。它转发该段落声明的 7 个键 + AriaAttributes + data-*外加 2 个

为什么加
className 已在契约上声明(在 controlled-input 段而非 DOM 段,因为 widget 还会组合它)。它就是元素上的 class。不转发会让 toDomProps 变成一个静默丢样式的助手 —— 而 < Input {...toDomProps(props)} / > 正是本改动教给未来每个 widget 作者的写法。
disabled 同上(与 readonly OR 后使用)。不转发会静默丢交互态。

即:新增一个静默失败模式,来换取白名单的字面纯粹,方向反了,所以两个都转发。若维护者认为应严格只留 7 个,我可以改为在 9 个 widget 里逐个显式传 className

反向的一个故意后果:契约没声明的 HTML 全局属性(如 role)不再转发。它此前只是搭着开放展开到达 DOM 的。若字段节点应当能写 role,正确做法是先在 FieldWidgetComponentProps 上声明再加进白名单 —— 修契约,不是放宽消费端(AGENTS.md #0.1)。

声明 = 强制(两个方向都由编译器绑住)

契约里「DOM pass-through」那一段已抽成具名类型 FieldWidgetDomProps,与 FieldWidgetComponentProps 交叉(对所有消费方是结构性 no-op)。toDomProps.ts两条编译期断言:

方向 断言 抓什么
DomPassThroughKey extends keyof FieldWidgetComponentProps 助手转发了契约已不再声明的键
keyof FieldWidgetDomProps extends DomPassThroughKey 声明了但没交付 —— 契约新增 DOM 键、助手没跟上

反方向正是 PM 复核点名的缺口,也是本仓视为一等缺陷的失败形态(#3290aria-required 声明了却没送达真实控件、#3222 的校验槽没人生产)。契约测试结构上够不着它 —— 它查的是「属性到达了」,不是「属性没到」。

实测反向断言承重:往 FieldWidgetDomProps 加一个 role?: string 而不加进 DOM_PASS_THROUGH_KEYS,type-check 立刻红:

src/widgets/toDomProps.ts(126,7): error TS2322: Type 'true' is not assignable to type 'never'.

(126 行正是 _everyDeclaredDomKeyIsForwarded。)还原后 11/11 type-check 通过。

className / disabled 只被正方向绑住 —— 它们是 DOM 合法且确实转发,但因 widget 还会解释它们而声明在 controlled-input 段,故意留在具名类型之外。changeset 里现在如实写明每条断言各保证哪个方向,不再用「so they cannot drift」这种担保不了的措辞。

契约测试

packages/fields/src/__tests__/widget-dom-leak-e2e.test.tsx —— 46 个注册 widget × 5 变体(表单 plain / 表单 author-extras / 表单校验失败后 / SDUI plain / SDUI author-extras),共 233 个断言,8.4s,连跑 4 次零 flake,不需要进 heavyDomTests

审计点名的四个坑都堵了:

  1. 不靠 React 警告 —— 遍历真实 DOM 属性。
  2. 两条路径都渲染 —— 只测表单路径会把 SDUI 报成干净的(见下方破坏验证:label 只在 SDUI 侧现形)。
  3. 错误变体先自证 —— 断言错误信息确实渲染了才扫 DOM。本仓的 required存在性检查(false/0 是值,cloud#972),所以统一用 null 起始值,boolean 也能真触发失败。
  4. happy-dom 判定器 —— 放行 data-*/aria-*/cmdk-*;一份 HTML 全局属性清单;核心是「属性名大小写不敏感地匹配 document.createElement(tag) 原型链上某个 IDL 属性」(自动覆盖 readonly→readOnly / maxlength→maxLength / colspan→colSpan,无需按标签手维护);4 条别名;SVG 走自己的清单(IDL 那招在 happy-dom 下对 SVG 不成立)。

判定器自证 fixture 当场赚回成本:我原以为 happy-dom 只缺 2 个 IDL(select[size]option[label]),干净 fixture 立刻报出另外两个真实缺口 —— textarea[wrap]col[span],都是标准 HTML。例外表现在 5 条,每条注明理由,全部由 fixture 实测得出而非猜测。

覆盖边界如实写在文件里:不驱动 popover 打开(Radix 需要 happy-dom 没有的 pointer-capture)。展开点在行内 trigger 上且总会渲染,所以展开点本身被覆盖了 —— 但只存在于展开后的内容没有。

FORM_FIELD_TYPES 一致性断言让这道门自动跟随注册表:新增字段类型不进测试表 = 测试变红。

破坏验证

Break A —— 把 TextField 换回裸展开const domProps = props as any):4 个用例变红,精确点名 widget、标签、属性、值、outerHTML

  • form/authored-extras → 5 个:zzcanary / zzcanaryobj="[object Object]" / zzcanarynum / zzcanarycamel / reference_to
  • form/validation-errorerror="F is required"
  • sdui/plainlabel="F"
  • sdui/authored-extras → 6 个

注意 form/plain 没红、sdui/plain 红了 —— 这正是审计说「只测表单路径会把 SDUI 的洞报成干净」的实测复现。还原后全绿。

Break B —— 从例外表删掉 option: label:干净 fixture 立刻变红并点名 < option > label="L"。证明判定器的自检是承重的,将来 happy-dom 升级导致 IDL 覆盖变化时会响亮失败,而不是静默失明。

验证

结果
契约测试 233 passed,8.4s,×4 零 flake
packages/fields 全量 45 files / 772 tests passed
全仓 vitest run 871 files / 10300 tests passed,0 failed
全仓 turbo type-check 78/78 successful
全仓 turbo lint 45/45 successful,0 errors
全仓 turbo build 43/43 successful
changeset:check fixed group ✅ / 无 major

changeset 标 minor(objectui 是 fixed group,绝不声明 major),行为变更写在正文:作者字段配置里的未知键不再落到 DOM 属性上。未触碰 content/docs/releases/

清单外发现(未在本 PR 修改)


Generated by Claude Code


Generated by Claude Code

Field widgets forwarded leftover props to their control with a bare spread,
so anything a host handed them became a DOM attribute — including arbitrary
keys an author wrote on a field config:

    <input placeholder="PH-f" zzcanary="CANARY-STR"
           zzcanaryobj="[object Object]" zzcanarynum="42" …>

React 19 is completely silent about an all-lowercase unknown attribute, which
is why this went unnoticed.

Adds `toDomProps(props)` — exported from `@object-ui/fields` — and routes the
14 widgets that spread onto a host element through it: text, textarea, number,
boolean, date, datetime, time, email, phone, url, password, currency, percent
and select.

It is a whitelist, not a list of keys to drop. The largest leak source is the
open tail of author-supplied keys, not any named renderer prop: the form
renderer forwards every key it does not destructure, and SchemaRenderer spreads
the whole authored node with no strip layer at all. A blacklist of today's
renderer-only props would pass every canary above.

The forwarded set is the one `FieldWidgetComponentProps` already declares, and
a compile-time assertion ties the helper to that declaration so the two cannot
drift.

Eleven of these widgets carried `const { inputType, ...domProps } = props as
any` under a "Filter out non-DOM props" comment. `inputType` is stripped by the
form renderer before a widget sees it, so the line filtered nothing and the
comment actively misled; both are gone.

Adds a contract test that renders every registered field widget through both
hosts and fails on any attribute HTML does not define for that element. It
walks real DOM attributes rather than React warnings, asserts a validation
error genuinely rendered before scanning the error variant, and calibrates its
own judge against a clean fixture (zero findings) and a planted one (all
found).

Fixes #3291
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 3, 2026 5:09pm

Request Review

@github-actions github-actions Bot added the tests label Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-COqhY_Us.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.47KB 3.09KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 476.66KB 104.69KB
core (index.js) 2.25KB 0.80KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 136.23KB 34.75KB
fields (index.js) 224.93KB 55.15KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 26.14KB 6.07KB
i18n (useSafeTranslation.js) 3.26KB 1.44KB
layout (index.js) 37.96KB 10.54KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.05KB 1.53KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.98KB 12.37KB
plugin-charts (index.js) 60.54KB 17.13KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 112.01KB 28.86KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 230.56KB 56.80KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 111.54KB 26.97KB
plugin-gantt (index.js) 162.25KB 39.55KB
plugin-grid (index.js) 185.08KB 49.04KB
plugin-kanban (index.js) 47.89KB 13.18KB
plugin-list (index.js) 104.94KB 25.32KB
plugin-map (index.js) 16.81KB 5.24KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.55KB 10.59KB
plugin-timeline (index.js) 25.76KB 7.33KB
plugin-tree (index.js) 8.34KB 2.82KB
plugin-view (index.js) 83.67KB 20.43KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.46KB 1.21KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

…ions

The first assertion only caught one direction — helper forwards a key the
contract no longer declares. The reverse (contract declares a DOM key the
helper never forwards) was completely silent, and the leak test structurally
cannot see it: that test looks for attributes that ARRIVE, not for ones that go
missing.

That silent direction is "declared but not delivered" — the failure class this
repo treats as first-class (objectui#3290's `aria-required` that never reached
a control; objectui#3222's validation slot nobody produced).

Extracts the contract's DOM pass-through block into a named
`FieldWidgetDomProps`, intersected into `FieldWidgetComponentProps` (a
structural no-op for every consumer), and adds the reverse assertion:
`keyof FieldWidgetDomProps extends DomPassThroughKey`.

Verified by breaking it: adding `role?: string` to `FieldWidgetDomProps`
without adding it to `DOM_PASS_THROUGH_KEYS` now fails type-check with
`toDomProps.ts(126,7): error TS2322: Type 'true' is not assignable to type
'never'`.

`className` / `disabled` stay outside the named type — they are DOM-legal and
forwarded, but widgets also interpret them, so they are bound in the forward
direction only. The changeset now states exactly which direction each assertion
guards instead of claiming the pair "cannot drift".
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-COqhY_Us.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.47KB 3.09KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 476.66KB 104.69KB
core (index.js) 2.25KB 0.80KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 136.23KB 34.75KB
fields (index.js) 224.93KB 55.15KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 26.14KB 6.07KB
i18n (useSafeTranslation.js) 3.26KB 1.44KB
layout (index.js) 37.96KB 10.54KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.05KB 1.53KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.98KB 12.37KB
plugin-charts (index.js) 60.54KB 17.13KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 112.01KB 28.86KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 230.56KB 56.80KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 111.54KB 26.97KB
plugin-gantt (index.js) 162.25KB 39.55KB
plugin-grid (index.js) 185.08KB 49.04KB
plugin-kanban (index.js) 47.89KB 13.18KB
plugin-list (index.js) 104.94KB 25.32KB
plugin-map (index.js) 16.81KB 5.24KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.55KB 10.59KB
plugin-timeline (index.js) 25.76KB 7.33KB
plugin-tree (index.js) 8.34KB 2.82KB
plugin-view (index.js) 83.67KB 20.43KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.46KB 1.21KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 3, 2026 18:20
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit 19b8c9b Aug 3, 2026
17 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-3291-todomprops branch August 3, 2026 18:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

字段 widget 把渲染器专用 props 原样展开到 DOM——schema="[object Object]" 出现在真实输入框上

2 participants