Gathering detailed insights and metrics for json-rules-engine-simplified
Gathering detailed insights and metrics for json-rules-engine-simplified
Gathering detailed insights and metrics for json-rules-engine-simplified
Gathering detailed insights and metrics for json-rules-engine-simplified
A simple rules engine expressed in JSON
npm install json-rules-engine-simplified
Typescript
Module System
Min. Node Version
Node Version
NPM Version
JavaScript (100%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
Apache-2.0 License
122 Stars
87 Commits
31 Forks
7 Watchers
2 Branches
6 Contributors
Updated on Jan 23, 2025
Latest Version
0.2.0
Package Id
json-rules-engine-simplified@0.2.0
Unpacked Size
195.83 kB
Size
55.44 kB
File Count
13
NPM Version
10.1.0
Node Version
20.9.0
Published on
Jul 23, 2024
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
3
36
A simple rules engine expressed in JSON
The primary goal of this project is to be an alternative of json-rules-engine for react-jsonschema-form-conditionals, as such it has similar interface and configuration, but simplified predicate language, similar to SQL.
and
or
and not
) that allow to have any arbitrary complexityInstall json-rules-engine-simplified
by running:
1npm install --s json-rules-engine-simplified
The simplest example of using json-rules-engine-simplified
1import Engine from 'json-rules-engine-simplified' 2 3let rules = [{ 4 conditions: { 5 firstName: "empty" 6 }, 7 event: { 8 type: "remove", 9 params: { 10 field: "password" 11 }, 12 } 13}]; 14 15/** 16 * Setup a new engine 17 */ 18let engine = new Engine(rules); 19 20let formData = { 21 lastName: "Smit" 22} 23 24// Run the engine to evaluate 25engine 26 .run(formData) 27 .then(events => { // run() returns remove event 28 events.map(event => console.log(event.type)); 29 }) 30
Rules engine expects to know all the rules in advance, it effectively drops builder pattern, but keeps the interface.
You don't have to specify all rules at the construction time, you can add rules in different time in process.
In order to add new rules to the Engine
use addRule
function.
For example, following declarations are the same
1import Engine from 'json-rules-engine-simplified';
2
3let engineA = new Engine();
4
5let rule = {
6 conditions: {
7 firstName: "empty"
8 },
9 event: {
10 type: "remove",
11 params: {
12 field: "password"
13 },
14 }
15};
16
17engineA.addRule(rule);
18
19let engineB = new Engine(rule);
20
In this case engineA
and engineB
will give the same results.
In order to prevent most common errors, Engine
does initial validation on the schema, during construction.
Validation is done automatically if you specify schema
during construction.
1let rules = [{ 2 conditions: { 3 firstName: "empty" 4 }, 5 event: { 6 type: "remove", 7 params: { field: "password" }, 8 } 9}]; 10 11let schema = { 12 properties: { 13 firstName: { type: "string" }, 14 lastName: { type: "string" } 15 } 16} 17 18let engine = new Engine(rules, schema);
Validation is done only during development, validation is disabled by default in production
.
WARNING!!! Currently validation does not support nested structures, so be extra careful, when using those.
Conditional logic is based on public predicate library with boolean logic extension.
Predicate library has a lot of predicates that we found more, than sufficient for our use cases.
To showcase conditional logic, we'll be using simple registration
schema
1let schema = { 2 definitions: { 3 hobby: { 4 type: "object", 5 properties: { 6 name: { type: "string" }, 7 durationInMonth: { type: "integer" }, 8 } 9 } 10 }, 11 title: "A registration form", 12 description: "A simple form example.", 13 type: "object", 14 required: [ 15 "firstName", 16 "lastName" 17 ], 18 properties: { 19 firstName: { 20 type: "string", 21 title: "First name" 22 }, 23 lastName: { 24 type: "string", 25 title: "Last name" 26 }, 27 age: { 28 type: "integer", 29 title: "Age", 30 }, 31 bio: { 32 type: "string", 33 title: "Bio", 34 }, 35 country: { 36 type: "string", 37 title: "Country" 38 }, 39 state: { 40 type: "string", 41 title: "State" 42 }, 43 zip: { 44 type: "string", 45 title: "ZIP" 46 }, 47 password: { 48 type: "string", 49 title: "Password", 50 minLength: 3 51 }, 52 telephone: { 53 type: "string", 54 title: "Telephone", 55 minLength: 10 56 }, 57 work: { "$ref": "#/definitions/hobby" }, 58 hobbies: { 59 type: "array", 60 items: { "$ref": "#/definitions/hobby" } 61 } 62 } 63}
Assuming action part is taken from react-jsonschema-form-conditionals
Let's say we want to remove
password
, when firstName
is missing, we can expressed it like this:
1let rules = [{ 2 conditions: { 3 firstName: "empty" 4 }, 5 event: { 6 type: "remove", 7 params: { 8 field: "password" 9 } 10 } 11}]
This translates into -
when firstName
is empty
, trigger remove
event
.
Empty
keyword is equal in predicate library and required
event will be performed only when predicate.empty(registration.firstName)
is true
.
Let's say we need to require
zip
, when age
is less
than 16
,
because the service we are using is legal only after 16
in some countries
1let rules = [{ 2 conditions: { 3 age: { less : 16 } 4 }, 5 event: { 6 type: "require", 7 params: { 8 field: "zip" 9 } 10 } 11}]
This translates into - when age
is less
than 16
, require
zip.
Less keyword is less in predicate and required
event will be returned only when predicate.empty(registration.age, 5)
is true
.
For the field AND is a default behavior.
Looking at previous rule, we decide that we want to change the rule and require
zip
,
when age
is between 16
and 70
, so it would be available
only to people older, than 16
and younger than 70
.
1let rules = [{ 2 conditions: { 3 age: { 4 greater: 16, 5 less : 70, 6 } 7 }, 8 event: { 9 type: "require", 10 params: { 11 field: "zip" 12 } 13 } 14}]
By default action will be applied only when both field conditions are true.
In this case, when age is greater
than 16
and less
than 70
.
Let's say we want to change the logic to opposite, and trigger event only when
age
is less
er then 16
or greater
than 70
,
1let rules = [{ 2 conditions: { 3 age: { 4 not: { 5 greater: 16, 6 less : 70, 7 } 8 } 9 }, 10 event: { 11 type: "require", 12 params: { 13 field: "zip" 14 } 15 } 16}]
This does it, since the final result will be opposite of the previous condition.
The previous example works, but it's a bit hard to understand, luckily we can express it differently
with or
conditional.
1let rules = [{ 2 conditions: { age: { 3 or: [ 4 { lessEq : 5 }, 5 { greaterEq: 70 } 6 ] 7 } 8 }, 9 event: { 10 type: "require", 11 params: { 12 field: "zip" 13 } 14 } 15}]
The result is the same as NOT
, but easier to grasp.
To support cases, when action depends on more, than one field meeting criteria we introduced multi fields boolean operations.
Let's say, when age
is less than 70 and country
is USA
we want to require
bio
.
1let rules = [{ 2 conditions: { 3 age: { less : 70 }, 4 country: { is: "USA" } 5 }, 6 event: { 7 type: "require", 8 params: { fields: [ "bio" ]} 9 } 10}]
This is the way we can express this. By default each field is treated as a separate condition and all conditions must be meet.
In addition to previous rule we need bio
, if state
is NY
.
1let rules = [{ 2 conditions: { 3 or: [ 4 { 5 age: { less : 70 }, 6 country: { is: "USA" } 7 }, 8 { 9 state: { is: "NY"} 10 } 11 ] 12 }, 13 event: { 14 type: "require", 15 params: { fields: [ "bio" ]} 16 } 17}]
When we don't require bio
we need zip
code.
1let rules = [{ 2 conditions: { 3 not: { 4 or: [ 5 { 6 age: { less : 70 }, 7 country: { is: "USA" } 8 }, 9 { 10 state: { is: "NY"} 11 } 12 ] 13 } 14 }, 15 event: { 16 type: "require", 17 params: { fields: [ "zip" ]} 18 } 19}]
Rules engine supports querying inside nested objects, with selectn, any data query that works in selectn, will work in here
Let's say we need to require state
, when work
has a name
congressman
, this is how we can do this:
1let rules = [{ 2 conditions: { 3 "work.name": { is: "congressman" } 4 }, 5 event: { 6 type: "require", 7 params: { fields: [ "state" ]} 8 } 9}]
Sometimes we need to make changes to the form if some nested condition is true.
For example if one of the hobbies
is baseball
, we need to make state
required
.
This can be expressed like this:
1let rules = [{ 2 conditions: { 3 hobbies: { 4 name: { is: "baseball" }, 5 } 6 }, 7 event: { 8 type: "require", 9 params: { fields: [ "state" ]} 10 } 11}]
Rules engine will go through all the elements in the array and trigger require
if any
of the elements meet the criteria.
If for some reason the list of predicates is insufficient for your needs, you can extend them pretty easy, by specifying additional predicates in global import object.
For example, if we want to add range
predicate, that would verify, that integer value is in range, we can do it like this:
1import predicate from "predicate"; 2import Engine from "json-rules-engine-simplified"; 3 4predicate.range = predicate.curry((val, range) => { 5 return predicate.num(val) && 6 predicate.array(range) && 7 predicate.equal(range.length, 2) && 8 predicate.num(range[0]) && 9 predicate.num(range[1]) && 10 predicate.greaterEq(val, range[0]) && 11 predicate.lessEq(val, range[1]); 12}); 13 14let engine = new Engine([{ 15 conditions: { age: { range: [ 20, 40 ] } }, 16 event: "hit" 17}]);
Validation will automatically catch new extension and work as expected.
Support of nested structures with selectn, so basically any query you can define in selectn you can use here.
For example if in previous example, age would be a part of person object, we could work with it like this:
1 let rules = [ { conditions: { "person.age": { range: [ 20, 40 ] } } } ];
Also in order to support systems where keys with "." not allowed (for example if you would like to store data in mongo), you can use $
to separate references:
For example, this is the same condition, but instead of .
it uses $
:
1 let rules = [ { conditions: { "person$age": { range: [ 20, 40 ] } } } ];
Sometimes you would want to validate formData
fields one against the other.
You can do this simply by appending $
to the beginning of reference.
For example, you want to trigger event only when a
is less then b
, when you don't know ahead a
or b
values
1let schema = { 2 type: "object", 3 properties: { 4 a: { type: "number" }, 5 b: { type: "number" } 6 } 7} 8 9let rules = [{ 10 conditions: { 11 a: { less: "$b" } 12 }, 13 event: "some" 14}] 15 16let engine = new Engine(schema, rules);
This is how you do it, in run time $b
will be replaces with field b
value.
Relevant fields work on nested objects as well as on any field condition.
Framework does not put any restrictions on event object, that will be triggered, in case conditions are meet
For example, event
can be a string:
1let rules = [{ 2 conditions: { ... }, 3 event: "require" 4}]
Or number
1let rules = [{ 2 conditions: { ... }, 3 event: 4 4}]
Or an object
1let rules = [{ 2 conditions: { ... }, 3 event: { 4 type: "require", 5 params: { fields: [ "state" ]} 6 } 7}]
You can even return an array of events, each of which will be added to final array of results
1let rules = [{ 2 conditions: { ... }, 3 event: [ 4 { 5 type: "require", 6 params: { field: "state"} 7 }, 8 { 9 type: "remove", 10 params: { fields: "fake" } 11 }, 12 ] 13}]
The project is licensed under the Apache Licence 2.0.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
0 existing vulnerabilities detected
Reason
license file detected
Details
Reason
Found 2/26 approved changesets -- score normalized to 0
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2025-07-07
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More