Skip to content

fix(plugin-grid,plugin-detail,plugin-list,core): 不物化列的字段类型不再给出排序头 (#3950) - #4965

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-3950-formula-sort-header
Aug 17, 2026
Merged

fix(plugin-grid,plugin-detail,plugin-list,core): 不物化列的字段类型不再给出排序头 (#3950)#4965
yinlianghui merged 3 commits into
mainfrom
claude/issue-3950-formula-sort-header

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Fixes #3950

结论先说

formula 列的排序头是修改前后都错的:它提供一个平台无法执行的排序。framework#6994 之前平台不说话(200,返回的正是它被要求据以排序的那些值,但没排序 —— 真实 SqlDriver 上 asc 与 desc 逐字节相同);之后是 400 INVALID_SORT。所以这个修法不依赖 pin 推进,无 Blocked-by

卡面两个开放验证点(先做,结论如下)

① RelatedList 等 data-table 渲染器是否共享 sortable 派生路径 —— 不共享。 服务端排序的 affordance 判定在三处各自调用 isExpandableFieldType,谁也不读谁:

位置 控件 条件
packages/plugin-grid/src/ObjectGrid.tsx withSortability 表头 manualSortingOn
packages/plugin-detail/src/RelatedList.tsx sortableColumns 内嵌表头 windowed
packages/plugin-detail/src/RelatedList.tsx 排序按钮行 按钮(data-list 才有) windowed

sort-values.ts 的文档注释其实已经记下了这个状态:「surfaces that sort on the server must instead not offer relational columns as sort keys (that half of #3096 lives at each entry point)」。第四处是 ListView 工具栏的排序选择器,它已经排除 formula(#4243),但用的是自己文件里的一份私有集合。

派生路径不共享,所以三处都改;并且把成员集合上提到 @object-ui/core,让四处读同一个判断。ListView 渲染的正是 type: 'object-grid',所以 ObjectGrid 一处即覆盖 ListView 的表头与独立 ObjectGrid 两个入口。

② corpus 里有没有 list view 真渲染 formula 列 —— 有,两处,所以这是今天就用户可见的缺陷:

  • examples/app-crm/src/views/opportunity.view.ts 的默认 columnslistViews.all.columns 都含 { field: 'expected_revenue' },而 expected_revenuecrm_opportunity 上的 Field.formula(opportunity.object.ts:51);
  • examples/app-showcase/src/ui/views/project.view.ts:24columns{ field: 'budget_remaining' },是 showcase project 上的 Field.formula(project.object.ts:65)。

两者都真的会渲染出值(isProjectableField 只看字段是否声明,formula 已声明 → 进 $select,服务端 on-read 水合),所以是「有值、可点、点了没用/报错」。即:CRM 的 Opportunities 列表、showcase 的 Projects 列表,点「Expected Revenue」/「Budget Remaining」表头 —— pin 推进前是静默的无效点击,推进后是用户可点出来的 400。

改法

一个门,两个理由,同一个机制。列被当作排序键的前提是两条同时成立:

  • relational(lookup / master_detail / user / tree):存的是外键 id,显示的是关联记录名;服务端只能按 id 排(objectstack#4256 已定不做 join)。
  • unmaterialized(formula):没有任何驱动为它物化列,压根没有可排的键。

成员集合从 ListView 的私有拷贝搬到 @object-ui/core(UNMATERIALIZED_FIELD_TYPES / isUnmaterializedFieldType),并且绑定到 @objectstack/spec 自己的存储事实而不是在这里重述一遍 —— 平台的 filter 门对自己用的就是这个理由(「so this gate and the drivers cannot disagree about which types have a column」)。重述会漂移,漂移的两个方向都坏:要么收掉一个能用的 affordance,要么留一个会 400 的。

刻意窄于 spec 的写入契约 COMPUTED_VALUE_TYPES(formula / summary / autonumber):summaryautonumber 各有真实维护的列、排序正常,按写入契约收会误伤两个能用的类型 —— 平台自己的 conformance 用例点名钉了这个坑。测试里两边都钉住。

刻意不改的两件事

  • 客户端排序不动。 行全在浏览器里,formula 值是服务端 on-read 水合出来的那一个,按单元格显示的值排是诚实的 —— 与 relational carve-out 同一个切分。所以门只在服务端排序分支上生效(manualSortingOn / windowed)。
  • 视图元数据里声明的排序不动。 它照旧发出去、照旧被平台点名拒绝。悄悄丢掉作者的声明是把作者的错误藏起来(contract-first);工具栏选择器保留「当前排序已用到的 formula 字段」正是为此 —— 那一行是把这个排序删掉的唯一入口。作者侧本该先拒的那一环目前是空的,已另立 objectstack#9257(SORT 轴没有 authoring gate,SEARCH 轴有 validate-searchable-fields),不在本 PR 范围。

文档(AGENTS.md #2)

两个包的 README 都有「Sorting」小节在讲这条规则,一并跟上:

  • packages/plugin-detail/README.md —— 原本准确描述了 relational 那一半,补上第二个理由,并点明这条规则作用在本卡片的两个排序入口上。
  • packages/plugin-list/README.md —— 这一段不是「补充」而是纠正,请 reviewer 特意看一眼:它原先写着 column-header sorting 「is unaffected: it is client-side over the rows already loaded」。这句自 objectui#3106 起就不成立(窗口化时表头点击本身就是服务端 $orderby),于是它恰好断言了与本次改动相反的事实 —— 两个错误是同一句话,无法只改一个。客户端那一半保留,但挪到它真正成立的地方(inline data、以及持有全部行的 grouped 视图)。

测试

新增/改动 27 个断言(全绿):

  • packages/core/src/utils/__tests__/unmaterialized-fields.test.ts —— 成员恰好是 formula;引用同一性(不是值相等:忠实拷贝写下的当天就通过一切值比较,之后静默漂移);summary/autonumber 反向钉住;与 EXPANDABLE_FIELD_TYPES 互斥(两个理由若重叠,消费端会有一支变成死代码,且两者给的补救建议不同)。
  • packages/plugin-grid/src/__tests__/ObjectGrid.unmaterializedSort.test.tsx —— formula 表头无排序 affordance;点它不产生任何 refetch(所以没有请求给平台去回 400);误伤对照:text/currency/percent/autonumber/summary 五列逐个真点表头并断言 $orderby 发出;客户端模式下 formula 表头保持可点。
  • packages/plugin-detail/src/__tests__/RelatedList.unmaterializedSort.test.tsx —— 两个排序入口各自钉住(表头 + data-list 的按钮行),各带 stored 列对照与客户端模式对照。
  • packages/plugin-list/src/__tests__/ListView.relationalSort.test.tsx —— [console] List sort: filterableFields doubles as the sort whitelist, and a header click discards the view's multi-level default with no way back #4243 的 formula 规则此前只有文字描述、没有行为钉子(fixture 里根本没有 formula 字段)。往共享 fixture 加一个,该文件原有的精确相等断言就顺带变成了这条规则的钉子。

反向验证(先预判后跑)

变异 1 —— 把 ObjectGrid + RelatedList 两处新加的门撤回(保留 ListView)。预判:恰好 4 红(grid 的表头与点击、related list 的表头与按钮),8 个误伤对照与 2 个客户端模式全绿,ListView 选择器全绿 —— 因为那条规则先于本 PR 存在。实测完全一致:Tests 4 failed | 23 passed

变异 2 —— 删掉 ListView 选择器里的第二条规则。预判 1 红,实测 3 红,方向一致而数量更多:多出来的两条是该文件原有的精确相等断言(toEqual(['Name','Amount'])toEqual([…,'Owner (by ID)'])),因为 score 进了共享 fixture,它们免费变成了这条规则的钉子。

一处照直说明,不套模板:变异 2 下 keeps a formula field the current sort already names 仍然绿。这是可预期的 —— 它断言的是「被保留」,而规则被删掉后该字段本来就不会被收掉,所以它是因为什么都没发生而通过。单独看它不承重;承重的是紧邻的 withholding 断言,两条合起来才区分「被例外保留」与「从未被收掉」。保留它是为了钉住例外的标签形状(不带 by-ID 后缀 —— 那是关于关系键的陈述,这个字段压根没有键可描述)。

变异都在 commit 之后做、用 git checkout -- 还原(不用 stash),还原后重跑 27 全绿。

门与检查

pnpm exec vitest run packages/core/ packages/plugin-grid/             →  163 files, 2544 passed
pnpm exec vitest run packages/plugin-detail/ packages/plugin-list/    →  124 files, 1411 passed
pnpm exec turbo run type-check --concurrency=2                        →  81 successful, 81 total
turbo run lint(四个包)                                               →  0 errors
node scripts/check-control-bytes.mjs                                  →  OK (4437 files)
node scripts/check-spec-symbol-derivation.mjs                         →  OK
node scripts/check-phantom-dependencies.mjs / -self-import.mjs        →  OK
node scripts/check-lint-coverage.mjs / -type-check-coverage.mjs       →  OK
node scripts/check-changeset-fixed.mjs / -no-major.mjs                →  OK
node scripts/check-doc-links.mjs                                      →  OK

changeset:.changeset/formula-column-sort-header-3950.md(patch × 4 包)。

相邻发现,均已立卡、未认领,不在本 PR:objectstack#9257(SORT 轴缺 authoring gate)、objectui#4966(ListView.tsx 一个 docblock 错位)、并在 objectui#4559 上留了坐标漂移说明(它的取证正落在本 PR 改写的那个注释块里,且它本身被本 PR 修掉)。

…ader from an unmaterialized column (#3950)

A `formula` value is computed on read and no driver materialises a column for
it, so a server `$orderby` naming one has nothing to order by. The sort never
worked; until objectstack#6994 the platform answered `200` with the requested
values unordered (asc byte-identical to desc on a real SQL driver), and it now
answers `400 INVALID_SORT`. Either way a clickable header offers a sort that
cannot be performed.

`ObjectGrid` gated the affordance on the reference-bearing family alone
(#3096). Unmaterialized types are a second reason a server sort is impossible,
not a different mechanism, so the same seam now reads both — as do the two sort
entry points of a related list, which derive the rule separately (measured: the
`sortable` derivation is NOT shared across the data-table renderers).

Membership moved out of a private set in `ListView` into `@object-ui/core`
(`UNMATERIALIZED_FIELD_TYPES` / `isUnmaterializedFieldType`), bound to
`@objectstack/spec`'s own storage predicate rather than restated, and pinned
narrower than the spec's write contract: `summary` and `autonumber` have real
columns and keep their headers.

Client-side sorting is untouched (the key is the value the cell shows, which is
honest), and a sort declared in view metadata is still sent and still refused by
name rather than silently dropped.

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
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) 40.21KB 10.79KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.71KB 1.22KB
auth (invitation-status.js) 1.22KB 0.70KB
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) 5.02KB 0.88KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
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) 498.79KB 111.24KB
core (index.js) 4.11KB 1.62KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 157.40KB 43.42KB
fields (index.js) 233.27KB 58.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 39.16KB 10.97KB
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.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
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.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.75KB 18.37KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 127.85KB 32.73KB
plugin-designer (index.js) 212.39KB 42.83KB
plugin-detail (index.js) 240.03KB 60.04KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 120.42KB 29.03KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 197.61KB 53.03KB
plugin-kanban (index.js) 52.72KB 14.54KB
plugin-list (index.js) 111.22KB 26.98KB
plugin-map (index.js) 17.91KB 5.72KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.97KB 11.33KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 83.81KB 20.49KB
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.66KB 3.50KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 27.53KB 9.41KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.28KB 0.68KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.55KB 2.07KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.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 (dashboard-filter-alias.js) 6.23KB 2.74KB
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) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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

…materialized rule (#3950)

Both packages document the relational carve-out; the same paragraphs now carry
the second reason a server sort is withheld.

`plugin-list`'s closing sentence also had to be corrected rather than extended:
it claimed column-header sorting "is unaffected: it is client-side over the rows
already loaded". That stopped being true at objectui#3106, which made a header
click a server `$orderby` whenever the grid shows one window of a larger
collection — so the sentence asserted the opposite of the rule this change
applies there, and the two errors are the same sentence. The client-side half is
kept and stated where it actually holds (inline data and the grouped view, which
holds every row it groups).

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Aug 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
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) 40.21KB 10.79KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.71KB 1.22KB
auth (invitation-status.js) 1.22KB 0.70KB
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) 5.02KB 0.88KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
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) 498.79KB 111.24KB
core (index.js) 4.11KB 1.62KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 157.40KB 43.42KB
fields (index.js) 233.27KB 58.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 39.16KB 10.97KB
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.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
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.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.75KB 18.37KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 127.85KB 32.73KB
plugin-designer (index.js) 212.39KB 42.83KB
plugin-detail (index.js) 240.03KB 60.04KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 120.42KB 29.03KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 197.61KB 53.03KB
plugin-kanban (index.js) 52.72KB 14.54KB
plugin-list (index.js) 111.22KB 26.98KB
plugin-map (index.js) 17.91KB 5.72KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.97KB 11.33KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 83.81KB 20.49KB
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.66KB 3.50KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 27.53KB 9.41KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.28KB 0.68KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.55KB 2.07KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.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 (dashboard-filter-alias.js) 6.23KB 2.74KB
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) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
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) 40.21KB 10.79KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.71KB 1.22KB
auth (invitation-status.js) 1.22KB 0.70KB
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) 5.02KB 0.88KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
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) 498.79KB 111.24KB
core (index.js) 4.11KB 1.62KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 157.40KB 43.42KB
fields (index.js) 233.27KB 58.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 39.16KB 10.97KB
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.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
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.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.75KB 18.37KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 127.85KB 32.73KB
plugin-designer (index.js) 212.39KB 42.83KB
plugin-detail (index.js) 240.03KB 60.04KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 120.42KB 29.03KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 197.61KB 53.03KB
plugin-kanban (index.js) 52.72KB 14.54KB
plugin-list (index.js) 111.22KB 26.98KB
plugin-map (index.js) 17.91KB 5.72KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.97KB 11.33KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 83.81KB 20.49KB
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.66KB 3.50KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 27.53KB 9.41KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.28KB 0.68KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.55KB 2.07KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.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 (dashboard-filter-alias.js) 6.23KB 2.74KB
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) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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

