Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ jobs:
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --all-projects

- name: Run types tests
if: matrix.node-version == env.PRIMARY_NODE_VERSION && matrix.os == env.PRIMARY_OS
Expand Down
32 changes: 32 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,38 @@ export default [

// Disable multiline check - TypeScript handles this
'no-unexpected-multiline': 'off',

// Disallow multiple spaces
'no-multi-spaces': 'error',

// Whitespace rules
'no-trailing-spaces': 'error',
'eol-last': ['error', 'always'],
'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0, maxBOF: 0 }],

// Spacing rules
'keyword-spacing': ['error', { before: true, after: true }],
'space-before-blocks': 'error',
'space-infix-ops': 'error',
'comma-spacing': ['error', { before: false, after: true }],
'semi-spacing': ['error', { before: false, after: true }],
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
'object-curly-spacing': ['error', 'always'],
'array-bracket-spacing': ['error', 'never'],
'space-in-parens': ['error', 'never'],
'computed-property-spacing': ['error', 'never'],

// Brace and block style
'brace-style': ['error', '1tbs', { allowSingleLine: true }],
'curly': ['error', 'all'],

// Other common rules
'arrow-spacing': ['error', { before: true, after: true }],
'template-curly-spacing': ['error', 'never'],
'rest-spread-spacing': ['error', 'never'],
'prefer-const': 'error',
'no-var': 'error',
'eqeqeq': ['error', 'always'],
},
},
{
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "json-expression-eval",
"version": "9.0.0",
"version": "9.0.1",
"description": "json serializable rule engine / boolean expression evaluator",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -15,6 +15,7 @@
"compile": "tsc -p tsconfig.build.json",
"test:cover": "vitest run --coverage",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"ci": "pnpm run lint && pnpm run compile && pnpm run test:tsd && pnpm run test:cover"
},
"repository": {
Expand Down
2 changes: 2 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
onlyBuiltDependencies:
- esbuild
22 changes: 11 additions & 11 deletions src/examples/engine/example.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ExpressionContext, engine, MyRule} from './lib/engine';
import { ExpressionContext, engine, MyRule } from './lib/engine';
import moment = require('moment');

