rulepilot
Version:
Rule parsing engine for JSON rules
173 lines (172 loc) • 10.1 kB
JavaScript
"use strict";
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _Evaluator_instances, _Evaluator_objectDiscovery, _Evaluator_nestedResults, _Evaluator_evaluateRule, _Evaluator_evaluateCondition, _Evaluator_processNestedResults, _Evaluator_accumulate, _Evaluator_checkConstraint;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Evaluator = void 0;
const object_discovery_1 = require("./object-discovery");
class Evaluator {
constructor() {
_Evaluator_instances.add(this);
_Evaluator_objectDiscovery.set(this, new object_discovery_1.ObjectDiscovery());
/** Stores any results from nested conditions */
_Evaluator_nestedResults.set(this, void 0);
}
/**
* Evaluates a rule against a set of criteria and returns the result.
* If the criteria is an array (indicating multiple criteria to test),
* the rule will be evaluated against each item in the array and
* an array of results will be returned.
*
* @param rule The rule to evaluate.
* @param criteria The criteria to evaluate the rule against.
*/
evaluate(rule, criteria) {
if (criteria instanceof Array) {
const result = [];
for (const c of criteria) {
// Clear any previous sub-results.
__classPrivateFieldSet(this, _Evaluator_nestedResults, [], "f");
result.push(__classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_evaluateRule).call(this, rule.conditions, c, rule === null || rule === void 0 ? void 0 : rule.default));
}
return __classPrivateFieldGet(this, _Evaluator_nestedResults, "f").length
? __classPrivateFieldGet(this, _Evaluator_nestedResults, "f")[0]
: result;
}
// Clear any previous sub-results.
__classPrivateFieldSet(this, _Evaluator_nestedResults, [], "f");
const e = __classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_evaluateRule).call(this, rule.conditions, criteria, rule === null || rule === void 0 ? void 0 : rule.default);
return __classPrivateFieldGet(this, _Evaluator_nestedResults, "f").length ? __classPrivateFieldGet(this, _Evaluator_nestedResults, "f")[0] : e;
}
}
exports.Evaluator = Evaluator;
_Evaluator_objectDiscovery = new WeakMap(), _Evaluator_nestedResults = new WeakMap(), _Evaluator_instances = new WeakSet(), _Evaluator_evaluateRule = function _Evaluator_evaluateRule(conditions, criteria, defaultResult, isSubRule = false) {
var _a;
// Cater for the case where the conditions property is not an array.
conditions = conditions instanceof Array ? conditions : [conditions];
// We should evaluate all conditions and return the result
// of the first condition that passes.
for (const condition of conditions) {
const result = __classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_evaluateCondition).call(this, condition, criteria);
if (result)
return isSubRule ? true : (_a = condition === null || condition === void 0 ? void 0 : condition.result) !== null && _a !== void 0 ? _a : true;
}
// If no conditions pass, we should return the default result of
// the rule or false if no default result is provided.
return defaultResult !== null && defaultResult !== void 0 ? defaultResult : false;
}, _Evaluator_evaluateCondition = function _Evaluator_evaluateCondition(condition, criteria) {
// The condition must have an 'any' or 'all' property.
const type = __classPrivateFieldGet(this, _Evaluator_objectDiscovery, "f").conditionType(condition);
if (!type)
return false;
// If the condition has nested results
__classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_processNestedResults).call(this, condition, criteria, type);
// Set the starting result
let result = undefined;
// Check each node in the condition.
for (const node of condition[type]) {
// Ignore sub-rules when evaluating the condition.
if (__classPrivateFieldGet(this, _Evaluator_objectDiscovery, "f").isConditionWithResult(node))
continue;
let fn;
if (__classPrivateFieldGet(this, _Evaluator_objectDiscovery, "f").isCondition(node))
fn = () => __classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_evaluateCondition).call(this, node, criteria);
if (__classPrivateFieldGet(this, _Evaluator_objectDiscovery, "f").isConstraint(node))
fn = () => __classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_checkConstraint).call(this, node, criteria);
// Process the node
result = __classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_accumulate).call(this, type, fn, result);
}
return result;
}, _Evaluator_processNestedResults = function _Evaluator_processNestedResults(condition, criteria, type) {
// Find all the nested conditions which have results
const candidates = condition[type].filter((node) => __classPrivateFieldGet(this, _Evaluator_objectDiscovery, "f").isConditionWithResult(node));
// For each candidate, check if all the sibling
// conditions and constraints pass
candidates.forEach((candidate) => {
let siblingsPass = undefined;
for (const node of condition[type]) {
if (__classPrivateFieldGet(this, _Evaluator_objectDiscovery, "f").isConditionWithResult(node))
continue;
if (__classPrivateFieldGet(this, _Evaluator_objectDiscovery, "f").isCondition(node)) {
siblingsPass = __classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_accumulate).call(this, type, () => __classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_evaluateCondition).call(this, node, criteria), siblingsPass);
}
if (__classPrivateFieldGet(this, _Evaluator_objectDiscovery, "f").isConstraint(node)) {
siblingsPass = __classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_accumulate).call(this, type, () => __classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_checkConstraint).call(this, node, criteria), siblingsPass);
}
}
if (!siblingsPass)
return;
// Evaluate the sub-rule
const passed = __classPrivateFieldGet(this, _Evaluator_instances, "m", _Evaluator_evaluateRule).call(this, candidate, criteria, false, true);
if (passed)
__classPrivateFieldGet(this, _Evaluator_nestedResults, "f").push(candidate.result);
});
}, _Evaluator_accumulate = function _Evaluator_accumulate(type, fn, result) {
result = undefined === result ? ["all", "none"].includes(type) : result;
switch (type) {
case "any":
result = result || fn();
break;
case "all":
result = result && fn();
break;
case "none":
result = result && !fn();
}
return result;
}, _Evaluator_checkConstraint = function _Evaluator_checkConstraint(constraint, criteria) {
// If the value contains '.' we should assume it is a nested property
const criterion = constraint.field.includes(".")
? __classPrivateFieldGet(this, _Evaluator_objectDiscovery, "f").resolveNestedProperty(constraint.field, criteria)
: criteria[constraint.field];
// If the criteria object does not have the field
// we are looking for, we should return false.
if (undefined === criterion) {
return false;
}
switch (constraint.operator) {
case "==":
return criterion == constraint.value;
case "!=":
return criterion != constraint.value;
case ">":
return criterion > constraint.value;
case ">=":
return criterion >= constraint.value;
case "<":
return criterion < constraint.value;
case "<=":
return criterion <= constraint.value;
case "in":
return (Array.isArray(constraint.value) &&
constraint.value.includes(criterion));
case "not in":
return (!Array.isArray(constraint.value) ||
!constraint.value.includes(criterion));
case "contains":
return Array.isArray(criterion) && criterion.includes(constraint.value);
case "not contains":
return (!Array.isArray(criterion) || !criterion.includes(constraint.value));
case "contains any":
return (Array.isArray(constraint.value) &&
constraint.value.some((x) => criterion.includes(x)));
case "not contains any":
return (!Array.isArray(constraint.value) ||
!constraint.value.some((x) => criterion.includes(x)));
case "matches":
return new RegExp(criterion).test(`${constraint.value}`);
case "not matches":
return !new RegExp(criterion).test(`${constraint.value}`);
default:
return false;
}
};