@halleyassist/rule-templater
v0.0.19
Published
The grammar for HalleyAssist rules
Readme
rule-templater
Parsing and preparation of rule templates for HalleyAssist rules.
Installation
npm install @halleyassist/rule-templaterUsage
The rule-templater package provides utilities for working with rule templates that contain variable placeholders.
Basic Example
const RuleTemplate = require('@halleyassist/rule-templater');
// Define a template with variables
const template = 'EventIs(${EVENT_TYPE}) && Value() > ${THRESHOLD}';
// Parse the template to get a RuleTemplate instance
const parsed = RuleTemplate.parse(template);
// Extract variables from the template (uses AST)
const variables = parsed.extractVariables();
console.log(variables);
// [
// { name: 'EVENT_TYPE', filters: [], positions: [{ start: 8, end: 21 }] },
// { name: 'THRESHOLD', filters: [], positions: [{ start: 36, end: 48 }] }
// ]
// Extract function calls from the template
const functions = parsed.extractFunctions();
console.log(functions);
// ['EventIs', 'Value']
// Validate that variables are provided correctly
const validation = parsed.validate({
EVENT_TYPE: { value: 'sensor-update', type: 'string' },
THRESHOLD: { value: 42, type: 'number' }
});
console.log(validation.valid); // true
// Prepare the template with actual values
const prepared = parsed.prepare({
EVENT_TYPE: { value: 'sensor-update', type: 'string' },
THRESHOLD: { value: 42, type: 'number' }
});
console.log(prepared);
// 'EventIs("sensor-update") && Value() > 42'Complex Example
const template = '!(EventIs(StrConcat("DeviceEvent:measurement:", ${ACTION})) && TimeLastTrueSet("last_measurement") || TimeLastTrueCheck("last_measurement") < ${TIME})';
const parsed = RuleTemplate.parse(template);
// Extract variables
const variables = parsed.extractVariables();
// [
// { name: 'ACTION', filters: [], positions: [{ start: 48, end: 57 }] },
// { name: 'TIME', filters: [], positions: [{ start: 142, end: 149 }] }
// ]
// Prepare with values
const prepared = parsed.prepare({
ACTION: { value: 'temperature', type: 'string' },
TIME: { value: 60, type: 'number' }
});
// Result: !(EventIs(StrConcat("DeviceEvent:measurement:", "temperature")) && TimeLastTrueSet("last_measurement") || TimeLastTrueCheck("last_measurement") < 60)Template Filters
Variables can have filters applied to transform their values. Filters are applied using the pipe (|) syntax:
const template = 'EventIs(${EVENT_TYPE|upper})';
const parsed = RuleTemplate.parse(template);
const variables = parsed.extractVariables();
// [{ name: 'EVENT_TYPE', filters: ['upper'], positions: [{ start: 8, end: 27 }] }]
// Prepare with filters applied
const prepared = parsed.prepare({
EVENT_TYPE: { value: 'sensor-update' }
});
// Result: EventIs(SENSOR-UPDATE)Multiple Filters
Filters can be chained together and are applied in sequence:
const template = 'EventIs(${EVENT|trim|upper|string})';
const parsed = RuleTemplate.parse(template);
const prepared = parsed.prepare({
EVENT: { value: ' test ' }
});
// Result: EventIs("TEST")Available Filters
- string: Convert to JSON string representation (adds quotes and escapes)
- upper: Convert to uppercase
- lower: Convert to lowercase
- capitalize: Capitalize first letter
- title: Convert to title case (capitalize each word)
- trim: Remove leading/trailing whitespace
- number: Convert to number
- boolean: Convert to boolean
- abs: Absolute value (for numbers)
- round: Round number to nearest integer
- floor: Round number down
- ceil: Round number up
- humanise_list: Join array values into natural language, optionally with a custom joiner like
humanise_list("or") - humanise_time: Convert seconds into a single human-readable unit like
1 hour; optionally round down to a minimum unit such ashumanise_time("minute")orhumanise_time("min") - time_start: Extract
fromfromtime period/time period agoand convert totime value - time_end: Extract
tofromtime period/time period agoand convert totime value
Filter Examples
// String transformation
'${name|upper}' with name='john' → JOHN
'${name|capitalize}' with name='john doe' → John doe
'${name|title}' with name='john doe' → John Doe
// Number operations
'${value|abs}' with value=-42 → 42
'${value|round}' with value=3.7 → 4
'${value|floor}' with value=3.9 → 3
// Chaining filters
'${text|trim|upper}' with text=' hello ' → HELLO
// Humanise arrays
'${names|humanise_list}' with names=['a','b','c'] → a, b and c
'${names|humanise_list("or")}' with names=['a','b','c'] → a, b or c
// Humanise time from seconds
'${duration|humanise_time}' with duration=3600 → 1 hour
'${duration|humanise_time("minute")}' with duration=71 → 1 minute
// Time period conversion
'${window|time_start}' with window={from:'08:00',to:'12:00'} → 08:00General String Templating
For non-rule text templates, use GeneralTemplate:
const RuleTemplate = require('@halleyassist/rule-templater');
const { GeneralTemplate } = RuleTemplate;
const template = 'If a door is opened between ${ALERT_PERIOD | time_start} AND ${ALERT_PERIOD | time_end}';
const variables = GeneralTemplate.getVariables(template);
// [{ name: 'ALERT_PERIOD', filters: ['time_start', 'time_end'], positions: [...] }]
const parsed = GeneralTemplate.parse(template);
const prepared = parsed.prepare({
ALERT_PERIOD: {
value: { from: '08:00', to: '12:00' },
type: 'time period'
}
});
// If a door is opened between 08:00 AND 12:00Variable Template
For a single variable expression, use VariableTemplate:
const { VariableTemplate } = require('@halleyassist/rule-templater');
const parsed = VariableTemplate.parse('ALERT_PERIOD|time_start|upper');
const variable = parsed.extractVariable();
// { name: 'ALERT_PERIOD', filters: ['time_start', 'upper'] }
const formatted = parsed.format({
ALERT_PERIOD: {
value: { from: '08:00', to: '12:00' },
type: 'time period'
}
});
// { value: '08:00', type: 'string' }API
RuleTemplate.parse(ruleTemplate)
Parses a rule template string and returns a RuleTemplate instance.
Parameters:
ruleTemplate(string): The template string containing${VARIABLE}placeholders
Returns: A RuleTemplate instance with:
ruleTemplateText: The original template stringast: The parsed Abstract Syntax Tree
ruleTemplate.extractVariables()
Extracts all variables from the template using the AST.
Returns: Array of objects with:
name(string): The variable namefilters(array): Array of filter names applied to the variablepositions(array): Array of position objects, each with:start(number): Zero-based start index of the variable in the template stringend(number): Zero-based end index of the variable in the template string
Note: If a variable appears multiple times in the template, all occurrences will be recorded in the positions array.
ruleTemplate.extractFunctions()
Extracts all function calls from the template using the AST.
Returns: Array of unique function names (sorted alphabetically) used in the template.
Example:
const template = 'EventIs("test") && Value() > 10 && TimeLastTrueCheck("last_check") < 60';
const parsed = RuleTemplate.parse(template);
const functions = parsed.extractFunctions();
console.log(functions);
// ['EventIs', 'TimeLastTrueCheck', 'Value']This is useful for comparing against a hub's list of available functions to ensure all functions used in the template are supported.
ruleTemplate.validate(variables)
Validates that all required variables are provided, have valid types, and reference only known template filters.
Parameters:
variables(object): Object mapping variable names to their values and types- Each variable should be an object with:
value: The value to substitute (string, number, or boolean)type(optional): The variable type ('string', 'number', 'boolean', etc.)
- Each variable should be an object with:
Returns: Object with:
valid(boolean): Whether validation passederrors(array): Array of error messages (empty if valid)warnings(array): Array of non-fatal warnings (empty if none)
ruleTemplate.prepare(variables)
Prepares the template by replacing variables with their values and applying any filters.
Parameters:
variables(object): Object mapping variable names to their values and types- Each variable should be an object with:
value: The value to substitute (string, number, or boolean)type(optional): The variable type ('string', 'number', 'boolean', etc.)
- Each variable should be an object with:
Returns: The prepared rule string with variables replaced and filters applied
GeneralTemplate.parse(templateText)
Parses a general string template and returns a GeneralTemplate instance.
GeneralTemplate.getVariables(templateText) (Static)
Extracts variables from a general string template without creating an instance manually.
generalTemplate.getVariables()
Extracts variables from a general string template.
generalTemplate.prepare(variables)
Prepares a general string template by replacing ${...} placeholders with values and applying filters.
generalTemplate.validate()
Validates the template itself and reports any unknown filters used in ${...} chains.
RuleTemplate.validateVariableNode(astNode, variableType) (Static)
Helper method to validate that an AST node matches the expected variable type.
Parameters:
astNode: The AST node to validatevariableType(string): The expected variable type
Returns: true if the node is valid for the given type, false otherwise
RuleTemplate.TemplateFilters (Static)
Access to the filter functions used by the template engine. Can be extended with custom filters.
Custom filters receive a cloned varData object ({ value, type }) used for template rendering. They can mutate both fields without mutating the original input variables.
Example:
const RuleTemplate = require('@halleyassist/rule-templater');
// Add a custom filter
RuleTemplate.TemplateFilters.reverse = (varData) => {
varData.value = String(varData.value).split('').reverse().join('');
return varData;
};
// Use the custom filter
const template = 'EventIs(${EVENT|reverse})';
const parsed = RuleTemplate.parse(template);
const result = parsed.prepare({ EVENT: { value: 'test' } });
// Result: EventIs(tset)VariableValidate
BNF-backed validators for each supported variable type.
Example:
const { VariableValidate } = require('@halleyassist/rule-templater');
const result = VariableValidate.validate({
value: { from: '08:00', to: '12:00', ago: [2, 'HOURS'] },
type: 'time period ago'
});
console.log(result.valid);
// trueUse VariableValidate.validateValue(type, value) to validate a raw value against a specific variable type.
Variable Types
The following variable types are supported:
stringnumberbooleanobjecttime periodtime period agotime valuenumber timestring arraynumber arrayboolean arrayobject array
Time Type Formats
The time-related variable types use the following structures when passed to prepare() or validate():
time value
Use a time-of-day string such as 08:00.
{
value: '08:00',
type: 'time value'
}This is useful when a rule expects a single time value, for example:
const prepared = parsed.prepare({
START_TIME: { value: '08:00', type: 'time value' }
});time period
Use an object with from and to properties, both using the same time-of-day string format as time value.
{
value: {
from: '08:00',
to: '12:00'
},
type: 'time period'
}When rendered into a rule template, this becomes:
08:00 TO 12:00time period ago
Use the same structure as time period, plus an ago tuple containing:
- the numeric offset
- the rule time unit token, for example
HOURS
{
value: {
from: '08:00',
to: '12:00',
ago: [2, 'HOURS']
},
type: 'time period ago'
}When rendered into a rule template, this becomes:
08:00 TO 12:00 AGO 2 HOURSThe time_start and time_end filters can extract from and to from either time period or time period ago and convert them to time value.
License
ISC