const context: ExpressionContext = {
Expand All @@ -14,7 +14,7 @@ const context: ExpressionContext = {
};

const run = async (_rules: MyRule[], _context: ExpressionContext) => {
const result = await engine.evaluateAll(_rules, _context, {dryRun: false});
const result = await engine.evaluateAll(_rules, _context, { dryRun: false });
console.log(`Evaluating rules ${JSON.stringify(_rules)} using context ${JSON.stringify(_context)}`);
console.log(`Result: ${JSON.stringify(result)}\n\n`);
};
Expand Down Expand Up @@ -47,10 +47,10 @@ rules = [
{
condition: {
and: [
{user: 'a@b.com'},
{maxCount: 5},
{times: {eq:{ref:'nested.value'}}},
{times: {lte:{op:'+', lhs: {ref:'nested.value'}, rhs: 1}}},
{ user: 'a@b.com' },
{ maxCount: 5 },
{ times: { eq: { ref: 'nested.value' } } },
{ times: { lte: { op: '+', lhs: { ref: 'nested.value' }, rhs: 1 } } },
],
},
consequence: {
Expand All @@ -76,8 +76,8 @@ rules = [
{
condition: {
and: [
{user: 'a@b.com'},
{maxCount: 1},
{ user: 'a@b.com' },
{ maxCount: 1 },
],
},
consequence: {
Expand All @@ -103,8 +103,8 @@ rules = [
{
condition: {
and: [
{not: {user: 'a@b.com'}},
{maxCount: 1},
{ not: { user: 'a@b.com' } },
{ maxCount: 1 },
],
},
consequence: {
Expand Down Expand Up @@ -146,7 +146,7 @@ rules = [
},
{
condition: {
times: {gte: 10},
times: { gte: 10 },
},
consequence: {
message: [
Expand Down
7 changes: 3 additions & 4 deletions src/examples/engine/lib/engine.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {functionsTable, ruleFunctionsTable} from './functionsFactory';
import {RulesEngine, Rule} from '../../..';
import {Moment} from 'moment';
import { functionsTable, ruleFunctionsTable } from './functionsFactory';
import { RulesEngine, Rule } from '../../..';
import { Moment } from 'moment';

export interface ExpressionContext {
userId: string;
Expand All @@ -24,4 +24,3 @@ export type MyRule = Rule<Payload, RuleFunction, ExpressionContext, ExpressionFu
export const engine = new RulesEngine<Payload, ExpressionContext, RuleFunction, ExpressionFunction,
Ignore, CustomEngineRuleFuncRunOptions>
(functionsTable, ruleFunctionsTable);

8 changes: 4 additions & 4 deletions src/examples/engine/lib/functionsFactory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {userFunc} from './functions/userFunc';
import {counterFunc} from './functions/counterFunc';
import {userRule} from './rules/userRule';
import { userFunc } from './functions/userFunc';
import { counterFunc } from './functions/counterFunc';
import { userRule } from './rules/userRule';

export const functionsTable = {
user: userFunc,
Expand All @@ -9,4 +9,4 @@ export const functionsTable = {

export const ruleFunctionsTable = {
userRule,
};
};
2 changes: 1 addition & 1 deletion src/examples/engine/lib/rules/userRule.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ResolvedConsequence} from '../../../../types';
import { ResolvedConsequence } from '../../../../types';

export const userRule = async (user: string, context: { userId: string },
runOpts: {validation: boolean, custom: {dryRun: boolean}})
Expand Down
54 changes: 27 additions & 27 deletions src/examples/evaluator/example.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {ExpressionContext, ExpressionFunction, getEvaluator, CustomEvaluatorFuncRunOptions} from './lib/evaluator';
import {Expression, ExpressionParts} from '../..';
import {Moment} from 'moment';
import { ExpressionContext, ExpressionFunction, getEvaluator, CustomEvaluatorFuncRunOptions } from './lib/evaluator';
import { Expression, ExpressionParts } from '../..';
import { Moment } from 'moment';
import moment = require('moment');

const context: ExpressionContext = {
Expand All @@ -17,7 +17,7 @@ const context: ExpressionContext = {

const run = async (expr: Expression<ExpressionContext, ExpressionFunction, Moment, CustomEvaluatorFuncRunOptions>,
ctx: ExpressionContext) => {
const result = await getEvaluator(expression).evaluate(ctx, {dryRun: true});
const result = await getEvaluator(expression).evaluate(ctx, { dryRun: true });
console.log(`Evaluating expression ${JSON.stringify(expr)} using context ${JSON.stringify(ctx)}`);
console.log(`Result: ${result}`);
};
Expand All @@ -30,85 +30,85 @@ run(expression, context);

expression = {
and: [
{user: 'a@b.com'},
{maxCount: 5},
{ user: 'a@b.com' },
{ maxCount: 5 },
],
};

run(expression, context);

expression = {
and: [
{user: 'a@b.com'},
{times: {eq:{ref:'nested.value'}}},
{ user: 'a@b.com' },
{ times: { eq: { ref: 'nested.value' } } },
],
};

run(expression, context);

expression = {
and: [
{user: 'a@b.com'},
{times: {eq:{op:'+', lhs: {ref:'nested.value'}, rhs: 1}}},
{ user: 'a@b.com' },
{ times: { eq: { op: '+', lhs: { ref: 'nested.value' }, rhs: 1 } } },
],
};

run(expression, context);

expression = {
and: [
{user: 'a@b.com'},
{maxCount: 1},
{ user: 'a@b.com' },
{ maxCount: 1 },
],
};

run(expression, context);

expression = {
or: [
{user: 'a@b.com'},
{maxCount: 6},
{ user: 'a@b.com' },
{ maxCount: 6 },
],
};

run(expression, context);

expression = {
or: [
{not: {user: 'a@b.com'}},
{maxCount: 1},
{ not: { user: 'a@b.com' } },
{ maxCount: 1 },
],
};

run(expression, context);

expression = {
or: [
{times: {lt: 5}},
{times: {between: [7, 9] as const}},
{times: {inq: [7, 9]}},
{userId: {inq: ['a', 'b']}},
{userId: {nin: ['a', 'b']}},
{userId: {regexp: '^a'}},
{userId: {regexpi: '^a'}},
{times: {gte: 10}},
{ times: { lt: 5 } },
{ times: { between: [7, 9] as const } },
{ times: { inq: [7, 9] } },
{ userId: { inq: ['a', 'b'] } },
{ userId: { nin: ['a', 'b'] } },
{ userId: { regexp: '^a' } },
{ userId: { regexpi: '^a' } },
{ times: { gte: 10 } },
],
};

run(expression, context);

expression = {
or: [
{times: 3},
{times: {gte: 10}},
{ times: 3 },
{ times: { gte: 10 } },
],
};

run(expression, context);

expression = {
or: [
{times: 3},
{ times: 3 },
{
'nested.value': 5,
},
Expand Down
7 changes: 3 additions & 4 deletions src/examples/evaluator/lib/evaluator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {functionsTable} from './functionsFactory';
import {Expression, ExpressionHandler} from '../../..';
import {Moment} from 'moment';
import { functionsTable } from './functionsFactory';
import { Expression, ExpressionHandler } from '../../..';
import { Moment } from 'moment';

export interface ExpressionContext {
userId: string;
Expand All @@ -22,4 +22,3 @@ export const getEvaluator = (expression: Expression<ExpressionContext, Expressio
Moment, CustomEvaluatorFuncRunOptions>) =>
new ExpressionHandler<ExpressionContext, ExpressionFunction, Moment, CustomEvaluatorFuncRunOptions>
(expression, functionsTable);

2 changes: 1 addition & 1 deletion src/examples/evaluator/lib/functions/counterFunc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const counterFunc = (maxCount: number, context: { times: number },
export const counterFunc = (maxCount: number, context: { times: number },
runOpts: {validation: boolean, custom: {dryRun: boolean}}): boolean => {
return context.times < maxCount;
};
4 changes: 2 additions & 2 deletions src/examples/evaluator/lib/functionsFactory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {userFunc} from './functions/userFunc';
import {counterFunc} from './functions/counterFunc';
import { userFunc } from './functions/userFunc';
import { counterFunc } from './functions/counterFunc';

export const functionsTable = {
user: userFunc,
Expand Down
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export * from './types';
export {evaluate, validate, evaluateWithReason} from './lib/evaluator';
export {validateRules, evaluateRules} from './lib/engine';
export {ExpressionHandler} from './lib/expressionHandler';
export {RulesEngine} from './lib/rulesEngine';
export { evaluate, validate, evaluateWithReason } from './lib/evaluator';
export { validateRules, evaluateRules } from './lib/engine';
export { ExpressionHandler } from './lib/expressionHandler';
export { RulesEngine } from './lib/rulesEngine';
10 changes: 5 additions & 5 deletions src/lib/engine.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {RuleFunctionsTable, Rule, FunctionsTable, Context, ResolvedConsequence, ValidationContext} from '../types';
import {evaluate, validate} from './evaluator';
import {objectKeys} from './helpers';
import {isRuleFunction} from './typeGuards';
import {evaluateEngineConsequence} from './engineConsequenceEvaluator';
import { RuleFunctionsTable, Rule, FunctionsTable, Context, ResolvedConsequence, ValidationContext } from '../types';
import { evaluate, validate } from './evaluator';
import { objectKeys } from './helpers';
import { isRuleFunction } from './typeGuards';
import { evaluateEngineConsequence } from './engineConsequenceEvaluator';

async function run<ConsequencePayload, C extends Context,
RF extends RuleFunctionsTable<C, ConsequencePayload, CustomEngineRuleFuncRunOptions>,
Expand Down
6 changes: 3 additions & 3 deletions src/lib/engineConsequenceEvaluator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ResolvedConsequence, Context, RuleConsequence, RuleConsequenceMessagePart} from '../types';
import {getFromPath} from './helpers';
import { ResolvedConsequence, Context, RuleConsequence, RuleConsequenceMessagePart } from '../types';
import { getFromPath } from './helpers';

export const evaluateEngineConsequence = async <ConsequencePayload, C extends Context, Ignore = never>
(context: C, consequence: RuleConsequence<ConsequencePayload, C, Ignore>)
Expand All @@ -15,7 +15,7 @@ export const evaluateEngineConsequence = async <ConsequencePayload, C extends Co
if (typeof msgPart === 'string') {
return msgPart;
}
const {value, exists} = getFromPath(context, msgPart.ref);
const { value, exists } = getFromPath(context, msgPart.ref);
if (!exists) {
throw new Error(`Invalid consequence ref - unknown context key ${msgPart.ref}`)
}
Expand Down
Loading