@suaveplan/form-builder - Universal dynamic form schema engine with conditional logic, async validation, dependency graph, undo/redo, Zod-schema generation, OTel instrumentation, and pluggable field-type registry.
Universal dynamic form schema engine. Renderer-agnostic — no DOM, no React, no Node-specific globals. Drives every dynamic form surface in the Suaveplan.
Documentation tree
- form-builder —
form-builder.tsexposes the top-levelFormBuilderclass — the public entry point for@suaveplan/form-builder. The…- form-builder —
form-builder.tsexposes the top-levelFormBuilderclass — the public entry point for@suaveplan/form-builder. The…
- form-builder —
- conditional —
conditional.tsis the rule-evaluation engine forFieldDefinition.conditional. It supports nine leaf operators (eq…- conditional —
conditional.tsis the rule-evaluation engine forFieldDefinition.conditional. It supports nine leaf operators (eq…
- conditional —
- dependency-graph —
dependency-graph.tsbuilds a directed acyclic graph of field-id dependencies derived from `FieldDefinition.conditiona…- dependency-graph —
dependency-graph.tsbuilds a directed acyclic graph of field-id dependencies derived from `FieldDefinition.conditiona…
- dependency-graph —
- form-error —
form-error.tsowns the stable error-code registry and the typedFormErrorclass for the entire `@suaveplan/form-bui…- form-error —
form-error.tsowns the stable error-code registry and the typedFormErrorclass for the entire `@suaveplan/form-bui…
- form-error —
- field-registry —
field-registry.tsis the extensible registry of field-type descriptors used byFormBuilder. It ships seventeen buil…- field-registry —
field-registry.tsis the extensible registry of field-type descriptors used byFormBuilder. It ships seventeen buil…
- field-registry —
- instruments —
instruments.tslazily resolves the OpenTelemetry meter and the five instruments emitted across the package: three cou…- instruments —
instruments.tslazily resolves the OpenTelemetry meter and the five instruments emitted across the package: three cou…
- instruments —
- schema —
schema.tsis the Zod boundary for the publicFormSchema,FieldDefinition, andFormSectiontypes. It exposes par…- schema —
schema.tsis the Zod boundary for the publicFormSchema,FieldDefinition, andFormSectiontypes. It exposes par…
- schema —
- serialise —
serialise.tsprovides versioned JSON round-trip forFormSchema. It is the bridge between the in-memory schema engin…- serialise —
serialise.tsprovides versioned JSON round-trip forFormSchema. It is the bridge between the in-memory schema engin…
- serialise —
- form-state —
form-state.tsis the mutable holder for the runtime state of a form instance: the current value map, the most recent…- form-state —
form-state.tsis the mutable holder for the runtime state of a form instance: the current value map, the most recent…
- form-state —
- undo-redo —
undo-redo.tsis a bounded LIFO of reverse-ops with redo support. It is the substrate forFormBuilder.enableHistory…- undo-redo —
undo-redo.tsis a bounded LIFO of reverse-ops with redo support. It is the substrate forFormBuilder.enableHistory…
- undo-redo —
- submission —
submission.tsis the orchestration layer that runs the canonicalprepare → validate → transform → submitpipeline f…- submission —
submission.tsis the orchestration layer that runs the canonicalprepare → validate → transform → submitpipeline f…
- submission —
- validation —
validation.tsis the iteration layer that runs every rule attached to a field or form and aggregates the resulting `V…- validation —
validation.tsis the iteration layer that runs every rule attached to a field or form and aggregates the resulting `V…
- validation —
- validators —
validators.tsprovides the eleven built-in rule runners used by the validation engine. Each runner takes the candidat…- validators —
validators.tsprovides the eleven built-in rule runners used by the validation engine. Each runner takes the candidat…
- validators —
- zod-schema —
zod-schema.tsexposestoZodSchema(form)— a function that compiles aFormSchemainto az.ZodObjectwhose `safeP…- zod-schema —
zod-schema.tsexposestoZodSchema(form)— a function that compiles aFormSchemainto az.ZodObjectwhose `safeP…
- zod-schema —
Features
- 17 built-in field types (
text,number,email,url,tel,password,select,multiselect,checkbox,radio,textarea,date,file,range,hidden,group,array) via an extensibleFieldRegistry. - 11 built-in validation rules (
required,minLength,maxLength,min,max,pattern,email,url,oneOf,equalsField,custom). Async-friendly viaPromise<boolean>predicates. - Conditional logic with 9 leaf operators (
eq,neq,in,notIn,gt,lt,gte,lte,contains) and boolean composition (and/or/not). - Field-level dependency graph with topological evaluation and cycle
detection (
FORM_BUILDER.CYCLIC_DEPENDENCY). - Bounded undo / redo on schema mutations.
- Typed event bus via
EventBus<FormEventMap>from@suaveplan/events. - Versioned JSON serialise / deserialise round-trip.
toZodSchema(form)generator forreact-hook-formconsumers.- Submission pipeline:
prepare → validate → transform → submit. - Six stable
FORM_BUILDER.*error codes viadefineCodes. - Three OTel counters and two histograms emitted on every code path.
- Injectable
Clock+IdGeneratorfor deterministic tests.
Install
bun add @suaveplan/form-builder
Usage
import { FormBuilder } from "@suaveplan/form-builder";
const builder = new FormBuilder().enableHistory();
builder.addField({
id: "name",
name: "name",
label: "Name",
type: "text",
validations: [{ type: "required" }],
});
builder.addField({
id: "subscribe",
name: "subscribe",
label: "Subscribe to newsletter",
type: "checkbox",
});
builder.addField({
id: "frequency",
name: "frequency",
label: "Frequency",
type: "select",
options: [
{ label: "Weekly", value: "weekly" },
{ label: "Monthly", value: "monthly" },
],
conditional: { field: "subscribe", operator: "eq", value: true },
});
builder.bus.on("values.changed", (values) =>
console.log("changed", values)
);
const schema = builder.createSchema("signup", "Sign-up form");
Submission
import { submit } from "@suaveplan/form-builder";
await submit({
fields: schema.fields,
values: { name: "alice", subscribe: true, frequency: "weekly" },
submitter: async (values, signal) =>
fetch("/api/forms", {
method: "POST",
signal,
body: JSON.stringify(values),
}),
});
License
MIT.