Value Encodings
Overview
Configuration values are plain JSON by default. AddBlockInput.config, a ConfigureBlock patch, a page's permission — all of it is written to source exactly as you pass it, as a literal.
Some options need TypeScript values rather than JSON literals. A form field needs a DataType instance, a relation needs a controller class, and a chart card needs a nested chart. The structured encodings below let the builder write those values and their imports. Use a raw $expr only as an escape hatch; drafts and query parameters reject it.
import type {
BlockValue,
DataTypeValue,
ExprValue,
RefValue,
} from "@antelopejs/interface-dms-builder";
Choose an Encoding
| You are writing | Encoding |
|---|---|
| A string, number, boolean, array or object | The value itself, as JSON. |
A value the catalog marks x-dataType | DataTypeValue |
| A reference to a builder-generated resource class | RefValue |
A nested component in an option marked x-component | BlockValue |
| Anything else that must reach the source as code | ExprValue |
The catalog is what tells you which option needs which: an option marked x-dataType in a block's ConfigSchema takes a DataTypeValue, and nothing else. See Catalog.
Write a DataType
DataTypeValue emits new <DataType>(<config>) and adds the import for you.
| Field | Type | Description |
|---|---|---|
$dataType | string | A DataTypeDescriptor.id from the catalog, e.g. "string" or "select". |
config | Record<string, unknown> | The DataType's options. Omit for a no-arg new <DataType>(). |
import { AddBlock } from "@antelopejs/interface-dms-builder";
await AddBlock({
page: "/customers",
name: "editor",
type: "Form",
config: {
fields: [
{
id: "name",
label: "Name",
type: { $dataType: "string", config: { placeholder: "Name" } },
},
],
},
});
// emits: type: new DefaultDataTypes.StringType({ placeholder: "Name" })
The same encoding is what a resource field's dataType takes, described in Resources.
A $dataType id the catalog does not list fails with invalid_config, and the message points you at GetCatalog for the valid ids.
Reference a Generated Class
RefValue emits the bare class identifier of a builder-generated resource, with its import added. It is the structured, validated alternative to ExprValue for a class reference — a RelationType's dataApiController, for instance.
| Field | Type | Description |
|---|---|---|
$ref.resource | string | A ResourceRef — the resource's name, e.g. "product". |
$ref.as | "dataApi" | "table" | "model" | Which of the resource's classes to reference. Defaults to "dataApi". |
import { AddField } from "@antelopejs/interface-dms-builder";
await AddField("order-line", {
name: "productId",
label: "Product",
dataType: {
$dataType: "relation",
config: { dataApiController: { $ref: { resource: "product" } } },
},
});
// emits: dataApiController: productDataAPI (imported from the resource's data-api.ts)
as selects among the three classes a resource generates: "dataApi" for the DataController, "table" for the Table, "model" for the BasicDataModel. An unknown resource fails with invalid_config, and the message points you at ListResources.
AddBlockInput.controller is the same reference in a dedicated field: a controller-leading block type takes a ResourceRef there rather than a RefValue in its config. See Page Operations.
Write a Raw Expression
ExprValue is emitted verbatim. Reach for it only when the builder cannot model the value structurally.
| Field | Type | Description |
|---|---|---|
$expr | string | TypeScript source, spliced into the emitted file as written. |
import { ConfigureBlock } from "@antelopejs/interface-dms-builder";
await ConfigureBlock("/revenue#total", {
valueFormat: { $expr: "REVENUE_FORMAT" },
});
Two constraints come with it:
- The referenced symbol must already be in scope in the target file.
$expradds no import. $expris refused inside a query's parameters — a query's values are data, and a raw expression would put uncheckable code inside a model body. See Queries.
Consumers that run the builder in a restricted mode may refuse $expr outright, since arbitrary code defeats the structured guarantees the rest of the surface provides. RefValue is allowed in that mode precisely because it is validated; prefer it whenever the value you need is a generated class.
Nest a Block in a Config Value
A few options hold a component rather than data — a ChartCard's chart, marked x-component in the catalog. BlockValue is how you write one: the builder emits the nested factory call and adds its import.
import type { BlockValue } from "@antelopejs/interface-dms-builder";
const chart: BlockValue = {
$block: { type: "ChartArea", config: { xaxisType: "datetime" } },
};
// → chart: ChartArea({ xaxisType: "datetime" })
type is a catalog key, like AddBlockInput.type; an unknown one is rejected with invalid_config. Read-back is symmetrical: a nested factory call whose callee is a known block type comes back as a $block sentinel rather than as $expr, so the value stays editable.
Most nesting does not go through config — it goes through .child, which you express with AddBlockInput.parent or with BlockDraft.children. Reach for $block only where the catalog marks an option x-component.
Next Steps
- Results and Errors - Branch on an operation's result and read its diagnostics