5. Persistence
Contents of DESL 0.1
| Property | Type | Description |
|---|---|---|
format |
"fbl" |
Required. The only format of DESL 0.1. |
bindings |
BindingRef[] | Required, at least one. One FBL binding per format family a document of this type may be written in, each <document>#<name> (FBL section 2.3), resolved against the specification's own location. |
typeMap |
map binding type → TypeMapEntry | How the bindings' types and attributes map onto the metamodel (5.2). |
ids |
IdStrategy | How elements get their ids (5.3). |
doc |
Doc |
- The bindings MUST read the same model: for the same content, every binding MUST yield the same elements, attributes and findings, so that a document's format never changes what it means.
- Two bindings MUST NOT claim the same extension or the same file name (FBL section 12.1). A host opening a body uses the binding whose claims take it (FBL section 8.1).
- Each binding's
templateis the body of a new document in its format. A host that creates a document MUST let the user choose the format at that moment, and MUST NOT offer to change a document's format afterwards: that would rewrite every byte. - A rule of a binding produces elements of the type its
typenames: an element type of the metamodel, or a type the type map maps.
A binding may name its types and attributes as its format does rather than as the metamodel does. typeMap maps each binding type, by its name, to a TypeMapEntry ($defs/TypeMapEntry):
| Property | Type | Description |
|---|---|---|
as |
"document", "header", "unreadable" or TypeRef |
Required. What the binding's entries of this type become. |
attributes |
map binding attribute → attribute name or null |
The binding's attribute names mapped to the metamodel's; unlisted attributes keep their names, and null leaves one out of the model. |
hostAttributes |
string[] | Attributes the binding reads and writes that are not attributes of the model. |
doc |
Doc |
- Without an entry, a binding type and its attributes MUST have the names of an element type and its attributes.
as: "document"makes the entry's attributes the document's (metamodel.document)."header"and"unreadable"keep the entry out of the model; anunreadableone is reported bystd.unreadableEntry(6.5).hostAttributesare not visible in CEL, on the surface or to constraints, and a writer keeps their values.
persistence.ids ($defs/IdStrategy) says how elements are identified: one rule for every type and, in types, rules for single types that override it.
| Property | Type | Description |
|---|---|---|
strategy |
"uuid-v4", "uuid-v7" (default), "derived" |
How ids are made: a random UUID, a time-ordered UUID, or computed from the model (5.3.2). |
encoding |
"hex" (default), "base36" |
The text form of a UUID (5.3.1). |
stable |
bool | An id never changes once it is made. Default true. No effect on derived ids. |
missing |
"assign" (default), "ephemeral" |
What happens to an element read without a usable id (5.3.4). Top level only. |
types |
map TypeRef → IdRule | Per-type rules (5.3.2). Top level only. |
doc |
Doc |
An IdRule ($defs/IdRule) has strategy, expression (required for derived), ephemeral (bool or Expression), reason (a Reason) and doc.
- Ids MUST be treated as opaque strings: a runtime MUST NOT decode one to recover a UUID, a time or a key. A hand-written id MUST be accepted whatever the strategy.
hexis the RFC 9562 hyphenated lowercase form.base36is the 128-bit value as an unsigned integer in lowercase base 36, left-padded with0to 25 characters.- An id for a new element MUST be generated once, when the transaction is first applied. Redoing the transaction MUST re-create the element with the same id, and a redo that would duplicate an id MUST be refused.
- A rule in
typesapplies to the named type; properties it leaves out are taken from the top-level rule. - With
derived, an element's id MUST be the value ofexpression, evaluated in theidentitycontext (9.3), which returns a string. It MUST be recomputed after the document is read and in every transaction before constraints are checked (10.3). An identity expression MUST NOT read the id of any element other than an ancestor ofself; a validator MUST reject one that visibly does. A runtime computes ids parents before children. - A derived id MUST NOT be altered to make it unique: two elements with one derived id are reported by
std.duplicateId(5.3.4). - When a derived id changes in a transaction, the runtime MUST rewrite every reference to it in the same transaction.
"ids": { "strategy": "uuid-v4", "encoding": "base36", "stable": true,
"types": { "Cell": { "strategy": "derived", "expression": "self.parent.id + '/' + self.property.id" } } }
A cell is its row and its property: the cell of row r1 for property p3 is r1/p3, and it stores no id of its own.
An element is ephemeral when its rule's ephemeral is true, or an expression that is true for it. Its id identifies it within one reading of the document only, such as a filter condition numbered by its place.
- A runtime MUST NOT store a reference by an ephemeral id, or anything else keyed by one.
- A gesture whose effect would be such a write MUST be refused before it is applied, with the rule's
reasonor, without one, a runtime sentence saying that the element's identity does not survive re-reading. - An ephemeral element MAY be selected, edited, and targeted by findings and operations within the session.
- All elements of one document share one id space.
- A runtime MUST open a document in which ids are missing or duplicated, and MUST report each case by
std.missingIdorstd.duplicateId(6.5). It MUST NOT refuse the document for it. - Of the elements that share an id, the first in reading order (10.2) MUST keep it: every reference resolves to it. The second and later are shown, and are ephemeral until the duplication is resolved.
- With
missing: "assign", an element without a usable id MUST receive a new one from its rule. The new id MUST NOT be written when the document is opened or validated, and MUST be written with the next edit that writes the document. Withmissing: "ephemeral", such an element is ephemeral. - A runtime MUST NOT change an id to resolve a duplicate, except by a user's action.
A step that changes two documents at once (a two-way reference between two files, a renamed file that others name) MUST be one undoable step in the host, whose undo restores both bodies, and which writes neither when either edit is refused. FBL keeps one history per body (FBL section 7.1); making the two one step is the host's.