| title | Bulk Action |
|---|---|
| description | Bulk Action protocol schemas |
{/*
Bulk Action Schemas
The vocabulary of a list view's bulkActionDefs — one entry per button in
the multi-select toolbar. Use a def for a mass data-plane mutation that no
action expresses (operation: 'update' with a patch, or 'delete'), or for
an operation: 'custom' + execution: 'aggregate' entry that dispatches the
action it NAMES once for the whole selection.
For the per-record dispatch, name the action in the view's
bulkActions: ['<name>'] instead — the bare-string form, promoted with the
action's own label, params and visible.
import { BulkActionDef, BulkActionExecution, BulkActionOperation, BulkActionParam } from '@objectstack/spec/ui';
import type { BulkActionDef, BulkActionExecution, BulkActionOperation, BulkActionParam } from '@objectstack/spec/ui';
// Validate data
const result = BulkActionDef.parse(data);| Property | Type | Required | Description |
|---|---|---|---|
| name | string |
✅ | Stable identifier — the audit-log action key, and (for an aggregate def) the name of the object action to dispatch. |
| label | string |
optional | Button + dialog-header text. Plain string: an authored def is not i18n-resolved (declare a real action and name it in bulkActions to get localization). |
| icon | string |
optional | Lucide icon name (e.g. "user-check", "trash-2"). |
| variant | Enum<'primary' | 'secondary' | 'danger' | 'ghost' | 'outline'> |
optional | Visual treatment of the button. |
| operation | Enum<'update' | 'delete' | 'custom'> |
✅ | What the executor does: 'update'/'delete' are data-plane mass mutations; 'custom' dispatches an object action (see execution). |
| execution | Enum<'perRecord' | 'aggregate'> |
optional | For operation: 'custom' — 'aggregate' dispatches the named action ONCE for the whole selection, carrying every id in params._selectedIds (objectui#3139). Required on a custom def: the per-record form is declared as bulkActions: ['<name>'] instead. |
| patch | Record<string, any> |
optional | For operation: 'update' — static field values applied to every selected record, merged UNDER the user-supplied params so a fixed value can be declared without exposing it in the dialog. |
| params | Record<string, any>[] |
optional | Inputs collected once before the run. Omit to skip the params step and go straight to confirm. |
| confirmText | string |
optional | Confirmation text shown above the affected-record summary. |
| confirmLabel | string |
optional | Custom Confirm button label (default: "Run"). |
| visible | string | { dialect: Enum<'cel' | 'cron' | 'template'>; source?: string; ast?: any; meta?: object } |
optional | Eligibility predicate (CEL), same shape as action.visible. Evaluated once PER SELECTED RECORD with that record bound: the button is offered when at least one passes, the run covers only those, and the rest are reported as skipped. A record-free predicate (features.x, current_user.y) therefore behaves as a plain button-level gate. Fail-closed — a predicate that faults excludes the record. |
| maxRecords | integer |
optional | Selection size above which the run is blocked. Set it on defs whose server work is expensive — an aggregate def carries every selected id in one request. |
| batchSize | integer |
optional | Records per executor batch (default 200). Data-plane operations only — an aggregate run is a single call by definition. |
perRecordaggregate
updatedeletecustom
| Property | Type | Required | Description |
|---|---|---|---|
| name | string |
✅ | Param key — becomes params[name] in the patch / action params bag. |
| label | string |
optional | Field label in the dialog. Plain string: an authored def is not i18n-resolved (see module header). |
| help | string |
optional | Help text under the field. (An ActionParam spells this helpText — known divergence, module header.) |
| type | Enum<'text' | 'textarea' | 'email' | 'url' | 'phone' | 'password' | 'secret' | 'markdown' | 'html' | 'richtext' | 'number' | 'currency' | 'percent' | 'date' | 'datetime' | 'time' | 'boolean' | 'toggle' | 'select' | 'multiselect' | 'radio' | 'checkboxes' | 'lookup' | 'master_detail' | 'tree' | 'user' | 'image' | 'file' | 'avatar' | 'video' | 'audio' | 'formula' | 'summary' | 'autonumber' | 'composite' | 'repeater' | 'record' | 'location' | 'address' | 'code' | 'json' | 'color' | 'rating' | 'slider' | 'signature' | 'qrcode' | 'progress' | 'tags' | 'vector'> |
✅ | Field widget to render, from the standard field-type vocabulary (text/number/select/lookup/date/…). |
| required | boolean |
optional | Blocks the Confirm button until a value is present. |
| default | any |
optional | Value applied when the dialog opens. (An ActionParam spells this defaultValue.) |
| options | { label: string; value: string | number | boolean }[] |
optional | Static options for select-style widgets. |
| object | string |
optional | Target object for a lookup widget. (An ActionParam spells this reference.) |
| labelField | string |
optional | Related-object field used as the option label for a lookup widget (defaults to name/full_name/email/id). |
| multiple | boolean |
optional | Allow picking multiple values — the param value becomes an array and is written to the patch as-is. |
| placeholder | string |
optional | Placeholder text. |