Files
skills/docs/ui/forms/form-builder/README.md
2026-05-29 08:20:56 +01:00

8.0 KiB

@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.ts exposes the top-level FormBuilder class — the public entry point for @suaveplan/form-builder. The…
    • form-builder — form-builder.ts exposes the top-level FormBuilder class — the public entry point for @suaveplan/form-builder. The…
  • conditional — conditional.ts is the rule-evaluation engine for FieldDefinition.conditional. It supports nine leaf operators (eq…
    • conditional — conditional.ts is the rule-evaluation engine for FieldDefinition.conditional. It supports nine leaf operators (eq…
  • dependency-graph — dependency-graph.ts builds a directed acyclic graph of field-id dependencies derived from `FieldDefinition.conditiona…
    • dependency-graph — dependency-graph.ts builds a directed acyclic graph of field-id dependencies derived from `FieldDefinition.conditiona…
  • form-error — form-error.ts owns the stable error-code registry and the typed FormError class for the entire `@suaveplan/form-bui…
    • form-error — form-error.ts owns the stable error-code registry and the typed FormError class for the entire `@suaveplan/form-bui…
  • field-registry — field-registry.ts is the extensible registry of field-type descriptors used by FormBuilder. It ships seventeen buil…
    • field-registry — field-registry.ts is the extensible registry of field-type descriptors used by FormBuilder. It ships seventeen buil…
  • instruments — instruments.ts lazily resolves the OpenTelemetry meter and the five instruments emitted across the package: three cou…
    • instruments — instruments.ts lazily resolves the OpenTelemetry meter and the five instruments emitted across the package: three cou…
  • schema — schema.ts is the Zod boundary for the public FormSchema, FieldDefinition, and FormSection types. It exposes par…
    • schema — schema.ts is the Zod boundary for the public FormSchema, FieldDefinition, and FormSection types. It exposes par…
  • serialise — serialise.ts provides versioned JSON round-trip for FormSchema. It is the bridge between the in-memory schema engin…
    • serialise — serialise.ts provides versioned JSON round-trip for FormSchema. It is the bridge between the in-memory schema engin…
  • form-state — form-state.ts is the mutable holder for the runtime state of a form instance: the current value map, the most recent…
    • form-state — form-state.ts is the mutable holder for the runtime state of a form instance: the current value map, the most recent…
  • undo-redo — undo-redo.ts is a bounded LIFO of reverse-ops with redo support. It is the substrate for FormBuilder.enableHistory…
    • undo-redo — undo-redo.ts is a bounded LIFO of reverse-ops with redo support. It is the substrate for FormBuilder.enableHistory…
  • submission — submission.ts is the orchestration layer that runs the canonical prepare → validate → transform → submit pipeline f…
    • submission — submission.ts is the orchestration layer that runs the canonical prepare → validate → transform → submit pipeline f…
  • validation — validation.ts is the iteration layer that runs every rule attached to a field or form and aggregates the resulting `V…
    • validation — validation.ts is the iteration layer that runs every rule attached to a field or form and aggregates the resulting `V…
  • validators — validators.ts provides the eleven built-in rule runners used by the validation engine. Each runner takes the candidat…
    • validators — validators.ts provides the eleven built-in rule runners used by the validation engine. Each runner takes the candidat…
  • zod-schema — zod-schema.ts exposes toZodSchema(form) — a function that compiles a FormSchema into a z.ZodObject whose `safeP…
    • zod-schema — zod-schema.ts exposes toZodSchema(form) — a function that compiles a FormSchema into a z.ZodObject whose `safeP…

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 extensible FieldRegistry.
  • 11 built-in validation rules (required, minLength, maxLength, min, max, pattern, email, url, oneOf, equalsField, custom). Async-friendly via Promise<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 for react-hook-form consumers.
  • Submission pipeline: prepare → validate → transform → submit.
  • Six stable FORM_BUILDER.* error codes via defineCodes.
  • Three OTel counters and two histograms emitted on every code path.
  • Injectable Clock + IdGenerator for 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.