Skip to content

Commit 29b9949

Browse files
fix(plugin-grid,fields,app-shell): a promoted bulk action's field-backed param resolves through the shared action-param resolver (objectui#12105) (#12119)
Fixes #12105 Clause-②: yes A promoted bulk action's field-backed param (`{ field, objectOverride }`) now resolves through the same resolver as the single-record path, so the selection bar's dialog renders the record picker and the confirm step shows the record's label. One resolver, shared: it moved out of `app-shell` into `@object-ui/fields`. There is no second copy. Implemented by `os-dev` for the `domain:ui` seat, session `https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz`, branch `claude/issue-12105-bulk-param-field-resolve`. ## The defect, reproduced red on `main` `a21ff9a` A throwaway probe against `main`'s source, with HotCRM's shape (`params: [{ field: 'crm_campaign', objectOverride: 'crm_campaign_member' }]`): - `resolveBulkActions` produced `{"field":"crm_campaign","objectOverride":"crm_campaign_member","required":true,"name":"crm_campaign","type":"text"}`. - `bulkParamToField` turned that into a `text` widget. - `BulkActionDialog` rendered `input#bulk-param-crm_campaign` with `type="text"`. The card's premise holds. `toBulkParam` renamed `field` onto `name`, defaulted `type` to `'text'`, and read `object` only off an inline `reference`. It never looked at the field definition. ## What changed, and why each package is touched - **`@object-ui/fields` receives the resolver.** `resolveActionParams.ts` moved here with `git mv`, unchanged in behaviour, together with the three-row param-type alias table and `resolveParamWidgetType`. The index exports them (list under Clause-② below). The one other edit to the moved file is a module-scoped type for the bundler-replaced `process.env.NODE_ENV` read. This package has no node typings. The runtime read is the same bytes it was in app-shell, and nothing ambient is declared, so it cannot shadow node's global anywhere. - **`@object-ui/app-shell` now imports the resolver from `@object-ui/fields`.** Its two callers changed (`useConsoleActionRuntime`, `RecordDetailView`), and `paramToField` / `ActionPreview` now read `resolveParamWidgetType` from fields. The resolver was never exported from app-shell, so its public face is unchanged. Most of the files counted under app-shell are tests whose import line moved. Two tests key on the file's path: the `INHERITED_TARGET` citation pin and core's column-identity ratchet. Both were repointed. - **`@object-ui/plugin-grid` is the fix.** - `resolveBulkActions.ts`: the fold no longer attaches promoted `params`. A new `resolvePromotedBulkParams(def, ctx)` runs `resolveActionParams` and renames the resolved param onto the bulk vocabulary (`helpText` → `help`, `defaultValue` → `default`, `referenceTo` → `object`, `displayField` → `labelField`, plus `dependsOn`, `accept` and `maxSize`). It contains no field lookup of its own. - `ObjectGrid.tsx`: `dispatchBulkActionDef` resolves when the dialog opens. It first awaits `useMetadata().ensureType('object')`, then unions the grid's own object schema with the session's objects (`withKnownObjects`, the grid's first). That is the record dialog's order, so an `objectOverride` target that has not loaded yet is waited for, not refused. - `BulkActionDialog.tsx`: a param the resolver could not resolve (`unresolvedField`) is refused before any widget is built, and Next is disabled. This matches `ActionParamDialog` and reuses its `actionDialog.unresolvedParam` copy. Without it, the resolver's own warning, "The action dialog refuses the param", would be false on the bulk path, and the missing-field case would still draw a raw text box. The key travels on a plugin-grid-local reader (`unresolvedFieldOf`), not as a declared `BulkActionParam` member, because that interface is also the authoring face (AGENTS.md #0.1). - The stale "left open there" sentence in the dialog's `dependsOn` note now says the promoted route exists. - **`@object-ui/core`: comments only.** Four comments named the resolver's old home (`expand-fields.ts`, `predicate-fields.ts`, `reference-keys.ts`, one test). The column-identity ratchet total goes from 12 to 11, because the hand-mapper's `name || field` read is gone. - **Docs:** the `fields` README has a new "Resolving declared action params" section. `plugin-grid.mdx` and `enhanced-actions.mdx` each gain a short paragraph. Changeset: `.changeset/12105-bulk-param-field-resolve.md`. ## Why the resolver moved instead of being injected I measured what the resolver imports before choosing. It reads `@object-ui/core` (`EXPANDABLE_FIELD_TYPES` and the `ActionParamDef` / `ActionParamOption` types), the fields widget fold (`resolveFormWidgetType`, through `resolveParamWidgetType`) and `@objectstack/spec/ui` (`resolveI18nLabel`). Its only app-shell-local import was `paramToField.ts`'s `resolveParamWidgetType`, a thin wrapper over the fields fold plus a three-row alias table. plugin-grid already depends on all three of those packages. `@object-ui/fields` is the one package both sides depend on that holds all of them. `@object-ui/core` cannot host the resolver, because it cannot import the fields widget map. - **Move (chosen).** It adds 5 values and 3 types to `@object-ui/fields`, and nothing to plugin-grid's or app-shell's published face. Every host gets the fix, including a grid rendered without the console. - **Inject (rejected).** It would add a context, a provider and a value type to plugin-grid, plus a provider mount in app-shell. That is fewer exports, but the fix would depend on the host. A grid with no provider would still need some answer for a field-backed param, and the old `toBulkParam` mapping is that answer. That leaves two resolution behaviours, and the one a host gets by forgetting the provider is the defect this card reports. - **The cost of the move.** `fields/src/resolveActionParams.ts` imports `resolveFormWidgetType` from the package barrel that re-exports it. That is a cycle in the source graph only, and only at call time: nothing at the module's top level reads it, and the published bundle is one file. The fields build printed no circular-dependency warning, and `check:node-esm-load` loads the built entry. ## Spec guidance sentence for the objectstack follow-up Per the card, objectstack is not edited here. Quoted verbatim from objectstack `origin/main` `7098acaef`, `packages/spec/src/ui/bulk-action.zod.ts`, `BulkActionParamSchema`'s `guidance.field`: > `field` declares a FIELD-BACKED param, and the bulk surface has no such route: `resolveActionParams` consults the object's field definitions for the single-record dialog, `resolveBulkActions`'s `toBulkParam` never does. Declare the param inline instead — `name` + `type`, plus `object` (and optionally `labelField`) for a picker. After this PR the clause "`toBulkParam` never does" is false for a promoted action. An action declared on the object and named in a view's `bulkActions` now resolves its field-backed params. An authored `bulkActionDefs[].params` entry still has no field-backed route, so the refusal stays right and only its reason and remedy change. The remedy could be: "or declare the action on the object with a field-backed param and name it in `bulkActions`". Three more sentences in the same file make the same claim: - the `BULK_PARAM_WIDGET_CONFIG_KEYS` prescription: "⛔ Declaring the key on the object's FIELD does not reach this dialog either: the bulk surface has no field-backed param route." - the JSDoc above that constant: "There is no field-backed param route on the bulk surface — `toBulkParam` never consults the object's field definitions" - the `dependsOn` JSDoc: "the FIELD-BACKED route (`resolveActionParams` resolves the object's field definitions), which is the very route the bulk surface does not have." For the widget-config one, note that a promoted lookup param receives only `object`, `labelField` and `dependsOn` from its field so far (see Acceptance notes). ## Tests New file: `packages/plugin-grid/src/__tests__/bulkFieldBackedParam-12105.test.tsx`, 8 cases. - **Pin:** a field-backed lookup param on the bulk path. Unit level: it resolves to `type: 'lookup'`, `object: 'crm_campaign'`, `labelField: 'name'`, and `bulkParamToField` builds the picker. A parity case shows the same answer as `resolveActionParam` for the same declaration. - **Pin, through the real `ObjectGrid` + `BulkActionDialog`:** `lookup-trigger-crm_campaign` renders and `input#bulk-param-crm_campaign` does not. The confirm step shows "Spring Launch" (via `findOne('crm_campaign', 'c1')`). Run sends `crm_campaign: 'c1'` once per selected record. The metadata store loads late, so the case also pins the `ensureType('object')` wait. - **CONTROL:** a literal-typed param is unchanged at unit level (same widget, label, help, default, placeholder; an inline `reference: 'sys_user'` still promotes to the person picker) and at grid level (`input#bulk-param-note` with `type="text"`). - **Refusal:** a field-backed param whose field is missing shows `bulk-param-unresolved-crm_campaign` with the `OBJECT.FIELD` locator, and Next is disabled. - `resolveBulkActions.test.ts`'s key-mapping case now asserts through `resolvePromotedBulkParams`. Results: - **Affected suites at `ef07e4e`:** `pnpm exec vitest run` over 47 files (plugin-grid bulk suites, app-shell `resolveActionParams*` / `paramToField*` / `ActionParamDialog*` / `ActionPreview*` / the identity and twin pins, core's ratchet and `ActionParamDef.options`, and `one-authority-per-exported-name-6273`): `Test Files 47 passed (47)`, `Tests 510 passed (510)`. The commits after it change typing, comments, docs and the changeset. The rerun at the final head is in the `os-dev-report` on the card. - **Ablation A2,** resolver starved of metadata (`resolveActionParams(raw, { ...ctx, objects: [] })`): the three field-backed pins went red, and both CONTROLs, the refusal case and the authored-def case stayed green. The mutation was proven on disk (anchor 1 → 0, blob `e2e6b01a` → `e770d6fb`), and so was the restore (blob equal to HEAD, `git diff HEAD` empty). - **Ablation A1,** the `ensureType('object')` wait removed from `ObjectGrid`: only the grid-level picker pin went red (it got the refusal instead), 7 of 8 green. Restore proven the same way. - **Reverse type check:** with an unknown key added to the context `ObjectGrid` hands the resolver, `pnpm --filter @object-ui/plugin-grid type-check` reported `TS2353 … does not exist in type 'ResolveActionParamsContext'`. That proves plugin-grid reads the rebuilt fields `.d.ts`. Restored, `git diff HEAD` empty. - **Type-check** (every output echoes `type-check`): `@object-ui/fields`, `@object-ui/plugin-grid`, `@object-ui/app-shell` and `@object-ui/core`, all exit 0, after a turbo build of each closure. The plugin-grid and fields run was at `d5e2f59`; the app-shell and core run was on the same code. ## Gates, all exit 0 at `5c915e9` - **Spec, deps and imports:** `check:spec-symbols`, `check:handler-key-reads`, `check:action-forward-parity`, `check:phantom-deps`, `check:unused-deps`, `check:self-import`, `check:esm-specifiers` (specifiers-only, as the root script runs it), `check:unreferenced-sources`. - **Line citations and bytes:** `check:new-line-citations` (`VERDICT new-cross-file-line-citations: 0 new citation(s)`), `check-control-bytes`. - **Changesets:** `check-changeset-presence` ("30 source file(s) of 4 released package(s) changed, and this change declares 1 changeset(s)"), `check-changeset-no-major`, `check-changeset-fixed`, `check:changeset-claims`, `check:pending-changeset-literals`. - **Tests and mocks:** `check:test-path-roots`, the three `check:vi-mock-*`, `check-type-check-coverage`. - **i18n:** `check:i18n-keys`. It confirms that the reused `actionDialog.unresolvedParam` default matches the en pack. No locale copy changed, so the other i18n checks were not run. - **Docs:** `check:readme-exports`, `check-doc-snippet-types`, `check-doc-example-types`, `check-doc-component-types`, `check:doc-example-readers`, `check:doc-fences`, `check-doc-example-ids`, `check-doc-links`. - **Dist and budget:** - `check:published-dist`. - `check:node-esm-load --force-build` at `5c915e9`: "37 of 37 gradable entries were built by this tree". A first run without `--force-build` was refused on provenance alone, because 6 untouched packages were replayed from a sibling worktree's shared turbo cache. - `check:eager-closure` at head: "Console eager closure is 3170.6 KB gzipped across 290 of 2474 chunks (budget: 3204.6 KB, headroom: 34.0 KB)". The base-to-head delta is in the `os-dev-report`. - **Lint, narrowed:** - Population: `eslint.config.js` applies its rules to `**/*.{ts,tsx}`. All 30 changed code files are in that population, and none came back as ignored. - Count: `eslint --format json` over those 30 files reported 30 results, 0 errors. - Invariance: the config enables no type-aware linting (no `parserOptions.project` or `projectService`), and no rule under `eslint-rules/` reads the disk. So this diff cannot change a verdict on a file it did not touch. - With `--no-inline-config` the same run shows 4 errors. They sit exactly on the 4 pre-existing `eslint-disable-next-line react-hooks/static-components` comments in these files. Repo-wide `pnpm lint` is CI's. ## Clause-② surface added New exports from `@object-ui/fields`: - values: `resolveActionParam`, `resolveActionParams`, `resolveParamWidgetType`, `withKnownObjects`, `RESOLVED_ONLY_PARAM_KEYS` - types: `RawActionParam`, `RawActionParamOption`, `ResolveActionParamsContext` Before this PR they were internal to app-shell (not exported from it). They are now exported from fields' package entry. Nothing is added to plugin-grid's package entry. `resolvePromotedBulkParams` and `unresolvedFieldOf` are module exports of `resolveBulkActions.ts`, which the index does not re-export. No prop, context member or locale key is added. The bulk dialog reuses the existing `actionDialog.unresolvedParam` key. ## Acceptance notes (noted, not filed) - **The bulk adapter forwards only part of a lookup's picker config.** A resolved lookup param reaches the bulk picker with only `object`, `labelField` and `dependsOn`. `lookupFilters`, `lookupColumns`, `lookupPageSize`, `descriptionField`, `idField` and `titleFormat` are resolved but not forwarded. `bulkParamToField` has no rename for them, while app-shell's `paramToField` does. Converging the two param adapters is its own change. The changeset and the docs say so. Carrier: whoever next converges `bulkParamToField` with `paramToField`. - **Bulk param labels skip `_actions.ACTION.params.PARAM` localization.** The record dialog runs that pass after resolution (`actionParamText`), and the bulk path never had it. This is a gap from before this PR, not something it introduces. Carrier: none. - **The bulk dialog's `dependsOn` note is still stale elsewhere.** It says `BulkActionParamSchema` "is not strict", but the spec has since closed that shape and declares `dependsOn`. Only the sentence this PR made false was edited. Carrier: none. - **`BULK_PARAM_TYPE_ALIASES` in `bulkParamToField.ts` duplicates the param alias table.** The table now lives in fields beside `resolveParamWidgetType`. Not touched, because it is a different defect class. Carrier: the same as the first note. --- _Generated by [Claude Code](https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent b7cd5c8 commit 29b9949

