Skip to content

Safe, typed expressions for JavaScript.

Keep rules like order.total >= 50 as text, in your config, your database, or your users' hands. Bonsai checks them against your types, runs them safely, and explains every result.

v1.3.1, MIT licence, zero dependencies

Free shipping

Expression

order.total >= 50 && customer.tier == "gold"

Edit it, or hover any part.

Data

  • order{ total: 84.5, country: "NZ" }
  • customer{ tier: "gold" }

Result

Ships free

true boolean

  • order.total >= 50true
  • customer.tier == "gold"true

Three calls in your app

  1. Describe your data

    Tell Bonsai what an expression can read.

    const env = bonsai({
      variables: { total: t.number() },
    })
  2. Check what someone wrote

    Catch typos and wrong types when the rule is saved.

    const rule = env.compile('total >= 50')
    // throws on a typo or a wrong type
  3. Run it

    Against real data, as often as you like.

    rule.evaluateSync({ total: 84.5 })
    // true

What you get

Mistakes are caught before anything runs

Checked against your types, with a fix where there is one.

Someone writesBonsai says
order.totl > 100did you mean "total"?
order.total >= "50"can't compare a number with text
user.email.endsWith("@acme")email may be missing
order.country == "UK"never true: country is NZ, AU, US or GB
How checking works

Every result explains itself

explain() tells you why, for every order.

order.total >= 50 && order.country == "NZ"

OrderResultWhy
120 to USPaysorder.country == "NZ" false
84.5 to NZShips freeorder.total >= 50 trueorder.country == "NZ" true
19.9 to GBPaysorder.total >= 50 falseorder.country == "NZ" false
Explaining results

Nothing in an expression can break your app

It reads only the data you pass in: no reaching your app, changing data, endless loops, or runaway memory.

Someone triesBlocked
user.constructorNo way into JavaScript
user.role = "admin"Expressions only read, never change data
"ab".repeat(1e9)Stopped at the size limit
How Bonsai stays safe

The same rule can filter your database

Translated to a query, so the database does the filtering.

Rule
order.total >= 50 && order.country in ["NZ", "AU"]
Query
WHERE ((`total` >= ?1) AND (`country` IN (?2, ?3)))
Values
?1 = 50, ?2 = "NZ", ?3 = "AU"
Database filters

How it compares

If the same rules must also run in Go or Java, use CEL. For a visual builder that stores JSON, use json-logic.

LibraryRules areChecked against typesSuggests fixesExplains resultsFilters a databaseBrowser, gzip
bonsai-jsText, JS-likeYesYesYesYes29.0 KB 69.2 KB checking in the browser
cel-jsText, CELYesNoNoNo24.3 KB
json-logic-jsJSONNoNoNoNo2.0 KB
jexlText, JS-likeNoNoNoNo6.1 KB
filtrexText, own syntaxNoNoNoNo7.8 KB

Bonsai's 29.0 KB is for rules checked on a server. The full comparison covers nine libraries, what each part of Bonsai's size buys, and when each library is the better choice.

Try it on one rule.

Five minutes from install to your first checked rule.

Released under the MIT License.