Addressing and Structure
Overview
Every builder operation identifies its target with a string ref. This page covers PageRef for a page, BlockPath for a block inside it, and CategoryRef for a menu category. Resources and Queries define their own refs.
Alongside the refs, this page describes the read shapes the builder hands back: the page metadata it can read and patch, the block tree it resolves from source, and the lightweight summaries the listing operations return. The operations that produce and consume these shapes live in Page Operations.
import type {
BlockPath,
CategoryRef,
PageRef,
} from "@antelopejs/interface-dms-builder";
const page: PageRef = "/dashboard";
const block: BlockPath = "/dashboard#kpis/row/revenue";
const category: CategoryRef = "pages.reports";
Address a Page
PageRef is a page's route string, built from its category's slug and the page's own name — a page named revenue under the root pages category is /revenue. CreatePage returns the ref it assigned, and every page operation takes it as its first argument.
import {
CreatePage,
GetPageStructure,
} from "@antelopejs/interface-dms-builder";
const created = await CreatePage({
name: "revenue",
displayName: "Revenue",
category: "pages",
});
if (created.ok) {
const structure = await GetPageStructure(created.data.ref); // "/revenue"
}
Address a Block
BlockPath addresses one block inside a page: <pageRef>#<name> for a top-level block, then /<childId> for each nesting level below it.
| Path | What it addresses |
|---|---|
/dashboard#kpis | The top-level block named kpis on /dashboard |
/dashboard#kpis/row | The .child("row", …) of that block |
/dashboard#kpis/row/revenue | The .child("revenue", …) of row |
The first segment after # is the static class member the block is declared as; every further segment is the id passed to .child(). AddBlock returns the path of the block it added, so you rarely build one by hand.
Address a Category
CategoryRef is a category's stable id. The three roots are pages, modules and settings; a category created under a parent is addressed by joining them with a dot, so a reports category under pages is pages.reports.
import { CreateCategory } from "@antelopejs/interface-dms-builder";
const category = await CreateCategory({
name: "reports",
displayName: "Reports",
parent: "pages",
});
// category.data.ref === "pages.reports"
Read a Page's Metadata
PageMeta is what the builder knows about a page. It comes back inside PageStructure, which GetPageStructure returns.
| Field | Type | Description |
|---|---|---|
ref | PageRef | The page's route string. |
id | string | The page id, the first argument of its PageController. |
displayName | string | The label shown in the menu. |
icon | string | Icon name, when the page declares one. |
category | CategoryRef | The category the page belongs to. |
order | number | Sort order within the category. |
description | string | The page's description. |
hidden | boolean | Whether the page is hidden from the menu. |
permission | PagePermission | The permission guarding the page. |
live | PageLive | The page's Direct / Pause options, when it is live. |
filepath | string | The file the page is declared in, resolved by the builder's source index rather than by the runtime registry. |
hidden, permission and live read back like any other field, as long as the page declares them literally. A permission built from a call or a reference — an Action, say — reads back as undefined: the builder reports only what it can also rewrite.
PagePermission mirrors the DMS MenuOptions.permission: a partial permission spec, whose fields are id, title, icon, description, dependencies and defaultGranted. A bare action id is not accepted — the DMS types the option as Partial<Permission> | Action, so a string would be written and then refused by the typecheck gate.
import type { PagePermission } from "@antelopejs/interface-dms-builder";
const permission: PagePermission = {
id: "reports.export",
title: "Export reports",
};
The DMS also accepts an Action there. That is a live object rather than data, so it has no encoding in the builder: a page declaring one reads its permission back as undefined.
PageLive mirrors the DMS MenuOptions.live: maxRate, slowRateAfter, rememberKey. Present, even empty ({}), the page is live — its blocks refetch through a Direct / Pause valve its LiveToggle switches; absent, they refetch on every event. Sent as null in a patch, it is taken off.
Patch a Page's Metadata
EditablePageMeta is the subset of PageMeta that ConfigurePage accepts as a patch: displayName, icon, category, order, description, hidden, permission and live. ref, id and filepath are not patchable — they follow from where the page lives.
Patching category moves the page, which changes its ref: the result carries the new one, and a route_changed warning says what the old route was. See Page Operations.
import type { EditablePageMeta } from "@antelopejs/interface-dms-builder";
const patch: Partial<EditablePageMeta> = {
displayName: "Monthly revenue",
order: 10,
};
Read the Block Tree
BlockNode is one block in a page's tree. The builder resolves each static member and each .child() call it can parse; anything it cannot parse still appears as a node, marked not editable.
| Field | Type | Description |
|---|---|---|
path | BlockPath | The block's address. |
name | string | Static key for a top-level block, .child() id for a nested one. |
type | string | null | The factory name, or null when it does not resolve. |
editable | boolean | false marks an opaque, non-canonical block. |
opaqueReason | string | Why the block is opaque. Set when editable === false. |
slot | string | The slot the block sits in, when its parent declares slots. |
meta | Record<string, unknown> | Child metadata beyond slot, such as { colSpan: 2 }. Keep it when constructing a draft from the tree. |
config | Record<string, unknown> | The block's options object. Present only when the block is editable. |
controller | ResourceRef | For a controller-leading block such as TableView, the resource its leading argument resolves to, when it is a known generated resource. |
children | BlockNode[] | Container blocks only, in .child() order. |
An opaque block is one the builder can see but not rewrite — a block behind an unresolved reference, or one produced by a computed expression rather than a direct factory call. Reading it is fine; ConfigureBlock, AddBlock under it and MoveBlock onto it fail with opaque_target, described in Results and Errors.
import { GetPageStructure } from "@antelopejs/interface-dms-builder";
const result = await GetPageStructure("/dashboard");
if (result.ok) {
for (const block of result.data.blocks) {
if (!block.editable) {
console.log(`${block.path} is opaque: ${block.opaqueReason}`);
}
}
}
Read a Page's Structure
PageStructure is the whole page as the builder sees it.
| Field | Type | Description |
|---|---|---|
page | PageMeta | The page's metadata. |
blocks | BlockNode[] | The top-level blocks, each carrying its own children. |
queries | QueryStructure[] | The query routes declared on the page, found by shape. See Queries. |
version | string | A content hash of the page file. |
version is what makes a write conditional: pass it back as expectedVersion on the next mutation and the operation fails with stale if the file changed in between. See Results and Errors.
Read the Listings
ListPages and ListCategories return summaries rather than full structures, so you can survey a project without parsing every page.
PageSummary:
| Field | Type | Description |
|---|---|---|
ref | PageRef | The page's route string. |
id | string | The page id. |
displayName | string | The label shown in the menu. |
category | CategoryRef | The category the page belongs to. |
moduleId | string | The module the page comes from, for pages declared inside a module. |
filepath | string | The file the page is declared in. |
icon | string | The icon the menu shows beside the page, when it declares one. |
order | number | Sorts the page among the others of its category, when it declares one. |
hidden | boolean | Whether the menu leaves the page out; its route still answers. |
CategorySummary:
| Field | Type | Description |
|---|---|---|
ref | CategoryRef | The category's id. |
displayName | string | The label shown in the menu. |
parent | CategoryRef | The parent category, absent for a root. |
import {
ListCategories,
ListPages,
} from "@antelopejs/interface-dms-builder";
const [pages, categories] = await Promise.all([
ListPages(),
ListCategories(),
]);
const rootPages = pages.filter((page) => page.category === "pages");
Patch a Category's Metadata
EditableCategoryMeta is what ConfigureCategory accepts: displayName, parent, icon and order. Re-parenting a category is refused — every page beneath it would change route at once; see Page Operations.
import type { EditableCategoryMeta } from "@antelopejs/interface-dms-builder";
const patch: Partial<EditableCategoryMeta> = { displayName: "Reports", order: 2 };
Next Steps
- Catalog - Discover the block types and DataTypes you can emit