Skip to main content

Configuration

The FDS Transformer uses a JSON configuration file to define how source fields map to FDS format. This guide covers the complete mapping configuration schema.

Configuration File

Create a mapping.json file in your project:

{
"$schema": "https://spec.vitness.me/schemas/transformer/v1.0.0/mapping.schema.json",
"version": "1.0.0",
"targetSchema": {
"version": "1.0.0",
"entity": "exercise"
},
"registries": { },
"mappings": { },
"enrichment": { },
"validation": { },
"output": { }
}

Schema Reference

Root Properties

PropertyTypeRequiredDescription
$schemastringNoJSON Schema URL for IDE validation
versionstringYesConfig version (e.g., "1.0.0")
targetSchemaobjectYesTarget FDS schema configuration
registriesobjectNoRegistry sources for lookups
mappingsobjectYesField mapping definitions
enrichmentobjectNoAI enrichment configuration
validationobjectNoValidation settings
outputobjectNoOutput format settings
pluginsarrayNoCustom transform plugins

targetSchema

Specifies which FDS schema to target:

{
"targetSchema": {
"version": "1.0.0",
"entity": "exercise",
"url": "https://spec.vitness.me/schemas/exercises/v1.0.0/exercise.schema.json"
}
}
PropertyTypeRequiredDescription
versionstringYesFDS schema version
entitystringNoEntity type: exercise, equipment, muscle, muscle-category, body-atlas
urlstringNoCustom schema URL (overrides default)

registries

Configure registry sources for lookups. Registries provide muscle, equipment, and category data for the registryLookup transform.

{
"registries": {
"muscles": {
"source": "local",
"local": "./registries/muscles.registry.json"
},
"equipment": {
"source": "local",
"local": "./registries/equipment.registry.json"
},
"muscleCategories": {
"source": "local",
"local": "./registries/muscle-categories.registry.json"
}
}
}
PropertyTypeDescription
sourcestringSource type: local, remote, inline
localstringPath to local registry file
urlstringURL for remote registry
inlinearrayInline registry entries
cachebooleanCache remote registries locally
fallbackstringFallback source if primary fails

Note: You must provide your own registry files. The transformer does not ship with pre-built registries.


mappings

Define how source fields map to FDS fields:

{
"mappings": {
"canonical.name": {
"from": "name",
"transform": "titleCase"
},
"canonical.slug": {
"from": "name",
"transform": "slugify"
},
"exerciseId": {
"from": null,
"transform": "uuid"
},
"targets.primary": {
"from": "target",
"transform": "registryLookup",
"options": {
"registry": "muscles",
"fuzzyMatch": true,
"threshold": 0.8
}
}
}
}

Mapping Types

Simple String Mapping:

{
"canonical.name": "name"
}

Object Mapping:

{
"canonical.name": {
"from": "name",
"transform": "titleCase",
"default": "Unknown Exercise",
"required": true
}
}

Mapping Properties

PropertyTypeDescription
fromstring | string[] | nullSource field path(s), or null for generated
transformstring | string[]Transform function(s) to apply
optionsobjectOptions passed to transform function
defaultanyDefault value if source is missing
requiredbooleanWhether field is required
conditionstringCondition expression for conditional mapping
enrichmentobjectField-level AI enrichment config

Nested Field Paths

Use dot notation for nested fields:

{
"canonical.name": "name",
"canonical.slug": { "from": "name", "transform": "slugify" },
"canonical.description": "description",
"classification.exerciseType": "type",
"classification.level": "difficulty"
}

Multiple Source Fields

Combine multiple source fields:

{
"canonical.name": {
"from": ["firstName", "lastName"],
"transform": "template",
"options": {
"template": "{{firstName}} {{lastName}}"
}
}
}

Chained Transforms

Apply multiple transforms in sequence:

{
"canonical.slug": {
"from": "name",
"transform": ["titleCase", "slugify"]
}
}

enrichment

Configure AI enrichment. See AI Enrichment Guide for details.

