Implementation Examples
This page provides practical examples of how to implement the core interfaces of neuron-js to build a custom rules engine tailored to your domain.
1. Condition Implementation
Conditions are logical predicates. They should be stateless and only depend on their parameters and the current ExecutionContext.
import { AbstractCondition, ExecutionResult, ExecutionContext } from '@sebasoft/neuron-js';
/**
* Checks if the current hour in the context state matches a target.
*/
export class TimeOfDayCondition extends AbstractCondition {
static TYPE = 'is_time_of_day';
async execute(context: ExecutionContext): Promise<ExecutionResult<boolean>> {
// 1. Resolve parameters (e.g., "morning", "afternoon")
const targetPeriod = this.params.get('period').getValue(context);
// 2. Access state from context
const currentHour = context.state.systemTime.getHours();
let isMatch = false;
if (targetPeriod === 'morning') isMatch = currentHour >= 6 && currentHour < 12;
if (targetPeriod === 'afternoon') isMatch = currentHour >= 12 && currentHour < 18;
// 3. Return result (Condition value is boolean)
return new ExecutionResult(isMatch, context, isMatch);
}
}2. Action Implementation
Actions perform side effects or mutate the context state. They are only triggered if the Rule's conditions pass.
import { AbstractAction, ExecutionResult, ExecutionContext } from '@sebasoft/neuron-js';
/**
* Adds a customized audit message to the context.
*/
export class AuditLogAction extends AbstractAction {
static TYPE = 'audit_log';
async execute(context: ExecutionContext): Promise<ExecutionResult<void>> {
const messageTemplate = this.params.get('template').getValue(context);
const user = context.state.user.name;
// Mutate the context by adding a new message
const nextContext = {
...context,
messages: [
...context.messages,
{ type: 'info', text: `${user}: ${messageTemplate}` }
]
};
return new ExecutionResult(true, nextContext);
}
}3. Parameter Implementation
Parameters resolve values. They can be static (hardcoded in JSON) or dynamic (resolving paths in state or environment variables).
import { AbstractParameter, ExecutionContext } from '@sebasoft/neuron-js';
/**
* Resolves a value from an environment variable.
*/
export class EnvVarParameter extends AbstractParameter<string> {
static TYPE = 'env_var';
getValue(context: ExecutionContext): string | null {
// Parameters can use their 'value' property as the key
const envKey = this.value;
return process.env[envKey] || this.defaultValue || null;
}
}4. Context Implementation
While ExecutionContext is a flexible interface, you should define a strict schema for your application's state to ensure type safety in your plugins.
export interface MyAppContext extends ExecutionContext {
state: {
user: { id: string; name: string; loyalty: number };
cart: { items: any[]; total: number };
systemTime: Date;
};
}5. Putting It All Together
The following example shows how to register these custom plugins and execute a script that uses them.
import { Neuron, Synapse } from '@sebasoft/neuron-js';
// 1. Initialize Registry and register plugins
const registry = new Neuron();
registry.registerCondition(TimeOfDayCondition.TYPE, TimeOfDayCondition);
registry.registerAction(AuditLogAction.TYPE, AuditLogAction);
registry.registerParameter(EnvVarParameter.TYPE, EnvVarParameter);
// 2. Create the Engine
const engine = new Synapse(registry);
// 3. Define Logic (JSON)
const script = {
id: 'morning-greeting',
rules: [{
id: 'greet-rule',
type: 'simple_rule',
conditions: [
{
type: 'is_time_of_day',
params: [{ name: 'period', type: 'simple_string', value: 'morning' }]
}
],
actions: [
{
type: 'audit_log',
params: [{ name: 'template', type: 'simple_string', value: 'Good morning!' }]
}
]
}]
};
// 4. Run it
const context: MyAppContext = {
state: {
user: { id: '1', name: 'Alice', loyalty: 10 },
cart: { items: [], total: 0 },
systemTime: new Date() // Suppose it's 9:00 AM
},
messages: []
};
const result = engine.execute(script, context);
console.log(result.context.messages); // [{ type: 'info', text: 'Alice: Good morning!' }]6. Logical Grouping (AND/OR)
neuron-js uses a "Sum of Products" approach to logic. Conditions are grouped into blocks.
- AND: All conditions within a block must be true.
- OR: If any block is true, the entire rule evaluates to true.
How to trigger OR
Setting orCondition: true on a condition starts a new block. This new block is joined to the previous block with an OR operator.
Example: (A AND B) OR (C AND D)
{
"id": "complex-logic",
"conditions": [
{ "id": "A", "type": "is_gold_member" },
{ "id": "B", "type": "has_high_balance" },
{
"id": "C",
"type": "is_new_user",
"options": { "orCondition": true }
},
{ "id": "D", "type": "has_promo_code" }
]
}Evaluation Logic: (A && B) || (C && D)
7. Condition Inversion (NOT)
Any condition can be negated by setting inverted: true in its options. This is handled by the runtime, so your plugin implementation doesn't need to worry about it.
{
"id": "not-blocked",
"conditions": [
{
"type": "is_user_blocked",
"options": { "inverted": true }
}
]
}Evaluation Logic: !is_user_blocked
