Skip to main content

Form Editor Reference

Every export documented here comes from @vef-framework-react/form-editor's package entry point (import { ... } from "@vef-framework-react/form-editor") — no internal module paths. For narrative explanations, see Overview, Embedding, Schema, and Linkage.

FormEditor

ExportShape
FormEditorPre-composed component. Object.assign'd with .Provider, .Shell, .Workspace, .Stage, .Toolbar, .Palette, .Properties — see Embedding.
FormEditorProvider (= FormEditor.Provider)Context root: store + registries + preview runtime. Props: FormEditorProviderProps.
FormEditorShell (= FormEditor.Shell)Chrome frame (layout measurement, device context, drag-drop, shortcuts). Props: FormEditorShellProps (children only).
FormEditorWorkspace (= FormEditor.Workspace)Palette / stage / properties band. Props: { children: ReactNode }.
FormEditorStage (= FormEditor.Stage)Canvas column. No props.
FormEditorToolbar (= FormEditor.Toolbar)Default toolbar. Props: ToolbarProps.

FormEditorProps

Extends FormEditorProviderProps (below) plus:

PropTypeNotes
brandToolbarBrand ({ name?, tag?, icon? })Toolbar brand mark; defaults to the framework's own name/glyph.
onPublish(schema: FormSchema) => voidRuns a validation pass first (errors block, warnings ask for confirmation); publish button is not rendered when omitted.
publishTextstringDefaults to "发布". Ignored when onPublish is omitted.
publishLoadingbooleanLoading state on the publish button. Ignored when onPublish is omitted.

FormEditorProviderProps

PropTypeNotes
initialSchemaFormSchemaRead once, on mount.
registriesPartial<DeviceRegistries>Per-device registry override; an omitted device falls back to registry, then the built-in defaults. Read once, on mount.
registryFormFieldRegistryShorthand for one registry shared by both devices. Read once, on mount.
evaluatorsLinkageEvaluatorsForwarded to the live preview — see Linkage.
dataSourceResolverDataSourceResolverForwarded to the live preview — see Embedding.
evaluationContextEvaluationContextForwarded to the live preview — see Linkage.
contextSourcesLinkageContextSource[]Design-time pick list for the condition builder — see Linkage.
onSchemaChange(schema: FormSchema) => voidFires on every committed edit.
apiRefRef<FormEditorApi>See below.
childrenReactNode

FormEditorApi

MemberSignature
getSchema() => FormSchema
setSchema(schema: FormSchema) => void — import semantics; resets the undo timeline
undo / redo() => void
canUndo / canRedo() => boolean
selectNode(nodeId: string | null) => void
setDevice(device: EditorDeviceMode) => void
setViewMode(mode: EditorViewMode) => void

ToolbarBrand / ToolbarProps

TypeShape
ToolbarBrand{ name?: string; tag?: string; icon?: ReactNode }
ToolbarProps{ brand?: ToolbarBrand; onPublish?: (schema: FormSchema) => void; publishText?: string; publishLoading?: boolean }

FormRenderer

ExportShape
FormRendererRuntime component. Props: FormRendererProps.

FormRendererProps

PropTypeNotes
schemaFormSchemaRequired.
devicePresentationDevice ("pc" | "mobile")Default "pc".
defaultValuesRecord<string, unknown>
disabledboolean
fieldPermissionsRecord<string, FieldPermission>Server-resolved clamp — see Linkage.
evaluatorsLinkageEvaluatorsSee Linkage.
dataSourceResolverDataSourceResolverSee Embedding.
evaluationContextEvaluationContextSee Linkage.
onSubmit(values: Record<string, unknown>) => void | Promise<void>Runs after schema-driven validation passes.
apiRefRef<FormRendererApi>See below.
containOverlaysbooleanDefault false. Mobile-only; pins overlay pickers to the renderer's own box.

FormRendererApi

