npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-templater

Usage

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 as humanise_time("minute") or humanise_time("min")
  • time_start: Extract from from time period / time period ago and convert to time value
  • time_end: Extract to from time period / time period ago and convert to time 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:00

General 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:00

Variable 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 string
  • ast: 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 name
  • filters (array): Array of filter names applied to the variable
  • positions (array): Array of position objects, each with:
    • start (number): Zero-based start index of the variable in the template string
    • end (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.)

Returns: Object with:

  • valid (boolean): Whether validation passed
  • errors (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.)

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 validate
  • variableType (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);
// true

Use VariableValidate.validateValue(type, value) to validate a raw value against a specific variable type.

Variable Types

The following variable types are supported:

  • string
  • number
  • boolean
  • object
  • time period
  • time period ago
  • time value
  • number time
  • string array
  • number array
  • boolean array
  • object 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:00

time 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 HOURS

The 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