Files
suaveplan/openspec/archive/2026-05-25-enterprise-form-builder/proposal.md
2026-05-27 23:29:53 +01:00

4.3 KiB

Proposal: Enterprise migration of @suaveplan/form-builder

  • Status: PROPOSED
  • Package: @suaveplan/form-builder
  • Category / plugin: ui / forms
  • Tier: universal (zero browser, zero React, zero Node)
  • Design: ./design.md
  • Spec delta: ./specs/form-builder/spec.md

Why

@suaveplan/form-builder is the canonical universal-tier schema engine for dynamic forms in the Suaveplan monorepo. The upstream copy at ../suaveplan/packages/universal/form-builder ships only ten field types, a thin synchronous validation surface (eight built-in rules), a single conditional operator family (eq / neq / in), no dependency graph, no undo/redo, no submission pipeline, no Zod schema generator, and no OpenTelemetry instrumentation. The renderer-agnostic schema engine is expected to drive every form surface in the monorepo (admin panels, marketing onboarding flows, the internal vault wizard), and the current surface is too thin to serve them. This migration ports the package onto genesis at enterprise quality: full field-type registry, async validation, cross-field validators, dependency-graph driven re-evaluation, undo/redo, JSON serialisation round-trip, Zod schema generation, OTel instruments, and a stable error-code namespace.

What changes

  • New package packages/ui/forms/form-builder/ at 0.1.0.
  • Zod-validated public schema (FormSchema, FieldDefinition, FormSection, ValidationRule, ConditionalRule).
  • Extensible FieldRegistry with 17 built-in field types: text, number, email, url, tel, password, select, multiselect, checkbox, radio, textarea, date, file, range, hidden, group, array.
  • Sync + async validation pipeline with AbortSignal propagation and 11 built-in rule kinds (required, minLength, maxLength, min, max, pattern, email, url, oneOf, equalsField, custom).
  • Conditional logic with 9 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.
  • Undo / redo on schema-mutation operations.
  • Form-state machine emitting typed events through EventBus<FormEventMap> from @suaveplan/events.
  • JSON serialise / deserialise round-trip with versioned wire format.
  • Zod schema generator: toZodSchema(form).
  • Submission pipeline: prepare → validate → transform → submit with injectable Submitter.
  • Six stable FORM_BUILDER.* error codes via defineCodes.
  • OTel instruments: three counters + two histograms.

Parity Checklist

Capability Upstream Migrated Notes
FormBuilder class ✅ ✅ Public API preserved.
Field types ✅ (10) ⬆️ (17) Added url, tel, password, multiselect, range, group, array.
Validation rules ✅ (8) ⬆️ (11) Added url, oneOf, equalsField. Async + cross-field validators added.
Conditional operators ✅ (3) ⬆️ (9 + boolean) Added notIn, gt, lt, gte, lte, contains, plus and / or / not.
Sections ✅ ✅ Plus removeSection.
Event bus ✅ ✅ Migrated from EventEmitter to EventBus<FormEventMap> from @suaveplan/events.
Error codes ✅ ⬆️ Migrated from BaseError to defineCodes("FORM_BUILDER", …).
Dependency graph + topo eval 🚫 ⬆️ New: required for chained conditional fields.
Undo / redo 🚫 ⬆️ New: bounded mutation-history stack.
JSON round-trip partial ⬆️ serialiseForm / deserialiseForm with Zod validation.
Zod schema generator 🚫 ⬆️ New: toZodSchema(form) for react-hook-form consumers.
Submission pipeline 🚫 ⬆️ New: pluggable Submitter with hooks.
OTel instrumentation 🚫 ⬆️ New: 3 counters + 2 histograms.
Field registry (extensibility) 🚫 ⬆️ New: downstream packages can contribute field types.

Impact

Affected dependents

None on genesis (form-builder is a leaf at migration time). Downstream integration with @suaveplan/react-forms is a follow-up change. Upstream main-branch consumers remain on the unmigrated copy until their own migration land.

Risk

Low. New package in a leaf position. Public API behaviour is exercised at 100% coverage.