MemberSignature
submit() => Promise<void> — same validation pipeline as a schema submit button
reset() => void
getValues() => Record<string, unknown> — the RAW live form state, including hidden fields' seeded defaults and read-only (non-writable-clamped) values
getSubmitValues() => Record<string, unknown> — exactly what the submit pipeline would deliver to onSubmit right now: the live values run through the same payload filtering (effectively-hidden fields dropped, non-writable-clamped keys excluded, subforms recursed per row), without running validation or the submit lifecycle. Added in v2.10.0 alongside the field-permission clamp

Field registries

ExportSignature
createDefaultRegistry() => FormFieldRegistry — PC registry: the built-in field/container definitions + the antd container chrome
createDefaultMobileRegistry() => FormFieldRegistry — antd-mobile registry: mobileFieldDefinitions + the antd-mobile container chrome
registerDefaults(registry: FormFieldRegistry) => void — applies the built-in field set + PC chrome to an existing registry
DeviceRegistries (type){ pc: FormFieldRegistry; mobile: FormFieldRegistry }

FormFieldRegistry

MemberSignatureNotes
register(definition: FieldDefinition) => voidKeyed by definition.config.type.
unregister(type: string) => boolean
get(type: string) => FieldDefinition | undefined
has(type: string) => boolean
list() => FieldDefinition[]
registerPropertyEntry(type: EntryType, component: FC<EntryComponentProps>) => voidDesign-time property-panel renderer.
getPropertyEntry(type: EntryType) => FC<EntryComponentProps> | undefinedFalls back to the panel's statically-imported built-ins when undefined.
setContainerChrome(chrome: ContainerChromeSet) => void
getContainerChrome() => ContainerChromeSetThrows if none installed.
subscribe(listener: () => void) => () => voidNotified on every mutation.
getRevision() => numberMonotonic; for useSyncExternalStore snapshots.

Property entries

Built-in property-entry renderer components (each FC<EntryComponentProps>), resolvable by EntryType:

EntryTypeComponent
"text"TextEntry
"key"KeyEntry
"number"NumberEntry
"checkbox"CheckboxEntry
"select"SelectEntry
"icon"IconEntry
"options-editor"OptionsSourceEntry
"linkage-rules"LinkageRulesEntry

EntryComponentProps: { entry: PropertyEntry; field: FormField; schema: FormSchema; onChange: (value: unknown) => void }. PropertyEntryTypeMap is the open interface EntryType is keyed from — augment it (declare module) to register a new entry kind alongside registerPropertyEntry.

Definition builders

ExportSignature
defineFieldDefinition<TField extends FormField, TValue = unknown>(definition: { config: FieldDefinitionConfig; Component: FC<FieldComponentProps<TField, TValue>>; properties: PropertiesDescriptor }) => FieldDefinition
defineContainerDefinition(definition: { config: FieldDefinitionConfig }) => FieldDefinition — no Component slot (containers render structurally)
definePropertyEntry<TField extends FormField, TValue>(entry: PropertyEntry<TField, TValue>) => PropertyEntry — pins the entry's field/value types at its declaration site

FieldDefinitionConfig

FieldTypeNotes
typeBlock["type"]Discriminator.
namestringPalette display name.
groupFieldGroup ("basic-input" | "selection" | "date-file" | "container" | "action" | "presentation")Palette category.
keyedbooleanWhether the field needs a unique data-binding key.
iconDynamicIconNameOptional; palette card / properties header icon.
iconUrlstringOptional.
create() => FieldCreateResultFactory returning type-specific defaults; id / key are engine-minted.

FieldComponentProps<TField, TValue>

FieldType
fieldTField
valueTValue
onChange(value: TValue) => void
errorsstring[] | undefined
domIdstring
disabledboolean | undefined
requiredboolean | undefined
labelPositionLabelPosition | undefined

FieldCreateResult = DistributedOmit<Block, "id" | "key">. FieldDefinition = { config: FieldDefinitionConfig; Component?: FC<FieldComponentProps>; properties: PropertiesDescriptor }.

PropertyEntry / PropertiesDescriptor

TypeShape
PropertyEntry<TField, TValue>{ id, label, type: EntryType, options?: PropertyEntryOption[], visible?: (field: TField) => boolean, readOnly?, description?, placeholder?, read: (field: TField) => TValue, write: (field: TField, value: TValue) => TField }
PropertyEntryOption{ value: string; label: string }
PropertyGroup{ id: string; label: string; tab?: PropertyTabId; entries: PropertyEntry[] }
PropertiesDescriptorPropertyGroup[]
PropertyTabId"props" | "validation" | "linkage" | "layout"

Editor store

ExportSignature
FormEditorStoreProviderStore context provider (installed by FormEditorProvider)
useFormEditorStore<T>(selector: (state: FormEditorStoreState) => T) => T
useFormEditorStoreApi() => FormEditorStoreApi (UnboundStore<FormEditorStoreState>)
useCurrentLayer() => PresentationLayer — the active device's presentation
isPaletteVisible(state: Pick<FormEditorStoreState, "viewMode">) => boolean

FormEditorStoreState is the store's full state + actions — most consumers read it through useFormEditorStore(selector) rather than the whole shape. Notable pieces:

MemberTypeNotes
schemaFormSchema
selectedIdstring | null
viewModeEditorViewMode ("edit" | "preview" | "json")
deviceEditorDeviceMode (= PresentationDevice)
formConfigOpen / formConfigTabboolean / FormConfigTabIdFormConfigTabId = "outline" | "form" | "variables" | "dataSources" | "linkage"
past / futureHistoryEntry[]HistoryEntry = { schema: FormSchema; device: EditorDeviceMode }
insertField, moveNode, removeNode, duplicateNodeactionsStructural edits, each one checkpointed undo step.
setSpan, setFlex, setColumnWidth, setStackSlotactionsLayout edits, coalesced per node.
editField(args, options?: EditCoalesceOptions), updateBlock(args, options?)actionsEditCoalesceOptions = { coalesceKey?: string } — consecutive edits sharing a key fold into one undo step.
setFieldKeyactionSanitizes, dedupes, and renames every reference to the old key.
patchSchema(patch: FormSchemaPatch) => voidFormSchemaPatch = Partial<Pick<FormSchema, "dataSources" | "id" | "linkage" | "variables">> & { gap?: GapScale }
renameVariable, removeVariable, removeDataSourceactionsRename/remove + reference cleanup.
undo, redo, canUndo, canRedoactions

Device and registry hooks

ExportSignatureNotes
DeviceProvider(props: { device: PresentationDevice; children }) => ReactElementSets the active device for registry resolution.
RegistryProvider(props: { registries: DeviceRegistries; children }) => ReactElement
useDeviceRegistries() => DeviceRegistriesThrows without an ancestor RegistryProvider.
useFieldRegistry() => FormFieldRegistryThe active device's registry; re-renders on registration changes.
useMobileScopeContainer() => () => HTMLElementgetContainer callback for antd-mobile overlays — the containing phone-frame element when one is present (design-time preview), else document.body (real mobile runtime).

Rendering parts

ExportProps
FieldShell{ children, domId, errors?, helperText?, label, labelPosition?, required? } — shared label/control/helper/error layout.
Label{ children, htmlFor?, position?: LabelPosition, required?, title? }

Data sources

ExportSignature
DataSourceProvider(props: { children, dataSources?: FormDataSource[], resolver?: DataSourceResolver, versions?: Record<string, number> }) => ReactElement — installed by FormEditor and FormRenderer.
useFieldOptions(source: FieldOptionSource | undefined) => { options: FieldOption[]; loading: boolean; error: boolean }
DataSourceResolver (type){ resolve: (request: RemoteDataSourceRequest, mapping?: RemoteOptionMapping) => Promise<FieldOption[]> }
noopDataSourceResolverDataSourceResolver whose resolve always returns [] — the default.
RemoteDataSourceRequest (type){ resource: string; action: string; version?: string; params?: Record<string, unknown> }
RemoteOptionMapping (type){ labelKey?: string; valueKey?: string; disabledKey?: string; descriptionKey?: string } — all default to "label" / "value".

