Skip to Content
FrontendCrud V2Crud GeneratorGenerator v2Version Manager Reference

Version Manager Reference

src/core/version-manager.js is the single source of truth for all versioned file I/O. No other module writes version files directly — everything goes through these functions.


Directory Layout

config/versioned/ {layer}/ ← "json" or "tsx" {Bundle}/ {Entity}/ v0/ data.json ← raw API snapshot (JSON layer) or index.tsx (TSX layer) changes.json ← optional customization file v1/ data.json changes.json

Versions are always strings: v0, v1, v2 … sorted numerically.


Version Discovery

getAllVersions(entity, layer)

Returns all version strings for an entity in ascending order (["v0", "v1", "v2"]). Returns [] if the entity directory does not exist.

getLatestVersion(entity, layer)

Returns the highest version string, e.g. "v2". Returns null if no versions exist.

getNextVersion(entity, layer)

Returns the next version string that does not yet exist. If v0 and v1 exist, returns "v2". If no versions exist, returns "v0".

versionExists(entity, version, layer)

Returns true if the version directory exists.

getVersionInfo(entity, layer)

Returns an array of info objects for every version:

[{ version: "v0", path: "./config/versioned/json/AccountingBundle/Account/v0", isLatest: false, createdAt: Date | null }]

isLatest is true only for the highest version. createdAt is derived from the directory’s mtime.


Reading & Writing Version Data

loadVersionData(entity, version, layer)

Reads and parses {versionPath}/data.json. Returns null if the file does not exist or fails to parse.

saveVersionData(entity, version, layer, data)

Writes data as formatted JSON to {versionPath}/data.json, creating intermediate directories as needed. Returns the absolute path written.


Changes File I/O

hasVersionedChangesFile(entity, version, layer)

Returns true if {versionPath}/changes.json exists.

getVersionedChangesFilePath(entity, version, layer)

Returns the path string for a version’s changes.json without reading it.

loadVersionedChangesFile(entity, version, layer)

Reads and parses {versionPath}/changes.json. Returns null if absent or unparseable.

saveVersionedChangesFile(entity, version, layer, data)

Writes the changes object to {versionPath}/changes.json. Returns the path written.


findLatestVersionWithChanges(entity, layer)

Iterates versions in descending order (newest first) and returns the first version that has a changes.json file. Returns null if none have changes.

Used by generateJsonMigration when --fromVersion=latest is passed.


createVersionedChangesTemplate(entity, version, layer, data, overwrite, baseVersion)

Creates a changes.json template for a version. The template is populated from the raw API data (data.json) and, optionally, inherited from a previous version.

ParameterTypeDescription
entitystringEntity path
versionstringTarget version
layerstring"json" or "tsx"
dataObjectExtracted from the version’s data.json: listingFields, formFields, filterFields, showFields, requiredFields, sortableFields, implementedInterfaces, resourceName, writeableProperties, apiFilters
overwritebooleanIf true, overwrite an existing changes.json
baseVersionstring|nullIf set, inherit selections from this version via mergeChangesTemplates()

Template sections created (JSON layer):

{ "listingFields": { "all": [...], "selected": [], "hide": [], "sortable": [...] }, "formModalShowFields": { "fields": [], "hide": [] }, "formModalEditFields": { "fields": [], "hide": [] }, "formModalAddFields": { "fields": [], "hide": [] }, "showFields": { "sections": {}, "hide": [] }, "formFields": { "sections": {}, "required": [], "optional": [], "hide": [] }, "filterFields": { "all": [...], "defaults": [], "hide": [] }, "sideboxFields": { "all": [], "hide": [] }, "fieldOverrides": {}, "exclusionList": { "listing": [], "forms": [], "show": [] }, "actions": { "listing": [], "showPage": [] } }

The all arrays in listingFields and filterFields are pre-populated with every available field name from the raw API data. The sortable array is pre-populated from sortableFields (the OrderFilter list from the API).

Returns the path of the created file.


mergeChangesTemplates(oldChanges, newTemplate)

Merges selections from a previous version’s changes.json (oldChanges) into a freshly-created template (newTemplate). Called automatically when baseVersion is supplied to createVersionedChangesTemplate.

What is preserved from oldChanges:

FieldPreservation rule
listingFields.selectedKept — filtered to fields that still exist in newTemplate.listingFields.all
listingFields.hideKept as-is
listingFields.sortableKept — filtered to still-existing fields
listingFields.orderKept as-is
listingFields.sortableKeysKept as-is
listingFields.columnWidthsKept as-is
listingFields.alignmentKept as-is
listingFields.defaultOrderKept as-is
listingFields.iconFieldsKept — filtered to still-existing fields
listingFields.hideSelectedKept — filtered to still-existing fields
formFields.sectionsSection structure preserved — each section’s fields filtered to still-existing fields; empty sections dropped
formFields.requiredKept — filtered to still-existing fields
formFields.optionalKept — filtered to still-existing fields
formFields.hideKept as-is
formFields.validationKept as-is
formFields.defaultValuesKept as-is
formFields.helperTextKept as-is
formModalShowFieldsKept — fields filtered to still-existing fields
formModalEditFieldsKept — fields filtered to still-existing fields
formModalAddFieldsKept — fields filtered to still-existing fields
showFields.sectionsPreserved — fields per section filtered to still-existing fields; empty sections dropped
showFields.hideKept as-is
filterFields.defaultsKept — filtered to fields that still exist in newTemplate.filterFields.all
filterFields.hideKept as-is
filterFields.rangeKept as-is
filterFields.rangeKeysKept as-is
filterFields.translatableKept as-is
filterFields.searchMethodsKept as-is
filterFields.helperTextKept as-is
filterFields.fieldIconKept as-is
filterFields.hideToolbarKept as-is
sideboxFieldsKept as-is
fieldOverridesKept as-is
exclusionListKept as-is
actionsKept as-is

What is NOT preserved:

  • Fields that no longer exist in the new API data (filtered out automatically)
  • Empty sections after filtering (dropped)
  • _comment, description, metadata keys

This means incremental generation is non-destructive by default. Your customizations survive API schema updates unless the field was actually removed from the API.


Path Helpers

getLayerBasePath(layer)config/versioned/{layer}

getEntityPath(entity, layer)config/versioned/{layer}/{Bundle}/{Entity}

Normalizes \ to / in the entity path.

getVersionPath(entity, version, layer)config/versioned/{layer}/{Bundle}/{Entity}/{version}

Last updated on