Copy link
Copy Markdown
Collaborator Author

PM 验收 ✅ ACCEPT(#3950,批次 19)

实物核验(按真实 merge-base 对账):12 files,+669/−72 —— 三处入口收敛(ObjectGrid withSortability、RelatedList 表头 + data-list 按钮行),成员集合上提 @object-ui/core 且绑定 spec 存储事实(引用而非重述);刻意窄于 COMPUTED_VALUE_TYPES(summary/autonumber 有真实列,误伤对照逐列真点钉住);客户端排序与视图声明面刻意不动,理由成立。模型标识 grep = 0;releases/ 零触碰;changeset patch×4 在位。

CI 亲读:20/20 check runs completed,零失败(dependabot/coverage 两项 path-filter skipped,计绿)。

反向验证读数:变异 2 预判 1 红实测 3 红 —— 方向一致,多出的两条是 score 进共享 fixture 后既有精确相等断言免费变成的钉子,如实报告不套模板;「因为什么都没发生而通过」的那条断言单独说明不承重,判定诚实。

corpus 两处真实标本(CRM expected_revenue、showcase budget_remaining)把这卡从理论缺陷坐实为今天就用户可见的缺陷。相邻立卡 os#9257(SORT 轴 authoring gate)与 #4966 已确认未混入本 PR。

undraft + auto-merge(squash)。


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 17, 2026 12:37
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 17, 2026
Merged via the queue into main with commit c1ef923 Aug 17, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3950-formula-sort-header branch August 17, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation package: core plugin tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Grid/list columns offer a sort header on formula fields, which the server refuses as of framework#6994

2 participants