34 files changed

Lines changed: 805 additions & 109 deletions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@object-ui/fields': minor
3+
'@object-ui/plugin-grid': patch
4+
'@object-ui/app-shell': patch
5+
---
6+
7+
A bulk action's field-backed param now renders its real control on the selection bar, and the confirm step names the picked record (objectui#12105).
8+
9+
A list view's `bulkActions: ['add_to_campaign']` runs the object's own action over the selected records. When that action declares a field-backed param, `{ field: 'crm_campaign', objectOverride: 'crm_campaign_member' }`, the selection bar's dialog drew a plain text box for it, and the confirm step listed the raw record id. The record page drew the record picker for the same action, because only its dialog looked the field up.
10+
11+
- **One resolver for both dialogs.** The function that resolves an action param against the object's field definitions (`resolveActionParam` / `resolveActionParams`) moves, unchanged, from `@object-ui/app-shell` into `@object-ui/fields`, the one package both dialogs depend on. The record dialog and the bulk dialog both call it. A field-backed param on the selection bar now gets its field's type, label (translated), options, default and picker target, as it does on the record page. A lookup param renders the record picker, and the confirm step shows the record's label.
12+
- **When it resolves.** The grid resolves a promoted action's params when the dialog opens, after it has loaded the session's object metadata, so a param whose owning object (`objectOverride`) has not loaded yet is waited for.
13+
- **A missing field is refused, not drawn as a text box.** A field-backed param whose field is not in the object metadata is shown as a refusal naming `object.field`, and the run is blocked, as the record dialog already does. The copy is the existing `actionDialog.unresolvedParam` string.
14+
- **Inline params are unchanged.** A param declared with its own `name` and `type` keeps the same widget, label, help text, default and `reference` target.
15+
16+
New `@object-ui/fields` exports: `resolveActionParam`, `resolveActionParams`, `resolveParamWidgetType`, `withKnownObjects`, `RESOLVED_ONLY_PARAM_KEYS`, and the types `RawActionParam`, `RawActionParamOption` and `ResolveActionParamsContext`. Before this change these were internal to `@object-ui/app-shell` and were not exported from it. No other export, prop, context member or language-pack key is added.
17+
18+
Not covered yet: on the selection bar, a resolved lookup param uses only its field's picker target, display field and `dependsOn`. The field's other picker settings (`lookupFilters`, `lookupColumns`, `descriptionField` and the rest) do not reach the bulk dialog yet.

