Skip to content

@object-ui/core has no default server dispatcher, so standalone consumers cannot run action.body at all #2904

Description

@os-zhuang

Split out of #2896, which is closed — the fix there was to stop mislabeling a server-side body ("No script provided for script action" → an error naming the real cause). This is the follow-up that #2896 explicitly deferred, filed so it is not buried in a closed issue.

Where things stand after #2895

ActionSchema.body (HookBodySchema — L1 expression / L2 sandboxed JS) is the spec's preferred binding for script actions. Bodies execute server-side, via POST /api/v1/actions/{object}/{action} → the runtime sandbox.

In the console this works: useConsoleActionRuntime registers a script handler, and registered handlers beat ActionRunner's built-ins.

handlers: { api: apiHandler, flow: flowHandler, script: serverActionHandler, modal: modalActionHandler },

Every other consumer of @object-ui/core — standalone renderers, SDUI hosts, embedded usage — registers no such handler, falls through to the built-in executeScript, and now gets a clear error telling them a body must run server-side. Clear, but still a dead end: there is no supported way to make it run without hand-rolling the dispatcher that app-shell already has.

Why it wasn't done in #2895

serverActionHandler needs three things core has no opinion about today:

  • auth — authFetch, session/token plumbing;
  • base URL — app-shell reads VITE_SERVER_URL, a bundler-specific env convention core should not inherit;
  • object resolution — which object the action is scoped to (objApiName), plus the record-id resolution dance (_rowRecord, recordIdField, toolbar selection fallback).

Adding a default dispatcher means core acquires opinions on all three, or takes them as injected config. That is a design decision, not a patch.

Options worth weighing

  1. A factory in core — createServerActionHandler({ fetch, baseUrl, resolveObject }) that consumers register themselves. Core stays opinion-free; the duplicated logic in useConsoleActionRuntime and RecordDetailView (which both hand-roll the same POST) collapses into one place.
  2. A registered-by-default handler wired from an existing provider (DataSourceProvider?) when one is present, so the common case needs no wiring.
  3. Leave it to consumers and treat the improved error message from fix(actions): one precedence for target/execute, and stop mislabeling server-side body #2895 as the whole answer — defensible if standalone body use is genuinely rare.

Option 1 looks strongest: it fixes the existing duplication between useConsoleActionRuntime and RecordDetailView regardless of whether standalone consumers ever adopt it, so it earns its keep either way.

Not in scope

Executing bodies client-side, in any form. L2 requires an isolated VM enforcing declared capabilities, timeoutMs and memoryMb — a browser has none, so client-side "enforcement" would be decoration. L1 is formula-engine (CEL) source, a different dialect from this package's ${…} ExpressionEvaluator, so interpreting it here would diverge silently. The client dispatches; it does not interpret. See #2896 for the full reasoning.

Refs #2895, #2896.

Activity

  1. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    Contributor

    维护者裁决(2026-08-03)

    批准选项 1:core 导出 createServerActionHandler({ fetch, baseUrl, resolveObject }) 工厂,消费者自行注册;顺手把 useConsoleActionRuntime 与 RecordDetailView 两处手写 POST 收敛进来(不管独立消费者用不用都回本)。

    排期:v17 后(纯新增 API,不 breaking,不占大版本窗口)。摘 needs-user-decision,加 pm:queue。


    Generated by Claude Code


    Generated by Claude Code

  2. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    Contributor

    排期提前(维护者 2026-08-03 追加裁决)—— 已认领,待 pin-bump 合并后派发

    会话 session_01NVPjPzmmAJ2Ngtvgg5MSRa。

    从「v17 后」提前至 rc.2 pin-bump 合并后立即派发。 原排期的顾虑是发版窗口带宽,不是协议约束——本单是纯新增 API(core 导出 createServerActionHandler 工厂),minor 语义,不破坏任何现有消费者,本就不占大版本窗口。v17 窗口批已 4/4 完成。

    排在 pin-bump 之后而非并行的唯一原因是文件相邻:本单要动 packages/app-shell 的 action runtime,pin-bump PR 正在动 packages/app-shell 的 AppContent(落地路由)与 ContextSelectors。大概率不相交,但等一个合并事件比处理一次冲突便宜。

    裁决内容不变:选项 1;顺手收敛 useConsoleActionRuntime 与 RecordDetailView 两处手写 POST(这部分不管独立消费者是否采用都回本);选项 2 缓议;客户端执行 body 永久出界。


    Generated by Claude Code


    Generated by Claude Code

  3. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    Contributor

    开发 agent 已开工(会话 session_01NVPjPzmmAJ2Ngtvgg5MSRa)

    分支:claude/issue-2904-dispatcher-factory(基于含 #3313/#3315 的最新 origin/main,commit d22ae31ce)。

    按裁决实施选项 1:

    • @object-ui/core 新增 createServerActionHandler({ fetch, baseUrl, resolveObject, ... }) 工厂(含标准 recordId 解析与 in-flight 防重入),/actions 信封规则(interpretActionResponse / actionErrorDetail)随之上移 core;
    • useConsoleActionRuntime 与 RecordDetailView 两处手写 POST 收敛为 app-shell 共享的 console 包装(popup 预开 / newTabUrl 快路径 / redirectUrl 约定),底层统一走 core 工厂;
    • RecordDetailView 的 target || name 识别漂移一并按 ADR-0110 D1 收敛为 name-only。

    Generated by Claude Code


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions