Form versioning and migrations
Since 0.1.13, QForm Builder provides a canonical, versioned persistence format. It lets the following areas evolve independently:
- the document envelope;
- the FormKit/Quasar schema;
- the serialized theme.
The engine remains compatible with raw schema arrays and historical unversioned autosave states.
Current versions
import {
QFORM_BUILDER_DOCUMENT_VERSION,
QFORM_BUILDER_SCHEMA_VERSION,
QFORM_BUILDER_THEME_VERSION,
} from '@vevedh/qform-builder-layer/migrations'The 0.1.13 values are:
QFORM_BUILDER_DOCUMENT_VERSION = 1
QFORM_BUILDER_SCHEMA_VERSION = 1
QFORM_BUILDER_THEME_VERSION = 2The theme version is independent from the schema version. Theme migration 1 to 2 adds the background, effects, motion, and icons groups introduced by the advanced Theme Builder.
Canonical document
{
"format": "qform-builder",
"documentVersion": 1,
"schemaVersion": 1,
"themeVersion": 2,
"exportedAt": "2026-06-27T15:00:00.000Z",
"schema": [],
"values": {},
"settings": {
"formName": "Form",
"preview": {
"width": 640,
"isFullWidth": false
},
"previewMode": "editing",
"columns": "default",
"theme": {}
},
"metadata": {
"source": "form-builder"
}
}The complete document is now the recommended export mode. Schema only remains available for historical integrations.
Pure API
import {
createQFormBuilderDocument,
migrateQFormBuilderDocument,
parseQFormBuilderDocument,
stringifyQFormBuilderDocument,
} from '@vevedh/qform-builder-layer/migrations'
const document = createQFormBuilderDocument({
schema,
values,
settings,
}, {
metadata: {
name: 'Support form',
source: 'nfz-forms',
},
})
const json = stringifyQFormBuilderDocument(document)
const result = parseQFormBuilderDocument(json)
console.log(result.sourceKind)
console.log(result.migrations)
console.log(result.warnings)migrateQFormBuilderDocument() accepts:
- a historical raw schema array;
- a
{ schema: [...] }envelope; - a historical
{ schema, values, settings, savedAt }state; - a canonical versioned document.
The result records exactly what the source included:
result.included.values
result.included.settingsThis prevents an old schema-only export from clearing current values or theme settings.
Component API
const builder = ref<FormBuilderPublicApi | null>(null)
const document = builder.value?.exportDocument({
metadata: { source: 'admin-ui' },
})
builder.value?.importDocument(document)
builder.value?.importDocument(json) // JSON strings are accepted tooImport options:
builder.value?.importDocument(legacySchema, {
applyValues: false,
applySettings: false,
})By default:
- the schema is always applied;
- values are only applied when the source contained values;
- settings and theme are only applied when the source contained settings.
Events:
<FormBuilder
@document-import="auditImport"
@document-export="auditExport"
/>document-import exposes the source, versions, executed migrations, warnings, and the groups that were actually applied.
Migration registry
import {
qFormBuilderMigrationRegistry,
resolveQFormBuilderMigrationPath,
} from '@vevedh/qform-builder-layer/migrations'
const themePath = resolveQFormBuilderMigrationPath('theme', 1)Each entry has a stable identifier, scope, source version, target version, and description. resolveQFormBuilderMigrationPath() resolves a contiguous deterministic path and fails when a step is missing or cyclic. An unsupported future version is rejected explicitly; it is never interpreted as an older version.
Autosave
Autosave now writes the canonical document. Reading remains compatible with the historical unversioned format. An existing localStorage entry is therefore migrated on the next save without a destructive prerequisite step.
Security
The engine:
- reuses
sanitizeQFormBuilderSchema(); - rejects
__proto__,prototype, andconstructorkeys; - rejects functions, symbols, non-finite numbers, and custom prototypes;
- bounds depth, value count, string size, and JSON size;
- filters settings and metadata through a closed contract;
- normalizes themes and icons through the existing allowlists;
- rejects future versions instead of silently downgrading them.
Document values must remain JSON-compatible. Browser File, Blob, and FileList objects are not embedded in .qform.json: upload binaries separately, then persist only a validated identifier or metadata. If a non-serializable value reaches autosave, the previous local snapshot is preserved and the error event receives AUTOSAVE_SERIALIZATION_FAILED.
The backend remains responsible for authentication, RBAC, quotas, concurrency control, and server-side validation before persistence.
Policy for future migrations
Every persisted format change must:
- increment only the version of the affected scope;
- add a pure deterministic migration to the registry;
- preserve the source object;
- document defaults and any potential data loss;
- add a runtime test, browser scenario, and FR/EN migration note;
- reject unknown future versions.