Shared Types
Overview
The types subpath holds the vocabulary every other part of the base domain is built from: the base props interface every component's props extend, the colour and size scales, the action targets a button or a row action can point at, the rule algebra that decides whether a row action applies to a row, the guard signatures a table view runs before a mutation, and a handful of small enums and value unions.
Most of it is exported from one subpath:
import {
Color,
FormMode,
HttpMethod,
Size,
} from "@antelopejs/interface-dms/base/types";
Four modules are not re-exported by that barrel and are imported from their own file: types/action-target, types/row-action, types/row-action-operators and types/guards. The guard types and RowActionRule are also re-exported by the table view, so they can equally be imported from @antelopejs/interface-dms/base.
| Symbols | Import from |
|---|---|
BaseComponentProps, ButtonVariant, Color, ColorValue, ComponentOrNoInput, CustomButton, CustomButtonSerialized, CustomButtonAvailability, CustomButtonUnavailability, CustomButtonHidden, ButtonColor, ErrorWithMessage, isErrorWithMessage, getErrorMessage, ExportStatus, ListableConfig, FieldMetadata, FormMode, DefaultValue, FormFieldValue, HttpMethod, JsonPrimitive, JsonValue, AxeOrientation, ReadonlyBehavior, ReadonlyBehaviorType, ReadonlyBehaviorConfig, Size, SizeValue, TONES, Tone, ICON_TONES, IconTone, ModalSize, ComponentId, WatchAction, WatchActionCondition, WatchFunctionParamMap, WatchFunctionId, WatchFunctionParams | @antelopejs/interface-dms/base/types |
ActionTarget, ActionTargetSerialized | @antelopejs/interface-dms/base/types/action-target |
RowActionRule, RowActionConfig, CustomRowAction, CustomRowActionSerialized, FieldEqualsRule, FieldNotEqualsRule, FieldInRule, FieldNotInRule, AndRule, OrRule, NotRule, LogicalRule | @antelopejs/interface-dms/base/types/row-action |
FIELD_OPERATORS, FIELD_KEY, FieldOperatorKey, FieldRule, AnyFieldRule, LOGICAL_OPERATOR_KEYS, LogicalOperatorKey | @antelopejs/interface-dms/base/types/row-action-operators |
EditGuardArgs, DeleteGuardArgs, BulkGuardArgs, NewGuardArgs, GuardFn, AfterWrite, TableViewGuards | @antelopejs/interface-dms/base/types/guards |
Base Component Props
BaseComponentProps is the one field every component props interface inherits.
import type { BaseComponentProps } from "@antelopejs/interface-dms/base/types";
Its declaration is shown below for reference; import the existing type rather than redeclaring it in your module:
import type { WatchAction } from "@antelopejs/interface-dms/base/types";
interface BaseComponentProps {
watchActions?: WatchAction[];
}
FormProps, TreeProps, TabProps, KpiCardProps, ChartCardProps, TopListCardProps, PeriodSelectorProps and the chart props all extend it. The ComponentBuilder from @antelopejs/interface-dms/component strips a caller-supplied watchActions from the options it receives and fills the field itself from .watch() / .watchOn() calls, so the property is the transport rather than something you set by hand.
Watch Actions
A WatchAction binds an event emitted by a component to a function exposed by a component.
import type {
WatchAction,
WatchActionCondition,
} from "@antelopejs/interface-dms/base/types";
| Field | Type | Description |
|---|---|---|
component | string | Identifier of the component that emits the event. ComponentId.SELF ("$component:self") refers to the declaring component. |
event | string | Event name, e.g. FormEvents.SUBMIT_SUCCESS. |
functionId | string | Identifier of the function to invoke, e.g. FormFunctions.SET_FIELD_HIDDEN. |
params | Record<string, unknown> | Arguments passed to the function. |
onParam | WatchActionCondition | WatchActionCondition[] | Only run when the event payload matches these { key, value } pairs. |
requirePermission | string | Permission id the viewer must hold for the action to be kept. |
WatchFunctionParamMap is an interface extended by declaration merging: each module that exposes a watch function adds its functionId and the exact parameter shape to the map. WatchFunctionId is keyof WatchFunctionParamMap & string, and WatchFunctionParams<F> resolves to the registered parameter shape for F, falling back to Record<string, unknown> | undefined for ids that are not in the map. Form and TableView both register their functions in this map — see Forms and Tables.
Colors and Sizes
import {
Color,
type ColorValue,
type ModalSize,
Size,
type SizeValue,
} from "@antelopejs/interface-dms/base/types";
Color is an enum of the semantic palette names: primary, secondary, success, info, warning, error, neutral. ColorValue is Color | (string & {}), so a component that accepts a ColorValue takes either an enum member or an arbitrary string while keeping the enum members in editor completion.
TONES (Tone, from types/tone) is neutral, primary, secondary, success, warning, error, info; ICON_TONES (IconTone) adds muted. color is for what is clicked (buttons, row actions, header actions, tabs), tone for what shows a state (pills, icon wells, banners, meters, notifications, blocks); see Display Blocks.
Size maps readable names onto the scale tokens: tiny → "xs", small → "sm", medium → "md", large → "lg", huge → "xl". SizeValue widens it the same way ColorValue widens Color.
ModalSize is a separate, wider union used only for modal action targets: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl".
Buttons
ButtonVariant is an enum with solid, outline, soft, ghost and link. ButtonColor is a plain string union — "primary" | "secondary" | "success" | "error" | "warning" | "info" | "neutral" — used by CustomButton in place of the Color enum.
CustomButton describes a toolbar button; it is what TableViewOptions.customButtons takes.
import { ButtonVariant } from "@antelopejs/interface-dms/base/types";
import type { CustomButton } from "@antelopejs/interface-dms/base/types";
const inviteButton: CustomButton = {
label: "$page.settings.members.invite.button",
icon: "i-ph-user-plus",
variant: ButtonVariant.solid,
color: "primary",
target: { type: "page", url: "/settings/users/invite" },
};
| Option | Type | Description |
|---|---|---|
id | string | Names the button so a quick action can press it. Unique within its table. |
label | string | Button label. Required. |
icon | string | Icon identifier. |
variant | ButtonVariant | Visual variant. |
color | ButtonColor | Colour name. |
target | ActionTarget | What pressing the button does. Required. |
permission | string | Action | Gate the button behind a permission. |
permissionId | string | Absolute permission id the button requires; wins over permission. |
availability | CustomButtonAvailability | Disable the button, with a reason, for a request it refuses. |
permission takes either form: a string names one of the owner's actions, relative to its permission id — the table's for a toolbar button ("add" → <table permission id>.add), the page's for a header button (DefaultLayout({ headerActions })) — and an Action references any component's action — another table's add, for instance. permissionId takes an absolute permission id ("settings.workspace.invites") and wins when both are set. A button the caller lacks the permission for is stripped from the serialized options rather than disabled, so it never reaches the browser. A declared permission that cannot be resolved to an id — an action on a page that never registered — fails closed and strips the button too.
A button with an id can be exposed as a quick action with a "button" target. The quick action opens what the button opens and inherits the button's permission: it is listed for exactly the callers the button is shown to.
availability is called per request with { tenantId, user } once the permission passed. Returning { reason } (a string or a $ i18n key) serves the button disabled, with the reason in its tooltip; undefined leaves it enabled, and so does a resolver that throws. It only shapes the UI: the operation behind the button must still refuse on its own.
CustomButtonSerialized is CustomButton with target replaced by ActionTargetSerialized and permission, permissionId and availability removed, since both are applied server-side; disabled and disabledReason carry what availability answered. The table view produces it when it serializes its options.
PermissionGate
PermissionGate, imported from @antelopejs/interface-dms/permission-gate, names the shared optional permission?: ButtonPermission and permissionId?: string fields described above. ButtonPermission is string | Action; permissionId takes precedence. The shape is shared by permission-filtered buttons, row actions, table tabs and views. It does not authorize the route that a control calls: protect that route with a guard too.
Action Targets
ActionTarget is a discriminated union on type. It is what a custom button or a custom row action points at.
import type { ActionTarget } from "@antelopejs/interface-dms/base/types/action-target";
type | Fields |
|---|---|
"drawer" | component: Component, title?, description?, direction?: DrawerDirection |
"modal" | component: Component, size?: ModalSize, title?, description? |
"sidePanel" | component: Component, plus SidePanelTargetOptions: title?, rowTitle?, positionLabel?, deepLink?, notFound? |
"page" | url: string: the page URL's {{params.X}} / {{query.X}} (a query pair without a value left out), then on a row action the row's {{row.field}}; see Template tokens |
"external" | url: string, newTab?: boolean; the same tokens as page |
"download" | url: string, filename?: string (DownloadActionTarget); the same tokens as page, the file name taking them as written. Fetched with the user's session and saved under filename, else the server's Content-Disposition name, else the URL's last segment; a failure is toasted |
"quickAction" | id: string (category:id, or the bare id) |
"api" | url: string, method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH", body?, copy?, successMessage: string; the same tokens as page |
"exportJob" | url: string, method?: "GET" | "POST", statusUrl?, downloadUrl?, labels?; the same tokens as page, plus {{job.id}} in statusUrl and downloadUrl |
SidePanelTargetOptions sets the header the DMS draws above a sidePanel target's component: title (the action's label by default; $: an i18n key), rowTitle, the title while a row is open, an i18n key or a literal receiving the row's fields ("{method} {path}"), positionLabel, an i18n key receiving { index } (from 1) and { total } for the row's position among the rows the table shows (the DMS's "{index} of {total}" by default), and deepLink, which keeps the open row in the URL like the action's own deepLink — on by default for a side panel, false opting out (the action's own deepLink wins when set), and notFound (SidePanelNotFoundTexts: title?, description?), the state the panel draws with a close button when the row a link names answers 404. The panel is non-modal and docked on the right, one at a time with a module's side panels.
DrawerDirection is "top" | "right" | "bottom" | "left", the edge the drawer slides in from: bottom by default, while left and right open a side sheet 440px wide (a side panel's default width) spanning the window's height.
A target carries no confirmation: the action does (CustomButton.confirm, CustomRowAction.confirm, RowActionConfig.confirm), with one type, ActionConfirm = ConfirmDialog | ConfirmDialogFrom (see below). The labels object on exportJob accepts title, exporting, downloading, successTitle, successMessage, errorTitle and retry, all optional strings.
const validateEmail: ActionTarget = {
type: "api",
url: "/settings/workspace/members/{{row._id}}/validate-email",
method: "POST",
successMessage: "$page.settings.members.action.validate_email_success",
};
Confirmations
import type {
ActionConfirm,
ConfirmDialog,
} from "@antelopejs/interface-dms/base/types/confirm-dialog";
ConfirmDialog is the one confirmation every action asks in: title, description?, params? (interpolated into the texts, besides a row action's row fields; a numeric count pluralizes them), icon?, color? (ButtonColor), confirmLabel?, cancelLabel?, impact?: ConfirmDialogImpact[] ({ icon, label, count? }), secondaryAction?: ConfirmDialogSecondaryAction ({ label, icon?, target }, a button next to cancel and confirm that leaves the dialog open; its target is an external link or { type: "download", url, filename? }, fetched with the user's session), blocked? (the dialog only explains why the action cannot run), confirmText? (type-to-confirm), confirmTextCaseInsensitive? (the typed text compared ignoring case), fields?: FormField[] and component?: Component. The values of fields are sent with the action's request — merged into an api target's body, the input winning — and a field error the server answers shows under its field. ConfirmDialogFrom is { from: string }: a URL (on a row action, built-in or custom, the row's fields filled in, such as {{row._id}}, plus {{row.id}}, the row's id) answering a ConfirmDialogSerialized, which serializeConfirmDialog(dialog) builds. ActionConfirm is either.
ActionTargetSerialized mirrors the union exactly, with component on the drawer, modal and sidePanel variants replaced by ComponentInfoSerialized.
Row Action Rules
A row action rule decides, per row, whether an action applies. The same rule value is sent to the frontend so the row's menu matches what the backend will accept, and evaluated on the backend before an edit, delete, archive or restore runs.
Field Operators
FIELD_OPERATORS is the catalogue of leaf comparisons. Each entry declares whether the rule value is an array and carries the comparison itself.
import {
FIELD_KEY,
FIELD_OPERATORS,
LOGICAL_OPERATOR_KEYS,
} from "@antelopejs/interface-dms/base/types/row-action-operators";
| Key | isArray | Comparison |
|---|---|---|
equals | false | fieldValue === ruleValue |
notEquals | false | fieldValue !== ruleValue |
in | true | ruleValue.includes(fieldValue) |
notIn | true | !ruleValue.includes(fieldValue) |
FieldOperatorKey is keyof typeof FIELD_OPERATORS. FIELD_KEY is the literal "field", the property name that carries the field name inside a leaf rule. LOGICAL_OPERATOR_KEYS is the tuple ["and", "or", "not"] and LogicalOperatorKey is its element type.
Rule Shapes
A leaf rule is { field: K } plus the operator key. FieldRule<T, Op> builds that shape for one operator over the keys of T, choosing T[K][] for the array operators and T[K] for the others. AnyFieldRule<T> is the union over all four operators.
import type {
AndRule,
FieldEqualsRule,
RowActionRule,
} from "@antelopejs/interface-dms/base/types/row-action";
interface Invoice extends Record<string, unknown> {
status: string;
archived: boolean;
}
const isDraft: FieldEqualsRule<Invoice> = { field: "status", equals: "draft" };
const editable: AndRule<Invoice> = {
and: [isDraft, { field: "archived", notEquals: true }],
};
The four named leaf aliases are FieldEqualsRule<T>, FieldNotEqualsRule<T>, FieldInRule<T> and FieldNotInRule<T>, each an alias of FieldRule<T, Op> for the matching operator. The three combinators are AndRule<T> ({ and: RowActionRule<T>[] }), OrRule<T> ({ or: RowActionRule<T>[] }) and NotRule<T> ({ not: RowActionRule<T> }); LogicalRule<T> is their union. RowActionRule<T> is AnyFieldRule<T> | LogicalRule<T>, so rules nest to any depth.
Attaching a Rule
RowActionConfig<T> is the object form of a built-in row action.
| Option | Type | Description |
|---|---|---|
isEnabled | boolean | Whether the action can be triggered. |
isVisible | boolean | Whether the action is shown. |
rule | RowActionRule<T> | Per-row condition. |
CustomRowAction<T> is an extra action in the row menu:
| Option | Type | Description |
|---|---|---|
label | string | Menu label. Required. |
icon | string | Icon identifier. |
target | ActionTarget | What the action does. Required. |
rule | RowActionRule<T> | Per-row condition. |
isVisible | boolean | Whether the action is shown. |
isDefault | boolean | Make this action the row click target, ahead of edit / details. |
CustomRowActionSerialized is the same shape with target: ActionTargetSerialized and an untyped RowActionRule.
Where these are consumed — TableViewOptions.rowActions, and the archive-mode defaults — is covered in Tables.
Guards
A guard is a function the table view calls before a mutating route runs. Throwing from it aborts the operation.
import type {
GuardFn,
EditGuardArgs,
TableViewGuards,
} from "@antelopejs/interface-dms/base/types/guards";
The imported GuardFn has this signature:
import type { RequestContext } from "@antelopejs/interface-api";
type AfterWrite = () => void | Promise<void>;
type GuardFn<TArgs, TController = unknown> = (
this: TController,
ctx: RequestContext,
args: TArgs,
) => void | AfterWrite | Promise<void | AfterWrite>;
A mutating guard may return an AfterWrite: the table view runs it once the write succeeded, in the same request. It is where work comparing the row before the write (what the guard saw) with the row after belongs, such as a notification; a failure there is logged and does not fail the request, since the write is done. What a get guard returns is ignored.
ctx is the RequestContext from @antelopejs/interface-api. this is the data controller instance, typed as such in TableViewGuards<T> (TController is T there), so a guard written as a function reaches the properties declared with @Model without a cast.
| Interface | Fields |
|---|---|
EditGuardArgs<T> | id: string, body: Partial<T>, current: T |
NewGuardArgs<T> | body: Partial<T> |
DeleteGuardArgs | ids: string[], values?: Record<string, unknown> (the answer of the confirmation's fields; unset when the request carried no body) |
BulkGuardArgs | ids: string[], values?: Record<string, unknown> (as on DeleteGuardArgs) |
TableViewGuards<T> collects one guard per action:
| Key | Guard argument |
|---|---|
edit | EditGuardArgs<T> |
delete | DeleteGuardArgs |
archive | BulkGuardArgs |
restore | BulkGuardArgs |
new | NewGuardArgs<T> |
T is the data controller instance TableView() infers from its controller; it defaults to Record<string, unknown> on every one of them. How guards are attached and when each one fires is documented in Tables.
Form and Field Types
FormMode
FormMode is the enum new, edit, view. It selects which variant of a form the table view generates and which readonly behaviour applies.
ReadonlyBehavior
ReadonlyBehaviorType is an enum with disabled, hidden and default. ReadonlyBehaviorConfig gives one value per mode:
import { ReadonlyBehaviorType } from "@antelopejs/interface-dms/base/types";
const createdAtBehavior = {
new: ReadonlyBehaviorType.hidden,
edit: ReadonlyBehaviorType.disabled,
view: ReadonlyBehaviorType.disabled,
};
ReadonlyBehavior is ReadonlyBehaviorType | ReadonlyBehaviorConfig, so a column may pass a single value that applies to every mode or a per-mode object. It is read by ColumnOptions.readonlyBehavior.
ListableConfig and FieldMetadata
FieldMetadata is the slice of a field's data-api metadata the table view reads when it builds a column or a form field.
| Field | Type | Description |
|---|---|---|
mandatory | Set<FormMode> | Modes in which the field is required. |
mode | AccessMode | Access mode from @antelopejs/interface-data-api/metadata. |
listable | ListableConfig | Which pluck modes include the field. |
sortable | boolean | Whether the field can be sorted on. |
ListableConfig is { list?: boolean; select?: boolean; export?: boolean } — one flag per pluck mode.
Field Values
DefaultValue and FormFieldValue are the same recursive union: string | number | boolean | Date | null and arrays of themselves. DefaultValue types FormField.defaultValue and ColumnOptions.defaultValue; FormFieldValue types a value held by a form field.
ComponentOrNoInput
type ComponentOrNoInput<T = undefined> = Component<T> | "noInput";
The literal "noInput" stands for a compare mode that takes no value — is_empty and is_not_empty are registered that way, and serializeType substitutes the literal for their filter component. The serialized counterpart used by data types is FilterComponent, documented in Data Types.
Transport and Miscellaneous
HttpMethod
HttpMethod is an enum of GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, keyed by the lowercase name (HttpMethod.get, HttpMethod.post, …). Every component with a fetchUrl also accepts a fetchUrlMethod of this type.
AxeOrientation
AxeOrientation is the enum horizontal / vertical. TabProps.orientation, BarChartProps.orientation and FormComponents.RadioGroupOptions.orientation take it.
ExportStatus
ExportStatus is the enum pending, completed, failed. It is the status stored on an export job and returned by the export-status route — see Helpers.
Json Values
type JsonPrimitive = string | number | boolean | null;
type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };
JsonValue describes anything that survives a JSON round trip.
Error Helpers
import {
getErrorMessage,
isErrorWithMessage,
} from "@antelopejs/interface-dms/base/types";
try {
await save();
} catch (error: unknown) {
if (isErrorWithMessage(error)) {
// error is narrowed to ErrorWithMessage
}
const message = getErrorMessage(error); // "Unknown error" when there is none
}
ErrorWithMessage is { message: string }. isErrorWithMessage is a type guard that accepts any object carrying a message property. getErrorMessage returns that message, or the string "Unknown error" for anything else.
Next Steps
- Forms - Build a form, its fields, and its validation schema.