8.9 KiB
API Definition Maintenance Guide
Single Source of Truth
All INAV JavaScript API definitions are centralized in:
js/transpiler/api/definitions/
When adding new INAV features, you only need to edit files in this directory.
Directory Structure
js/transpiler/api/definitions/
├── index.js # Exports all definitions
├── events.js # Event handlers (timer, whenChanged, etc.)
├── flight.js # Flight parameters (read-only)
├── gvar.js # Global variables (read/write)
├── helpers.js # Math & utility functions
├── override.js # Override settings (writable)
├── pid.js # Programming PID controllers
├── rc.js # RC channels (read/write)
└── waypoint.js # Waypoint navigation
Definition Format
Each definition file exports objects following this structure:
module.exports = {
propertyName: {
type: 'number' | 'boolean' | 'string' | 'object' | 'function',
desc: 'Human-readable description',
unit: 'Unit of measurement (optional)',
readonly: true | false,
range: [min, max], // Optional: valid value range
inavOperand: {
type: 2, // OPERAND_TYPE constant
value: 1 // Operand value for INAV
},
inavOperation: 27, // Optional: OPERATION constant
},
// Nested objects
nestedObject: {
type: 'object',
desc: 'Description',
properties: {
subProperty: {
type: 'number',
desc: 'Sub-property description',
// ... same structure as above
}
}
}
};
What Uses These Definitions
1. Semantic Analyzer (analyzer.js)
- Validates property access
- Checks writable properties
- Validates value ranges
- Auto-updates when definitions change
2. Type Definitions (types.js)
- Generates TypeScript definitions for Monaco Editor
- Provides IntelliSense autocomplete
- Auto-generates from definitions
3. Code Generator (codegen.js)
- Maps JavaScript to INAV operands
- Uses
inavOperandandinavOperationfields - Requires manual update for new operations
4. Decompiler (decompiler.js)
- Reverse maps INAV to JavaScript
- Uses operand mappings from API definitions
- Auto-updates when definitions change
Adding a New Property
Example: Adding flight.compassHeading
1. Edit js/transpiler/api/definitions/flight.js:
module.exports = {
// ... existing properties ...
compassHeading: {
type: 'number',
unit: '°',
desc: 'Compass heading in degrees (0-359)',
readonly: true,
range: [0, 359],
inavOperand: {
type: 2, // OPERAND_TYPE.FLIGHT
value: 40 // FLIGHT_PARAM.COMPASS_HEADING
}
}
};
2. Update inav_constants.js (if needed):
Only if adding a completely new INAV firmware feature:
const FLIGHT_PARAM = {
// ... existing params ...
COMPASS_HEADING: 40
};
const FLIGHT_PARAM_NAMES = {
// ... existing names ...
[FLIGHT_PARAM.COMPASS_HEADING]: 'compassHeading'
};
3. That's it!
The following automatically update:
- ✅ Semantic analyzer validates
flight.compassHeading - ✅ TypeScript definitions show in autocomplete
- ✅ Range checking works automatically
- ✅ Decompiler recognizes the property
Adding a New Writable Property
Example: Adding override.vtx.frequency
1. Edit js/transpiler/api/definitions/override.js:
module.exports = {
// ... existing properties ...
vtx: {
type: 'object',
desc: 'VTX control',
properties: {
power: { /* ... */ },
band: { /* ... */ },
channel: { /* ... */ },
// NEW PROPERTY
frequency: {
type: 'number',
unit: 'MHz',
desc: 'VTX frequency in MHz',
readonly: false, // Writable!
range: [5000, 6000],
inavOperation: 50 // New operation code
}
}
}
};
2. Update inav_constants.js:
const OPERATION = {
// ... existing operations ...
OVERRIDE_VTX_FREQUENCY: 50
};
3. Update codegen.js (manual):
Add code generation logic:
// In generateAction() method
if (stmt.target === 'inav.override.vtx.frequency') {
return this.pushLogicCommand(
OPERATION.OVERRIDE_VTX_FREQUENCY,
{ type: OPERAND_TYPE.VALUE, value: 0 },
this.valueOperand(stmt.value),
activatorId
);
}
Adding a New Top-Level API Object
Example: Adding inav.sensors
1. Create js/transpiler/api/definitions/sensors.js:
'use strict';
module.exports = {
acc: {
type: 'boolean',
desc: 'Accelerometer sensor detected',
readonly: true,
inavOperand: {
type: 2, // FLIGHT
value: 50 // New param ID
}
},
mag: {
type: 'boolean',
desc: 'Magnetometer sensor detected',
readonly: true,
inavOperand: {
type: 2,
value: 51
}
}
// ... more sensors
};
2. Update js/transpiler/api/definitions/index.js:
'use strict';
module.exports = {
flight: require('./inav.flight.js'),
override: require('./inav.override.js'),
rc: require('./rc.js'),
gvar: require('./gvar.js'),
waypoint: require('./inav.waypoint.js'),
pid: require('./pid.js'),
helpers: require('./helpers.js'),
events: require('./events.js'),
sensors: require('./sensors.js') // ADD THIS
};
3. Update TypeScript types in types.js generation:
The type generator should automatically pick it up, but verify:
// In generateTypeDefinitions()
dts += generateInterfaceFromDefinition('sensors', apiDefinitions.sensors);
Validation Checklist
When adding/modifying API definitions:
- Property has correct
type - Has descriptive
desc - Has
unitif applicable readonlyflag is correctrangeis specified for numeric valuesinavOperandmaps to correct INAV constantinavOperationspecified for writable properties- Updated
index.jsif new file - Updated
inav_constants.jsif new INAV feature - Updated
codegen.jsfor new writable properties - Tested with sample code
- TypeScript definitions generate correctly
Testing Changes
After modifying definitions:
// 1. Test semantic analysis
const code = `
if (sensors.acc) {
// ...
}
`;
const transpiler = new Transpiler();
const result = transpiler.transpile(code);
// Should not have errors
// 2. Test type generation
const { generateTypeDefinitions } = require('./api/types.js');
const dts = generateTypeDefinitions(apiDefinitions);
// Should include new properties
// 3. Test in Monaco Editor
// Open configurator, verify autocomplete shows new properties
Common Mistakes
❌ Wrong: Editing analyzer.js directly
// DON'T DO THIS in analyzer.js:
this.inavAPI = {
'flight': {
properties: ['homeDistance', 'newProperty'] // Hard-coded!
}
};
✅ Right: Edit definition file
// DO THIS in inav.flight.js:
module.exports = {
newProperty: {
type: 'number',
desc: 'New property',
// ...
}
};
❌ Wrong: Duplicating definitions
// DON'T duplicate in multiple files
// decompiler.js - NO!
const FLIGHT_PARAMS = {
1: 'homeDistance'
};
// analyzer.js - NO!
properties: ['homeDistance']
✅ Right: Use centralized definitions
// DO THIS - import from definitions
const apiDefinitions = require('./../api/definitions/index.js');
const flightDef = apiDefinitions.flight;
File Dependencies
js/transpiler/api/definitions/
├── index.js
├── events.js
├── flight.js
├── gvar.js
├── helpers.js
├── override.js
├── pid.js
├── rc.js
└── waypoint.js
↓
Used by:
├── analyzer.js (validation)
├── types.js (TypeScript generation)
├── codegen.js (code generation)
└── decompiler.js (via inav_constants.js)
Migration from Hardcoded Values
If you find hardcoded API definitions elsewhere in the code:
- Identify the hardcoded values
- Check if they exist in
api/definitions/ - If not, add them to appropriate definition file
- Replace hardcoded values with imports
- Test thoroughly
- Remove old hardcoded definitions
Example:
// Before (hardcoded in analyzer.js)
this.inavAPI = {
'flight': {
properties: ['homeDistance', 'altitude']
}
};
// After (using definitions)
const apiDefinitions = require('./../api/definitions/index.js');
this.inavAPI = this.buildAPIStructure(apiDefinitions);
Summary
One Rule: Edit only js/transpiler/api/definitions/*.js
Everything else updates automatically (except codegen.js which requires manual updates for new operations).
This ensures:
- ✅ Single source of truth
- ✅ No duplication
- ✅ Easy maintenance
- ✅ Fewer bugs
- ✅ Automatic validation
- ✅ Automatic type generation