UNPKG

rulepilot

Version:

Rule parsing engine for JSON rules

173 lines (172 loc) 10.1 kB
"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; } };