Skip to content

14. Processing model

Contents of DISL 0.4

14.1 Loading a specification

  1. Parse the JSON; reject duplicate keys.
  2. Check versions: disl major version supported (or its deprecated alias, section 18).
  3. Resolve imports recursively, verify integrity hashes, detect cycles, apply include filters, prefix names with aliases.
  4. Validate structure against the JSON Schema $defs/Specification.
  5. Resolve names: every type, style, shape, marker, icon, form, tool, snap profile, layout, operation, function and plugin reference must resolve.
  6. Flatten inheritance: compute C3 linearisations; merge attributes, ports, containment; check narrowing rules (4.7).
  7. Derive built-ins: generate built-in constraints (8.7), default notations, default forms and the default toolbox where absent.
  8. Compile CEL: parse and type-check every expression in its context (12.3); estimate costs against language.limits.celCost; check user functions for recursion other than the bounded self-recursion of 3.4, and plugin function names for clashes (13.1.1).
  9. Check semantic rules not expressible in JSON Schema: reserved attribute names, placement attribute types compatible with axes, snapping rules compatible with axis kinds, x2 vs width exclusivity, handle parameters exist, label editable requires attribute binding, exactly one of path/parts/svg/plugin in shapes, (DISL 0.4) a compartment's groupBy names an enumeration attribute of every item type and its collapsed keys are values of that enumeration, a force layout's tiers name node types, view.bind is used only with format: "fbl" for kinds view.store lists, and all other MUST statements of this document.
  10. Build the runtime model.

Validators report every error or warning in a specification with the JSON Pointer of its location, a severity (error makes the specification unusable; warning does not) and a message.

14.2 Loading a DID definition

A runtime loads a stored diagram as DID specifies (DID-specification.md, section 8.1): it checks the language id and version against the specification's, runs this specification's migrations (11.9), validates, resolves view data, evaluates live constraints and renders.

Loading is tolerant of ids (11.5.4). A runtime MUST load a model in which ids are missing or duplicated rather than refuse it, and MUST in this order:

  1. read the elements in reading order (8.6), reporting std.missingId for each element without a usable id and, following persistence.ids.compare, std.duplicateId for each second and later element with an id already used;
  2. give an element without a usable id a new id under missing: "assign" (held in memory and written with the next save, never on open) or treat it as ephemeral under missing: "ephemeral", and treat every second and later holder of a duplicated id as ephemeral;
  3. compute the derived ids (11.5.2), nodes before relations, letting references follow a computed id that differs from the stored one;
  4. compute the derived elements (4.11.5), derived node types then derived relation types, each in declaration order;
  5. resolve view data, ignoring and reporting by std.ephemeralViewData any view data, style override or suppression keyed by an ephemeral id (11.5.3); a view key that names no element once derived elements are computed is kept and ignored.

A model read through FBL is loaded in the same order, with FBL's reading order and FBL raising the reader findings.

For a diagram whose persistence format is "fbl", the body is read through its binding and the view data comes from the registration, as FBL specifies (FBL-specification.md, section 14.2).

14.3 Unknown content

Unknown types, attributes and view properties in a stored diagram are preserved as DID specifies (DID-specification.md, section 8.2), and unknown elements are reported by std.typeExists.

14.4 The editing transaction

Every user gesture, form commit, operation or quick fix is one transaction:

The editing transaction. A gesture passes through gesture constraints, which may prevent it, then snapping, then the change is applied. Then run the before hooks, which may abort, the after hooks, and the recomputation of derived values and bindings. Invariants with enforcement prevent then either reject the transaction, which rolls it back, or let it commit as one undo step. After the commit, live constraints are evaluated, debounced, and the diagram is rendered.
Diagram as text (Mermaid)
---
config:
  flowchart:
    rankSpacing: 20
---
flowchart TB
  accTitle: The editing transaction
  accDescr: A gesture passes through gesture constraints, which may prevent it, then snapping, then the change is applied. Then run the before hooks, which may abort, the after hooks, and the recomputation of derived values and bindings. Invariants with enforcement prevent then either reject the transaction, which rolls it back, or let it commit as one undo step. After the commit, live constraints are evaluated, debounced, and the diagram is rendered.
  gesture([gesture]) --> gestureConstraints["gesture constraints<br/>prevent?"]
  gestureConstraints --> snapping[snapping] --> apply[apply change]
  apply --> before["#quot;before#quot; hooks<br/>may abort"]
  before --> after["#quot;after#quot; hooks"] --> recompute["recompute derived<br/>values & bindings"]
  recompute --> invariants["invariants with<br/>enforcement #quot;prevent#quot;"]
  invariants -- reject --> rollback([roll back])
  invariants --> commit["commit<br/>(one undo step)"]
  commit --> live["live constraints<br/>(debounced)"] --> render([render])

Transactions are atomic: either all effects apply or none. Remote changes from collaborators apply as transactions without hooks but with constraint evaluation. Changes of viewer state (11.6) and the steps of a simulation (9.6) are not transactions: they change no model, run no hook or constraint, and create no undo step.

The "recompute derived values & bindings" step also recomputes derived ids (11.5.2) and rewrites references to any id that changed, and then recomputes the derived elements (4.11.5), all before invariants are checked. Expressions evaluated later in the transaction see derived elements as of the working state. A runtime MAY recompute incrementally, but the result MUST equal a full recomputation. Derived elements are never part of the undo step: undo and redo restore the stored elements and recompute the derived ones, and the selection and stored view data follow them by id. Ids the transaction generates for new elements are generated once, when it is first applied; redo re-creates the elements with the same ids and is refused when that would duplicate an id (11.5.1).

14.5 Saving

  1. Evaluate constraints with save timing. If blockSaveOn: "error" and errors exist, ask the user (saving MUST remain possible under a different name or as a draft if the runtime supports drafts).
  2. Write the DID definition canonically and atomically, as DID specifies (DID-specification.md, section 8.3). With format: "fbl", write the body's edits as FBL specifies (FBL-specification.md, section 6.6) instead.

Referenced from