Form-Document Schema
Generated from the committed
form-document-schema.jsonartifact — regenerate withbun run generate:form-document-schema(after a registry change) followed bybun run generate:form-document-schema-docs. Do not hand-edit this page.
The single JSON Schema (draft 2020-12) describing the canonical step_configuration document a FORM step persists: a discriminated union on each element's type, composed from every registered element plugin's config and validation contract. It is generated, never hand-written, so it cannot drift from the plugins that define each element type.
It serves three roles from one artifact: a strict authoring/save validator, this generation-target reference, and — later — the AI structured-output tool schema.
Schema version 5 (draft https://json-schema.org/draft/2020-12/schema). The canonical step_configuration shape for a FORM step (SPIKE §11.1), composed from every registered element plugin's configSchema + validationContract (SPIKE §11.2) -- generated from utils/registry.js, never hand-written.
Envelope
Top-level keys of the step_configuration document.
| Key | Required | Description |
|---|---|---|
schemaVersion | yes | Format version normalize() upgrades every doc to (app/api/utils/form_utils.py CURRENT_SCHEMA_VERSION). |
schema | yes | |
elementsMap | yes | Answers stay id-keyed permanently (SPIKE §11.1 invariant 2) -- each element's key (above) anchors only the calculated-value/AI-authoring/piping layers. A condition/dependency reference that crosses a LIST/TABLE repeating-scope boundary is rejected by the graph validator (app/api/utils/form_graph.py detect_repeating_scope), not structurally expressible here (SPIKE §11.4.4). |
dependencies | no | Derived from every element's conditions.filterSet by canonicalize() -- persisted for the FE client-authority visibility evaluator, but drop-and-recomputed on every normalize pass and never independently authored or trusted (SPIKE §11.1 invariant 6). |
settings | no | Form-level overlay resolved by resolveFormSettings against its own defaults (SPIKE §11.1.1) -- an absent/empty object resolves to full defaults. |
Element types
One row per registered element plugin -- a discriminated union on elementsMap[id].type. config and validation list the property keys each type accepts (empty means the type carries no config/validation object of its own).
| Type | Kind | Config keys | Validation keys |
|---|---|---|---|
CHECKBOX_GROUP | field | description, optionSource, options, default | required |
COLUMNS | layout (per-column containment) | numberOfColumns | — |
DATE | field | description, placeholder, default, format | required, min, max, compare |
DATE_TIME | field | description, placeholder, default, format, is24hr | required, min, max, compare |
DIVIDER | field | spacing | — |
DROPDOWN | field | description, placeholder, multiple, optionSource, options, default | required |
FILE_DOWNLOAD | field | description, placeholder, files | — |
FILE_UPLOAD | field | description, placeholder, extensions | required |
GROUP | container (flat containment) | — | — |
HEADING | field | text, size, align | — |
IMAGE | field | url, width, height, alt, align | — |
LABEL | field | description | — |
LINK | field | url, text, align | — |
LIST | container (flat containment) | description, instanceLabel, initialInstances, minInstances, maxInstances, canAdd, canRemove | required |
NUMBER_INPUT | field | description, placeholder, default, maskPreset, mask, sensitive | required, min, max, integer, compare |
PARAGRAPH | field | text | — |
PHONE | field | description, placeholder, defaultCountry | required |
RADIO_GROUP | field | description, optionSource, options, default | required |
RICH_TEXT | field | description, default, lines | required |
SIGNATURE | field | description, enabledModes, defaultDrawThickness, signatureFonts, allowedFileTypes | required |
TABLE | table (per-row column schema) | description, initialRows, canAddRows, canRemoveRows, columns | minRows, maxRows, required |
TEXT_AREA | field | description, placeholder, default, sensitive | required, minLength, maxLength, format, regex, regexMessage |
TEXT_INPUT | field | description, placeholder, default, maskPreset, mask, sensitive | required, minLength, maxLength, format, regex, regexMessage |
Constraints not expressible in JSON Schema
Every structural/type/shape constraint is enforced by the schema itself. The following are documented as notes because they depend on runtime evaluation (JSONLogic) or cross-element graph analysis the schema cannot express:
- Answers stay id-keyed permanently (SPIKE §11.1 invariant 2) -- each element's
key(above) anchors only the calculated-value/AI-authoring/piping layers. A condition/dependency reference that crosses a LIST/TABLE repeating-scope boundary is rejected by the graph validator (app/api/utils/form_graph.py detect_repeating_scope), not structurally expressible here (SPIKE §11.4.4). - Author-stable semantic anchor for the calculated-value/AI-authoring/piping layers only -- answers stay id-keyed permanently (SPIKE §11.1 invariant 2).
- Derived back-pointer, recomputed by canonicalize() on every normalize pass -- never authored.
- Derived from every element's
conditions.filterSetby canonicalize() -- persisted for the FE client-authority visibility evaluator, but drop-and-recomputed on every normalize pass and never independently authored or trusted (SPIKE §11.1 invariant 6). - Form-level overlay resolved by resolveFormSettings against its own defaults (SPIKE §11.1.1) -- an absent/empty object resolves to full defaults.
- ISO date string, not a JS Date -- coerced through parseToType at evaluation time (engine/validationRules.js valueAtLeast/valueAtMost), never enforced as a date format here.
- JSONLogic-evaluated predicate (engine/validationRules.js RULE_KINDS.compare) -- shape only, never the comparison semantics, which are compiled + evaluated at runtime.
- filterSet is compiled to JSONLogic at evaluation time (engine/jsonLogicCompiler.js compileFilterSet) -- described here by shape only, never the compiled/evaluated semantics.