Creating Plugins
This guide walks you through creating custom plugins for x-fidelity.
Plugin Structure
A basic plugin must implement the XFiPlugin
interface:
interface XFiPlugin {
name: string;
version: string;
facts?: FactDefn[];
operators?: OperatorDefn[];
onError?: (error: Error) => PluginError;
}
Basic Example
// my-plugin.js
module.exports = {
name: 'my-plugin',
version: '1.0.0',
facts: [{
name: 'myCustomFact',
fn: async () => ({ result: 'custom data' })
}],
operators: [{
name: 'myCustomOperator',
fn: (factValue, expectedValue) => factValue === expectedValue
}],
onError: (error) => ({
message: `Plugin error: ${error.message}`,
level: 'warning',
details: error.stack
})
};
Project Structure
my-plugin/
├── src/
│ ├── facts/
│ │ ├── customFact.ts
│ │ └── customFact.test.ts
│ ├── operators/
│ │ ├── customOperator.ts
│ │ └── customOperator.test.ts
│ ├── rules/
│ │ └── sampleRules.json
│ └── index.ts
├── package.json
├── tsconfig.json
├── jest.config.js
└── README.md
Creating Facts
Facts collect and provide data:
interface FactDefn {
name: string;
fn: (params: any, almanac: any) => Promise<any>;
priority?: number;
}
Example fact:
const myFact = {
name: 'myCustomFact',
fn: async (params, almanac) => {
try {
const data = await collectData(params);
return { result: data };
} catch (error) {
logger.error(`Error in myCustomFact: ${error}`);
throw error;
}
},
priority: 1
};
Creating Operators
Operators evaluate conditions:
interface OperatorDefn {
name: string;
fn: (factValue: any, params: any) => boolean;
}
Example operator:
const myOperator = {
name: 'myCustomOperator',
fn: (factValue, expectedValue) => {
try {
return evaluateCondition(factValue, expectedValue);
} catch (error) {
logger.error(`Error in myCustomOperator: ${error}`);
return false;
}
}
};
Error Handling
Implement custom error handling:
interface PluginError {
message: string;
level: 'warning' | 'error' | 'fatality' | 'exempt';
details?: any;
}
Example error handler:
const onError = (error) => ({
message: `Plugin error: ${error.message}`,
level: determineErrorLevel(error),
details: {
stack: error.stack,
code: (error as any).code,
context: (error as any).context
}
});
Sample Rules
Include sample rules with your plugin:
{
"name": "custom-plugin-rule",
"conditions": {
"all": [
{
"fact": "myCustomFact",
"operator": "myCustomOperator",
"value": "expected value"
}
]
},
"event": {
"type": "warning",
"params": {
"message": "Custom rule triggered"
}
}
}
Testing
Create comprehensive tests:
describe('myPlugin', () => {
it('should validate custom fact', async () => {
const result = await myFact.fn();
expect(result).toEqual({ result: 'custom data' });
});
it('should validate custom operator', () => {
const result = myOperator.fn('test', 'test');
expect(result).toBe(true);
});
});
Publishing
- Create a package.json:
{
"name": "xfi-my-plugin",
"version": "1.0.0",
"main": "index.js",
"peerDependencies": {
"x-fidelity": "^3.0.0"
}
}
- Publish to npm:
npm publish
Next Steps
- See Plugin Examples
- Learn Best Practices