Status: canonical metadata schema for VLCode-Lite and DocCenter Path
4Scope:
- Defines the canonical
ProjectMetaJSON consumed by metadata diff, workflow regeneration, project context, and DocCenter-backed tooling- Normalizes legacy extractor output into one stable shape
- Aligns Theme metadata with VL 3.7 / THEME 6.6
ProjectMeta is the machine-readable project model for a VL workspace.
It exists to support:
Apps/, Sections/, ExtComponents/, Services/, Database/, and Theme/This spec defines the canonical schema. Legacy field names may still be accepted by importers, but they are not canonical output.
Canonical root object:
{
"$schema": "VL-ProjectMeta/3.0",
"projectName": "SmartCampus",
"projectDescription": "optional",
"vlVersion": "3.7",
"config": {
"deviceTarget": "web",
"screenResolution": "1440x900"
},
"fileManifest": [],
"valueDomains": {},
"apps": [],
"sections": [],
"components": [],
"services": [],
"dataSchema": {
"tables": [],
"relations": []
},
"theme": null,
"dependencyGraph": null
}
apps[*].id is the canonical app identifiersections[*].id is the canonical section identifiercomponents[*].id is the canonical component identifierservices[*].domainId is the canonical service-domain identifierservices[*].methods[*].id is the canonical method identifierservices[*].methods[*].serviceId is the canonical fully-qualified service identifier, normally domainId.methodIddataSchema.tables[*].id is the canonical table identifiertheme.id is the canonical theme identifierLegacy aliases such as appId, sectionId, componentId, tableName, serviceDomains, servicesUsed, and componentRefs are compatibility inputs only.
Canonical default file locations:
Apps/<id>.vxSections/<id>.scExtComponents/<id>.cpServices/<domainId>.vsDatabase/<projectName>.vdb or explicit per-table/file-level mapping when availableTheme/Theme.vthIf filePath is omitted during normalization, tooling may synthesize the conventional path above.
{
"id": "AdminApp",
"filePath": "Apps/AdminApp.vx",
"vlVersion": "3.7",
"device": "web",
"resolution": "1440x900",
"description": "",
"globalVars": [],
"pages": [
{
"id": "Dashboard",
"route": "/dashboard",
"sections": ["DashboardMain"],
"sectionRefs": [
{
"sectionId": "DashboardMain",
"instanceId": "mainSection",
"layoutProps": {}
}
],
"componentRefs": [],
"layout": []
}
],
"routeMap": {
"/dashboard": "DashboardMain"
},
"homeRoute": "/dashboard",
"wiring": [],
"navSectionInstanceId": null,
"navEventName": null
}
Rules:
pages[*].id must be stable inside the apppages[*].sections contains section IDs onlypages[*].sectionRefs[*].sectionId must resolve to an existing sectionrouteMap is a convenience index, not a second source of truth{
"id": "DashboardMain",
"filePath": "Sections/DashboardMain.sc",
"vlVersion": "3.7",
"previewSize": null,
"publicProps": [],
"publicEvents": [],
"publicMethods": [],
"globalVars": [],
"derivedVars": [],
"consumesServices": [
"CampusService.getOverview",
"CampusService.listAlerts"
],
"usesComponents": [
"StatCard",
"AlertTable"
],
"interactiveElements": [],
"internalMethods": [],
"pipeFuncs": [],
"isNavSection": false,
"navMenuItems": [],
"navItemInstanceId": null,
"keyStates": {},
"description": ""
}
Rules:
consumesServices is an array of service ID strings, not nested service objectsusesComponents is an array of component ID strings, not component ref objectsconsumesServices[*] must resolve to an existing services[*].methods[*].serviceIdusesComponents[*] must resolve to an existing components[*].id{
"id": "StatCard",
"filePath": "ExtComponents/StatCard.cp",
"vlVersion": "3.7",
"previewSize": null,
"publicProps": [],
"publicEvents": [],
"derivedVars": [],
"interactiveElements": [],
"internalMethods": [],
"pipeFuncs": [],
"description": ""
}
{
"domainId": "CampusService",
"filePath": "Services/CampusService.vs",
"vlVersion": "3.7",
"envVars": [],
"methods": [
{
"id": "getOverview",
"serviceId": "CampusService.getOverview",
"type": null,
"params": [],
"returns": {},
"expose": null,
"sig": null
}
],
"virtualTables": [
{
"id": "OverviewView",
"source": "campus_overview",
"fields": ["id", "title"],
"extraSpecs": {}
}
],
"transactions": [],
"backendComponents": []
}
Rules:
domainId is canonical; id/name are compatibility inputs onlymethods[*].id is canonical; methods[*].name is compatibility input onlymethods[*].serviceId should be emitted even when it can be derivedvirtualTables[*].source must reference an existing table ID when it points to a physical table{
"dataSchema": {
"tables": [
{
"id": "campus_overview",
"name": "campus_overview",
"filePath": "",
"fields": [
{
"name": "id",
"type": "STRING",
"notNull": true,
"default": null,
"enumRef": null,
"sourceField": null
}
],
"indexes": [],
"seedData": null
}
],
"relations": [
{
"id": "overview_to_building",
"from": "campus_overview",
"to": "building",
"cardinality": "N:1"
}
]
}
}
Rules:
id, not tableNamefields; columns is compatibility input onlyenumRef must resolve into valueDomains.enums[*].name when used{
"theme": {
"id": "Theme",
"name": "Theme",
"filePath": "Theme/Theme.vth",
"vlVersion": "3.7",
"rootTag": "Theme-Enterprise-6.6",
"meta": {
"mode": "light",
"version": "6.6.0",
"styleSpaceVersion": "1.6",
"base_theme": "Platform/Theme-Default-Light@1",
"profile": "enterprise"
},
"slots": {
"intent.primary.intentBg": "#2563EB",
"size.md.sizeMinHeight": "40px",
"state.focus.stateShadow": "@intent.intentFocusRing"
},
"designTokens": [],
"componentVariants": [],
"bindingRules": [],
"overrides": []
}
}
Rules:
# Meta plus # Point Slot Valuestheme.slots is the canonical compiled metadata view of .vth point-slot assignmentsdesignTokens, componentVariants, bindingRules, and overrides are compatibility carry-through fields only; new tooling should not require themValidation must reject or flag at least the following:
enumRef points at a non-existent enumNormalizers may accept the following legacy inputs:
| Legacy input | Canonical output |
|---|---|
project.name / project.projectName |
projectName |
database |
dataSchema |
serviceDomains |
services |
appId |
apps[*].id |
sectionId |
sections[*].id |
componentId |
components[*].id |
servicesUsed / services / serviceDomains on section |
consumesServices |
componentRefs / components on section |
usesComponents |
tableName / entityId |
dataSchema.tables[*].id |
columns |
fields |
methods[*].name |
methods[*].id |
theme.pointSlotValues / theme.pointSlots |
theme.slots |
Compatibility input does not change canonical output names.
Any producer that claims compliance with Metadata Spec 3.0 must emit:
$schema: "VL-ProjectMeta/3.0"sections[*].consumesServices as string IDssections[*].usesComponents as string IDsservices[*].methods[*].iddataSchema.tables[*].idtheme.slots when theme slot data is availableConsumers should:
Use exactly:
VL-ProjectMeta/3.0