Mobile

ExportShape
mobileFieldDefinitionsFieldDefinition[] — antd-mobile leaf renderers (every built-in leaf type except code-editor) plus the structural containers, reusing each PC field's config / properties.
useMobileScopeContainerSee Device and registry hooks.

Schema types

See Schema for the narrative model. Top-level and supporting types:

ExportShape
FormSchema{ id, version: 2, variables?, dataSources?, linkage?, presentations: { pc: PresentationLayer; mobile?: PresentationLayer } }
PresentationLayer{ gap?: GapScale; children: Block[] }
RuntimeSchemaPresentationLayer & { id: string; variables?, dataSources?, linkage? } — produced by toRuntimeSchema
BlockFormField | ContainerNode
FormFieldFormFieldTypeMap[keyof FormFieldTypeMap] — see Leaf field interfaces
FormFieldTypeMapOpen interface (module-augmentable) mapping type → field interface
KeyedFormFieldExtract<FormField, KeyedNode>
ContainerNodeSectionNode | TabsNode | SubformNode | FlexNode | GridNode (closed set)
CONTAINER_TYPESArray<ContainerNode["type"]>
KeyedNode{ key: string; columnType?: ColumnDataType }
KeyedNodeUnionExtract<Block, KeyedNode>
Validatable{ validate?: { required?, minLength?, maxLength?, min?, max?, pattern?, message? } }
PresentationDevice"pc" | "mobile"
LabelPosition"top" | "left" | "right"
GapScale"small" | "medium" | "large"
GAP_SCALESreadonly GapScale[]
DEFAULT_GAP_SCALE"medium"
ROW_COLS24
FlexSlot{ grow?: number; shrink?: number; basis?: string }
CssLength{ value: number; unit: "px" | "%" }
StackSlot{ width?: CssLength; minWidth?: CssLength; maxWidth?: CssLength; align?: "start" | "center" | "end" }
ColumnDataType"string" | "text" | "integer" | "decimal" | "boolean" | "date" | "datetime" | "json"
FieldOption{ label: string; value: string | number }
FieldOptionSourceStaticOptionSource | RefOptionSource | RemoteOptionSource (kind: "static" | "ref" | "remote")
FormDataSourceStaticDataSource | RemoteDataSource (each { id, name, kind, ... })
FormVariable{ id, name, type: "string" | "number" | "boolean" | "json", defaultValue? }

Leaf field interfaces

Every leaf field extends the shared base: { id: string; label?: string; labelPosition?: LabelPosition; span?: number; flex?: FlexSlot; columnWidth?: number; stack?: StackSlot; linkage?: FieldLinkage }. Keyed fields additionally carry { key: string; columnType?: ColumnDataType }; Validatable fields additionally carry { validate?: {...} } (see Schema types). Only each interface's own fields are listed below.

