发现于 objectstack#7137 的实施过程(objectui 分支 claude/issue-7137-timeline-lineitems-filter-sort,基线 f3b2874e1)。观察类 —— 今天没有用户能踩到它,记录以免下次有人再复制第五、第六份。
事实
sort → $orderby 的下降逻辑在三个 object-bound block 里各内联了一份逐字相同的私有函数:
packages/plugin-gantt/src/ObjectGantt.tsx:315packages/plugin-map/src/ObjectMap.tsx:113packages/plugin-calendar/src/ObjectCalendar.tsx:111
objectstack#7137 给 object-timeline 和 record:line_items 各补了 sort 读点,要是照抄就是第五、第六份,所以那个 PR 把这份逻辑提到了 @object-ui/core(packages/core/src/utils/sort-query.ts,导出 convertSortToQueryParams,带单测)。三份私有副本没有动 —— 迁移不在 #7137 的范围内,故立此单。
迁移本身是机械的:删掉本地函数、从 @object-ui/core 导入(三个包都已经依赖它)。
一处行为差异,迁移时会一并被修掉
共享版有两点与私有副本不同,都是更贴合已声明的契约,而不是新增容忍:
- 不带
order 的数组项按升序处理,而不是丢弃。 私有副本要求 item.field 与 item.order 两个键都在,否则跳过该项 —— 于是 sort: [{ field: 'amount' }] 会静默丢掉整个排序(全部项都缺 order 时 reduce 出 {})。同一份副本在字符串写法上却已经把「只有字段名」当升序("amount" → { amount: 'asc' }),所以这是两种写法之间的不一致,不是刻意的严格。QueryParams.$orderby 自己声明的数组成员也是 { field: string; order?: 'asc' | 'desc' }(order 可选),@object-ui/data-objectstack 的 serializeOrderBy 同样按升序处理缺失的方向。 - 无可排序内容返回
undefined,不返回 {}。 空对象是个 truthy 值,只是恰好被 adapter 的序列化器当成「无排序」;undefined 是把这件事说出来。
可达性说明(免得把严重度判高):SortConfig.order / ElementDataSourceSort.order 在 objectui 自己的类型里都是必填,所以类型化的调用方写不出缺 order 的项;能走到这一步的只有未类型化的已存视图元数据(ElementSavedView 声明为 Record< string, unknown >,按设计是松的)。这也是本单标 finding 而不是缺陷单的原因。
(注:上一版正文里 Record< string, unknown > 的泛型参数被 GitHub 的正文清洗器当成 HTML 标签吃掉了 —— < 紧跟字母会被就地剥掉。此处按仓内约定在 < 后留空格重写。)
建议范围
去重
已搜过 open issues(convertSortToQueryParams / $orderby / 重复 + 文件路径),无同题单。相关:objectstack#7137(共享 sink 的引入者)。
发现于 objectstack#7137 的实施过程(objectui 分支
claude/issue-7137-timeline-lineitems-filter-sort,基线f3b2874e1)。观察类 —— 今天没有用户能踩到它,记录以免下次有人再复制第五、第六份。事实
sort → $orderby的下降逻辑在三个 object-bound block 里各内联了一份逐字相同的私有函数:packages/plugin-gantt/src/ObjectGantt.tsx:315packages/plugin-map/src/ObjectMap.tsx:113packages/plugin-calendar/src/ObjectCalendar.tsx:111objectstack#7137 给
object-timeline和record:line_items各补了 sort 读点,要是照抄就是第五、第六份,所以那个 PR 把这份逻辑提到了@object-ui/core(packages/core/src/utils/sort-query.ts,导出convertSortToQueryParams,带单测)。三份私有副本没有动 —— 迁移不在 #7137 的范围内,故立此单。迁移本身是机械的:删掉本地函数、从
@object-ui/core导入(三个包都已经依赖它)。一处行为差异,迁移时会一并被修掉
共享版有两点与私有副本不同,都是更贴合已声明的契约,而不是新增容忍:
order的数组项按升序处理,而不是丢弃。 私有副本要求item.field与item.order两个键都在,否则跳过该项 —— 于是sort: [{ field: 'amount' }]会静默丢掉整个排序(全部项都缺 order 时 reduce 出{})。同一份副本在字符串写法上却已经把「只有字段名」当升序("amount"→{ amount: 'asc' }),所以这是两种写法之间的不一致,不是刻意的严格。QueryParams.$orderby自己声明的数组成员也是{ field: string; order?: 'asc' | 'desc' }(order 可选),@object-ui/data-objectstack的serializeOrderBy同样按升序处理缺失的方向。undefined,不返回{}。 空对象是个 truthy 值,只是恰好被 adapter 的序列化器当成「无排序」;undefined是把这件事说出来。可达性说明(免得把严重度判高):
SortConfig.order/ElementDataSourceSort.order在 objectui 自己的类型里都是必填,所以类型化的调用方写不出缺 order 的项;能走到这一步的只有未类型化的已存视图元数据(ElementSavedView声明为Record< string, unknown >,按设计是松的)。这也是本单标finding而不是缺陷单的原因。(注:上一版正文里
Record< string, unknown >的泛型参数被 GitHub 的正文清洗器当成 HTML 标签吃掉了 ——<紧跟字母会被就地剥掉。此处按仓内约定在<后留空格重写。)建议范围
import { convertSortToQueryParams } from '@object-ui/core',删掉本地副本。packages/plugin-view/src/ObjectView.tsx:448那条直接透传$orderby: sort的路径也统一走这个 sink(它是 list-view 容器路径,目前依赖 adapter 端的serializeOrderBy兜住四种形状 —— 也能工作,只是仓里就有了两种形态)。去重
已搜过 open issues(
convertSortToQueryParams/$orderby/ 重复 + 文件路径),无同题单。相关:objectstack#7137(共享 sink 的引入者)。