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 🙏

© 2024 – Pkg Stats / Ryan Hefner

user-logic

v0.0.1

Published

Data-driven logic for ultra-configurable applications

Downloads

31

Readme

User logic

This library allows you to safely evaluate logic user-defined logic. Through this, you can (lazy) load decision making logic from various sources, and analyze them in very interesting ways. As an application, think of dynamically configurable alerts based on analytics, tuning an algorithm that decides when to re-engage the user by e-mail, or defining some simple operations that need to be done during the migration of a data model. This library is inspired by JSON logic, but a lot cleaner, more extensible, customizable and debuggable. Also, not using symbols like != and >= makes this much more readable when storing logic in YAML format.

Installation

npm install user-logic --save

Usage

import { UserLogic } from 'user-logic'

const willReceiveWiskeyBottleLogic = new UserLogic({definition: {
    and: [
        {gt: ['$user.years-of-membership', 5]},
        {gt: ['$user.age', 21]},
        '$user.likes.alcohol',
    ]
}})

console.log(willReceiveWiskeyBottleLogic.evaluate({user: {
    'years-of-membership': 6,
    'age': 52,
    'likes': {flowers: false, alcohol: true}
}}))

There are operations that take one or multiple operands, but they all work in the way above.

Supported operations

You can find the whole list of supported operations in the unit tests.

Customization

You can pass in custom operations to the constructor of UserLogic:

import { UserLogic } from 'user-logic'
import { unaryOperator } from 'user-logic/lib/operations'

const customLogic = new UserLogic({definition: {bla: 'Hello'}, operations: {
    bla: unaryOperator(value => value() + '!!!')
}})

console.log(customLogic.evaluate({})) // Hello!!!

The simplest way is to use either unaryOperator as shown above, or binaryOperator. Both take functions that evaluate the passed in expressions when called, allowing for short-circuit operators. The binaryOperator is used like this, and supports operators with more than one operand:

import { UserLogic } from 'user-logic'
import { binaryOperator } from 'user-logic/lib/operations'

const customLogic = new UserLogic({definition: {add: [3, 5, 7]}, operations: {
    add: binaryOperator((left, right) => left() + right())
}})

console.log(customLogic.evaluate({})) // 15

If you want to do more advanced stuff, you can construct custom nodes:

const customLogic = new UserLogic({definition: {'if': [true', 'True!', 'False!']}, operations: {
    ifNode: ({definition, parse}) => {
        const [condition, ifTrue, ifFalse] = definition.map(parse)
        return {
            evaluate: context => {
                if (condition.evaluate(context)) {
                    return ifTrue.evaluate(context)
                } else {
                    return ifFalse.evaluate(context)
                }
            }
        }
    }
})

To override lookup behavior, you can override the valueTemplate operation which receives the path without a $ sign as the definition. This is a very simple implementation:

import * as objectPath from 'object-path'

export function valueTemplateNode({definition, reportLookup}) {
    const path = definition.split('.')
    return {
        evaluate: context => {
            return objectPath['withInheritedProps'].get(context, path)
        }
    }
}

Logic maps

Sometimes, you need to construct an object with a few values including logic, of which a few might be conditional. For this use case, we have logic maps:

import { UserLogic, makeLogicMap, evalLogicMap } from 'user-logic'

const logicMap = makeLogicMap({
    foo: {gt: ['$age', 18]},
    bar: {gt: ['$age', 21]}
})
console.log(evalLogicMap(logicMap, {age: 20})) // {foo: true, bar: false}

const conditionalLogicMap = makeLogicMap([
    {name: 'Joe'},
    {if: {lt: ['$age', 18]}
     minor: true}
])
console.log(evalLogicMap(conditionalLogicMap, {age: 20})) // {name: 'Joe'}
console.log(evalLogicMap(conditionalLogicMap, {age: 15})) // {name: 'Joe', minor: true}

Reporting lookups

If you need to know which variables your logic can access, pass in the reportLookup option to the constructor:

const lookups = []
const logic = new UserLogic({definition: {eq: ['$foo.test', 5]}, reportLookup: path => lookups.push(path)})
expect(lookups).to.deep.equal([
    ['foo', 'test']
])

Future development

This library has the goal of making applications more flexible with user-defined business logic portable across various languages, while being super debuggable and useful to work on complex logic with inter-disciplinary teams. In this content, I'd like to see the following features:

  • Solid debugging tools: The basic hooks that allow users to step through the logic, and even its compilation, to enable better visualization.
  • Awesome error reporting: Tagging each node in the logic with a specific ID and attaching those to errors, so clear visualization of errors.
  • Logic reversing: Generating possible sceneraios for a given logic set for generating better unit tests. For example {and: ['$foo', '$bar']} with $foo and $bar being booleans would generate [{foo: true, bar: true}, {foo: true, bar: false}, {foo: false, bar: true}, {foo: false, bar: false}].
  • A framework independent, highly customizable visual editor: Something like Scratch and Blockly that would be suitable for working on logic in serious business settings.