ACTION_STATUSES
re-export| Package | @formscript/engine |
|---|---|
| Specification | 4.26, 4.27, 4.28, 4.35, 5.2, 5.5, 5.8, 6.1, 6.3, 7.11, 8.3, 8.5, 8.6, 8.7, 8.8, 8.9, 8.10, 9, 9.1, 9.2, 9.3, 9.4, 9.5, 11 |
| Depends on | ast, decimal, errors, evaluator, scope, types, validation |
@formscript/engine
The reactive evaluation engine of the FormScript Language Specification 1.0.0, §9.
The engine owns the runtime state of a form and evaluates it. A transaction is the only way the state changes, and the eight steps of Spec-9.6 are executed literally and in order:
- fix
$nowfor the whole transaction (Spec-9.8), - apply the inputs — content, touched flag, override source, relation holder —
and abort with
E-REL-02when two members of one relation are assigned (Spec-8.12), - take the transitive closure of the changed nodes over the dependency graph,
- topologically sort the dirty set, breaking ties by document order (Spec-9.9),
- evaluate the dirty nodes in one pass (Spec-9.7); an error value that reaches
a non-field node aborts with
E-EVAL-07, - judge the validity of every field (Spec-10.13),
- project the output document through the
outputblock (Spec-4.35), - commit, then apply the effects (Spec-8.8).
Specification sections implemented
| Section | What this package implements |
|---|---|
| §9.1 | The determinism contract: one snapshot sequence per (document, profile, environment, action sequence) |
| §9.2 | The dependency graph, its node kinds and the cycle check (Spec-9.3, Spec-9.4) |
| §9.3 | Transactions, dirty closure, topological order, single evaluation pass |
| §9.4 | Derived fields, overridable fields (Spec-9.10, Spec-9.11) |
| §9.5 | Structural propagation of a repeat (Spec-9.12, Spec-9.13) |
| §5.2, §5.5, §5.8 | The three axes of a field and the read rule |
| §6.1 | The error value |
| §4.35 | The output projection |
| §7.11 | Repeat element identity and reindexing |
| §8.3, §8.8, §8.9, §8.10 | Applicability, effects, overridable fields, snapshots |
| §8.5, §8.6, §8.7 | state declarations: the members of a state, the root scope they live in |
| §6.3 | $action_name.status and $source_name.status |
| §4.26, §4.27, §4.28 | The choice set of a field, declared or taken from a source |
Validation itself lives in @formscript/validation; this package calls it and
owns the transaction around it.
Public API
class Engine {
constructor(document: Document, options?: EngineOptions);
readonly model: FormModel;
readonly diagnostics: Diagnostic[]; // static diagnostics, including cycle detection
get validity(): FormValidationResult;
get output(): Record<string, unknown> | null;
valueOf(path: readonly string[]): unknown;
stateOf(path: readonly string[]): FieldRuntime | null;
choiceSetOf(path: readonly string[]): ChoiceSet | null;
applyInput(assignments: readonly Assignment[]): TransactionResult;
mutateRepeat(path: readonly string[], mutation: RepeatMutation): TransactionResult;
submit(): TransactionResult;
exportSnapshot(): FormSnapshot;
restore(snapshot: FormSnapshot): TransactionResult;
}
Also exported: buildFormModel, buildDependencyGraph, RuntimeState,
pathOfExpression and serializeValue.
The graph
function buildDependencyGraph(model: FormModel, options: GraphOptions): DependencyGraph;
interface DependencyGraph {
readonly nodes: readonly GraphNode[];
readonly nodeById: ReadonlyMap<string, GraphNode>;
readonly reads: ReadonlyMap<string, readonly string[]>;
readonly dependents: ReadonlyMap<string, readonly string[]>;
findCycle(): readonly string[] | null;
dirtyFrom(changed: Iterable<string>): ReadonlySet<string>;
topologicalOrder(dirty: ReadonlySet<string>): readonly string[];
}
Node ids are stable and readable: value:<path>, applicability:<path>,
derived:<path>:<property>, collection:<path>, relation:<name>:<path>,
state:<name> and form:valid.
The host supplies what the engine cannot fetch
The engine is synchronous and performs no I/O (Spec-9.1). What only the host
knows is passed through EngineOptions:
interface EngineOptions {
readonly actionStatus?: (name: string) => ActionStatus | undefined;
readonly sourceStatus?: (name: string) => SourceStatus | undefined;
readonly sourceValue?: (name: string) => unknown;
}
$action_name.status reads idle | pending | success | error (Spec-6.3,
Spec-11.2) and $source_name.status reads idle | pending | ready | error
(Spec-6.3, Spec-11.7); a declared action or source with no provider reads
idle. sourceValue is the value a source produced, from which the choice set
of a field that declares optionsFrom is materialised (Spec-4.26).
Judgement calls
$form.validobserves the previous transaction's judgement. Step 5 (evaluation) precedes step 6 (validity) in Spec-9.6, so an expression evaluated in step 5 cannot see the validity computed in the same step. The engine therefore resolves$form.validto the validity committed at the end of the last transaction, and the nodes that read it are refreshed in every transaction (they depend on the syntheticform:validnode, which is always dirty). The value is deterministic, which is what Spec-9.1 requires.- The applicability of an entity depends on the applicability of its ancestors. Spec-9.3 defines an edge only for expression reads, but Spec-8.2 makes an entity applicable only if every enclosing entity is applicable. The engine adds that structural edge so the topological order is correct.
- A relation member that is not held is produced by a
relationnode. The graph is rebuilt whenever the held member changes, because the producer of a member depends on which member is held (Spec-8.12, Spec-8.13). - A value-based constraint on an empty field is not evaluated by the engine's
validity pass — that rule belongs to
@formscript/validationand is described in its README. - An unparsable input becomes
unparsedcontent with the raw text retained (Spec-5.6). The coercion rules of Spec-5.3 are implemented forstring,boolean,number,integer,enum,date,datetime,arrayandobject; a value that does not fit the declared type isunparsedrather than an error. - A reference into a repeat from outside the repeat's context reads
null. The specification only defines indexed access (household.members[0].name, §6.3). Inside a repeat, the engine resolves a member to the element currently being evaluated; outside, the reference has no element and reads as absent. - Nested repeats share one element set per innermost repeat. The specification does not define per-parent element sets, so the engine keeps one element count per repeat and uses the innermost repeat's index for the cell of a field.
- A repeat's value is the array of its element objects, each element holding the values of its members (Spec-9.3, the collection node).
- The output document omits an inapplicable field and emits an explicit
nullfor an applicable but empty one (Spec-4.35). A field whose content isunparsedorfailedblocks the output document entirely. - Numbers are serialised in canonical decimal form as a string
(Spec-4.36). The engine returns a plain object rather than JSON text, so a
decimal is emitted as its canonical string — the form Spec-4.36 prescribes
for a serialiser that cannot emit canonical decimals. An
integeris emitted the same way, because Spec-4.36 covers it too. - An effect is applied after the commit (Spec-8.8). A
reseteffect returns an overridable field tocomputedand a relation to its held member; the recomputation happens in the next transaction, because step 5 permits only one evaluation pass (Spec-9.7). - The
$nowvalue comes fromEngineOptions.now. The engine never reads the wall clock itself, so a run is reproducible (Spec-9.1, Spec-9.8). - A
stateis a declaration, not an entity. It contributes no data level and never appears in the output document (Spec-7.1). Its name is declared in the form root scope, so a state that shadows a field name isE-REF-02and the field keeps its place (Spec-8.7). Referencing the state evaluates its members in the current transaction; astatenode in the graph keeps the consumers of a state ordered after the fields the state reads. - The choice set of a field is materialised on demand. It comes from the
field's
optionsattribute or from the value of the source it names (Spec-4.26, Spec-4.27); a source that has not resolved leaves the set empty and marks it pending rather than invalid (Spec-11.7). A value outside the enum of the field isE-TYPE-04and aborts the transaction (Spec-4.28).
Not implemented
actionblocks are not executed. Theirwhenguards are ordinary expressions and are evaluated like any other, but the action's remote effects belong to §11, which is outside §9.sourcefetching is not implemented; the host supplies a source's status and its value throughEngineOptions.
Public API
60 exported symbols.
ActionStatus
type
The status of an action (Spec-6.3, Spec-11.2).
ancestorsOf
re-exportapplicabilityNodeId
functionAppliedEffect
interfaceAssignment
interfaceattributeExpression
re-exportattributeString
function
The literal string of an attribute, when it has one.
buildDependencyGraph
function
Builds the dependency graph of §9.2; an edge `X → Y` exists iff expression `Y` reads node `X` (Spec-9.3).
buildFormModel
re-exportChoiceOption
interface
An option of a materialised choice set (Spec-4.26).
ChoiceSet
interface
The choice set of a field, materialised from its `options` or from its source (Spec-4.26, Spec-4.27).
cloneRuntime
functioncollectionNodeId
functiondataEntities
re-exportDependencyGraph
interfacederivedNodeId
functionEffectModel
interfaceenclosingRepeat
re-exportEngine
class
The reactive evaluation engine of §9; the eight steps of Spec-9.6 run in order.
EngineOptions
interfaceentitiesOfKind
re-exportEntityKind
typeentityOfNode
function
The entity a node belongs to.
FieldModel
interfaceFieldRuntime
interface
The three stored axes of a field, plus its value and override source.
FORM_VALID_NODE_ID
const
The node that holds `$form.valid` (Spec-10.17).
FormModel
interfaceFormSnapshot
interfaceGraphNode
interfaceGraphOptions
interfaceinitialRuntime
re-exportisActionStatus
re-exportisSourceStatus
re-exportliteralBoolean
re-exportNodeKind
typeOptionModel
interface
A declared option of a field's choice set (Spec-4.25).
OutputEntryModel
interfaceOverrideSource
typepathOfExpression
function
The path a `path_ref` expression names, or `null`.
producerNodeIds
function
The nodes that hold a field's value.
readPaths
function
The declaration paths an expression reads.
readsFormValid
function
Whether an expression reads `$form.valid`.
referencePath
function
The path of a reference expression, or `null` when it is not a plain path.
RelationEquationModel
interfaceRelationModel
interfacerelationNodeId
functionRepeatMutation
typeresolveLongest
function
The longest prefix of a path that names a declaration (Spec-6.20).
RuntimeState
re-exportserializeValue
function
The canonical decimal form of a number (Spec-4.36, Spec-5.33).
SnapshotField
interfaceSOURCE_STATUSES
re-exportSourceStatus
type
The status of a source (Spec-6.3, Spec-11.7).
StateMemberModel
interface
A member of a `state` block (Spec-8.5).
StateModel
interface
A `state` declaration; it contributes no data level (Spec-7.1, Spec-8.5).
stateNodeId
function
The node that holds a `state`'s members (Spec-8.5).
stateReads
function
The states an expression reads (Spec-8.7).
TransactionResult
interfacevalueNodeId
function