InterfacetypeExtendsOwn fields
TextfieldField"textfield"Keyed, Validatableplaceholder?, helperText?, prefixIcon?: DynamicIconName, size?: "small"|"middle"|"large", allowClear?: boolean, maxLength?: number, inputType?: "text"|"password"
CodeEditorField"code-editor"Keyed, Validatableplaceholder?, helperText?, language?: CodeEditorLanguage, minHeight?: number, maxHeight?: number, showLineNumbers?: boolean, showFoldGutter?: boolean, tabSize?: number
NumberField"number"Keyed, Validatableplaceholder?, helperText?, min?: number, max?: number, step?: number, size?, prefix?: string, suffix?: string, precision?: number, controls?: boolean (default shown)
SwitchField"switch"Keyed (not Validatable)helperText?, onText?: string, offText?: string, size?: "default"|"small"
SelectField"select"Keyed, Validatableplaceholder?, helperText?, allowClear?: boolean, size?, showSearch?: boolean, dataSource?: FieldOptionSource
RadioField"radio"Keyed, ValidatablehelperText?, dataSource?: FieldOptionSource, optionType?: "default"|"button", buttonStyle?: "outline"|"solid", direction?: "horizontal"|"vertical"
CheckboxGroupField"checkbox-group"Keyed, ValidatablehelperText?, dataSource?: FieldOptionSource, direction?: "horizontal"|"vertical"
TextareaField"textarea"Keyed, Validatableplaceholder?, helperText?, rows?: number, autoSize?: boolean, maxLength?: number, showCount?: boolean, size?, allowClear?: boolean
DateField"date"Keyed, Validatableplaceholder?, helperText?, allowClear?: boolean (default true)
DatetimeField"datetime"Keyed, Validatableplaceholder?, helperText?, allowClear?: boolean (default true)
DateRangeField"daterange"Keyed, ValidatablehelperText?, allowClear?: boolean (default true)
ButtonField"button"(non-keyed)action?: "submit"|"reset"|"button" (default "submit"), buttonType?: "primary"|"default"|"dashed"|"text"|"link" (default "primary"), danger?: boolean, size?, block?: boolean, ghost?: boolean, shape?: "default"|"round", icon?: DynamicIconName
DividerField"divider"(non-keyed)title?: string, titlePlacement?: "left"|"center"|"right" (default "center"), dashed?: boolean
AlertBlockField"alert-block"(non-keyed)message?: string, description?: string, alertType?: "info"|"success"|"warning"|"error", showIcon?: boolean (default true), closable?: boolean, banner?: boolean
ParagraphField"paragraph"(non-keyed)text?: string, textType?: "secondary"|"success"|"warning"|"danger", strong?: boolean, italic?: boolean

Container node interfaces

Every container extends the shared block base (id, span?, flex?, columnWidth?, stack?, linkage?).

InterfacetypeOwn fields
CardSection / CollapseSection (SectionNode)"section"variant: "card" | "collapse", title?: string, gap?: GapScale, children: Block[]CollapseSection adds defaultCollapsed?: boolean
TabsNode"tabs"gap?: GapScale, tabs: TabItem[]TabItem = { id: string; label: string; children: Block[] }
StackSubform / TableSubform (SubformNode)"subform"Keyed. variant: "stack" | "table", label?: string, template: Block[], minRows?: number, maxRows?: number, addLabel?: stringStackSubform adds gap?: GapScale; TableSubform adds size?: "small" | "middle" | "large"
FlexNode"flex"children: Block[], direction?: "row" | "column" (default "row"), justify?: FlexJustify, align?: FlexAlign, wrap?: boolean (default false), gap?: number
GridNode"grid"children: Block[], columns?: number (default 2), gap?: number, rowGap?: number (default = gap)

FlexJustify = "start" | "center" | "end" | "between" | "around". FlexAlign = "start" | "center" | "end" | "stretch".

Field and container definition exports

One FieldDefinition constant per built-in type, registered by createDefaultRegistry/createDefaultMobileRegistry — see Schema → Leaf fields and Schema → Containers for the type / group mapping.

textfieldDefinition, codeEditorDefinition, numberFieldDefinition, switchFieldDefinition, selectFieldDefinition, radioFieldDefinition, checkboxGroupFieldDefinition, textareaFieldDefinition, dateFieldDefinition, datetimeFieldDefinition, dateRangeFieldDefinition, buttonDefinition, sectionDefinition, tabsDefinition, subformDefinition, flexDefinition, gridDefinition, dividerDefinition, alertBlockDefinition, paragraphDefinition.

Schema engine functions

