Uh oh!
There was an error while loading. Please reload this page.
fix(actions): forward bodyExtra end-to-end through the action chain - #3924
Merged
Conversation
An action's static request body (`bodyExtra`) was dropped one hop before the ActionRunner. Every action renderer forwards an EXPLICIT whitelist of keys — deliberately, so a key no renderer honours cannot look wired — and none of them listed `bodyExtra`. Since @objectstack/spec 17 made it the only way a `type: 'api'` action can carry a payload (`params` keeps its single meaning as the parameter DEFINITION array, per the #5777 ruling, direction A), and the ADR-0087 `inline-action-api-params-to-body-extra` conversion rewrites older object-form `params` pages onto it at load, a previously-working published page validated, published, and then POSTed an empty body. - components: `element:button` forwards `bodyExtra`, which brings its whitelist to exact parity with spec's `InlineActionSchema` pick list. The four declared- action renderers (`action:button`, `action:group`, `action:icon`, `action:menu`) forward it too — they are the consumers the type addition below exists for. `action:bar` needs no change: it spreads the member action onto its child renderer. - core: `ActionRunner.executeAPI` — the path taken when no host registered an `api` handler — assembles the body with `bodyExtra` merged LAST, matching the spec's documented semantics and both branches of the console `apiHandler`. An ARRAY `params` no longer contributes to the body: it is a definition list, not a payload. - core: the compat window the #5777 ruling ordered. A non-array `params` on a `type: 'api'` action is still read as the static payload for one version window and now says so, naming `bodyExtra`. Scoped to exactly the shape the ADR-0087 conversion rewrites (`Array.isArray` discriminator + `api` type guard), so it stays out of #6828's third meaning of object-form `params` on a url action, and filtered against the host `_rowRecord`/`recordId` stash so it cannot fire on ordinary declared-action clicks. - types: `ActionSchema` declares `bodyExtra`, typed off the spec rather than restated. `useConsoleActionRuntime`'s `apiHandler` needed no change — it already merged `bodyExtra` last on both its branches. Fixesobjectstack-ai/objectstack#6837
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
commented
Aug 9, 2026
CollaboratorAuthor
✅ 验收通过,含扩围批准(objectui 分片 PM,session 实物核验:base 验收要点:
范围外发现 objectstack#6938(bodyShape 被全部五个渲染器白名单丢弃)立单规范(先查重、unassigned、仅路由标签),v17 冻结期不派,归后续分诊。 GraphQL 配额未恢复则 undraft+auto-merge 由 PM 随后补发。 Generated by Claude Code |
yinlianghui
marked this pull request as ready for review
August 9, 2026 05:38
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixesobjectstack-ai/objectstack#6837
背景
bodyExtra是type: 'api'动作的静态请求体。spec 半边(objectstack#6819)已合入:params只保留「参数定义数组」一个含义,ADR-0087 的inline-action-api-params-to-body-extra转换在加载时把旧的对象形params页面改写成bodyExtra。而 objectui 每个动作渲染器都用一份显式转发白名单(刻意如此:没有渲染器读的键不该看起来像接好了),这些名单里都没有
bodyExtra。结果:一个已发布页面通过校验、成功发布,然后在到达 runner 前一跳丢掉 payload,POST 出去是空体。这是发布完整性件。四个落点的实况
packages/components/src/renderers/basic/elements.tsxelement:button白名单补bodyExtra: action.bodyExtra。补完后该名单与 spec 的InlineActionSchemapick list 逐字段对齐(那份 pick list 正是本白名单所镜像的契约)。同文件只有这一处execute()转发。packages/core/src/actions/ActionRunner.ts— 弃用窗 + 合并顺序:780/790分叉在实际 main 上是:840/850,且那处分叉服务于参数收集,与 payload 组装无关 —— runner 里原本完全没有读bodyExtra的地方。所以合并顺序落在 runner 自己组装 body 的位置(executeAPI),弃用告警落在「非数组params」的判定上。executeAPI回退路径useConsoleActionRuntime.tsx—— issue 正文点明这一处在ActionRunner.executeAPI(body = JSON.stringify(action.params || context.data || {}),两个调用点)。useConsoleActionRuntime.tsx免改:apiHandler两条分支都已经把bodyExtra最后合并(:308-309 绝对 HTTP 分支、:385-386 dataSource 分支),复核后未动一行。packages/types/src/ui-action.tsActionSchema补bodyExtra,类型从 spec 派生而非手抄,形状不会与契约漂移。具体做了什么
转发白名单(components)。 除
element:button外,四个声明式动作渲染器(action:button/action:group/action:icon/action:menu)同样漏掉了bodyExtra,一并补上。这是对派发范围的一次有意扩大,在此明说请复核:理由是落点 ④ 只有这些渲染器才是它的消费者 ——element:button走readProps拿到的是无类型的对象袋,根本用不到ActionSchema,所以若不补这四处,落点 ④ 就是一个「没有任何渲染器读」的死字段,恰好是 spec 注释里警告的那种失败。改动本身是同一个键、同一跳、零契约决策。action:bar免改:它把成员动作整体 spread 到子渲染器上,天然带过去(已核验)。合并顺序(core)。
ActionRunner.executeAPI(没有 host 注册apihandler 时走的路径)现在集中组装 body,bodyExtra最后合并,与 spec 对该键的描述("merged last (overrides user params)")以及 consoleapiHandler两条分支一致。顺带:数组形params不再进 body —— 它是定义列表不是 payload,把ActionParam定义当请求体 POST 出去从来不是任何端点想要的。弃用窗(core)。 非数组
params在type: 'api'上继续被当作静态 payload 读一个版本窗口,并在 dev 下 warn-once 地点名bodyExtra。判定条件与 ADR-0087 转换逐条对齐(Array.isArray作判别式 +api类型守卫),因此:params是第三义(interpolateTarget的param作用域、executeUrl的params.newTab),bodyExtra不是它的替代品,api守卫正是把这条挡在外面的东西。params的内部键(_rowRecord/_selectedIds/recordId),否则几乎每次声明式动作点击都会误报。告警是 dev-only + warn-once,与既有的
warnOnUnknownActionKeys同一套约定。响亮的那一半在生产者侧(contract-first):spec 的params字段直接拒收对象形并在报错里点名bodyExtra,转换负责改写存量源 —— 到了 runner 这一层生产者已经说过话,剩下要兜的只是 host 在代码里手搓 ActionDef 的情形,所以 warn 是正确的严重级别。测试
新增两个文件,
pnpm exec vitest run全绿:packages/core/src/actions/__tests__/ActionRunner.bodyExtra.test.ts(11 个)—— 合并顺序(status在两边取不同值,反向合并必红)、纯bodyExtrapayload、ApiConfig分支、无bodyExtra时的 back-compat 钉子、数组params不进 body;弃用窗的正例 + 四个负例(url 型静默、数组静默、host stash 静默、host stash 旁边带真实作者键时仍告警)。packages/components/src/__tests__/action-bodyExtra-forward.test.tsx(4 个)——element:button/action:button/action:menu三个渲染器转发到 runner;以及一条端到端:渲染 → 点击 → 断言真实 fetch 请求体里bodyExtra压过同名旧params键(不注册自定义 handler,让点击走完白名单 + runner 合并两跳)。反向验证分三次跑,方向事先各自预测,结果全部吻合:
stays SILENT负例依旧绿 —— 因为功能不在时什么都不 warn,not.toMatch空绿。这一点如实记录,撤销无法验证这三条。api判定 + 去掉 host stash 过滤)→ url 负例与 host stash 负例转红。Array.isArray那条 → 数组负例也转红。三条负例各由其对应的守卫分支单独钉住,非空绿。bodyExtra转发 → 4 个全红,端到端那条的请求体退回status: 'draft'(旧params未被覆盖),正是预测的样子。其余验证:
pnpm exec vitest run packages/core/ packages/types/→ 99 files / 1909 tests passed(含actionKeys.pin.test.ts)pnpm exec vitest run packages/components/→ 102 files / 823 tests passeduseConsoleActionRuntime.test.tsx+RecordDetailView.headerApiInterpolation.test.tsx→ 47 passedpnpm exec turbo run type-check --concurrency=2→ 78/78 successful,零错check-control-bytes.mjsOK;check-changeset-no-major.mjsOK;改动文件控制字节自查零命中changeset
.changeset/quiet-doors-forward-bodyextra.md,patch覆盖@object-ui/components/@object-ui/core/@object-ui/types。Generated by Claude Code