‎content/docs/core/enhanced-actions.mdx‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ gets its real widget, not a text box (ADR-0059):
153153
- **Field-backed params** declare `field` (+ optional `objectOverride`) and
154154
inherit label, type, options, lookup picker config, `multiple`, `accept`,
155155
and `maxSize` from the object's field definition; inline properties override.
156+
The same resolution runs when the action is named in a list's `bulkActions`
157+
and launched from the selection bar, so a field-backed lookup param gets the
158+
record picker there too. On the selection bar, the field's picker target,
159+
display field and `dependsOn` reach the picker; its other picker settings
160+
(`lookupFilters`, `lookupColumns`, …) do not yet.
156161
- `required` blocks submit while the value is empty; `visible` (a CEL
157162
predicate over `features` / `current_user` / `data`) hides a param
158163
entirely — e.g. gate a param on an opt-in server capability. There is no

‎content/docs/plugins/plugin-grid.mdx‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,15 @@ carrying callbacks.
579579
}
580580
```
581581

582+
A name in `bulkActions` runs that action once per selected record. If the
583+
action declares `params`, the selection bar's dialog collects them once, before
584+
the run, and resolves them as the record page's action dialog does. A
585+
field-backed param (`{ "field": "crm_campaign", "objectOverride":
586+
"crm_campaign_member" }`) takes its type, label, options, default and picker
587+
target from that field, so a lookup param renders the record picker and the
588+
confirm step shows the picked record's label. A param whose field is not in
589+
the object metadata is refused, and the run is blocked.
590+
582591
### Row callbacks are component props
583592

584593
`onRowClick`, `onRowSelect`, `onCellChange`, `onRowSave`, `onBatchSave`,

‎packages/app-shell/src/hooks/useConsoleActionRuntime.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import { ActionParamDialog, type ParamDialogState } from '../views/ActionParamDi
4646
import { ActionResultDialog, type ResultDialogState } from '../views/ActionResultDialog.js';
4747
import { FlowRunner, type ScreenFlowState, type ScreenSpec } from '../views/FlowRunner.js';
4848
import { FlowRefusalNotice, type FlowRefusalState } from '../views/FlowRefusalNotice.js';
49-
import { resolveActionParams, withKnownObjects } from '../utils/resolveActionParams.js';
49+
import { resolveActionParams, withKnownObjects } from '@object-ui/fields';
5050
import { EnvironmentEntitlementDialog, type EntitlementDialogState } from '../environment/EnvironmentEntitlementDialog.js';
5151
import { entitlementDialogFromError, type EntitlementDialogSpec } from '../environment/entitlements.js';
5252
import { resolvePageVarTokens } from '../utils/resolvePageVarTokens.js';

‎packages/app-shell/src/utils/__tests__/referenceArms-6837.divergent.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
*/
5252
import { describe, it, expect } from 'vitest';
5353
import { deriveRelatedLists } from '../deriveRelatedLists';
54-
import { resolveActionParams } from '../resolveActionParams';
54+
import { resolveActionParams } from '@object-ui/fields';
5555

5656
/** Broken-on-purpose: the two keys disagree, so the answer names the reader's choice. */
5757
const DIVERGENT = { type: 'master_detail' as const, reference: 'canonical_target', reference_to: 'legacy_target' };

‎packages/app-shell/src/utils/decisionOutputParams.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
decisionOutputParams,
2828
foldDecisionOutputs,
2929
} from './decisionOutputParams';
30-
import { resolveActionParams } from './resolveActionParams';
30+
import { resolveActionParams } from '@object-ui/fields';
3131
import { paramToField } from './paramToField';
3232

3333
const t = (key: string) => `t:${key}`;

‎packages/app-shell/src/utils/expandableFamily.identity-5874.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ import {
6060
resolveActionParams,
6161
type ResolveActionParamsContext,
6262
type RawActionParam,
63-
} from './resolveActionParams';
63+
} from '@object-ui/fields';
6464

6565
/**
6666
* The spec's own `FieldType` vocabulary.

‎packages/app-shell/src/utils/paramToField.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
import { describe, it, expect, vi, afterEach } from 'vitest';
1919
import { FORM_FIELD_TYPES } from '@object-ui/fields';
2020
import { EXPANDABLE_FIELD_TYPES, type ActionParamDef } from '@object-ui/core';
21-
import { paramToField, paramDegradesWithoutTarget, resolveParamWidgetType } from './paramToField';
21+
import { paramToField, paramDegradesWithoutTarget } from './paramToField';
22+
import { resolveParamWidgetType } from '@object-ui/fields';
2223

2324
const p = (over: Partial<ActionParamDef>): ActionParamDef => ({
2425
name: 'x',

‎packages/app-shell/src/utils/paramToField.ts‎

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,11 @@
1313
* asserting param support ⊇ form support.
1414
*/
1515
import { EXPANDABLE_FIELD_TYPES, type ActionParamDef } from '@object-ui/core';
16-
import { resolveFormWidgetType } from '@object-ui/fields';
17-
18-
/**
19-
* Param-only type spellings the dialog historically accepted, folded onto the
20-
* canonical form widget vocabulary. These are legacy dialect entries kept for
21-
* params already authored with them — new params should use spec `FieldType`
22-
* values directly.
23-
*/
24-
const PARAM_TYPE_ALIASES: Record<string, string> = {
25-
checkbox: 'boolean',
26-
reference: 'lookup',
27-
'datetime-local': 'datetime',
28-
// NOTE: spec's `autonumber` (vs the widget-map key `auto_number`) is folded
29-
// in the shared `mapFieldTypeToFormType`, so `resolveFormWidgetType` already
30-
// handles it — no param-only alias needed here.
31-
};
32-
33-
/**
34-
* Resolve a param `type` to the form widget key that renders it. Any type in
35-
* `FORM_FIELD_TYPES` resolves to itself (identity — asserted by the drift
36-
* test); aliases and unknown types resolve through the same fallback chain the
37-
* form applies (unknown → `text`).
38-
*/
39-
export function resolveParamWidgetType(paramType: string): string {
40-
return resolveFormWidgetType(PARAM_TYPE_ALIASES[paramType] ?? paramType);
41-
}
16+
// The param-type fold (its alias table included) moved to `@object-ui/fields`
17+
// beside the resolver that reads it first (objectui#12105). This adapter asks
18+
// the same question one step later, so it reads the same function — never a
19+
// local copy of the table.
20+
import { resolveParamWidgetType } from '@object-ui/fields';
4221

4322
/**
4423
* Widget keys whose picker cannot query without an explicitly DECLARED target,

‎packages/app-shell/src/utils/resolveActionParams.declaredLookupLegs-7435.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ import {
5656
resolveActionParams,
5757
type ResolveActionParamsContext,
5858
type RawActionParam,
59-
} from './resolveActionParams';
59+
} from '@object-ui/fields';
6060
import { paramToField } from './paramToField';
6161

6262
/** The camel filter value — kept by reference so identity is assertable. */

0 commit comments

Comments
 (0)