ExportSignaturePurpose
createEmptySchema() => FormSchemaFresh document, empty pc presentation.
createId(prefix: IdPrefix) => stringMint a prefixed cuid2 node id.
generateUniqueKey(layer, baseKey, scope?) => stringScope-unique key allocation.
nextUniqueKey(used: Set<string>, baseKey: string) => stringSame, against a pre-collected set.
sanitizeKey(key: string) => stringStrip non-word characters.
isKeyedNode(node: Block) => node is KeyedNodeUnion
isKeyedField(field: FormField) => field is KeyedFormField
isContainerNode(node: Block) => node is ContainerNode
isLeafField(node: Block) => node is FormField
walkNodes(layer, visit: (node: Block, scope: readonly string[]) => void) => voidDepth-first, every node. scope is the chain of enclosing subform keys ([] at the root).
walkFields(layer, visit: (field: FormField, scope: readonly string[]) => void) => voidDepth-first, leaf fields only.
findNode / findField(layer, id: string) => Block | undefined / FormField | undefined
findParentContainer(layer, blockId: string) => ContainerNode | undefined
insertBlock(layer, block: Block, target: DropTarget) => PresentationLayer
moveBlock(layer, nodeId: string, target: DropTarget) => PresentationLayer
cloneBlock(block: Block, allocateKey: ((base: string) => string) | null) => Block
setSpan(layer, blockId: string, span: number | undefined) => PresentationLayer
setFlex(layer, blockId: string, flex: FlexSlot | undefined) => PresentationLayer
editField(layer, fieldId: string, updater: (field: FormField) => FormField) => PresentationLayer
removeBlock(layer, id: string) => PresentationLayer
updateNode(layer, id: string, updater: (node: Block) => Block) => PresentationLayer
currentLayer(schema: FormSchema, device: PresentationDevice) => PresentationLayer
emptyLayer() => PresentationLayer
resolvePresentation(schema, device) => PresentationLayer | undefined
withPresentation(schema, device, layer) => FormSchema
toRuntimeSchema(schema, device) => RuntimeSchema | undefined
validateSchema(candidate: unknown, registries: DeviceRegistries) => ValidateSchemaResultValidateSchemaResult = { valid: boolean; schema?: FormSchema; issues: ValidationIssue[] }
inferColumnType(field: FormField) => ColumnDataType
toColumnDefinitions(schema: FormSchema) => ColumnDefinition[]ColumnDefinition = { key, columnType, maxLength?, precision? }
toFormFieldDefinitions(schema: FormSchema) => FormFieldDefinition[]FormFieldDefinition = { key, kind: FieldKind, label, options? }; FieldKind = "input" | "textarea" | "select" | "number" | "date" | "upload"
convertPresentation(pc: PresentationLayer, rules: ConversionRegistry, target: FormFieldRegistry) => { layer: PresentationLayer; report: ConversionReport }ConversionReport = { convertedCount: number; dropped: Array<{ sourceType, reason, label? }> }
createDefaultConversionRules() => ConversionRegistry
ConversionRegistry (class).register({ type, convert }): this / .convert(block, ctx): BlockConversionBlockConversion = { status: "converted"; block } | { status: "unwrapped"; blocks } | { status: "dropped"; sourceType; reason }
BlockConversionRule{ type: Block["type"]; convert: (source: Block, ctx: ConversionContext) => BlockConversion }The shape .register() takes — one rule per source block type.
ConversionContext{ target: FormFieldRegistry; convertBlocks: (blocks: Block[]) => Block[] }Passed to convert; convertBlocks recurses a container's body through the same rule set.

DropTarget = { kind: "beside"; anchorId: string; side: "before" \| "after" } | { kind: "container"; containerId: string; tabIndex?: number } | { kind: "append" }.

Linkage

See Linkage for the full model. Exported surface:

