
Security News
npm ‘is’ Package Hijacked in Expanding Supply Chain Attack
The ongoing npm phishing campaign escalates as attackers hijack the popular 'is' package, embedding malware in multiple versions.
json-expression-eval
Advanced tools
A Fully typed Node.js module that evaluates a json described boolean expressions using dynamic functions and a given context.
Expressions can also be evaluated in a rule engine
manner.
The module is strictly typed, ensuring that passed expressions are 100% valid at compile time.
This module is especially useful if you need to serialize complex expressions / rules (to be saved in a DB for example)
npm install json-expression-eval
Or
yarn add json-expression-eval
Please see tests and examples dir for more usages and examples (under /src)
import {evaluate, Expression, ExpressionHandler, validate, ValidationContext} from 'json-expression-eval';
import {Moment} from 'moment';
import moment = require('moment');
interface IExampleContext {
userId: string;
times: number | undefined;
date: Moment;
nested: {
value: number | null;
value4: number;
nested2: {
value2?: number;
value3: boolean;
};
};
}
type IExampleContextIgnore = Moment;
type IExampleFunctionTable = {
countRange: ([min, max]: [min: number, max: number], ctx: { times: number | undefined }) => boolean;
}
type IExampleExpression = Expression<IExampleContext, IExampleFunctionTable, IExampleContextIgnore>; // We pass Moment here to avoid TS exhaustion
const context: IExampleContext = {
userId: 'a@b.com',
times: 3,
date: moment(),
nested: {
value: null,
value4: 5,
nested2: {
value3: true,
},
},
};
// For validation we must provide a full example context
const validationContext: ValidationContext<IExampleContext, IExampleContextIgnore> = {
userId: 'a@b.com',
times: 3,
date: moment(),
nested: {
value: 5,
value4: 6,
nested2: {
value2: 6,
value3: true,
},
},
};
const functionsTable: IExampleFunctionTable = {
countRange: async ([min, max]: [min: number, max: number], ctx: { times: number | undefined }): Promise<boolean> => {
return ctx.times === undefined ? false : ctx.times >= min && ctx.times < max;
},
};
const expression: IExampleExpression = {
or: [
{
userId: 'a@b.com',
},
{
and: [
{
countRange: [2, 6],
},
{
'nested.nested2.value3': true,
},
{
times: {
lte: {
ref: 'nested.value4'
}
},
},
],
},
],
};
// Example usage 1
const handler =
new ExpressionHandler<IExampleContext, IExampleFunctionTable, IExampleContextIgnore>(expression, functionsTable);
await handler.validate(validationContext); // Should not throw
console.log(await handler.evaluate(context)); // true
// Example usage 2
await validate<IExampleContext, IExampleFunctionTable, IExampleContextIgnore>(expression, validationContext, functionsTable); // Should not throw
console.log(await evaluate<IExampleContext, IExampleFunctionTable, IExampleContextIgnore>(expression, context, functionsTable)); // true
There are 4 types of operators you can use (evaluated in that order of precedence):
and
- accepts a non-empty list of expressionsor
- accepts a non-empty list of expressionsnot
- accepts another expressions<user defined funcs>
- accepts any type of argument and evaluated by the user defined functions and the given context. Can be async.<compare funcs>
- operates on one of the context properties and compares it to a given value.
{property: {op: value}}
gt
- >gte
- >=lt
- <lte
- <=eq
- ===neq
- !==regexp: RegExp
- True if matches the compiled regular expression.regexpi: RegExp
- True if matches the compiled regular expression with the i
flag set.nin: any[]
- True if not in an array of values. Comparison is done using the ===
operatorinq: any[]
- True if in an array of values. Comparison is done using the ===
operatorbetween: readonly [number, number] (as const)
- True if the value is between the two specified values: greater than or equal to first value and less than or equal to second value.{property: value}
eq
op)Nested properties in the context can also be accessed using a dot notation (see example above) In each expression level, you can only define 1 operator, and 1 only
You can reference values (and nested values) from the context using the {"ref":""} (see example above) on the right-hand side of expressions (not in parameters to user defined functions though)
Example expressions, assuming we have the user
and maxCount
user defined functions in place can be:
{
"or":[
{
"not":{
"user":"a@b.com"
}
},
{
"maxCount":1
},
{
"times": { "eq" : 5}
},
{
"times": { "eq" : { "ref": "nested.preoprty"}}
},
{
"country": "USA"
}
]
}
Please see tests and examples dir for more usages and examples (under /src)
import {ValidationContext, validateRules, evaluateRules, RulesEngine, Rule, ResolvedConsequence} from 'json-expression-eval';
import {Moment} from 'moment';
import moment = require('moment');
interface IExampleContext {
userId: string;
times: number | undefined;
date: Moment;
nested: {
value: number | null;
nested2: {
value2?: number;
value3: boolean;
};
};
}
type IExampleContextIgnore = Moment;
type IExamplePayload = number;
type IExampleFunctionTable = {
countRange: ([min, max]: [min: number, max: number], ctx: { times: number | undefined }) => boolean;
}
type IExampleRuleFunctionTable = {
userRule: (user: string, ctx: IExampleContext) => Promise<void | ResolvedConsequence<IExamplePayload>>;
}
type IExampleRule = Rule<IExamplePayload, IExampleRuleFunctionTable, IExampleContext,
IExampleFunctionTable, IExampleContextIgnore>;
const context: IExampleContext = {
userId: 'a@b.com',
times: 3,
date: moment(),
nested: {
value: null,
nested2: {
value3: true,
},
},
};
// For validation we must provide a full example context
const validationContext: ValidationContext<IExampleContext, IExampleContextIgnore> = {
userId: 'a@b.com',
times: 3,
date: moment(),
nested: {
value: 5,
nested2: {
value2: 6,
value3: true,
},
},
};
const functionsTable: IExampleFunctionTable = {
countRange: ([min, max]: [min: number, max: number], ctx: { times: number | undefined }): boolean => {
return ctx.times === undefined ? false : ctx.times >= min && ctx.times < max;
},
};
const ruleFunctionsTable: IExampleRuleFunctionTable = {
userRule: async (user: string, ctx: IExampleContext): Promise<void | ResolvedConsequence<number>> => {
if (ctx.userId === user) {
return {
message: `Username ${user} is not allowed`,
custom: 543,
}
}
},
};
const rules: IExampleRule[] = [
{
condition: {
or: [
{
userId: 'a@b.com',
},
{
and: [
{
countRange: [2, 6],
},
{
'nested.nested2.value3': true,
},
],
},
],
},
consequence: {
message: ['user', {
ref: 'userId',
}, 'should not equal a@b.com'],
custom: 579,
},
},
{
userRule: 'b@c.com',
},
];
// Example usage 1
const engine = new RulesEngine<IExamplePayload, IExampleContext, IExampleRuleFunctionTable,
IExampleFunctionTable, IExampleContextIgnore>(functionsTable, ruleFunctionsTable);
await engine.validate(rules, validationContext); // Should not throw
console.log(JSON.stringify(await engine.evaluateAll(rules, context))); // [{"message":"user a@b.com should not equal a@b.com","custom":579}]
// Example usage 2
await validateRules<IExamplePayload, IExampleContext, IExampleRuleFunctionTable,
IExampleFunctionTable, IExampleContextIgnore>(rules, validationContext, functionsTable, ruleFunctionsTable); // Should not throw
console.log(JSON.stringify(await evaluateRules<IExamplePayload, IExampleContext, IExampleRuleFunctionTable,
IExampleFunctionTable, IExampleContextIgnore>(rules, context, functionsTable, ruleFunctionsTable, false))); // [{"message":"user a@b.com should not equal a@b.com","custom":579}]
FAQs
json serializable rule engine / boolean expression evaluator
The npm package json-expression-eval receives a total of 232 weekly downloads. As such, json-expression-eval popularity was classified as not popular.
We found that json-expression-eval demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
The ongoing npm phishing campaign escalates as attackers hijack the popular 'is' package, embedding malware in multiple versions.
Security News
A critical flaw in the popular npm form-data package could allow HTTP parameter pollution, affecting millions of projects until patched versions are adopted.
Security News
Bun 1.2.19 introduces isolated installs for smoother monorepo workflows, along with performance boosts, new tooling, and key compatibility fixes.