{
"enrichment": {
"enabled": true,
"provider": "openrouter",

"tiers": {
"simple": {
"model": "anthropic/claude-haiku-4.5",
"temperature": 0.1,
"maxTokens": 1000,
"batchSize": 5,
"priority": "speed"
},
"medium": {
"model": "anthropic/claude-sonnet-4.5",
"temperature": 0.1,
"maxTokens": 1500,
"batchSize": 3,
"priority": "balanced"
},
"complex": {
"model": "anthropic/claude-sonnet-4.5",
"temperature": 0.1,
"maxTokens": 2000,
"batchSize": 1,
"priority": "accuracy"
}
},

"fields": {
"canonical.aliases": { "tier": "simple", "prompt": "aliases" },
"classification.movement": { "tier": "complex", "prompt": "biomechanics" }
},

"fallback": {
"retries": 2,
"degradeModel": true,
"useDefaults": true
},

"rateLimit": {
"requestsPerMinute": 50,
"backoffStrategy": "exponential"
},

"checkpoint": {
"enabled": true,
"saveInterval": 10
}
}
}

validation

Configure output validation:

{
"validation": {
"enabled": true,
"strict": false,
"failOnError": false,
"outputErrors": "./validation-errors.json"
}
}
PropertyTypeDefaultDescription
enabledbooleantrueEnable schema validation
strictbooleanfalseFail on any validation error
failOnErrorbooleanfalseStop processing on first error
outputErrorsstring-Path to write validation errors

output

Configure output format:

{
"output": {
"format": "json",
"pretty": true,
"directory": "./fds-output",
"naming": "{{canonical.slug}}",
"singleFile": false,
"singleFileName": "exercises.json"
}
}
PropertyTypeDefaultDescription
formatstringjsonOutput format: json, jsonl, ndjson
prettybooleantruePretty-print JSON
directorystring./Output directory
namingstring{{canonical.slug}}Filename template
singleFilebooleanfalseOutput all to single file
singleFileNamestringoutput.jsonName for single file output

plugins

Load custom transform plugins:

{
"plugins": [
"./plugins/my-transforms.js",
{
"name": "./plugins/custom.js",
"options": {
"apiKey": "..."
}
}
]
}

See Plugin Development for details.


Complete Example

{
"$schema": "https://spec.vitness.me/schemas/transformer/v1.0.0/mapping.schema.json",
"version": "1.0.0",
"targetSchema": {
"version": "1.0.0",
"entity": "exercise"
},
"registries": {
"muscles": {
"source": "local",
"local": "./registries/muscles.registry.json"
},
"equipment": {
"source": "local",
"local": "./registries/equipment.registry.json"
}
},
"mappings": {
"exerciseId": {
"from": null,
"transform": "uuid"
},
"schemaVersion": {
"from": null,
"default": "1.0.0"
},
"canonical.name": {
"from": "name",
"transform": "titleCase",
"required": true
},
"canonical.slug": {
"from": "name",
"transform": "slugify"
},
"classification.exerciseType": {
"from": "type",
"default": "strength"
},
"targets.primary": {
"from": "target",
"transform": "registryLookup",
"options": {
"registry": "muscles",
"fuzzyMatch": true,
"returnFormat": "array"
}
},
"equipment.required": {
"from": "equipment",
"transform": ["toArray", "registryLookup"],
"options": {
"registry": "equipment",
"fuzzyMatch": true
}
},
"metrics.primary": {
"from": null,
"default": { "type": "reps", "unit": "count" }
},
"metadata": {
"from": null,
"transform": "autoGenerate",
"options": {
"fields": ["createdAt", "updatedAt", "status"]
}
}
},
"enrichment": {
"enabled": true,
"provider": "openrouter",
"tiers": {
"simple": {
"model": "anthropic/claude-haiku-4.5",
"temperature": 0.1,
"maxTokens": 1000,
"batchSize": 5,
"priority": "speed"
},
"medium": {
"model": "anthropic/claude-sonnet-4.5",
"temperature": 0.1,
"maxTokens": 1500,
"batchSize": 3,
"priority": "balanced"
},
"complex": {
"model": "anthropic/claude-sonnet-4.5",
"temperature": 0.1,
"maxTokens": 2000,
"batchSize": 1,
"priority": "accuracy"
}
},
"fields": {
"canonical.aliases": { "tier": "simple", "prompt": "aliases" },
"classification.movement": { "tier": "complex", "prompt": "biomechanics" },
"targets.secondary": { "tier": "complex", "prompt": "biomechanics" }
}
},
"validation": {
"enabled": true,
"strict": false
},
"output": {
"format": "json",
"pretty": true,
"directory": "./fds-output"
}
}

See Also