ExportSignature / Shape
validateLinkageSchema(layer: PresentationLayer, formLinkage?: FieldLinkage) => LinkageValidationResult ({ issues: ValidationIssue[] })
resolveLinkageEvaluators(overrides?: LinkageEvaluators) => Required<LinkageEvaluators>
defaultEvaluateExpression(source, values, context?) => boolean
defaultEvaluateAssignExpression(source, values, context?) => unknown
defaultEvaluateScriptAction(source, values, context?) => LinkageScriptResult | void
isStateAction / isEffectAction(action: FieldLinkageAction) => action is StateAction / EffectAction
isFieldEventTriggerKind(kind: LinkageTriggerKind) => boolean
getFieldPermission(permissions: Record<string, FieldPermission> | undefined, key: string) => FieldPermission | undefined — own-property-guarded lookup (a field keyed like an Object prototype member never reads a prototype slot). Added in v2.10.0
isWritableFieldPermission(permission: FieldPermission | undefined) => booleantrue for undefined (no clamp entry), "editable", and "required"; false for "visible" / "hidden". Added in v2.10.0
STATE_ACTION_TYPESreadonly StateActionType[] (show, hide, enable, disable, require, optional, assign, script)
EFFECT_ACTION_TYPESreadonly EffectActionType[] (set_field, set_variable, refresh_data_source, alert, api_call, navigate, submit, reset)
LINKAGE_ACTION_TYPESreadonly LinkageActionType[] — the union of the two above
KEYED_ONLY_ACTIONSReadonlySet<LinkageActionType> (require, optional, assign)
FIELD_TRIGGER_KINDS / FORM_TRIGGER_KINDSreadonly LinkageTriggerKind[] — legal triggers per rule scope
FIELD_EVENT_TRIGGER_KINDSreadonly LinkageTriggerKind[] (change, focus, blur, click)
LINKAGE_OPERATORSreadonly LinkageOperator[] (eq, ne, gt, lt, gte, lte, contains, empty, notEmpty)
ALERT_LEVELSreadonly LinkageAlertLevel[] (info, success, warning, error)

Types: FieldLinkage, FieldLinkageRule, FieldLinkageAction, FieldLinkageDefaults, LinkageTrigger, LinkageTriggerKind, LinkageCondition (LinkageConditionLeaf | LinkageConditionGroup | LinkageConditionExpression), LinkageOperator, StateAction, StateActionType, EffectAction, EffectActionType, LinkageActionType, LinkageActionValue, LinkageAlertLevel, ConditionRetrigger ("edge" | "always"), LinkageScriptResult, LinkageEvaluators, EvaluationContext, LinkageContextSource, EffectDispatchContext, FieldPermission ("hidden" | "visible" | "editable" | "required").

Validation

ExportSignature
formatIssueMessage(code: ValidationIssueCode, params?) => string — every issue message flows through this
ValidationIssue (type){ path: string; code: ValidationIssueCode; severity: ValidationSeverity; message: string; ruleId?: string }
ValidationIssueCode (type)Closed union of several dozen stable machine-readable codes (schema envelope, block tree, layout, linkage, …)
ValidationSeverity (type)"error" | "warning"

message is pre-rendered Chinese product copy; program against code and path, which are stable across locales.

Chrome

ContainerChromeSet is the per-device set of presentational shells for structural containers, installed via FormFieldRegistry.setContainerChrome:

MemberProps type
SectionSectionChromeProps = { title?: ReactNode; variant?: "card" | "collapse"; defaultCollapsed?: boolean; children: ReactNode }
TabsTabsChromeProps = { items: ChromeTabItem[]; activeKey?: string; onChange?: (key: string) => void }ChromeTabItem = { key: string; label: ReactNode; children: ReactNode }
SubformSubformChromeProps = { title?: ReactNode; children: ReactNode }
SubformRowSubformRowChromeProps = { removeControl?: ReactNode; children: ReactNode }
AddButtonAddControlChromeProps = { label: ReactNode; onClick: () => void }
RemoveButtonRemoveControlChromeProps = { onClick: () => void }

flex and grid are pure CSS layout and use no chrome — identical on both devices.