Skip to content

Debugging Business Rules

This guide covers the business rules evaluation lifecycle, tracing API, config validation, the DevTools panel, and solutions to common issues.


Rule Tracing API

The rule tracing system records every rule evaluation event, making it possible to see exactly what happened and why.

Enabling Tracing

typescript
import {
  enableRuleTracing,
  disableRuleTracing,
  getRuleTraceLog,
  clearRuleTraceLog,
} from "@formosaic/core/devtools";

enableRuleTracing();

// ... user interacts with form ...

const log = getRuleTraceLog();
log.forEach(event => {
  console.log(
    `[${event.type}] ${event.triggerField}=${JSON.stringify(event.triggerValue)} -> ${event.affectedField}`
  );
});

disableRuleTracing();

Real-Time Callback

typescript
enableRuleTracing((event) => {
  console.log(`[${event.type}] ${event.triggerField} -> ${event.affectedField}`, event.newState);
});

IRuleTraceEvent Shape

PropertyTypeDescription
timestampnumberDate.now() when the rule fired
type"revert" | "apply" | "combo" | "dropdown" | "order" | "init"Which phase produced this event
triggerFieldstringThe field whose value change triggered evaluation
triggerValueunknownThe value that triggered the rule
affectedFieldstringThe field whose state was changed
previousStatePartial<IRuntimeFieldState>State before rule applied
newStatePartial<IRuntimeFieldState>State after rule applied

FormDevTools

The built-in DevTools panel provides a visual inspector with 7 tabs:

TabContent
RulesPer-field runtime state: type, required, hidden, readOnly, active rules
ValuesJSON dump of all current form values
ErrorsJSON dump of current form errors
GraphText-based dependency graph
PerfPer-field render counts, hot field detection
DepsSortable dependency table with effect types, cycle detection
TimelineChronological event log with filtering
typescript
import { FormDevTools } from "@formosaic/core/devtools";

<FormDevTools
  configName="myForm"
  formState={runtimeFormState}
  formValues={getValues()}
  formErrors={formState.errors}
  dirtyFields={formState.dirtyFields}
  enabled={process.env.NODE_ENV === "development"}
/>

Config Validation

typescript
import { validateFieldConfigs } from "@formosaic/core";

const errors = validateFieldConfigs(fieldConfigs, registeredComponents);
if (errors.length > 0) {
  errors.forEach(err => console.error(`[${err.type}] ${err.fieldName}: ${err.message}`));
}

Checks for: missing dependency targets, self-dependencies, unregistered components, unregistered validators, missing dropdown options, and circular dependencies.


Common Issues and Solutions

"Rule not applying"

The most common cause is a value mismatch. Dependency keys are matched using string comparison: String(fieldValue) === ruleKey.

  • Boolean values: true must match "true", not "True"
  • Numbers: 42 must match "42"
  • null/undefined: Match against the empty string ""

"Circular dependency" warning

typescript
const errors = validateFieldConfigs(fieldConfigs);
const cycles = errors.filter(e => e.type === "circular_dependency");

Refactor the config so dependencies form a DAG (directed acyclic graph).

"Field reverted unexpectedly"

When a field's value changes, ALL dependents from the previous value are reverted to their default config state before new rules are applied. Use the trace log to see the full revert-then-apply sequence.


Debugging Checklist

  1. Enable tracing -- enableRuleTracing() to capture all rule events
  2. Open DevTools -- Add <FormDevTools> to visualize rules, values, errors, and the dependency graph
  3. Validate config -- Run validateFieldConfigs() to catch structural issues
  4. Check value types -- Dependency keys use string comparison
  5. Check the trace log -- Look for "revert" events that may be clearing state
  6. Check combo conditions -- All combo rule conditions must match simultaneously
  7. Check dropdown keys -- Option values must match actual IOption.value values

Released under the MIT License.