Technical Documentation

Internals: Reactivity & Rule Engine

The Rule Engine (rule-engine.ts) is the reactive brain of DFBE. it manages field dependencies and calculates dynamic properties.

1. Automated Dependency Discovery

DFBE does not require developers to manually specify when a field should re-render. The engine "discovers" dependencies by parsing the schema.

What triggers a dependency?

  • Logic: Any field mentioned in a condition.
  • Interpolation: Any field used inside {{...}} in a label or helper text.
  • Cascading: Any field mentioned in dataSource.dependsOn.

2. Smart Visibility Defaults

A subtle but crucial feature of the engine is its Automated Default Visibility:

  1. Case A: A field has no visibility rules. -> Default: Visible.
  2. Case B: A field has rules that can hide it (visible: false). -> Default: Visible.
  3. Case C: A field has rules that only show it (visible: true). -> Default: Hidden.

This prevents "Logic Orphans"—fields that should be hidden until a condition is met but are shown initially because the rule hasn't triggered yet.

3. String Interpolation & Math Engine

The engine supports dynamic values in UI text and computed values.

The Interpolator

It uses a regex-based replacer to swap {{path.to.field}} with actual values from the form state.

The Math Evaluator

For computedValue effects, the engine can evaluate arithmetic expressions.

  • Security: It validates the expression against /^[0-9+\-*/().\s]+$/ before evaluation to ensure no arbitrary JavaScript is executed.
  • Context: You can combine interpolation and math: {{price}} * {{quantity}}.

4. Precedence & Evaluation Order

  • Precedence: Effects are applied in the order they are defined in the rules array. If multiple rules affect the same property (e.g. disabled), the last successful rule wins.
  • Batching: FieldRenderer calculates the entire computedState in one useMemo block, ensuring atomic updates to the UI.

5. Logic Engine Operators

The ConditionEvaluator supports a variety of operators, each with specific handling:

  • eq / neq: Strict equality.
  • in / notIn: Array inclusion (checks if value exists in the target array).
  • isChecked / isNotChecked: Specifically for boolean checkbox states.

Recursive Depth Guard

Logic groups (and/or) are evaluated recursively up to a depth of 5. This protects the engine from stack overflow in recursive or circular logic configurations. If the depth exceeds this limit, the condition will immediately evaluate to false (treated as not matching) and log an error to the console.


6. Exported Utility Functions

All of the following are public exports available from @dfbe/core for advanced consumers who need to build custom rule evaluators, testing utilities, or debug tools.

extractFieldDependencies(field)

Returns the array of field names that a single field depends on — derived from visibleIf, rules[].condition, interpolation templates, and dataSource.dependsOn.

import { extractFieldDependencies } from "@dfbe/core"; const deps = extractFieldDependencies(field); // e.g. ["employment_status", "province"]

extractLogicDependencies(node, deps)

Lower-level recursive function that traverses a LogicNode tree and populates a Set<string> with all field names referenced in condition nodes.

interpolateString(template, formValues)

Replaces {{path}} tokens in any string with live values from the form state. Supports dot-notation paths.

import { interpolateString } from "@dfbe/core"; const result = interpolateString("Halo {{profile.name}}!", formValues); // "Halo Budi!"

computeValue(template, formValues)

Extends interpolateString by also evaluating the result as a math expression if it matches the safe math pattern.

import { computeValue } from "@dfbe/core"; computeValue("{{price}} * {{qty}}", { price: 15000, qty: 3 }); // 45000 computeValue("Total: {{price}} IDR", { price: 15000 }); // "Total: 15000 IDR" (not a math expression — returned as-is)

evaluateMath(expression)

Safely evaluates a pure numeric math expression using new Function. The expression is first validated against a strict regex — only digits and + - * / ( ) . are allowed.

import { evaluateMath } from "@dfbe/core"; evaluateMath("(10 + 5) * 3"); // 45 evaluateMath("10 /"); // "10 /" — incomplete expression, returned as-is evaluateMath("eval('x')"); // "eval('x')" — rejected by regex

evaluateLogic(node, formValues, depth?)

Public entry point for evaluating a full LogicNode (condition or group) tree.

import { evaluateLogic } from "@dfbe/core"; const isVisible = evaluateLogic(rule.condition, formValues);

evaluateCondition(condition, formValues)

Legacy compatibility wrapper for VisibleIfCondition (deprecated visibleIf field). Wraps into a conditionNode and delegates to evaluateSingleCondition.