Skip to content

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

5.1 Bindings

  • 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 template is 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 type names: an element type of the metamodel, or a type the type map maps.

5.2 Type map

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; an unreadable one is reported by std.unreadableEntry (6.5).
  • hostAttributes are not visible in CEL, on the surface or to constraints, and a writer keeps their values.

5.3 Identifiers

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.

5.3.1 Generation

  • 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.
  • hex is the RFC 9562 hyphenated lowercase form. base36 is the 128-bit value as an unsigned integer in lowercase base 36, left-padded with 0 to 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.

5.3.2 Per-type rules and derived ids

  • A rule in types applies 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 of expression, evaluated in the identity context (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 of self; 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.

5.3.3 Ephemeral ids

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 reason or, 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.

5.3.4 Missing and duplicate ids

  • 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.missingId or std.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. With missing: "ephemeral", such an element is ephemeral.
  • A runtime MUST NOT change an id to resolve a duplicate, except by a user's action.

5.4 Steps that change two documents

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.

Referenced from