Properties (PropertyDecl)
Generated from the doc schema (CatalogDocSchema, @canonify/kernel-catalog/doc): each field’s type, default and description are the schema’s own, and each example is a bundle file that readBundle decodes. A field’s Example is its value in one of those examples.
PropertyDecl
A property of a type.
One of, told apart by kind:
PropertyDecl.stored
A stored value.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
kind |
"stored" |
yes | — | "stored" |
A stored property: a value in its own column. |
id |
PropId | yes (propose assigns it; a new declaration leaves it out) | — | "pr_2h6k8m4q0z" |
A property id (pr_…); renames keep it. |
name |
Ident | yes | — | "name" |
The property name. |
label |
Text | yes (a draft may leave it out: propose writes a default made from the name, catalog.label_defaulted) |
— | "Name" |
The words shown for it. |
type |
ValueType | yes | — | {"t":"text"} |
What the property holds. |
required |
boolean | no | false |
true |
Whether every row must have a value; default false. |
default |
Expr | SequenceDefault | no | — | "today()" |
The value of a new row whose create leaves it blank: a constant of the type, today() / now(), or a sequence (a text or integer property). |
history |
boolean | no | — | true |
Whether every change of the value is kept as history. |
searchable |
SearchSpec | no | — | {"weight":"body"} |
Whether and how the property is searched. |
sensitive |
"personal" | "secret" |
no | — | "personal" |
Personal data is masked for readers without the right; a secret is never shown (authz). |
migrate |
PropertyDecl.stored.migrate | no (a draft’s only: propose takes it out before the hash, and a stored doc refuses it) | — | (see the example) | How this change converts the rows that exist; absent = the change needs no transform. |
An item of properties in a type file (types/<name>.json).
{
"kind": "stored",
"name": "email",
"label": "Email",
"type": { "t": "text", "format": "email" },
"required": true
}
An item of properties in a type file (types/<name>.json).
{
"kind": "stored",
"name": "placed_on",
"label": "Placed on",
"type": { "t": "date" },
"required": true,
"default": "today()",
"history": true,
"searchable": { "weight": "body" },
"sensitive": "personal"
}
An item of properties in a type file (types/<name>.json).
{
"kind": "stored",
"name": "priority",
"label": "Priority",
"type": { "t": "integer" },
"required": true,
"migrate": {
"fill": "band_rank",
"convert": "band_rank",
"map": { "urgent": "high" },
"inverse": "band_rank"
}
}
PropertyDecl.ref
A reference to another row.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
kind |
"ref" |
yes | — | "ref" |
A reference to one row of another type. |
id |
PropId | yes (propose assigns it; a new declaration leaves it out) | — | "pr_2d4f6h8k0m" |
A property id (pr_…); renames keep it. |
name |
Ident | yes | — | "customer" |
The property name. |
label |
Text | yes (a draft may leave it out: propose writes a default made from the name, catalog.label_defaulted) |
— | "Customer" |
The words shown for it. |
target |
TypeId | yes | — | "ty_7k2m9q4z1a" |
The type it refers to. |
required |
boolean | no | false |
true |
Whether every row must have a value; default false. |
onDelete |
"restrict" | "cascade" | "set_null" |
yes | — | "cascade" |
What deleting the target row does: refuse, delete this row too, or clear the reference. |
history |
boolean | no | — | true |
Whether every change of the value is kept as history. |
searchable |
SearchSpec | no | — | {"weight":"title"} |
Whether and how the property is searched. |
inverse |
PropertyDecl.ref.inverse | no | — | {"name":"orders","label":"Orders"} |
The reverse reference on the target (C22); default name = this type’s name. |
settles |
boolean | no | true |
true |
Whether a terminal state of the target locks this row (runtime); default true. |
sensitive |
"personal" | "secret" |
no | — | "personal" |
Personal data is masked for readers without the right; a secret is never shown (authz). |
migrate |
PropertyDecl.ref.migrate | no (a draft’s only: propose takes it out before the hash, and a stored doc refuses it) | — | (see the example) | How this change converts the rows that exist; absent = the change needs no transform. |
An item of properties in a type file (types/<name>.json).
{
"kind": "ref",
"name": "customer",
"label": "Customer",
"target": "ty_7k2m9q4z1a",
"required": true,
"onDelete": "restrict",
"history": true,
"searchable": { "weight": "title" },
"inverse": { "name": "orders", "label": "Orders" },
"settles": false,
"sensitive": "personal",
"migrate": {
"fill": "first_customer",
"convert": "first_customer",
"inverse": "customer"
}
}
PropertyDecl.ref.inverse
The reverse reference on the target (C22); default name = this type’s name.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
name |
Ident | yes | — | "orders" |
The name CX reads the reverse collection by, on the target. |
label |
Text | yes (a draft may leave it out: propose writes a default made from the name, catalog.label_defaulted) |
— | "Orders" |
The words shown for the reverse collection. |
PropertyDecl.derived
A value computed from the row.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
kind |
"derived" |
yes | — | "derived" |
A derived property: a CX expression over the row, never stored. |
id |
PropId | yes (propose assigns it; a new declaration leaves it out) | — | — | A property id (pr_…); renames keep it. |
name |
Ident | yes | — | "total" |
The property name. |
label |
Text | yes (a draft may leave it out: propose writes a default made from the name, catalog.label_defaulted) |
— | "Total" |
The words shown for it. |
params |
ParamDecl[] | no | — | [{"name":"day","type":"date"}] |
The parameters a reader passes (cx.md §2.10). |
expr |
Expr | yes | — | "lines.sum(l => l.amount)" |
The CX expression it computes. |
type |
ValueType | no | — | {"t":"money","currency":"DKK"} |
The asserted type; compile checks it equals the inferred type (derived.type_mismatch). |
visibility |
"over_readable" | "whole_or_withheld" |
yes | — | "over_readable" |
Over the rows the reader may read, or withheld unless the reader may read every row it uses. |
searchable |
SearchSpec | no | — | {"weight":"body"} |
Whether and how the property is searched. |
An item of properties in a type file (types/<name>.json).
{
"kind": "derived",
"name": "total",
"label": "Total",
"expr": "lines.sum(l => l.amount)",
"type": { "t": "money", "currency": "DKK" },
"visibility": "over_readable",
"searchable": { "weight": "body" }
}
An item of properties in a type file (types/<name>.json).
{
"kind": "derived",
"name": "open_on",
"label": "Open on",
"params": [{ "name": "day", "type": "date" }],
"expr": "placed_on <= day",
"visibility": "whole_or_withheld"
}
PropertyDecl.state
The row’s lifecycle state.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
kind |
"state" |
yes | — | "state" |
A state property: the row’s state in a lifecycle. |
id |
PropId | yes (propose assigns it; a new declaration leaves it out) | — | — | A property id (pr_…); renames keep it. |
name |
Ident | yes | — | "status" |
The property name. |
label |
Text | yes (a draft may leave it out: propose writes a default made from the name, catalog.label_defaulted) |
— | "Status" |
The words shown for it. |
lifecycle |
FsmId | yes | — | "lc_2k4m6p8r0t" |
The lifecycle it follows. |
history |
true |
no | — | true |
A state always keeps its history. |
An item of properties in a type file (types/<name>.json).
{
"kind": "state",
"name": "status",
"label": "Status",
"lifecycle": "lc_2k4m6p8r0t",
"history": true
}
PropertyDecl.stored.migrate
How this change converts the rows that exist; absent = the change needs no transform.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
fill |
Expr | no | — | "band_rank" |
The value of each existing row when the property is added as required. |
convert |
Expr | no | — | "band_rank" |
The new value of each row when the property is retyped; null means the row cannot be converted. |
inverse |
Expr | no | — | "band_rank" |
The old value of each row, which makes a retype exact on rewind. |
map |
Record<Ident, Ident> | no | — | {"urgent":"high"} |
Old enum code to new code, for rows that hold a value this change drops. |
PropertyDecl.ref.migrate
How this change converts the rows that exist; absent = the change needs no transform.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
fill |
Expr | no | — | "first_customer" |
The value of each existing row when the property is added as required. |
convert |
Expr | no | — | "first_customer" |
The new value of each row when the property is retyped; null means the row cannot be converted. |
inverse |
Expr | no | — | "customer" |
The old value of each row, which makes a retype exact on rewind. |
SequenceDefault
A number the write engine allocates when a create leaves the property blank: gap-free per scope, in the write transaction.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
sequence |
string | yes | — | "FV/{year}/" |
The template the number follows: literal text and the date parts {year} and {month}, which set the scope (one counter per year, per month, or one for the property). |
start |
integer (greater than or equal to 0) | no | 1 |
1001 |
The first number of each scope; default 1. |
digits |
integer (between 1 and 12) | no | — | 4 |
Writes the number with at least this many digits, zero-padded (a text property only). |
The default of a stored text or integer property.
{ "sequence": "FV/{year}/", "start": 1001, "digits": 4 }