@shepherdnerds/json-rules-engine 7.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +3 -0
- package/.claude/settings.local.json +24 -0
- package/.github/workflows/deploy.yml +45 -0
- package/.github/workflows/node.js.yml +28 -0
- package/CHANGELOG.md +167 -0
- package/LICENSE +15 -0
- package/README.md +235 -0
- package/dist/almanac.js +269 -0
- package/dist/condition.js +331 -0
- package/dist/debug.js +18 -0
- package/dist/engine-default-operator-decorators.js +42 -0
- package/dist/engine-default-operators.js +50 -0
- package/dist/engine.js +451 -0
- package/dist/errors.js +32 -0
- package/dist/fact.js +129 -0
- package/dist/index.js +3 -0
- package/dist/json-rules-engine.js +48 -0
- package/dist/operator-decorator.js +60 -0
- package/dist/operator-map.js +178 -0
- package/dist/operator.js +50 -0
- package/dist/rule-result.js +80 -0
- package/dist/rule.js +525 -0
- package/dist/scoped-almanac.js +120 -0
- package/package.json +96 -0
- package/types/index.d.ts +312 -0
package/dist/almanac.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
8
|
+
|
|
9
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
10
|
+
|
|
11
|
+
var _fact = require('./fact');
|
|
12
|
+
|
|
13
|
+
var _fact2 = _interopRequireDefault(_fact);
|
|
14
|
+
|
|
15
|
+
var _errors = require('./errors');
|
|
16
|
+
|
|
17
|
+
var _debug = require('./debug');
|
|
18
|
+
|
|
19
|
+
var _debug2 = _interopRequireDefault(_debug);
|
|
20
|
+
|
|
21
|
+
var _jsonpathPlus = require('jsonpath-plus');
|
|
22
|
+
|
|
23
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
24
|
+
|
|
25
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
26
|
+
|
|
27
|
+
function defaultPathResolver(value, path) {
|
|
28
|
+
return (0, _jsonpathPlus.JSONPath)({ path: path, json: value, wrap: false });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Fact results lookup
|
|
33
|
+
* Triggers fact computations and saves the results
|
|
34
|
+
* A new almanac is used for every engine run()
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
var Almanac = function () {
|
|
38
|
+
function Almanac() {
|
|
39
|
+
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
40
|
+
|
|
41
|
+
_classCallCheck(this, Almanac);
|
|
42
|
+
|
|
43
|
+
this.factMap = new Map();
|
|
44
|
+
this.factResultsCache = new Map(); // { cacheKey: Promise<factValu> }
|
|
45
|
+
this.allowUndefinedFacts = Boolean(options.allowUndefinedFacts);
|
|
46
|
+
this.pathResolver = options.pathResolver || defaultPathResolver;
|
|
47
|
+
this.events = { success: [], failure: [] };
|
|
48
|
+
this.ruleResults = [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Adds a success event
|
|
53
|
+
* @param {Object} event
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_createClass(Almanac, [{
|
|
58
|
+
key: 'addEvent',
|
|
59
|
+
value: function addEvent(event, outcome) {
|
|
60
|
+
if (!outcome) throw new Error('outcome required: "success" | "failure"]');
|
|
61
|
+
this.events[outcome].push(event);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* retrieve successful events
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
}, {
|
|
69
|
+
key: 'getEvents',
|
|
70
|
+
value: function getEvents() {
|
|
71
|
+
var outcome = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
72
|
+
|
|
73
|
+
if (outcome) return this.events[outcome];
|
|
74
|
+
return this.events.success.concat(this.events.failure);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Adds a rule result
|
|
79
|
+
* @param {Object} event
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
}, {
|
|
83
|
+
key: 'addResult',
|
|
84
|
+
value: function addResult(ruleResult) {
|
|
85
|
+
this.ruleResults.push(ruleResult);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* retrieve successful events
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
}, {
|
|
93
|
+
key: 'getResults',
|
|
94
|
+
value: function getResults() {
|
|
95
|
+
return this.ruleResults;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Retrieve fact by id, raising an exception if it DNE
|
|
100
|
+
* @param {String} factId
|
|
101
|
+
* @return {Fact}
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
}, {
|
|
105
|
+
key: '_getFact',
|
|
106
|
+
value: function _getFact(factId) {
|
|
107
|
+
return this.factMap.get(factId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Registers fact with the almanac
|
|
112
|
+
* @param {[type]} fact [description]
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
}, {
|
|
116
|
+
key: '_addConstantFact',
|
|
117
|
+
value: function _addConstantFact(fact) {
|
|
118
|
+
this.factMap.set(fact.id, fact);
|
|
119
|
+
this._setFactValue(fact, {}, fact.value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Sets the computed value of a fact
|
|
124
|
+
* @param {Fact} fact
|
|
125
|
+
* @param {Object} params - values for differentiating this fact value from others, used for cache key
|
|
126
|
+
* @param {Mixed} value - computed value
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
}, {
|
|
130
|
+
key: '_setFactValue',
|
|
131
|
+
value: function _setFactValue(fact, params, value) {
|
|
132
|
+
var cacheKey = fact.getCacheKey(params);
|
|
133
|
+
var factValue = Promise.resolve(value);
|
|
134
|
+
if (cacheKey) {
|
|
135
|
+
this.factResultsCache.set(cacheKey, factValue);
|
|
136
|
+
}
|
|
137
|
+
return factValue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Add a fact definition to the engine. Facts are called by rules as they are evaluated.
|
|
142
|
+
* @param {object|Fact} id - fact identifier or instance of Fact
|
|
143
|
+
* @param {function} definitionFunc - function to be called when computing the fact value for a given rule
|
|
144
|
+
* @param {Object} options - options to initialize the fact with. used when "id" is not a Fact instance
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
}, {
|
|
148
|
+
key: 'addFact',
|
|
149
|
+
value: function addFact(id, valueOrMethod, options) {
|
|
150
|
+
var factId = id;
|
|
151
|
+
var fact = void 0;
|
|
152
|
+
if (id instanceof _fact2.default) {
|
|
153
|
+
factId = id.id;
|
|
154
|
+
fact = id;
|
|
155
|
+
} else {
|
|
156
|
+
fact = new _fact2.default(id, valueOrMethod, options);
|
|
157
|
+
}
|
|
158
|
+
(0, _debug2.default)('almanac::addFact', { id: factId });
|
|
159
|
+
this.factMap.set(factId, fact);
|
|
160
|
+
if (fact.isConstant()) {
|
|
161
|
+
this._setFactValue(fact, {}, fact.value);
|
|
162
|
+
}
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Adds a constant fact during runtime. Can be used mid-run() to add additional information
|
|
168
|
+
* @deprecated use addFact
|
|
169
|
+
* @param {String} fact - fact identifier
|
|
170
|
+
* @param {Mixed} value - constant value of the fact
|
|
171
|
+
*/
|
|
172
|
+
|
|
173
|
+
}, {
|
|
174
|
+
key: 'addRuntimeFact',
|
|
175
|
+
value: function addRuntimeFact(factId, value) {
|
|
176
|
+
(0, _debug2.default)('almanac::addRuntimeFact', { id: factId });
|
|
177
|
+
var fact = new _fact2.default(factId, value);
|
|
178
|
+
return this._addConstantFact(fact);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Returns the value of a fact, based on the given parameters. Utilizes the 'almanac' maintained
|
|
183
|
+
* by the engine, which cache's fact computations based on parameters provided
|
|
184
|
+
* @param {string} factId - fact identifier
|
|
185
|
+
* @param {Object} params - parameters to feed into the fact. By default, these will also be used to compute the cache key
|
|
186
|
+
* @param {String} path - object
|
|
187
|
+
* @return {Promise} a promise which will resolve with the fact computation.
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
}, {
|
|
191
|
+
key: 'factValue',
|
|
192
|
+
value: function factValue(factId) {
|
|
193
|
+
var _this = this;
|
|
194
|
+
|
|
195
|
+
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
196
|
+
var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
|
|
197
|
+
|
|
198
|
+
var factValuePromise = void 0;
|
|
199
|
+
var fact = this._getFact(factId);
|
|
200
|
+
if (fact === undefined) {
|
|
201
|
+
if (this.allowUndefinedFacts) {
|
|
202
|
+
return Promise.resolve(undefined);
|
|
203
|
+
} else {
|
|
204
|
+
return Promise.reject(new _errors.UndefinedFactError('Undefined fact: ' + factId));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (fact.isConstant()) {
|
|
208
|
+
factValuePromise = Promise.resolve(fact.calculate(params, this));
|
|
209
|
+
} else {
|
|
210
|
+
var cacheKey = fact.getCacheKey(params);
|
|
211
|
+
var cacheVal = cacheKey && this.factResultsCache.get(cacheKey);
|
|
212
|
+
if (cacheVal) {
|
|
213
|
+
factValuePromise = Promise.resolve(cacheVal);
|
|
214
|
+
(0, _debug2.default)('almanac::factValue cache hit for fact', { id: factId });
|
|
215
|
+
} else {
|
|
216
|
+
(0, _debug2.default)('almanac::factValue cache miss, calculating', { id: factId });
|
|
217
|
+
factValuePromise = this._setFactValue(fact, params, fact.calculate(params, this));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (path) {
|
|
221
|
+
(0, _debug2.default)('condition::evaluate extracting object', { property: path });
|
|
222
|
+
return factValuePromise.then(function (factValue) {
|
|
223
|
+
if (factValue != null && (typeof factValue === 'undefined' ? 'undefined' : _typeof(factValue)) === 'object') {
|
|
224
|
+
var pathValue = _this.pathResolver(factValue, path);
|
|
225
|
+
(0, _debug2.default)('condition::evaluate extracting object', { property: path, received: pathValue });
|
|
226
|
+
return pathValue;
|
|
227
|
+
} else {
|
|
228
|
+
(0, _debug2.default)('condition::evaluate could not compute object path of non-object', { path: path, factValue: factValue, type: typeof factValue === 'undefined' ? 'undefined' : _typeof(factValue) });
|
|
229
|
+
return factValue;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return factValuePromise;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Interprets value as either a primitive, or if a fact, retrieves the fact value
|
|
239
|
+
*/
|
|
240
|
+
|
|
241
|
+
}, {
|
|
242
|
+
key: 'getValue',
|
|
243
|
+
value: function getValue(value) {
|
|
244
|
+
if (value != null && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && Object.prototype.hasOwnProperty.call(value, 'fact')) {
|
|
245
|
+
// value = { fact: 'xyz' }
|
|
246
|
+
return this.factValue(value.fact, value.params, value.path);
|
|
247
|
+
}
|
|
248
|
+
return Promise.resolve(value);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Resolves a path - only valid in scoped context (ScopedAlmanac)
|
|
253
|
+
* This method exists to provide a clear error when path-only conditions
|
|
254
|
+
* are used outside of a nested condition context
|
|
255
|
+
* @param {string} path - the path to resolve
|
|
256
|
+
* @return {Promise} rejects with an error
|
|
257
|
+
*/
|
|
258
|
+
|
|
259
|
+
}, {
|
|
260
|
+
key: 'resolvePath',
|
|
261
|
+
value: function resolvePath(path) {
|
|
262
|
+
return Promise.reject(new Error('Almanac::resolvePath - path-only conditions (path: "' + path + '") can only be used inside nested conditions. ' + 'Ensure this condition is within a "some" or "every" operator block.'));
|
|
263
|
+
}
|
|
264
|
+
}]);
|
|
265
|
+
|
|
266
|
+
return Almanac;
|
|
267
|
+
}();
|
|
268
|
+
|
|
269
|
+
exports.default = Almanac;
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
|
|
8
|
+
|
|
9
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
10
|
+
|
|
11
|
+
var _debug = require('./debug');
|
|
12
|
+
|
|
13
|
+
var _debug2 = _interopRequireDefault(_debug);
|
|
14
|
+
|
|
15
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
|
+
|
|
17
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
18
|
+
|
|
19
|
+
var Condition = function () {
|
|
20
|
+
function Condition(properties) {
|
|
21
|
+
_classCallCheck(this, Condition);
|
|
22
|
+
|
|
23
|
+
if (!properties) throw new Error('Condition: constructor options required');
|
|
24
|
+
var booleanOperator = Condition.booleanOperator(properties);
|
|
25
|
+
Object.assign(this, properties);
|
|
26
|
+
if (booleanOperator) {
|
|
27
|
+
var subConditions = properties[booleanOperator];
|
|
28
|
+
var subConditionsIsArray = Array.isArray(subConditions);
|
|
29
|
+
if (booleanOperator !== 'not' && !subConditionsIsArray) {
|
|
30
|
+
throw new Error('"' + booleanOperator + '" must be an array');
|
|
31
|
+
}
|
|
32
|
+
if (booleanOperator === 'not' && subConditionsIsArray) {
|
|
33
|
+
throw new Error('"' + booleanOperator + '" cannot be an array');
|
|
34
|
+
}
|
|
35
|
+
this.operator = booleanOperator;
|
|
36
|
+
// boolean conditions always have a priority; default 1
|
|
37
|
+
this.priority = parseInt(properties.priority, 10) || 1;
|
|
38
|
+
if (subConditionsIsArray) {
|
|
39
|
+
this[booleanOperator] = subConditions.map(function (c) {
|
|
40
|
+
return new Condition(c);
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
this[booleanOperator] = new Condition(subConditions);
|
|
44
|
+
}
|
|
45
|
+
} else if (!Object.prototype.hasOwnProperty.call(properties, 'condition')) {
|
|
46
|
+
var hasFact = Object.prototype.hasOwnProperty.call(properties, 'fact');
|
|
47
|
+
var hasOperator = Object.prototype.hasOwnProperty.call(properties, 'operator');
|
|
48
|
+
var hasValue = Object.prototype.hasOwnProperty.call(properties, 'value');
|
|
49
|
+
|
|
50
|
+
// Check if this is a nested condition (operator: 'some' with conditions property)
|
|
51
|
+
if (this.isNestedCondition()) {
|
|
52
|
+
// Nested array conditions require fact (to get the array)
|
|
53
|
+
if (!hasFact) {
|
|
54
|
+
throw new Error('Condition: constructor "fact" property required for nested conditions');
|
|
55
|
+
}
|
|
56
|
+
// Parse the nested conditions tree
|
|
57
|
+
this.conditions = new Condition(properties.conditions);
|
|
58
|
+
} else if (this.isScopedCondition()) {
|
|
59
|
+
// Scoped conditions (path only, no fact) - used inside nested conditions
|
|
60
|
+
// where the "fact" is implicitly the current array item
|
|
61
|
+
if (!hasOperator) {
|
|
62
|
+
throw new Error('Condition: constructor "operator" property required');
|
|
63
|
+
}
|
|
64
|
+
if (!hasValue) {
|
|
65
|
+
throw new Error('Condition: constructor "value" property required');
|
|
66
|
+
}
|
|
67
|
+
// path is already validated by isScopedCondition()
|
|
68
|
+
} else {
|
|
69
|
+
// Regular fact-based condition
|
|
70
|
+
if (!hasFact) {
|
|
71
|
+
throw new Error('Condition: constructor "fact" property required');
|
|
72
|
+
}
|
|
73
|
+
if (!hasOperator) {
|
|
74
|
+
throw new Error('Condition: constructor "operator" property required');
|
|
75
|
+
}
|
|
76
|
+
if (!hasValue) {
|
|
77
|
+
throw new Error('Condition: constructor "value" property required');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// a non-boolean condition does not have a priority by default. this allows
|
|
82
|
+
// priority to be dictated by the fact definition
|
|
83
|
+
if (Object.prototype.hasOwnProperty.call(properties, 'priority')) {
|
|
84
|
+
properties.priority = parseInt(properties.priority, 10);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Converts the condition into a json-friendly structure
|
|
91
|
+
* @param {Boolean} stringify - whether to return as a json string
|
|
92
|
+
* @returns {string,object} json string or json-friendly object
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
_createClass(Condition, [{
|
|
97
|
+
key: 'toJSON',
|
|
98
|
+
value: function toJSON() {
|
|
99
|
+
var stringify = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
100
|
+
|
|
101
|
+
var props = {};
|
|
102
|
+
if (this.priority) {
|
|
103
|
+
props.priority = this.priority;
|
|
104
|
+
}
|
|
105
|
+
if (this.name) {
|
|
106
|
+
props.name = this.name;
|
|
107
|
+
}
|
|
108
|
+
var oper = Condition.booleanOperator(this);
|
|
109
|
+
if (oper) {
|
|
110
|
+
if (Array.isArray(this[oper])) {
|
|
111
|
+
props[oper] = this[oper].map(function (c) {
|
|
112
|
+
return c.toJSON(false);
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
props[oper] = this[oper].toJSON(false);
|
|
116
|
+
}
|
|
117
|
+
} else if (this.isConditionReference()) {
|
|
118
|
+
props.condition = this.condition;
|
|
119
|
+
} else if (this.isNestedCondition()) {
|
|
120
|
+
props.operator = this.operator;
|
|
121
|
+
props.fact = this.fact;
|
|
122
|
+
props.conditions = this.conditions.toJSON(false);
|
|
123
|
+
if (this.factResult !== undefined) {
|
|
124
|
+
props.factResult = this.factResult;
|
|
125
|
+
}
|
|
126
|
+
if (this.result !== undefined) {
|
|
127
|
+
props.result = this.result;
|
|
128
|
+
}
|
|
129
|
+
if (this.params) {
|
|
130
|
+
props.params = this.params;
|
|
131
|
+
}
|
|
132
|
+
if (this.path) {
|
|
133
|
+
props.path = this.path;
|
|
134
|
+
}
|
|
135
|
+
} else if (this.isScopedCondition()) {
|
|
136
|
+
// Scoped condition (path only, no fact)
|
|
137
|
+
props.operator = this.operator;
|
|
138
|
+
props.value = this.value;
|
|
139
|
+
props.path = this.path;
|
|
140
|
+
if (this.factResult !== undefined) {
|
|
141
|
+
props.factResult = this.factResult;
|
|
142
|
+
}
|
|
143
|
+
if (this.valueResult !== undefined) {
|
|
144
|
+
props.valueResult = this.valueResult;
|
|
145
|
+
}
|
|
146
|
+
if (this.result !== undefined) {
|
|
147
|
+
props.result = this.result;
|
|
148
|
+
}
|
|
149
|
+
if (this.params) {
|
|
150
|
+
props.params = this.params;
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
props.operator = this.operator;
|
|
154
|
+
props.value = this.value;
|
|
155
|
+
props.fact = this.fact;
|
|
156
|
+
if (this.factResult !== undefined) {
|
|
157
|
+
props.factResult = this.factResult;
|
|
158
|
+
}
|
|
159
|
+
if (this.valueResult !== undefined) {
|
|
160
|
+
props.valueResult = this.valueResult;
|
|
161
|
+
}
|
|
162
|
+
if (this.result !== undefined) {
|
|
163
|
+
props.result = this.result;
|
|
164
|
+
}
|
|
165
|
+
if (this.params) {
|
|
166
|
+
props.params = this.params;
|
|
167
|
+
}
|
|
168
|
+
if (this.path) {
|
|
169
|
+
props.path = this.path;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (stringify) {
|
|
173
|
+
return JSON.stringify(props);
|
|
174
|
+
}
|
|
175
|
+
return props;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Takes the fact result and compares it to the condition 'value', using the operator
|
|
180
|
+
* LHS OPER RHS
|
|
181
|
+
* <fact + params + path> <operator> <value>
|
|
182
|
+
*
|
|
183
|
+
* @param {Almanac} almanac
|
|
184
|
+
* @param {Map} operatorMap - map of available operators, keyed by operator name
|
|
185
|
+
* @returns {Boolean} - evaluation result
|
|
186
|
+
*/
|
|
187
|
+
|
|
188
|
+
}, {
|
|
189
|
+
key: 'evaluate',
|
|
190
|
+
value: function evaluate(almanac, operatorMap) {
|
|
191
|
+
var _this = this;
|
|
192
|
+
|
|
193
|
+
if (!almanac) return Promise.reject(new Error('almanac required'));
|
|
194
|
+
if (!operatorMap) return Promise.reject(new Error('operatorMap required'));
|
|
195
|
+
if (this.isBooleanOperator()) {
|
|
196
|
+
return Promise.reject(new Error('Cannot evaluate() a boolean condition'));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
var op = operatorMap.get(this.operator);
|
|
200
|
+
if (!op) {
|
|
201
|
+
return Promise.reject(new Error('Unknown operator: ' + this.operator));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Handle scoped conditions (path only, no fact)
|
|
205
|
+
// These are used inside nested conditions where the almanac is a ScopedAlmanac
|
|
206
|
+
if (this.isScopedCondition()) {
|
|
207
|
+
return Promise.all([almanac.getValue(this.value), almanac.resolvePath(this.path)]).then(function (_ref) {
|
|
208
|
+
var _ref2 = _slicedToArray(_ref, 2),
|
|
209
|
+
rightHandSideValue = _ref2[0],
|
|
210
|
+
leftHandSideValue = _ref2[1];
|
|
211
|
+
|
|
212
|
+
var result = op.evaluate(leftHandSideValue, rightHandSideValue);
|
|
213
|
+
(0, _debug2.default)('condition::evaluate (scoped)', {
|
|
214
|
+
path: _this.path,
|
|
215
|
+
leftHandSideValue: leftHandSideValue,
|
|
216
|
+
operator: _this.operator,
|
|
217
|
+
rightHandSideValue: rightHandSideValue,
|
|
218
|
+
result: result
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
result: result,
|
|
222
|
+
leftHandSideValue: leftHandSideValue,
|
|
223
|
+
rightHandSideValue: rightHandSideValue,
|
|
224
|
+
operator: _this.operator
|
|
225
|
+
};
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Regular fact-based condition
|
|
230
|
+
return Promise.all([almanac.getValue(this.value), almanac.factValue(this.fact, this.params, this.path)]).then(function (_ref3) {
|
|
231
|
+
var _ref4 = _slicedToArray(_ref3, 2),
|
|
232
|
+
rightHandSideValue = _ref4[0],
|
|
233
|
+
leftHandSideValue = _ref4[1];
|
|
234
|
+
|
|
235
|
+
var result = op.evaluate(leftHandSideValue, rightHandSideValue);
|
|
236
|
+
(0, _debug2.default)('condition::evaluate', {
|
|
237
|
+
leftHandSideValue: leftHandSideValue,
|
|
238
|
+
operator: _this.operator,
|
|
239
|
+
rightHandSideValue: rightHandSideValue,
|
|
240
|
+
result: result
|
|
241
|
+
});
|
|
242
|
+
return {
|
|
243
|
+
result: result,
|
|
244
|
+
leftHandSideValue: leftHandSideValue,
|
|
245
|
+
rightHandSideValue: rightHandSideValue,
|
|
246
|
+
operator: _this.operator
|
|
247
|
+
};
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Returns the boolean operator for the condition
|
|
253
|
+
* If the condition is not a boolean condition, the result will be 'undefined'
|
|
254
|
+
* @return {string 'all', 'any', or 'not'}
|
|
255
|
+
*/
|
|
256
|
+
|
|
257
|
+
}, {
|
|
258
|
+
key: 'booleanOperator',
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Returns the condition's boolean operator
|
|
263
|
+
* Instance version of Condition.isBooleanOperator
|
|
264
|
+
* @returns {string,undefined} - 'any', 'all', 'not' or undefined (if not a boolean condition)
|
|
265
|
+
*/
|
|
266
|
+
value: function booleanOperator() {
|
|
267
|
+
return Condition.booleanOperator(this);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Whether the operator is boolean ('all', 'any', 'not')
|
|
272
|
+
* @returns {Boolean}
|
|
273
|
+
*/
|
|
274
|
+
|
|
275
|
+
}, {
|
|
276
|
+
key: 'isBooleanOperator',
|
|
277
|
+
value: function isBooleanOperator() {
|
|
278
|
+
return Condition.booleanOperator(this) !== undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Whether the condition represents a reference to a condition
|
|
283
|
+
* @returns {Boolean}
|
|
284
|
+
*/
|
|
285
|
+
|
|
286
|
+
}, {
|
|
287
|
+
key: 'isConditionReference',
|
|
288
|
+
value: function isConditionReference() {
|
|
289
|
+
return Object.prototype.hasOwnProperty.call(this, 'condition');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Whether the condition is a nested condition (operator: 'some' with 'conditions' property)
|
|
294
|
+
* @returns {Boolean}
|
|
295
|
+
*/
|
|
296
|
+
|
|
297
|
+
}, {
|
|
298
|
+
key: 'isNestedCondition',
|
|
299
|
+
value: function isNestedCondition() {
|
|
300
|
+
return this.operator === 'some' && Object.prototype.hasOwnProperty.call(this, 'conditions');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Whether this is a scoped condition (path only, no fact)
|
|
305
|
+
* Used inside nested conditions where the "fact" is the current array item
|
|
306
|
+
* The path is resolved directly against the scoped item
|
|
307
|
+
* @returns {Boolean}
|
|
308
|
+
*/
|
|
309
|
+
|
|
310
|
+
}, {
|
|
311
|
+
key: 'isScopedCondition',
|
|
312
|
+
value: function isScopedCondition() {
|
|
313
|
+
return !Object.prototype.hasOwnProperty.call(this, 'fact') && Object.prototype.hasOwnProperty.call(this, 'path') && Object.prototype.hasOwnProperty.call(this, 'operator') && !this.isBooleanOperator();
|
|
314
|
+
}
|
|
315
|
+
}], [{
|
|
316
|
+
key: 'booleanOperator',
|
|
317
|
+
value: function booleanOperator(condition) {
|
|
318
|
+
if (Object.prototype.hasOwnProperty.call(condition, 'any')) {
|
|
319
|
+
return 'any';
|
|
320
|
+
} else if (Object.prototype.hasOwnProperty.call(condition, 'all')) {
|
|
321
|
+
return 'all';
|
|
322
|
+
} else if (Object.prototype.hasOwnProperty.call(condition, 'not')) {
|
|
323
|
+
return 'not';
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}]);
|
|
327
|
+
|
|
328
|
+
return Condition;
|
|
329
|
+
}();
|
|
330
|
+
|
|
331
|
+
exports.default = Condition;
|
package/dist/debug.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
function createDebug() {
|
|
8
|
+
try {
|
|
9
|
+
if (typeof process !== 'undefined' && process.env && process.env.DEBUG && process.env.DEBUG.match(/json-rules-engine/) || typeof window !== 'undefined' && window.localStorage && window.localStorage.debug && window.localStorage.debug.match(/json-rules-engine/)) {
|
|
10
|
+
return console.debug.bind(console);
|
|
11
|
+
}
|
|
12
|
+
} catch (ex) {
|
|
13
|
+
// Do nothing
|
|
14
|
+
}
|
|
15
|
+
return function () {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exports.default = createDebug();
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
var _operatorDecorator = require('./operator-decorator');
|
|
8
|
+
|
|
9
|
+
var _operatorDecorator2 = _interopRequireDefault(_operatorDecorator);
|
|
10
|
+
|
|
11
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
|
+
|
|
13
|
+
var OperatorDecorators = [];
|
|
14
|
+
|
|
15
|
+
OperatorDecorators.push(new _operatorDecorator2.default('someFact', function (factValue, jsonValue, next) {
|
|
16
|
+
return factValue.some(function (fv) {
|
|
17
|
+
return next(fv, jsonValue);
|
|
18
|
+
});
|
|
19
|
+
}, Array.isArray));
|
|
20
|
+
OperatorDecorators.push(new _operatorDecorator2.default('someValue', function (factValue, jsonValue, next) {
|
|
21
|
+
return jsonValue.some(function (jv) {
|
|
22
|
+
return next(factValue, jv);
|
|
23
|
+
});
|
|
24
|
+
}));
|
|
25
|
+
OperatorDecorators.push(new _operatorDecorator2.default('everyFact', function (factValue, jsonValue, next) {
|
|
26
|
+
return factValue.every(function (fv) {
|
|
27
|
+
return next(fv, jsonValue);
|
|
28
|
+
});
|
|
29
|
+
}, Array.isArray));
|
|
30
|
+
OperatorDecorators.push(new _operatorDecorator2.default('everyValue', function (factValue, jsonValue, next) {
|
|
31
|
+
return jsonValue.every(function (jv) {
|
|
32
|
+
return next(factValue, jv);
|
|
33
|
+
});
|
|
34
|
+
}));
|
|
35
|
+
OperatorDecorators.push(new _operatorDecorator2.default('swap', function (factValue, jsonValue, next) {
|
|
36
|
+
return next(jsonValue, factValue);
|
|
37
|
+
}));
|
|
38
|
+
OperatorDecorators.push(new _operatorDecorator2.default('not', function (factValue, jsonValue, next) {
|
|
39
|
+
return !next(factValue, jsonValue);
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
exports.default = OperatorDecorators;
|