Skip to content

Commit ee72aae

Browse files
xuyushun441-sysos-zhuangclaude
authored
fix(spec): render action body editor as composite (language + source), not [object Object] (#1855)
* fix(spec): render action `body` as composite, not a flat code editor An action's `body` is a discriminated union (HookBodySchema: `{ language, source, capabilities?, timeoutMs? }`), the same shape hooks use. The action form layout mapped the whole field to `{ widget: 'code' }`, so the inspector fed the union object to a single JS editor and rendered "[object Object]". Mirror the working hook.form.ts layout: a composite with a `language` select, a `source` code editor, and the L2-only capability/timeout knobs. `visibleOn: data.type == 'script'` is preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(changeset): action body composite fix Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent a00fc5e commit ee72aae

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
fix(spec): render action `body` as a composite editor (language + source) instead of a flat code field
6+
7+
An action's `body` is a discriminated union (`HookBodySchema`), the same shape hooks use, but `action.form.ts` mapped the whole field to `{ widget: 'code' }`, so the Studio inspector fed the union object to a single JS editor and rendered `[object Object]`. The layout now mirrors the working `hook.form.ts`: a composite with a `language` select, a `source` code editor, and the L2-only capability/timeout knobs.

packages/spec/src/ui/action.form.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,26 @@ export const actionForm = defineForm({
3030
fields: [
3131
{ field: 'target', visibleOn: "data.type != 'script'", helpText: 'URL, flow name, or API endpoint to call' },
3232
{ field: 'method', visibleOn: "data.type == 'api'", helpText: 'HTTP method (GET, POST, PUT, DELETE)' },
33-
{ field: 'body', widget: 'code', visibleOn: "data.type == 'script'", helpText: 'JavaScript code to execute' },
33+
{
34+
field: 'body',
35+
type: 'composite',
36+
visibleOn: "data.type == 'script'",
37+
helpText: 'Either an L1 expression or an L2 sandboxed JS body',
38+
// Mirrors hook.form.ts: `body` is a discriminated union
39+
// (HookBodySchema) on `language`, not a bare string. A flat
40+
// `widget: 'code'` fed the whole object to the editor and rendered
41+
// "[object Object]". Render the union as language + source (+ the
42+
// L2-only capability/timeout knobs).
43+
fields: [
44+
{ field: 'language', type: 'select', required: true, helpText: 'expression = pure formula; js = sandboxed JavaScript', options: [
45+
{ label: 'Expression (L1)', value: 'expression' },
46+
{ label: 'JavaScript (L2 sandboxed)', value: 'js' },
47+
] },
48+
{ field: 'source', type: 'code', language: 'javascript', required: true, helpText: 'Function body source — no top-level imports' },
49+
{ field: 'capabilities', type: 'tags', helpText: 'Allowed ctx APIs (api.read, api.write, crypto.uuid, log, …)' },
50+
{ field: 'timeoutMs', type: 'number', helpText: 'Per-invocation timeout (ms)' },
51+
],
52+
},
3453
{ field: 'params', type: 'repeater', helpText: 'User input parameters (show form before executing)' },
3554
{ field: 'confirmText', helpText: 'Confirmation message (e.g., "Are you sure?")' },
3655
{ field: 'successMessage', helpText: 'Success message after completion' },

0 commit comments

Comments
 (0)