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

@aimidy/render-rules

v1.1.3

Published

A library for rendering rules in TypeScript

Readme

render-rules

Lightweight, composable rule evaluation engine for objects (and arrays of objects). Define simple field conditions or nest logical groups (all / any) with optional negation. Includes rich date/time operators and relative-now comparisons.

Features

  • Simple field conditions (equality, comparison, membership, string ops)
  • Nested logical groups: all (AND) / any (OR)
  • not inversion available on any condition or group
  • Array row evaluation (rows array is treated with OR semantics)
  • Date & time operators (absolute & relative to Date.now())
  • Robust handling of mixed input types: Date, ISO string, timestamp number
  • Graceful failure (invalid date inputs yield false, unknown operator throws)

Installation

npm install @aimidy/render-rules

Basic Usage

import { evaluateCondition } from '@aimidy/render-rules';

const rule = { field: 'age', operator: 'equals', value: 30 };
const row = { age: 30 };

console.log(evaluateCondition(rule, row)); // true

Configuration Options

The evaluateCondition function accepts an optional options parameter for customizing evaluation behavior:

interface EvaluateOptions {
    treatMissingRowAsFalse?: boolean; // default: true
    onError?: (err: Error, info: { rule: Rule | Condition; row: any }) => void;
}

treatMissingRowAsFalse

Controls how null or undefined rows are handled:

// Default behavior (treatMissingRowAsFalse: true)
evaluateCondition(rule, null); // false
evaluateCondition(rule, undefined); // false

// Alternative behavior (treatMissingRowAsFalse: false)
evaluateCondition(rule, null, { treatMissingRowAsFalse: false }); // true
evaluateCondition(rule, undefined, { treatMissingRowAsFalse: false }); // true

onError Callback

Provides custom error handling instead of throwing exceptions:

const options = {
    onError: (error, info) => {
        console.log('Evaluation error:', error.message);
        console.log('Rule:', info.rule);
        console.log('Row:', info.row);
    },
};

// Instead of throwing, calls onError and returns false
evaluateCondition({ field: 'x', operator: 'unknown', value: 1 }, { x: 1 }, options); // false (calls onError callback)

Supported Operators

| Category | Operators | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | Comparison | equals, notEquals, greaterThan, lessThan | | Collection/String | contains, startsWith, endsWith, in, notIn | | Date / Time | dateEquals, dateNotEquals, dateAfter, dateBefore, dateOnOrAfter, dateOnOrBefore, nowAfterPlusMinutes, nowBeforePlusMinutes |

Date / Time Operator Semantics

| Operator | Meaning (row.dt vs rule.value) | | ---------------------- | ----------------------------------- | | dateEquals | row time === value time | | dateNotEquals | row time !== value time | | dateAfter | row time > value time | | dateBefore | row time < value time | | dateOnOrAfter | row time >= value time | | dateOnOrBefore | row time <= value time | | nowAfterPlusMinutes | Date.now() > (row time + minutes) | | nowBeforePlusMinutes | Date.now() < (row time + minutes) |

Accepted date inputs for both sides (where applicable): Date instance, ISO 8601 string, or numeric timestamp (milliseconds). If either side cannot be parsed into a valid date the result is false (for dateNotEquals, implementation also returns false when invalid).

Example: Date & Relative Time

const row = {
    startAt: '2024-01-01T10:00:00.000Z',
    endAt: new Date('2024-01-01T12:00:00.000Z'),
};

// startAt after 09:00Z?
evaluateCondition(
    { field: 'startAt', operator: 'dateAfter', value: '2024-01-01T09:00:00.000Z' },
    row,
); // true

// endAt on or after noon?
evaluateCondition(
    { field: 'endAt', operator: 'dateOnOrAfter', value: '2024-01-01T12:00:00.000Z' },
    row,
); // true

// Has 30 minutes passed since startAt?
evaluateCondition({ field: 'startAt', operator: 'nowAfterPlusMinutes', value: 30 }, row);

// Are we still before startAt + 90 minutes?
evaluateCondition({ field: 'startAt', operator: 'nowBeforePlusMinutes', value: 90 }, row);

Nested Groups (all / any)

const groupRule = {
    all: [
        { field: 'age', operator: 'greaterThan', value: 18 },
        { field: 'status', operator: 'equals', value: 'active' },
    ],
};

evaluateCondition(groupRule, { age: 25, status: 'active' }); // true

// With error handling
evaluateCondition(
    groupRule,
    { age: 25, status: 'active' },
    {
        onError: (error) => console.log('Error:', error.message),
    },
); // true

Combining any + all:

const nested = {
    any: [
        { field: 'role', operator: 'equals', value: 'admin' },
        {
            all: [
                { field: 'age', operator: 'greaterThan', value: 18 },
                { field: 'status', operator: 'equals', value: 'active' },
            ],
        },
    ],
};

evaluateCondition(nested, { age: 25, status: 'active', role: 'user' }); // true

Array Row Evaluation (OR semantics)

If you pass an array of rows, the rule is considered satisfied if ANY row matches.

const rows = [{ age: 20 }, { age: 30 }];
evaluateCondition({ field: 'age', operator: 'equals', value: 30 }, rows); // true

Negation (not)

evaluateCondition({ field: 'age', operator: 'equals', value: 30, not: true }, { age: 30 }); // false

evaluateCondition(
    {
        all: [
            { field: 'age', operator: 'greaterThan', value: 18 },
            { field: 'status', operator: 'equals', value: 'active' },
        ],
        not: true,
    },
    { age: 25, status: 'active' },
); // false

Error Handling

By default, unknown operators and invalid inputs throw errors:

try {
    evaluateCondition({ field: 'x', operator: 'unknown', value: 1 }, { x: 1 });
} catch (err) {
    console.error(err); // Unknown operator: unknown
}

Custom Error Handling with onError

Instead of throwing exceptions, you can provide an error callback:

const options = {
    onError: (error, info) => {
        console.log('Evaluation failed:', error.message);
        console.log('Rule:', info.rule);
        console.log('Row data:', info.row);
        // Log to monitoring service, etc.
    },
};

// Returns false instead of throwing
evaluateCondition({ field: 'x', operator: 'unknown', value: 1 }, { x: 1 }, options); // false (calls onError callback)

// Also handles invalid row types
evaluateCondition({ field: 'age', operator: 'equals', value: 30 }, 'not an object', options); // false (calls onError callback)

Handling Missing Rows

Control how null or undefined rows are treated:

// Default: treat missing rows as false
evaluateCondition(rule, null); // false
evaluateCondition(rule, undefined); // false

// Alternative: treat missing rows as true
evaluateCondition(rule, null, { treatMissingRowAsFalse: false }); // true
evaluateCondition(rule, undefined, { treatMissingRowAsFalse: false }); // true

Testing

This project uses Jest.

npm test

License

ISC