@formskit/engine
v1.0.0
Published
Headless forms engine. Types, authoring contracts, expression vectors, runtime evaluation, form input sessions, lint, and JSON Schema. Hosts supply a FieldRegistry and their own UI.
Readme
@formskit/engine
Headless forms runtime. Evaluates expressions, drives form input sessions, validates schemas, and produces submission payloads. No UI — hosts render FormState themselves.
Install
import {
createFormInputSession,
computeFormState,
setFieldValue,
buildSubmission,
lintSchema,
evaluateExpr,
defineRegistry,
type FormSchema,
} from '@formskit/engine'Form input session
Every call requires a host registry:
const registry = defineRegistry({ /* ... */ })
const session = createFormInputSession(schema, {
registry,
ctx: { user: { role: 'admin' } },
})
const state = computeFormState(session)
// state.fields[id].visible, .required, .errors, .value
// state.canSubmit
session = setFieldValue(session, 'name', 'Ada')
const submission = buildSubmission(session)buildSubmission strips hidden and non-input fields. Hidden values stay in session.draft until submit.
Lint
Validate a schema against your registry before publish:
const issues = lintSchema(schema, registry)
// unknownFieldType, unknownOptionKey, broken refs, etc.Package layout (src)
See STRUCTURE.md for the full breakdown and "where does X live?" guide.
Quick summary:
expressions/— grammar, builders (ref/expr), evaluator, vectors (contract tests)registry/—FieldRegistry,defineRegistry,opt, contracts,makeStubRegistry,TEST_FIELD_REGISTRYauthoring/—field*,defineForm*,defineRegistryField*,ordered*runtime/—createFormInputSession,computeFormState, etc.traversal/—walkFields,computeInnerRefs,lintSchematypes/— core schema + runtime types (split for clarity)fixtures/— conformance schemas +TUTORIAL_FORMindex.ts(full),authoring.ts(subpath barrel)
Expressions
Lower-level exports for custom tooling:
evaluateExpr(expr, { fields, ctx })
evalBool(boolOrExpr, scope)
collectExprRefs(expr)
normalizeExpr(expr)Semantics are locked by EXPRESSION_VECTORS (exported from this package). Vector tests live alongside the evaluator.
Renderer contract
Read FormState — do not re-implement required, visible, or validation in the UI. Submit via buildSubmission(session).
Server validation
Run the same session API server-side with the same registry and submitted values in draft, then check computeFormState(session).canSubmit.
