fix(components,app-shell,core): interpolate {field} tokens in record_header api action targets - #3395
Merged
Merged
Conversation
…header api action targets (#3391) A type:'api' action declared on locations:['record_header'] with a target like /api/v1/data/:object/{id} was sent with the literal {id} (%7Bid%7D on the wire -> 400): page:header dispatched the authored action verbatim, so the api handler's interpolation guard (params._rowRecord) never fired, while the SAME declaration interpolated fine from list_item. - page:header now dispatches record actions in the same shape ObjectGrid row actions and DeclaredActionsBar use: the record stashed under params._rowRecord and a spec-shaped params ARRAY surfaced as actionParams. Dispatching a fresh object also stops ActionRunner's collected-params merge from mutating the authored schema node. - RecordDetailView's api handler falls back to the page record for {field} interpolation when no _rowRecord is stashed (same-object actions only, mirroring the recordIdParam fallback), and its flow handler strips the client-side stash from the trigger body like the shared runtime does. - ActionRunner's built-in executeAPI strips _rowRecord from the fallback body serialization, matching serverActionHandler's wire contract. Closes #3391 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeCMshyrRvShuMt4dppH2N
) Standalone Vite entry (/record-header-preview.html) mounting the REAL RecordDetailView with a stub dataSource and no backend, reproducing the issue's exact declaration (record_header type:'api' action targeting /api/v1/data/:object/{id} with a required field-backed param, bodyExtra and confirmText). Same pattern as row-actions-preview: excluded from the production build, exists so the {field} interpolation fix can be browser-verified end-to-end (confirm dialog -> param dialog -> PATCH URL carrying the real record id instead of the literal %7Bid%7D). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeCMshyrRvShuMt4dppH2N
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
…tion The strip was defense-in-depth beyond what #3391 needs, and it broke a PINNED contract: bulk dispatches through the built-in executeAPI carry each record under params._rowRecord on the wire, which is how hosts without a custom api handler (and objectBulkActionDispatch.test.tsx's two per-record assertions) identify the record a request targets. The console api/flow/server handlers strip the stash themselves, so the #3391 fix is unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeCMshyrRvShuMt4dppH2N
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
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 free
to 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.
Closes #3391
Problem
A
type:'api'action declared onlocations:['record_header']with a target like/api/v1/data/:object/{id}was sent with the literal{id}(%7Bid%7Don the wire → 400). The SAME declaration interpolated fine fromlist_item— the two executors disagreed.Root cause:
page:headerdispatched the authored action verbatim, so the record page api handler's{field}interpolation guard (params._rowRecord) never fired — the header executor simply never stashed the current record the way ObjectGrid row actions /DeclaredActionsBardo.Fix
page:header(components) — record actions now dispatch in the same shape ObjectGrid row actions andDeclaredActionsBaruse: the record stashed underparams._rowRecord, a spec-shapedparamsARRAY surfaced asactionParams. Dispatching a fresh object also stops ActionRunner's collected-params merge from mutating the authored schema node between invocations. Non-record hosts dispatch unchanged.RecordDetailView.apiHandler(app-shell) — falls back to the page record for{field}interpolation when no_rowRecordis stashed, guarded to same-object actions only (mirrors the existingrecordIdParamfallback), so other dispatch paths on the record page are covered too.RecordDetailView.flowHandler(app-shell) — strips the client-side_rowRecordstash from the flow trigger body, matchinguseConsoleActionRuntime's flowHandler.(An earlier commit also stripped
_rowRecordfrom coreexecuteAPI's fallback body serialization; that was reverted — it broke the pinned bulk-dispatch contract inobjectBulkActionDispatch.test.tsx, where hosts without a custom api handler identify each per-record request by the_rowRecordthe built-in path carries on the wire. The console handlers strip the stash themselves, so #3391 doesn't need it.)Verification
page:headerdispatch shape (stash, array→actionParams, authored-node purity, non-record passthrough) and 4 on the real view handlers via the captured-provider harness ({id}from page record, stash wins over page record, retargeted actions never interpolate from the parent, flow body strips the stash).type-checkandlintclean on the changed packages./record-header-preview.html, same pattern asrow-actions-preview) mounting the realRecordDetailViewwith the issue's exact action declaration. Same interaction flow (复制 → confirm → required param → confirm), before/after on the same driver:PATCH /api/v1/data/os_tianshun_ehr_production_plan/%7Bid%7D— the issue's 400, verbatimPATCH /api/v1/data/os_tianshun_ehr_production_plan/plan-42with body{"copy_ovr_start":"2026-08-15","copy_now":true}, no_rowRecordleakPure bug fix — no changeset per repo convention.
🤖 Generated with Claude Code
https://claude.ai/code/session_01AeCMshyrRvShuMt4dppH2N