diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..5ca31e5 --- /dev/null +++ b/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "modules": false, + "useBuiltIns": false + } + ] + ], + "plugins": [ + "@babel/plugin-proposal-export-default-from" + ] +} diff --git a/.eslintrc.json b/.eslintrc.json index 12bf434..915e83c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,33 +1,8 @@ { - "extends": "google", - + "extends": ["airbnb", "plugin:prettier/recommended"], "rules": { - "no-var": "off", - "indent": [ - "error", - 2 - ], - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "double", { - "avoidEscape": true - } - ], - "semi": [ - "error", - "always" - ], - "max-len": [ - "warn", { - "ignoreComments": true - } - ], - "prefer-spread": ["off"], - "prefer-rest-params": ["off"], - "camelcase" : ["off"] + "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], + "no-use-before-define": ["error", { "functions": false }], + "camelcase": "off" } } diff --git a/.gitignore b/.gitignore index ab3a2e7..bc9ffb1 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ node_modules tests/*.json +package-lock.json + # editor and IDE remnants *~ .idea/ diff --git a/.huskyrc b/.huskyrc new file mode 100644 index 0000000..4d077c8 --- /dev/null +++ b/.huskyrc @@ -0,0 +1,5 @@ +{ + "hooks": { + "pre-commit": "lint-staged" + } +} diff --git a/.lintstagedrc b/.lintstagedrc new file mode 100644 index 0000000..846e186 --- /dev/null +++ b/.lintstagedrc @@ -0,0 +1,3 @@ +{ + "src/**/*.{js,jsx,json}": ["eslint --fix", "git add"] +} diff --git a/.npmignore b/.npmignore index 013b7f4..566887d 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,5 @@ tests/tests.json +src play.html bower.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..266594c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "semi": true, + "singleQuote": true, + "bracketSpacing": true +} diff --git a/README.md b/README.md index cf86a90..f759058 100644 --- a/README.md +++ b/README.md @@ -8,24 +8,33 @@ The same format can also be executed in PHP by the library [json-logic-php](http ## Installation -To parse JsonLogic rules in a JavaScript frontend, install this library is via [Bower](http://bower.io/): +To parse JsonLogic rules in a JavaScript backend (like Node.js), install this library via [NPM](https://www.npmjs.com/): ```bash -bower install --save json-logic-js +npm install @axa-ch/json-logic-js --save ``` -To parse JsonLogic rules in a JavaScript backend (like Node.js), install this library via [NPM](https://www.npmjs.com/): +Note that this project provides: +- an ESM build for cherry picking +- a [module loader](http://ricostacruz.com/cheatsheets/umdjs.html) that also makes it suitable for RequireJS projects. +- a minified bundle ready for the browser -```bash -npm install json-logic-js -``` +If that doesn't suit you, and you want to manage updates yourself, the entire library is self-contained in `dist/jsonLogic.js` and you can download it straight into your project as you see fit. -Note that this project uses a [module loader](http://ricostacruz.com/cheatsheets/umdjs.html) that also makes it suitable for RequireJS projects. +## Cherry-picked build -If that doesn't suit you, and you want to manage updates yourself, the entire library is self-contained in `logic.js` and you can download it straight into your project as you see fit. +If the default bundle size is too big for you or you only need certain operations, just utilize the ESM build and create your customized `jsonLogic` object as follows: -```bash -curl -O https://raw.githubusercontent.com/jwadhams/json-logic-js/master/logic.js +```js +import createJsonLogic from './createJsonLogic'; + +// pick just what you need, or create your own +import { equal } from './operations'; +import { and, or } from './visitors'; + +const jsonLogic = createJsonLogic({ equal }, { and, or }); + +export default jsonLogic; ``` ## Examples @@ -122,7 +131,7 @@ jsonLogic.apply(false, i_wasnt_even_supposed_to_be_here); ## Compatibility -This library makes use of `Array.map` and `Array.reduce`, so it's not *exactly* Internet Explorer 8 friendly. +This library makes use of `Array.isArray`, `Array.forEach`, `Array.map` and `Array.reduce`, so it's not *exactly* Internet Explorer 8 friendly. If you want to use JsonLogic *and* support deprecated browsers, you could easily use [BabelJS's polyfill](https://babeljs.io/docs/usage/polyfill/) or directly incorporate the polyfills documented on MDN for [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) and [reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce). diff --git a/bower.json b/bower.json deleted file mode 100644 index b7842d1..0000000 --- a/bower.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "json-logic-js", - "version": "1.2.2", - "homepage": "https://github.com/jwadhams/json-logic-js", - "authors": [ - "Jeremy Wadhams " - ], - "description": "Serialize complex logic in JSON, run it in JavaScript", - "main": "logic.js", - "moduleType": [ - "globals" - ], - "keywords": [ - "json", - "logic" - ], - "license": "MIT", - "private": false, - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "tests", - "gulpfile.js" - ] -} diff --git a/dist/jsonLogic.js b/dist/jsonLogic.js new file mode 100644 index 0000000..a817f40 --- /dev/null +++ b/dist/jsonLogic.js @@ -0,0 +1,728 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.jsonLogic = factory()); +}(this, function () { 'use strict'; + + var isArray = Array.isArray; + + function _typeof(obj) { + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); + } + + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } + } + + function _iterableToArray(iter) { + if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); + } + + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance"); + } + + function isLogic(logic) { + return _typeof(logic) === 'object' && // An object + logic !== null && // but not null + !isArray(logic) && // and not an array + Object.keys(logic).length === 1 // with exactly one key + ; + } + + function getOperator(logic) { + return Object.keys(logic)[0]; + } + + function createJsonLogic(_operations, _visitors) { + var operations = {}; + var visitors = {}; + + if (_operations) { + Object.keys(_operations).forEach(function (name) { + var operation = _operations[name]; + addOperation(operation.code || name, operation); + }); + } + + if (_visitors) { + Object.keys(_visitors).forEach(function (name) { + var visitor = _visitors[name]; + addVisitor(visitor.code || name, visitor); + }); + } + + function addOperation(name, code) { + operations[name] = code; + } + + function removeOperation(name) { + delete operations[name]; + } + + function addVisitor(name, code) { + if (isArray(name)) { + name.forEach(function (key) { + return addVisitor(key, code); + }); + return; + } + + visitors[name] = code; + } + + function removeVisitor(name) { + if (isArray(name)) { + name.forEach(removeVisitor); + return; + } + + delete visitors[name]; + } + + function apply(logic) { + var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + // Does this array contain logic? Only one way to find out. + if (isArray(logic)) { + return logic.map(function (l) { + return apply(l, data); + }); + } // You've recursed to a primitive, stop! + + + if (!isLogic(logic)) { + return logic; + } + + var op = getOperator(logic); + var values = logic[op]; + var i; // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} + + if (!isArray(values)) { + values = [values]; + } // apply matching visitors first + + + if (typeof visitors[op] === 'function') { + return visitors[op](apply, data, values); + } // Everyone else gets immediate depth-first recursion + + + values = values.map(function (val) { + return apply(val, data); + }); // The operation is called with "data" bound to its "this" and "values" passed as arguments. + // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments + + var operator = operations[op]; + + if (typeof operator === 'function') { + if (operator.withApply) { + values.unshift(apply); + } + + return operator.apply(data, values); + } + + if (op.indexOf('.') > 0) { + // Contains a dot, and not in the 0th position + var sub_ops = String(op).split('.'); + var operation = operations; + + for (i = 0; i < sub_ops.length; i++) { + // Descending into operations + operation = operation[sub_ops[i]]; + + if (operation === undefined) { + throw new Error("Unrecognized operation ".concat(op, " (failed at ").concat(sub_ops.slice(0, i + 1).join('.'), ")")); + } + } + + return operation.apply(data, values); + } + + throw new Error("Unrecognized operation ".concat(op)); + } + + return { + apply: apply, + add_operation: addOperation, + rm_operation: removeOperation, + add_visitor: addVisitor, + rm_visitor: removeVisitor + }; + } + + function variable(a, b) { + var not_found = b === undefined ? null : b; + var data = this; + + if (typeof a === 'undefined' || a === '' || a === null) { + return data; + } + + var sub_props = String(a).split('.'); + + for (var i = 0; i < sub_props.length; i++) { + if (data === null) { + return not_found; + } // Descending into data + + + data = data[sub_props[i]]; + + if (data === undefined) { + return not_found; + } + } + + return data; + } + + variable.code = 'var'; + + function missing(apply) { + /* + Missing can receive many keys as many arguments, like {"missing:[1,2]} + Missing can also receive *one* argument that is an array of keys, + which typically happens if it's actually acting on the output of another command + (like 'if' or 'merge') + */ + var are_missing = []; + + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + var keys = isArray(args[0]) ? args[0] : args; + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = apply({ + var: key + }, this); + + if (value === null || value === '') { + are_missing.push(key); + } + } + + return are_missing; + } + + missing.withApply = true; + + function missingSome(apply, need_count, options) { + // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. + var are_missing = apply({ + missing: options + }, this); + + if (options.length - are_missing.length >= need_count) { + return []; + } + + return are_missing; + } + + missingSome.code = 'missing_some'; + missingSome.withApply = true; + + function add() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return parseFloat(a, 10) + parseFloat(b, 10); + }, 0); + } + + add.code = '+'; + + function divide(a, b) { + return a / b; + } + + divide.code = '/'; + + function modulo(a, b) { + return a % b; + } + + modulo.code = '%'; + + function multiply() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return parseFloat(a, 10) * parseFloat(b, 10); + }, 1); + } + + multiply.code = '*'; + + function substract(a, b) { + if (b === undefined) { + return -a; + } + + return a - b; + } + + substract.code = '-'; + + function merge() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return a.concat(b); + }, []); + } + + // eslint-disable-next-line import/prefer-default-export + + function equal(a, b) { + // eslint-disable-next-line eqeqeq + return a == b; + } + + equal.code = '=='; + + /* + This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. + + Spec and rationale here: http://jsonlogic.com/truthy + */ + + function truthy(value) { + if (isArray(value) && value.length === 0) { + return false; + } + + return !!value; + } + + function falsy(a) { + return !truthy(a); + } + + falsy.code = '!'; + + function notEqual(a, b) { + // eslint-disable-next-line eqeqeq + return a != b; + } + + notEqual.code = '!='; + + function strictEqual(a, b) { + return a === b; + } + + strictEqual.code = '==='; + + function strictNotEqual(a, b) { + return a !== b; + } + + strictNotEqual.code = '!=='; + + truthy.code = '!!'; + + function indexOf(a, b) { + if (!b || typeof b.indexOf === 'undefined') return false; + return b.indexOf(a) !== -1; + } + + indexOf.code = 'in'; + + function log(a) { + // eslint-disable-next-line no-console + console.log(a); + return a; + } + + function method(obj, methodName, args) { + // eslint-disable-next-line prefer-spread + return obj[methodName].apply(obj, args); + } + + function greater(a, b) { + return a > b; + } + + greater.code = '>'; + + function greaterEqual(a, b) { + return a >= b; + } + + greaterEqual.code = '>='; + + function lower(a, b, c) { + return c === undefined ? a < b : a < b && b < c; + } + + lower.code = '<'; + + function lowerEqual(a, b, c) { + return c === undefined ? a <= b : a <= b && b <= c; + } + + lowerEqual.code = '<='; + + function max() { + return Math.max.apply(Math, arguments); + } + + function min() { + return Math.min.apply(Math, arguments); + } + + function cat() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.join(''); + } + + function substr(source, start, end) { + if (end < 0) { + // JavaScript doesn't support negative end, this emulates PHP behavior + var temp = String(source).substr(start); + return temp.substr(0, temp.length + end); + } + + return String(source).substr(start, end); + } + + + + var operations = /*#__PURE__*/Object.freeze({ + variable: variable, + missing: missing, + missingSome: missingSome, + add: add, + divide: divide, + modulo: modulo, + multiply: multiply, + substract: substract, + merge: merge, + equal: equal, + falsy: falsy, + notEqual: notEqual, + strictEqual: strictEqual, + strictNotEqual: strictNotEqual, + truthy: truthy, + indexOf: indexOf, + log: log, + method: method, + greater: greater, + greaterEqual: greaterEqual, + lower: lower, + lowerEqual: lowerEqual, + max: max, + min: min, + cat: cat, + substr: substr + }); + + function all(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; // All of an empty set is false. Note, some and none have correct fallback after the for loop + + if (!scopedData.length) { + return false; + } + + for (var i = 0; i < scopedData.length; i += 1) { + if (!truthy(apply(scopedLogic, scopedData[i]))) { + return false; // First falsy, short circuit + } + } + + return true; // All were truthy + } + + function filter(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + + if (!isArray(scopedData)) { + return []; + } // Return only the elements from the array in the first argument, + // that return truthy when passed to the logic in the second argument. + // For parity with JavaScript, reindex the returned array + + + return scopedData.filter(function (datum) { + return truthy(apply(scopedLogic, datum)); + }); + } + + function map(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + + if (!isArray(scopedData)) { + return []; + } + + return scopedData.map(function (datum) { + return apply(scopedLogic, datum); + }); + } + + function none(apply, data, values) { + var filtered = apply({ + filter: values + }, data); + return filtered.length === 0; + } + + function reduce(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + var initial = typeof values[2] !== 'undefined' ? values[2] : null; + + if (!isArray(scopedData)) { + return initial; + } + + return scopedData.reduce(function (accumulator, current) { + return apply(scopedLogic, { + current: current, + accumulator: accumulator + }); + }, initial); + } + + function some(apply, data, values) { + var filtered = apply({ + filter: values + }, data); + return filtered.length > 0; + } + + function and(apply, data, values) { + var current; + + for (var i = 0; i < values.length; i++) { + current = apply(values[i], data); + + if (!truthy(current)) { + return current; + } + } + + return current; // Last + } + + function condition(apply, data, values) { + var i; + /* 'if' should be called with a odd number of parameters, 3 or greater + This works on the pattern: + if( 0 ){ 1 }else{ 2 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; + The implementation is: + For pairs of values (0,1 then 2,3 then 4,5 etc) + If the first evaluates truthy, evaluate and return the second + If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) + given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) + given 0 parameters, return NULL (not great practice, but there was no Else) + */ + + for (i = 0; i < values.length - 1; i += 2) { + if (truthy(apply(values[i], data))) { + return apply(values[i + 1], data); + } + } + + if (values.length === i + 1) { + return apply(values[i], data); + } + + return null; + } + + condition.code = ['if', '?:']; + + function or(apply, data, values) { + var current; + + for (var i = 0; i < values.length; i++) { + current = apply(values[i], data); + + if (truthy(current)) { + return current; + } + } + + return current; // Last + } + + + + var visitors = /*#__PURE__*/Object.freeze({ + all: all, + filter: filter, + map: map, + none: none, + reduce: reduce, + some: some, + and: and, + condition: condition, + or: or + }); + + function getValues(logic) { + return logic[getOperator(logic)]; + } + + /** + * Return an array that contains no duplicates (original not modified) + * @param {array} array Original reference array + * @return {array} New array with no duplicates + */ + function arrayUnique(array) { + var a = []; + + for (var i = 0, l = array.length; i < l; i++) { + if (a.indexOf(array[i]) === -1) { + a.push(array[i]); + } + } + + return a; + } + + function usesData(logic) { + var collection = []; + + if (isLogic(logic)) { + var op = getOperator(logic); + var values = logic[op]; + + if (!isArray(values)) { + values = [values]; + } + + if (op === 'var') { + // This doesn't cover the case where the arg to var is itself a rule. + collection.push(values[0]); + } else { + // Recursion! + values.forEach(function (val) { + collection.push.apply(collection, _toConsumableArray(usesData(val))); + }); + } + } + + return arrayUnique(collection); + } + + function ruleLike(rule, pattern) { + // console.log("Is ". JSON.stringify(rule) . " like " . JSON.stringify(pattern) . "?"); + if (pattern === rule) { + return true; + } // TODO : Deep object equivalency? + + + if (pattern === '@') { + return true; + } // Wildcard! + + + if (pattern === 'number') { + return typeof rule === 'number'; + } + + if (pattern === 'string') { + return typeof rule === 'string'; + } + + if (pattern === 'array') { + // !logic test might be superfluous in JavaScript + return isArray(rule) && !isLogic(rule); + } + + if (isLogic(pattern)) { + if (isLogic(rule)) { + var pattern_op = getOperator(pattern); + var rule_op = getOperator(rule); + + if (pattern_op === '@' || pattern_op === rule_op) { + // echo "\nOperators match, go deeper\n"; + return ruleLike(getValues(rule, false), getValues(pattern, false)); + } + } + + return false; // pattern is logic, rule isn't, can't be eq + } + + if (isArray(pattern)) { + if (isArray(rule)) { + if (pattern.length !== rule.length) { + return false; + } + /* + Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT) + */ + + + for (var i = 0; i < pattern.length; i += 1) { + // If any fail, we fail + if (!ruleLike(rule[i], pattern[i])) { + return false; + } + } + + return true; // If they *all* passed, we pass + } + + return false; // Pattern is array, rule isn't + } // Not logic, not array, not a === match for rule. + + + return false; + } + + var jsonLogic = createJsonLogic(operations, visitors); // restore original public API + + jsonLogic.is_logic = isLogic; + jsonLogic.truthy = truthy; + jsonLogic.get_operator = getOperator; + jsonLogic.get_values = getValues; + jsonLogic.uses_data = usesData; + jsonLogic.rule_like = ruleLike; + + return jsonLogic; + +})); +//# sourceMappingURL=jsonLogic.js.map diff --git a/dist/jsonLogic.js.map b/dist/jsonLogic.js.map new file mode 100644 index 0000000..24420b5 --- /dev/null +++ b/dist/jsonLogic.js.map @@ -0,0 +1 @@ +{"version":3,"file":"jsonLogic.js","sources":["../src/helpers/isArray.js","../src/helpers/isLogic.js","../src/helpers/getOperator.js","../src/createJsonLogic.js","../src/operations/accessor/variable.js","../src/operations/accessor/missing.js","../src/operations/accessor/missingSome.js","../src/operations/arithmetic/add.js","../src/operations/arithmetic/divide.js","../src/operations/arithmetic/modulo.js","../src/operations/arithmetic/multiply.js","../src/operations/arithmetic/substract.js","../src/operations/array/merge.js","../src/operations/array/index.js","../src/operations/logic/equal.js","../src/helpers/truthy.js","../src/operations/logic/falsy.js","../src/operations/logic/notEqual.js","../src/operations/logic/strictEqual.js","../src/operations/logic/strictNotEqual.js","../src/operations/logic/truthy.js","../src/operations/misc/indexOf.js","../src/operations/misc/log.js","../src/operations/misc/method.js","../src/operations/numeric/greater.js","../src/operations/numeric/greaterEqual.js","../src/operations/numeric/lower.js","../src/operations/numeric/lowerEqual.js","../src/operations/numeric/max.js","../src/operations/numeric/min.js","../src/operations/string/cat.js","../src/operations/string/substr.js","../src/visitors/array/all.js","../src/visitors/array/filter.js","../src/visitors/array/map.js","../src/visitors/array/none.js","../src/visitors/array/reduce.js","../src/visitors/array/some.js","../src/visitors/logic/and.js","../src/visitors/logic/condition.js","../src/visitors/logic/or.js","../src/helpers/getValues.js","../src/helpers/arrayUnique.js","../src/helpers/usesData.js","../src/helpers/ruleLike.js","../src/index.js"],"sourcesContent":["export default Array.isArray;\n","import isArray from './isArray';\n\nfunction isLogic(logic) {\n return (\n typeof logic === 'object' && // An object\n logic !== null && // but not null\n !isArray(logic) && // and not an array\n Object.keys(logic).length === 1 // with exactly one key\n );\n}\n\nexport default isLogic;\n","function getOperator(logic) {\n return Object.keys(logic)[0];\n}\n\nexport default getOperator;\n","import isArray from './helpers/isArray';\nimport isLogic from './helpers/isLogic';\nimport getOperator from './helpers/getOperator';\n\nfunction createJsonLogic(_operations, _visitors) {\n const operations = {};\n const visitors = {};\n\n if (_operations) {\n Object.keys(_operations).forEach(name => {\n const operation = _operations[name];\n\n addOperation(operation.code || name, operation);\n });\n }\n\n if (_visitors) {\n Object.keys(_visitors).forEach(name => {\n const visitor = _visitors[name];\n\n addVisitor(visitor.code || name, visitor);\n });\n }\n\n function addOperation(name, code) {\n operations[name] = code;\n }\n\n function removeOperation(name) {\n delete operations[name];\n }\n\n function addVisitor(name, code) {\n if (isArray(name)) {\n name.forEach(key => addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data = {}) {\n // Does this array contain logic? Only one way to find out.\n if (isArray(logic)) {\n return logic.map(l => apply(l, data));\n }\n // You've recursed to a primitive, stop!\n if (!isLogic(logic)) {\n return logic;\n }\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if (!isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(val => apply(val, data));\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if (typeof operator === 'function') {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }\n if (op.indexOf('.') > 0) {\n // Contains a dot, and not in the 0th position\n const sub_ops = String(op).split('.');\n let operation = operations;\n for (i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if (operation === undefined) {\n throw new Error(\n `Unrecognized operation ${op} (failed at ${sub_ops\n .slice(0, i + 1)\n .join('.')})`\n );\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(`Unrecognized operation ${op}`);\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","function variable(a, b) {\n const not_found = b === undefined ? null : b;\n let data = this;\n\n if (typeof a === 'undefined' || a === '' || a === null) {\n return data;\n }\n\n const sub_props = String(a).split('.');\n\n for (let i = 0; i < sub_props.length; i++) {\n if (data === null) {\n return not_found;\n }\n // Descending into data\n data = data[sub_props[i]];\n if (data === undefined) {\n return not_found;\n }\n }\n\n return data;\n}\n\nvariable.code = 'var';\n\nexport default variable;\n","import isArray from '../../helpers/isArray';\n\nfunction missing(apply, ...args) {\n /*\n Missing can receive many keys as many arguments, like {\"missing:[1,2]}\n Missing can also receive *one* argument that is an array of keys,\n which typically happens if it's actually acting on the output of another command\n (like 'if' or 'merge')\n */\n\n const are_missing = [];\n const keys = isArray(args[0]) ? args[0] : args;\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const value = apply({ var: key }, this);\n if (value === null || value === '') {\n are_missing.push(key);\n }\n }\n\n return are_missing;\n}\n\nmissing.withApply = true;\n\nexport default missing;\n","function missingSome(apply, need_count, options) {\n // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence.\n const are_missing = apply({ missing: options }, this);\n\n if (options.length - are_missing.length >= need_count) {\n return [];\n }\n return are_missing;\n}\n\nmissingSome.code = 'missing_some';\nmissingSome.withApply = true;\n\nexport default missingSome;\n","function add(...args) {\n return args.reduce((a, b) => parseFloat(a, 10) + parseFloat(b, 10), 0);\n}\n\nadd.code = '+';\n\nexport default add;\n","function divide(a, b) {\n return a / b;\n}\n\ndivide.code = '/';\n\nexport default divide;\n","function modulo(a, b) {\n return a % b;\n}\n\nmodulo.code = '%';\n\nexport default modulo;\n","function multiply(...args) {\n return args.reduce((a, b) => parseFloat(a, 10) * parseFloat(b, 10), 1);\n}\n\nmultiply.code = '*';\n\nexport default multiply;\n","function substract(a, b) {\n if (b === undefined) {\n return -a;\n }\n return a - b;\n}\n\nsubstract.code = '-';\n\nexport default substract;\n","function merge(...args) {\n return args.reduce((a, b) => a.concat(b), []);\n}\n\nexport default merge;\n","// eslint-disable-next-line import/prefer-default-export\nexport { default as merge } from './merge';\n","function equal(a, b) {\n // eslint-disable-next-line eqeqeq\n return a == b;\n}\n\nequal.code = '==';\n\nexport default equal;\n","import isArray from './isArray';\n\n/*\n This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer.\n\n Spec and rationale here: http://jsonlogic.com/truthy\n */\nfunction truthy(value) {\n if (isArray(value) && value.length === 0) {\n return false;\n }\n\n return !!value;\n}\n\nexport default truthy;\n","import truthy from '../../helpers/truthy';\n\nfunction falsy(a) {\n return !truthy(a);\n}\n\nfalsy.code = '!';\n\nexport default falsy;\n","function notEqual(a, b) {\n // eslint-disable-next-line eqeqeq\n return a != b;\n}\n\nnotEqual.code = '!=';\n\nexport default notEqual;\n","function strictEqual(a, b) {\n return a === b;\n}\n\nstrictEqual.code = '===';\n\nexport default strictEqual;\n","function strictNotEqual(a, b) {\n return a !== b;\n}\n\nstrictNotEqual.code = '!==';\n\nexport default strictNotEqual;\n","import truthy from '../../helpers/truthy';\n\ntruthy.code = '!!';\n\nexport default truthy;\n","function indexOf(a, b) {\n if (!b || typeof b.indexOf === 'undefined') return false;\n return b.indexOf(a) !== -1;\n}\n\nindexOf.code = 'in';\n\nexport default indexOf;\n","function log(a) {\n // eslint-disable-next-line no-console\n console.log(a);\n\n return a;\n}\n\nexport default log;\n","function method(obj, methodName, args) {\n // eslint-disable-next-line prefer-spread\n return obj[methodName].apply(obj, args);\n}\n\nexport default method;\n","function greater(a, b) {\n return a > b;\n}\n\ngreater.code = '>';\n\nexport default greater;\n","function greaterEqual(a, b) {\n return a >= b;\n}\n\ngreaterEqual.code = '>=';\n\nexport default greaterEqual;\n","function lower(a, b, c) {\n return c === undefined ? a < b : a < b && b < c;\n}\n\nlower.code = '<';\n\nexport default lower;\n","function lowerEqual(a, b, c) {\n return c === undefined ? a <= b : a <= b && b <= c;\n}\n\nlowerEqual.code = '<=';\n\nexport default lowerEqual;\n","function max(...args) {\n return Math.max(...args);\n}\n\nexport default max;\n","function min(...args) {\n return Math.min(...args);\n}\n\nexport default min;\n","function cat(...args) {\n return args.join('');\n}\n\nexport default cat;\n","function substr(source, start, end) {\n if (end < 0) {\n // JavaScript doesn't support negative end, this emulates PHP behavior\n const temp = String(source).substr(start);\n return temp.substr(0, temp.length + end);\n }\n return String(source).substr(start, end);\n}\n\nexport default substr;\n","import truthy from '../../helpers/truthy';\n\nfunction all(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n // All of an empty set is false. Note, some and none have correct fallback after the for loop\n if (!scopedData.length) {\n return false;\n }\n for (let i = 0; i < scopedData.length; i += 1) {\n if (!truthy(apply(scopedLogic, scopedData[i]))) {\n return false; // First falsy, short circuit\n }\n }\n return true; // All were truthy\n}\n\nexport default all;\n","import isArray from '../../helpers/isArray';\nimport truthy from '../../helpers/truthy';\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(datum => truthy(apply(scopedLogic, datum)));\n}\n\nexport default filter;\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(datum => apply(scopedLogic, datum));\n}\n\nexport default map;\n","function none(apply, data, values) {\n const filtered = apply({ filter: values }, data);\n\n return filtered.length === 0;\n}\n\nexport default none;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if (!isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(\n (accumulator, current) => apply(scopedLogic, { current, accumulator }),\n initial\n );\n}\n\nexport default reduce;\n","function some(apply, data, values) {\n const filtered = apply({ filter: values }, data);\n\n return filtered.length > 0;\n}\n\nexport default some;\n","import truthy from '../../helpers/truthy';\n\nfunction and(apply, data, values) {\n let current;\n\n for (let i = 0; i < values.length; i++) {\n current = apply(values[i], data);\n if (!truthy(current)) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default and;\n","import truthy from '../../helpers/truthy';\n\nfunction condition(apply, data, values) {\n let i;\n\n /* 'if' should be called with a odd number of parameters, 3 or greater\n This works on the pattern:\n if( 0 ){ 1 }else{ 2 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };\n\n The implementation is:\n For pairs of values (0,1 then 2,3 then 4,5 etc)\n If the first evaluates truthy, evaluate and return the second\n If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)\n given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)\n given 0 parameters, return NULL (not great practice, but there was no Else)\n */\n for (i = 0; i < values.length - 1; i += 2) {\n if (truthy(apply(values[i], data))) {\n return apply(values[i + 1], data);\n }\n }\n\n if (values.length === i + 1) {\n return apply(values[i], data);\n }\n\n return null;\n}\n\ncondition.code = ['if', '?:'];\n\nexport default condition;\n","import truthy from '../../helpers/truthy';\n\nfunction or(apply, data, values) {\n let current;\n\n for (let i = 0; i < values.length; i++) {\n current = apply(values[i], data);\n if (truthy(current)) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default or;\n","import getOperator from './getOperator';\n\nfunction getValues(logic) {\n return logic[getOperator(logic)];\n}\n\nexport default getValues;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i = 0, l = array.length; i < l; i++) {\n if (a.indexOf(array[i]) === -1) {\n a.push(array[i]);\n }\n }\n return a;\n}\n\nexport default arrayUnique;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport arrayUnique from './arrayUnique';\n\nfunction usesData(logic) {\n const collection = [];\n\n if (isLogic(logic)) {\n const op = getOperator(logic);\n let values = logic[op];\n\n if (!isArray(values)) {\n values = [values];\n }\n\n if (op === 'var') {\n // This doesn't cover the case where the arg to var is itself a rule.\n collection.push(values[0]);\n } else {\n // Recursion!\n values.forEach(val => {\n collection.push(...usesData(val));\n });\n }\n }\n\n return arrayUnique(collection);\n}\n\nexport default usesData;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport getValues from './getValues';\n\nfunction ruleLike(rule, pattern) {\n // console.log(\"Is \". JSON.stringify(rule) . \" like \" . JSON.stringify(pattern) . \"?\");\n if (pattern === rule) {\n return true;\n } // TODO : Deep object equivalency?\n if (pattern === '@') {\n return true;\n } // Wildcard!\n if (pattern === 'number') {\n return typeof rule === 'number';\n }\n if (pattern === 'string') {\n return typeof rule === 'string';\n }\n if (pattern === 'array') {\n // !logic test might be superfluous in JavaScript\n return isArray(rule) && !isLogic(rule);\n }\n\n if (isLogic(pattern)) {\n if (isLogic(rule)) {\n const pattern_op = getOperator(pattern);\n const rule_op = getOperator(rule);\n\n if (pattern_op === '@' || pattern_op === rule_op) {\n // echo \"\\nOperators match, go deeper\\n\";\n return ruleLike(getValues(rule, false), getValues(pattern, false));\n }\n }\n return false; // pattern is logic, rule isn't, can't be eq\n }\n\n if (isArray(pattern)) {\n if (isArray(rule)) {\n if (pattern.length !== rule.length) {\n return false;\n }\n /*\n Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT)\n */\n for (let i = 0; i < pattern.length; i += 1) {\n // If any fail, we fail\n if (!ruleLike(rule[i], pattern[i])) {\n return false;\n }\n }\n return true; // If they *all* passed, we pass\n }\n return false; // Pattern is array, rule isn't\n }\n\n // Not logic, not array, not a === match for rule.\n return false;\n}\n\nexport default ruleLike;\n","import createJsonLogic from './createJsonLogic';\nimport * as operations from './operations';\nimport * as visitors from './visitors';\nimport isLogic from './helpers/isLogic';\nimport truthy from './helpers/truthy';\nimport getOperator from './helpers/getOperator';\nimport getValues from './helpers/getValues';\nimport usesData from './helpers/usesData';\nimport ruleLike from './helpers/ruleLike';\n\nconst jsonLogic = createJsonLogic(operations, visitors);\n\n// restore original public API\njsonLogic.is_logic = isLogic;\njsonLogic.truthy = truthy;\njsonLogic.get_operator = getOperator;\njsonLogic.get_values = getValues;\njsonLogic.uses_data = usesData;\njsonLogic.rule_like = ruleLike;\n\nexport default jsonLogic;\n"],"names":["Array","isArray","isLogic","logic","Object","keys","length","getOperator","createJsonLogic","_operations","_visitors","operations","visitors","forEach","name","operation","addOperation","code","visitor","addVisitor","removeOperation","key","removeVisitor","apply","data","map","l","op","values","i","val","operator","withApply","unshift","indexOf","sub_ops","String","split","undefined","Error","slice","join","add_operation","rm_operation","add_visitor","rm_visitor","variable","a","b","not_found","sub_props","missing","are_missing","args","value","var","push","missingSome","need_count","options","add","reduce","parseFloat","divide","modulo","multiply","substract","merge","concat","equal","truthy","falsy","notEqual","strictEqual","strictNotEqual","log","console","method","obj","methodName","greater","greaterEqual","lower","c","lowerEqual","max","Math","min","cat","substr","source","start","end","temp","all","scopedData","scopedLogic","filter","datum","none","filtered","initial","accumulator","current","some","and","condition","or","getValues","arrayUnique","array","usesData","collection","ruleLike","rule","pattern","pattern_op","rule_op","jsonLogic","is_logic","get_operator","get_values","uses_data","rule_like"],"mappings":"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"} \ No newline at end of file diff --git a/dist/jsonLogic.min.js b/dist/jsonLogic.min.js new file mode 100644 index 0000000..b55b17c --- /dev/null +++ b/dist/jsonLogic.min.js @@ -0,0 +1,2 @@ +!(function(r,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(r=r||self).jsonLogic=n()})(this,(function(){"use strict";var d=Array.isArray;function n(r){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(r){return typeof r}:function(r){return r&&"function"==typeof Symbol&&r.constructor===Symbol&&r!==Symbol.prototype?"symbol":typeof r})(r)}function u(r){return (function(r){if(Array.isArray(r)){for(var n=0,t=new Array(r.length);n=n?[]:e}function o(){for(var r=arguments.length,n=new Array(r),t=0;t",A.code=">=",j.code="<",E.code="<=";var O=Object.freeze({variable:r,missing:t,missingSome:e,add:o,divide:i,modulo:f,multiply:c,substract:a,merge:function(){for(var r=arguments.length,n=new Array(r),t=0;t parseFloat(a, 10) + parseFloat(b, 10), 0);\n}\n\nadd.code = '+';\n\nexport default add;\n","function multiply(...args) {\n return args.reduce((a, b) => parseFloat(a, 10) * parseFloat(b, 10), 1);\n}\n\nmultiply.code = '*';\n\nexport default multiply;\n","function merge(...args) {\n return args.reduce((a, b) => a.concat(b), []);\n}\n\nexport default merge;\n","import isArray from './helpers/isArray';\nimport isLogic from './helpers/isLogic';\nimport getOperator from './helpers/getOperator';\n\nfunction createJsonLogic(_operations, _visitors) {\n const operations = {};\n const visitors = {};\n\n if (_operations) {\n Object.keys(_operations).forEach(name => {\n const operation = _operations[name];\n\n addOperation(operation.code || name, operation);\n });\n }\n\n if (_visitors) {\n Object.keys(_visitors).forEach(name => {\n const visitor = _visitors[name];\n\n addVisitor(visitor.code || name, visitor);\n });\n }\n\n function addOperation(name, code) {\n operations[name] = code;\n }\n\n function removeOperation(name) {\n delete operations[name];\n }\n\n function addVisitor(name, code) {\n if (isArray(name)) {\n name.forEach(key => addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data = {}) {\n // Does this array contain logic? Only one way to find out.\n if (isArray(logic)) {\n return logic.map(l => apply(l, data));\n }\n // You've recursed to a primitive, stop!\n if (!isLogic(logic)) {\n return logic;\n }\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if (!isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(val => apply(val, data));\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if (typeof operator === 'function') {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }\n if (op.indexOf('.') > 0) {\n // Contains a dot, and not in the 0th position\n const sub_ops = String(op).split('.');\n let operation = operations;\n for (i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if (operation === undefined) {\n throw new Error(\n `Unrecognized operation ${op} (failed at ${sub_ops\n .slice(0, i + 1)\n .join('.')})`\n );\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(`Unrecognized operation ${op}`);\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","import createJsonLogic from './createJsonLogic';\nimport * as operations from './operations';\nimport * as visitors from './visitors';\nimport isLogic from './helpers/isLogic';\nimport truthy from './helpers/truthy';\nimport getOperator from './helpers/getOperator';\nimport getValues from './helpers/getValues';\nimport usesData from './helpers/usesData';\nimport ruleLike from './helpers/ruleLike';\n\nconst jsonLogic = createJsonLogic(operations, visitors);\n\n// restore original public API\njsonLogic.is_logic = isLogic;\njsonLogic.truthy = truthy;\njsonLogic.get_operator = getOperator;\njsonLogic.get_values = getValues;\njsonLogic.uses_data = usesData;\njsonLogic.rule_like = ruleLike;\n\nexport default jsonLogic;\n","import isArray from '../../helpers/isArray';\nimport truthy from '../../helpers/truthy';\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(datum => truthy(apply(scopedLogic, datum)));\n}\n\nexport default filter;\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(datum => apply(scopedLogic, datum));\n}\n\nexport default map;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if (!isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(\n (accumulator, current) => apply(scopedLogic, { current, accumulator }),\n initial\n );\n}\n\nexport default reduce;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport arrayUnique from './arrayUnique';\n\nfunction usesData(logic) {\n const collection = [];\n\n if (isLogic(logic)) {\n const op = getOperator(logic);\n let values = logic[op];\n\n if (!isArray(values)) {\n values = [values];\n }\n\n if (op === 'var') {\n // This doesn't cover the case where the arg to var is itself a rule.\n collection.push(values[0]);\n } else {\n // Recursion!\n values.forEach(val => {\n collection.push(...usesData(val));\n });\n }\n }\n\n return arrayUnique(collection);\n}\n\nexport default usesData;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i = 0, l = array.length; i < l; i++) {\n if (a.indexOf(array[i]) === -1) {\n a.push(array[i]);\n }\n }\n return a;\n}\n\nexport default arrayUnique;\n"],"names":["createJsonLogic","arrayUnique"],"mappings":"69CACqB,CAAA,uNCAA,CAAA,08BCAA,CAAA,wvBCGrB,CAAA,kFA8BmB,CAAA,qEAzBkB,CAAA,mEAQF,CAAA,mJAoCZ,CAAA,uIAsBC,CAAA,igBCjENA,CAAAA,sNCGS,CAAA,iGCHH,CAAA,sLCEpB,CAAA,ydCSiB,CAAA,8CChBrB,CAAA,4FDsBSC,CAAAA"} \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100755 index 75c7add..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,18 +0,0 @@ -var gulp = require("gulp"); -var exec = require("child_process").exec; - -gulp.task("test", function(cb) { - exec( - "node testrunner.js", - {cwd: "tests"}, - function(err, stdout, stderr) { - console.log(stdout); - console.log(stderr); - cb(err); - } - ); -}); - -gulp.task("default", function() { - gulp.watch(["**/*.js", "tests/tests.json"], ["test"]); -}); diff --git a/lib/createJsonLogic.js b/lib/createJsonLogic.js new file mode 100644 index 0000000..9c44581 --- /dev/null +++ b/lib/createJsonLogic.js @@ -0,0 +1,125 @@ +import isArray from './helpers/isArray'; +import isLogic from './helpers/isLogic'; +import getOperator from './helpers/getOperator'; + +function createJsonLogic(_operations, _visitors) { + var operations = {}; + var visitors = {}; + + if (_operations) { + Object.keys(_operations).forEach(function (name) { + var operation = _operations[name]; + addOperation(operation.code || name, operation); + }); + } + + if (_visitors) { + Object.keys(_visitors).forEach(function (name) { + var visitor = _visitors[name]; + addVisitor(visitor.code || name, visitor); + }); + } + + function addOperation(name, code) { + operations[name] = code; + } + + function removeOperation(name) { + delete operations[name]; + } + + function addVisitor(name, code) { + if (isArray(name)) { + name.forEach(function (key) { + return addVisitor(key, code); + }); + return; + } + + visitors[name] = code; + } + + function removeVisitor(name) { + if (isArray(name)) { + name.forEach(removeVisitor); + return; + } + + delete visitors[name]; + } + + function apply(logic) { + var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + // Does this array contain logic? Only one way to find out. + if (isArray(logic)) { + return logic.map(function (l) { + return apply(l, data); + }); + } // You've recursed to a primitive, stop! + + + if (!isLogic(logic)) { + return logic; + } + + var op = getOperator(logic); + var values = logic[op]; + var i; // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} + + if (!isArray(values)) { + values = [values]; + } // apply matching visitors first + + + if (typeof visitors[op] === 'function') { + return visitors[op](apply, data, values); + } // Everyone else gets immediate depth-first recursion + + + values = values.map(function (val) { + return apply(val, data); + }); // The operation is called with "data" bound to its "this" and "values" passed as arguments. + // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments + + var operator = operations[op]; + + if (typeof operator === 'function') { + if (operator.withApply) { + values.unshift(apply); + } + + return operator.apply(data, values); + } + + if (op.indexOf('.') > 0) { + // Contains a dot, and not in the 0th position + var sub_ops = String(op).split('.'); + var operation = operations; + + for (i = 0; i < sub_ops.length; i++) { + // Descending into operations + operation = operation[sub_ops[i]]; + + if (operation === undefined) { + throw new Error("Unrecognized operation ".concat(op, " (failed at ").concat(sub_ops.slice(0, i + 1).join('.'), ")")); + } + } + + return operation.apply(data, values); + } + + throw new Error("Unrecognized operation ".concat(op)); + } + + return { + apply: apply, + add_operation: addOperation, + rm_operation: removeOperation, + add_visitor: addVisitor, + rm_visitor: removeVisitor + }; +} + +export default createJsonLogic; \ No newline at end of file diff --git a/lib/helpers/arrayUnique.js b/lib/helpers/arrayUnique.js new file mode 100644 index 0000000..49bc327 --- /dev/null +++ b/lib/helpers/arrayUnique.js @@ -0,0 +1,18 @@ +/** + * Return an array that contains no duplicates (original not modified) + * @param {array} array Original reference array + * @return {array} New array with no duplicates + */ +function arrayUnique(array) { + var a = []; + + for (var i = 0, l = array.length; i < l; i++) { + if (a.indexOf(array[i]) === -1) { + a.push(array[i]); + } + } + + return a; +} + +export default arrayUnique; \ No newline at end of file diff --git a/lib/helpers/getOperator.js b/lib/helpers/getOperator.js new file mode 100644 index 0000000..994698f --- /dev/null +++ b/lib/helpers/getOperator.js @@ -0,0 +1,5 @@ +function getOperator(logic) { + return Object.keys(logic)[0]; +} + +export default getOperator; \ No newline at end of file diff --git a/lib/helpers/getValues.js b/lib/helpers/getValues.js new file mode 100644 index 0000000..c6d9040 --- /dev/null +++ b/lib/helpers/getValues.js @@ -0,0 +1,7 @@ +import getOperator from './getOperator'; + +function getValues(logic) { + return logic[getOperator(logic)]; +} + +export default getValues; \ No newline at end of file diff --git a/lib/helpers/isArray.js b/lib/helpers/isArray.js new file mode 100644 index 0000000..088ca26 --- /dev/null +++ b/lib/helpers/isArray.js @@ -0,0 +1 @@ +export default Array.isArray; \ No newline at end of file diff --git a/lib/helpers/isLogic.js b/lib/helpers/isLogic.js new file mode 100644 index 0000000..e1868f6 --- /dev/null +++ b/lib/helpers/isLogic.js @@ -0,0 +1,13 @@ +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +import isArray from './isArray'; + +function isLogic(logic) { + return _typeof(logic) === 'object' && // An object + logic !== null && // but not null + !isArray(logic) && // and not an array + Object.keys(logic).length === 1 // with exactly one key + ; +} + +export default isLogic; \ No newline at end of file diff --git a/lib/helpers/ruleLike.js b/lib/helpers/ruleLike.js new file mode 100644 index 0000000..2b7e589 --- /dev/null +++ b/lib/helpers/ruleLike.js @@ -0,0 +1,72 @@ +import isArray from './isArray'; +import isLogic from './isLogic'; +import getOperator from './getOperator'; +import getValues from './getValues'; + +function ruleLike(rule, pattern) { + // console.log("Is ". JSON.stringify(rule) . " like " . JSON.stringify(pattern) . "?"); + if (pattern === rule) { + return true; + } // TODO : Deep object equivalency? + + + if (pattern === '@') { + return true; + } // Wildcard! + + + if (pattern === 'number') { + return typeof rule === 'number'; + } + + if (pattern === 'string') { + return typeof rule === 'string'; + } + + if (pattern === 'array') { + // !logic test might be superfluous in JavaScript + return isArray(rule) && !isLogic(rule); + } + + if (isLogic(pattern)) { + if (isLogic(rule)) { + var pattern_op = getOperator(pattern); + var rule_op = getOperator(rule); + + if (pattern_op === '@' || pattern_op === rule_op) { + // echo "\nOperators match, go deeper\n"; + return ruleLike(getValues(rule, false), getValues(pattern, false)); + } + } + + return false; // pattern is logic, rule isn't, can't be eq + } + + if (isArray(pattern)) { + if (isArray(rule)) { + if (pattern.length !== rule.length) { + return false; + } + /* + Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT) + */ + + + for (var i = 0; i < pattern.length; i += 1) { + // If any fail, we fail + if (!ruleLike(rule[i], pattern[i])) { + return false; + } + } + + return true; // If they *all* passed, we pass + } + + return false; // Pattern is array, rule isn't + } // Not logic, not array, not a === match for rule. + + + return false; +} + +export default ruleLike; \ No newline at end of file diff --git a/lib/helpers/truthy.js b/lib/helpers/truthy.js new file mode 100644 index 0000000..2105689 --- /dev/null +++ b/lib/helpers/truthy.js @@ -0,0 +1,16 @@ +import isArray from './isArray'; +/* + This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. + + Spec and rationale here: http://jsonlogic.com/truthy + */ + +function truthy(value) { + if (isArray(value) && value.length === 0) { + return false; + } + + return !!value; +} + +export default truthy; \ No newline at end of file diff --git a/lib/helpers/usesData.js b/lib/helpers/usesData.js new file mode 100644 index 0000000..a212e6f --- /dev/null +++ b/lib/helpers/usesData.js @@ -0,0 +1,39 @@ +function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } + +function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } + +function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } + +function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } + +import isArray from './isArray'; +import isLogic from './isLogic'; +import getOperator from './getOperator'; +import arrayUnique from './arrayUnique'; + +function usesData(logic) { + var collection = []; + + if (isLogic(logic)) { + var op = getOperator(logic); + var values = logic[op]; + + if (!isArray(values)) { + values = [values]; + } + + if (op === 'var') { + // This doesn't cover the case where the arg to var is itself a rule. + collection.push(values[0]); + } else { + // Recursion! + values.forEach(function (val) { + collection.push.apply(collection, _toConsumableArray(usesData(val))); + }); + } + } + + return arrayUnique(collection); +} + +export default usesData; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..f6eeca1 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,18 @@ +import createJsonLogic from './createJsonLogic'; +import * as operations from './operations'; +import * as visitors from './visitors'; +import isLogic from './helpers/isLogic'; +import truthy from './helpers/truthy'; +import getOperator from './helpers/getOperator'; +import getValues from './helpers/getValues'; +import usesData from './helpers/usesData'; +import ruleLike from './helpers/ruleLike'; +var jsonLogic = createJsonLogic(operations, visitors); // restore original public API + +jsonLogic.is_logic = isLogic; +jsonLogic.truthy = truthy; +jsonLogic.get_operator = getOperator; +jsonLogic.get_values = getValues; +jsonLogic.uses_data = usesData; +jsonLogic.rule_like = ruleLike; +export default jsonLogic; \ No newline at end of file diff --git a/lib/operations/accessor/index.js b/lib/operations/accessor/index.js new file mode 100644 index 0000000..dcf58ba --- /dev/null +++ b/lib/operations/accessor/index.js @@ -0,0 +1,3 @@ +export { default as variable } from './variable'; +export { default as missing } from './missing'; +export { default as missingSome } from './missingSome'; \ No newline at end of file diff --git a/lib/operations/accessor/missing.js b/lib/operations/accessor/missing.js new file mode 100644 index 0000000..6b9366e --- /dev/null +++ b/lib/operations/accessor/missing.js @@ -0,0 +1,33 @@ +import isArray from '../../helpers/isArray'; + +function missing(apply) { + /* + Missing can receive many keys as many arguments, like {"missing:[1,2]} + Missing can also receive *one* argument that is an array of keys, + which typically happens if it's actually acting on the output of another command + (like 'if' or 'merge') + */ + var are_missing = []; + + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + var keys = isArray(args[0]) ? args[0] : args; + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = apply({ + var: key + }, this); + + if (value === null || value === '') { + are_missing.push(key); + } + } + + return are_missing; +} + +missing.withApply = true; +export default missing; \ No newline at end of file diff --git a/lib/operations/accessor/missingSome.js b/lib/operations/accessor/missingSome.js new file mode 100644 index 0000000..391f0c6 --- /dev/null +++ b/lib/operations/accessor/missingSome.js @@ -0,0 +1,16 @@ +function missingSome(apply, need_count, options) { + // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. + var are_missing = apply({ + missing: options + }, this); + + if (options.length - are_missing.length >= need_count) { + return []; + } + + return are_missing; +} + +missingSome.code = 'missing_some'; +missingSome.withApply = true; +export default missingSome; \ No newline at end of file diff --git a/lib/operations/accessor/variable.js b/lib/operations/accessor/variable.js new file mode 100644 index 0000000..26adb22 --- /dev/null +++ b/lib/operations/accessor/variable.js @@ -0,0 +1,28 @@ +function variable(a, b) { + var not_found = b === undefined ? null : b; + var data = this; + + if (typeof a === 'undefined' || a === '' || a === null) { + return data; + } + + var sub_props = String(a).split('.'); + + for (var i = 0; i < sub_props.length; i++) { + if (data === null) { + return not_found; + } // Descending into data + + + data = data[sub_props[i]]; + + if (data === undefined) { + return not_found; + } + } + + return data; +} + +variable.code = 'var'; +export default variable; \ No newline at end of file diff --git a/lib/operations/arithmetic/add.js b/lib/operations/arithmetic/add.js new file mode 100644 index 0000000..fa7661a --- /dev/null +++ b/lib/operations/arithmetic/add.js @@ -0,0 +1,12 @@ +function add() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return parseFloat(a, 10) + parseFloat(b, 10); + }, 0); +} + +add.code = '+'; +export default add; \ No newline at end of file diff --git a/lib/operations/arithmetic/divide.js b/lib/operations/arithmetic/divide.js new file mode 100644 index 0000000..c279ad4 --- /dev/null +++ b/lib/operations/arithmetic/divide.js @@ -0,0 +1,6 @@ +function divide(a, b) { + return a / b; +} + +divide.code = '/'; +export default divide; \ No newline at end of file diff --git a/lib/operations/arithmetic/index.js b/lib/operations/arithmetic/index.js new file mode 100644 index 0000000..fe1093b --- /dev/null +++ b/lib/operations/arithmetic/index.js @@ -0,0 +1,5 @@ +export { default as add } from './add'; +export { default as divide } from './divide'; +export { default as modulo } from './modulo'; +export { default as multiply } from './multiply'; +export { default as substract } from './substract'; \ No newline at end of file diff --git a/lib/operations/arithmetic/modulo.js b/lib/operations/arithmetic/modulo.js new file mode 100644 index 0000000..6cb0204 --- /dev/null +++ b/lib/operations/arithmetic/modulo.js @@ -0,0 +1,6 @@ +function modulo(a, b) { + return a % b; +} + +modulo.code = '%'; +export default modulo; \ No newline at end of file diff --git a/lib/operations/arithmetic/multiply.js b/lib/operations/arithmetic/multiply.js new file mode 100644 index 0000000..9c12558 --- /dev/null +++ b/lib/operations/arithmetic/multiply.js @@ -0,0 +1,12 @@ +function multiply() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return parseFloat(a, 10) * parseFloat(b, 10); + }, 1); +} + +multiply.code = '*'; +export default multiply; \ No newline at end of file diff --git a/lib/operations/arithmetic/substract.js b/lib/operations/arithmetic/substract.js new file mode 100644 index 0000000..a6a5bc2 --- /dev/null +++ b/lib/operations/arithmetic/substract.js @@ -0,0 +1,10 @@ +function substract(a, b) { + if (b === undefined) { + return -a; + } + + return a - b; +} + +substract.code = '-'; +export default substract; \ No newline at end of file diff --git a/lib/operations/array/index.js b/lib/operations/array/index.js new file mode 100644 index 0000000..f4bf94f --- /dev/null +++ b/lib/operations/array/index.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export { default as merge } from './merge'; \ No newline at end of file diff --git a/lib/operations/array/merge.js b/lib/operations/array/merge.js new file mode 100644 index 0000000..2b3f749 --- /dev/null +++ b/lib/operations/array/merge.js @@ -0,0 +1,11 @@ +function merge() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return a.concat(b); + }, []); +} + +export default merge; \ No newline at end of file diff --git a/lib/operations/index.js b/lib/operations/index.js new file mode 100644 index 0000000..34f019c --- /dev/null +++ b/lib/operations/index.js @@ -0,0 +1,7 @@ +export * from './accessor'; +export * from './arithmetic'; +export * from './array'; +export * from './logic'; +export * from './misc'; +export * from './numeric'; +export * from './string'; \ No newline at end of file diff --git a/lib/operations/logic/equal.js b/lib/operations/logic/equal.js new file mode 100644 index 0000000..1330968 --- /dev/null +++ b/lib/operations/logic/equal.js @@ -0,0 +1,7 @@ +function equal(a, b) { + // eslint-disable-next-line eqeqeq + return a == b; +} + +equal.code = '=='; +export default equal; \ No newline at end of file diff --git a/lib/operations/logic/falsy.js b/lib/operations/logic/falsy.js new file mode 100644 index 0000000..667df45 --- /dev/null +++ b/lib/operations/logic/falsy.js @@ -0,0 +1,8 @@ +import truthy from '../../helpers/truthy'; + +function falsy(a) { + return !truthy(a); +} + +falsy.code = '!'; +export default falsy; \ No newline at end of file diff --git a/lib/operations/logic/index.js b/lib/operations/logic/index.js new file mode 100644 index 0000000..38ecd86 --- /dev/null +++ b/lib/operations/logic/index.js @@ -0,0 +1,6 @@ +export { default as equal } from './equal'; +export { default as falsy } from './falsy'; +export { default as notEqual } from './notEqual'; +export { default as strictEqual } from './strictEqual'; +export { default as strictNotEqual } from './strictNotEqual'; +export { default as truthy } from './truthy'; \ No newline at end of file diff --git a/lib/operations/logic/notEqual.js b/lib/operations/logic/notEqual.js new file mode 100644 index 0000000..4889718 --- /dev/null +++ b/lib/operations/logic/notEqual.js @@ -0,0 +1,7 @@ +function notEqual(a, b) { + // eslint-disable-next-line eqeqeq + return a != b; +} + +notEqual.code = '!='; +export default notEqual; \ No newline at end of file diff --git a/lib/operations/logic/strictEqual.js b/lib/operations/logic/strictEqual.js new file mode 100644 index 0000000..e2e9c9f --- /dev/null +++ b/lib/operations/logic/strictEqual.js @@ -0,0 +1,6 @@ +function strictEqual(a, b) { + return a === b; +} + +strictEqual.code = '==='; +export default strictEqual; \ No newline at end of file diff --git a/lib/operations/logic/strictNotEqual.js b/lib/operations/logic/strictNotEqual.js new file mode 100644 index 0000000..9edaf8a --- /dev/null +++ b/lib/operations/logic/strictNotEqual.js @@ -0,0 +1,6 @@ +function strictNotEqual(a, b) { + return a !== b; +} + +strictNotEqual.code = '!=='; +export default strictNotEqual; \ No newline at end of file diff --git a/lib/operations/logic/truthy.js b/lib/operations/logic/truthy.js new file mode 100644 index 0000000..ab17b82 --- /dev/null +++ b/lib/operations/logic/truthy.js @@ -0,0 +1,3 @@ +import truthy from '../../helpers/truthy'; +truthy.code = '!!'; +export default truthy; \ No newline at end of file diff --git a/lib/operations/misc/index.js b/lib/operations/misc/index.js new file mode 100644 index 0000000..ac367e8 --- /dev/null +++ b/lib/operations/misc/index.js @@ -0,0 +1,3 @@ +export { default as indexOf } from './indexOf'; +export { default as log } from './log'; +export { default as method } from './method'; \ No newline at end of file diff --git a/lib/operations/misc/indexOf.js b/lib/operations/misc/indexOf.js new file mode 100644 index 0000000..c55b4f9 --- /dev/null +++ b/lib/operations/misc/indexOf.js @@ -0,0 +1,7 @@ +function indexOf(a, b) { + if (!b || typeof b.indexOf === 'undefined') return false; + return b.indexOf(a) !== -1; +} + +indexOf.code = 'in'; +export default indexOf; \ No newline at end of file diff --git a/lib/operations/misc/log.js b/lib/operations/misc/log.js new file mode 100644 index 0000000..8c0b316 --- /dev/null +++ b/lib/operations/misc/log.js @@ -0,0 +1,7 @@ +function log(a) { + // eslint-disable-next-line no-console + console.log(a); + return a; +} + +export default log; \ No newline at end of file diff --git a/lib/operations/misc/method.js b/lib/operations/misc/method.js new file mode 100644 index 0000000..c3aac36 --- /dev/null +++ b/lib/operations/misc/method.js @@ -0,0 +1,6 @@ +function method(obj, methodName, args) { + // eslint-disable-next-line prefer-spread + return obj[methodName].apply(obj, args); +} + +export default method; \ No newline at end of file diff --git a/lib/operations/numeric/greater.js b/lib/operations/numeric/greater.js new file mode 100644 index 0000000..190710b --- /dev/null +++ b/lib/operations/numeric/greater.js @@ -0,0 +1,6 @@ +function greater(a, b) { + return a > b; +} + +greater.code = '>'; +export default greater; \ No newline at end of file diff --git a/lib/operations/numeric/greaterEqual.js b/lib/operations/numeric/greaterEqual.js new file mode 100644 index 0000000..825ed90 --- /dev/null +++ b/lib/operations/numeric/greaterEqual.js @@ -0,0 +1,6 @@ +function greaterEqual(a, b) { + return a >= b; +} + +greaterEqual.code = '>='; +export default greaterEqual; \ No newline at end of file diff --git a/lib/operations/numeric/index.js b/lib/operations/numeric/index.js new file mode 100644 index 0000000..23f69df --- /dev/null +++ b/lib/operations/numeric/index.js @@ -0,0 +1,6 @@ +export { default as greater } from './greater'; +export { default as greaterEqual } from './greaterEqual'; +export { default as lower } from './lower'; +export { default as lowerEqual } from './lowerEqual'; +export { default as max } from './max'; +export { default as min } from './min'; \ No newline at end of file diff --git a/lib/operations/numeric/lower.js b/lib/operations/numeric/lower.js new file mode 100644 index 0000000..37f9172 --- /dev/null +++ b/lib/operations/numeric/lower.js @@ -0,0 +1,6 @@ +function lower(a, b, c) { + return c === undefined ? a < b : a < b && b < c; +} + +lower.code = '<'; +export default lower; \ No newline at end of file diff --git a/lib/operations/numeric/lowerEqual.js b/lib/operations/numeric/lowerEqual.js new file mode 100644 index 0000000..0308b0f --- /dev/null +++ b/lib/operations/numeric/lowerEqual.js @@ -0,0 +1,6 @@ +function lowerEqual(a, b, c) { + return c === undefined ? a <= b : a <= b && b <= c; +} + +lowerEqual.code = '<='; +export default lowerEqual; \ No newline at end of file diff --git a/lib/operations/numeric/max.js b/lib/operations/numeric/max.js new file mode 100644 index 0000000..2d104aa --- /dev/null +++ b/lib/operations/numeric/max.js @@ -0,0 +1,5 @@ +function max() { + return Math.max.apply(Math, arguments); +} + +export default max; \ No newline at end of file diff --git a/lib/operations/numeric/min.js b/lib/operations/numeric/min.js new file mode 100644 index 0000000..5749f36 --- /dev/null +++ b/lib/operations/numeric/min.js @@ -0,0 +1,5 @@ +function min() { + return Math.min.apply(Math, arguments); +} + +export default min; \ No newline at end of file diff --git a/lib/operations/string/cat.js b/lib/operations/string/cat.js new file mode 100644 index 0000000..baef7f3 --- /dev/null +++ b/lib/operations/string/cat.js @@ -0,0 +1,9 @@ +function cat() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.join(''); +} + +export default cat; \ No newline at end of file diff --git a/lib/operations/string/index.js b/lib/operations/string/index.js new file mode 100644 index 0000000..b0c996d --- /dev/null +++ b/lib/operations/string/index.js @@ -0,0 +1,2 @@ +export { default as cat } from './cat'; +export { default as substr } from './substr'; \ No newline at end of file diff --git a/lib/operations/string/substr.js b/lib/operations/string/substr.js new file mode 100644 index 0000000..d1a6c0d --- /dev/null +++ b/lib/operations/string/substr.js @@ -0,0 +1,11 @@ +function substr(source, start, end) { + if (end < 0) { + // JavaScript doesn't support negative end, this emulates PHP behavior + var temp = String(source).substr(start); + return temp.substr(0, temp.length + end); + } + + return String(source).substr(start, end); +} + +export default substr; \ No newline at end of file diff --git a/lib/visitors/array/all.js b/lib/visitors/array/all.js new file mode 100644 index 0000000..cf72795 --- /dev/null +++ b/lib/visitors/array/all.js @@ -0,0 +1,20 @@ +import truthy from '../../helpers/truthy'; + +function all(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; // All of an empty set is false. Note, some and none have correct fallback after the for loop + + if (!scopedData.length) { + return false; + } + + for (var i = 0; i < scopedData.length; i += 1) { + if (!truthy(apply(scopedLogic, scopedData[i]))) { + return false; // First falsy, short circuit + } + } + + return true; // All were truthy +} + +export default all; \ No newline at end of file diff --git a/lib/visitors/array/filter.js b/lib/visitors/array/filter.js new file mode 100644 index 0000000..00b458d --- /dev/null +++ b/lib/visitors/array/filter.js @@ -0,0 +1,20 @@ +import isArray from '../../helpers/isArray'; +import truthy from '../../helpers/truthy'; + +function filter(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + + if (!isArray(scopedData)) { + return []; + } // Return only the elements from the array in the first argument, + // that return truthy when passed to the logic in the second argument. + // For parity with JavaScript, reindex the returned array + + + return scopedData.filter(function (datum) { + return truthy(apply(scopedLogic, datum)); + }); +} + +export default filter; \ No newline at end of file diff --git a/lib/visitors/array/index.js b/lib/visitors/array/index.js new file mode 100644 index 0000000..6e5d691 --- /dev/null +++ b/lib/visitors/array/index.js @@ -0,0 +1,6 @@ +export { default as all } from './all'; +export { default as filter } from './filter'; +export { default as map } from './map'; +export { default as none } from './none'; +export { default as reduce } from './reduce'; +export { default as some } from './some'; \ No newline at end of file diff --git a/lib/visitors/array/map.js b/lib/visitors/array/map.js new file mode 100644 index 0000000..784b662 --- /dev/null +++ b/lib/visitors/array/map.js @@ -0,0 +1,16 @@ +import isArray from '../../helpers/isArray'; + +function map(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + + if (!isArray(scopedData)) { + return []; + } + + return scopedData.map(function (datum) { + return apply(scopedLogic, datum); + }); +} + +export default map; \ No newline at end of file diff --git a/lib/visitors/array/none.js b/lib/visitors/array/none.js new file mode 100644 index 0000000..718fa9a --- /dev/null +++ b/lib/visitors/array/none.js @@ -0,0 +1,8 @@ +function none(apply, data, values) { + var filtered = apply({ + filter: values + }, data); + return filtered.length === 0; +} + +export default none; \ No newline at end of file diff --git a/lib/visitors/array/reduce.js b/lib/visitors/array/reduce.js new file mode 100644 index 0000000..bc14848 --- /dev/null +++ b/lib/visitors/array/reduce.js @@ -0,0 +1,20 @@ +import isArray from '../../helpers/isArray'; + +function reduce(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + var initial = typeof values[2] !== 'undefined' ? values[2] : null; + + if (!isArray(scopedData)) { + return initial; + } + + return scopedData.reduce(function (accumulator, current) { + return apply(scopedLogic, { + current: current, + accumulator: accumulator + }); + }, initial); +} + +export default reduce; \ No newline at end of file diff --git a/lib/visitors/array/some.js b/lib/visitors/array/some.js new file mode 100644 index 0000000..302df02 --- /dev/null +++ b/lib/visitors/array/some.js @@ -0,0 +1,8 @@ +function some(apply, data, values) { + var filtered = apply({ + filter: values + }, data); + return filtered.length > 0; +} + +export default some; \ No newline at end of file diff --git a/lib/visitors/index.js b/lib/visitors/index.js new file mode 100644 index 0000000..93efdf6 --- /dev/null +++ b/lib/visitors/index.js @@ -0,0 +1,2 @@ +export * from './array'; +export * from './logic'; \ No newline at end of file diff --git a/lib/visitors/logic/and.js b/lib/visitors/logic/and.js new file mode 100644 index 0000000..b79efde --- /dev/null +++ b/lib/visitors/logic/and.js @@ -0,0 +1,17 @@ +import truthy from '../../helpers/truthy'; + +function and(apply, data, values) { + var current; + + for (var i = 0; i < values.length; i++) { + current = apply(values[i], data); + + if (!truthy(current)) { + return current; + } + } + + return current; // Last +} + +export default and; \ No newline at end of file diff --git a/lib/visitors/logic/condition.js b/lib/visitors/logic/condition.js new file mode 100644 index 0000000..3581211 --- /dev/null +++ b/lib/visitors/logic/condition.js @@ -0,0 +1,32 @@ +import truthy from '../../helpers/truthy'; + +function condition(apply, data, values) { + var i; + /* 'if' should be called with a odd number of parameters, 3 or greater + This works on the pattern: + if( 0 ){ 1 }else{ 2 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; + The implementation is: + For pairs of values (0,1 then 2,3 then 4,5 etc) + If the first evaluates truthy, evaluate and return the second + If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) + given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) + given 0 parameters, return NULL (not great practice, but there was no Else) + */ + + for (i = 0; i < values.length - 1; i += 2) { + if (truthy(apply(values[i], data))) { + return apply(values[i + 1], data); + } + } + + if (values.length === i + 1) { + return apply(values[i], data); + } + + return null; +} + +condition.code = ['if', '?:']; +export default condition; \ No newline at end of file diff --git a/lib/visitors/logic/index.js b/lib/visitors/logic/index.js new file mode 100644 index 0000000..495b2bc --- /dev/null +++ b/lib/visitors/logic/index.js @@ -0,0 +1,3 @@ +export { default as and } from './and'; +export { default as condition } from './condition'; +export { default as or } from './or'; \ No newline at end of file diff --git a/lib/visitors/logic/or.js b/lib/visitors/logic/or.js new file mode 100644 index 0000000..f852823 --- /dev/null +++ b/lib/visitors/logic/or.js @@ -0,0 +1,17 @@ +import truthy from '../../helpers/truthy'; + +function or(apply, data, values) { + var current; + + for (var i = 0; i < values.length; i++) { + current = apply(values[i], data); + + if (truthy(current)) { + return current; + } + } + + return current; // Last +} + +export default or; \ No newline at end of file diff --git a/logic.js b/logic.js deleted file mode 100644 index 6b827df..0000000 --- a/logic.js +++ /dev/null @@ -1,464 +0,0 @@ -/* globals define,module */ -/* -Using a Universal Module Loader that should be browser, require, and AMD friendly -http://ricostacruz.com/cheatsheets/umdjs.html -*/ -;(function(root, factory) { - if (typeof define === "function" && define.amd) { - define(factory); - } else if (typeof exports === "object") { - module.exports = factory(); - } else { - root.jsonLogic = factory(); - } -}(this, function() { - "use strict"; - /* globals console:false */ - - if ( ! Array.isArray) { - Array.isArray = function(arg) { - return Object.prototype.toString.call(arg) === "[object Array]"; - }; - } - - /** - * Return an array that contains no duplicates (original not modified) - * @param {array} array Original reference array - * @return {array} New array with no duplicates - */ - function arrayUnique(array) { - var a = []; - for (var i=0, l=array.length; i": function(a, b) { - return a > b; - }, - ">=": function(a, b) { - return a >= b; - }, - "<": function(a, b, c) { - return (c === undefined) ? a < b : (a < b) && (b < c); - }, - "<=": function(a, b, c) { - return (c === undefined) ? a <= b : (a <= b) && (b <= c); - }, - "!!": function(a) { - return jsonLogic.truthy(a); - }, - "!": function(a) { - return !jsonLogic.truthy(a); - }, - "%": function(a, b) { - return a % b; - }, - "log": function(a) { - console.log(a); return a; - }, - "in": function(a, b) { - if(!b || typeof b.indexOf === "undefined") return false; - return (b.indexOf(a) !== -1); - }, - "cat": function() { - return Array.prototype.join.call(arguments, ""); - }, - "substr":function(source, start, end) { - if(end < 0){ - // JavaScript doesn't support negative end, this emulates PHP behavior - var temp = String(source).substr(start); - return temp.substr(0, temp.length + end); - } - return String(source).substr(start, end); - }, - "+": function() { - return Array.prototype.reduce.call(arguments, function(a, b) { - return parseFloat(a, 10) + parseFloat(b, 10); - }, 0); - }, - "*": function() { - return Array.prototype.reduce.call(arguments, function(a, b) { - return parseFloat(a, 10) * parseFloat(b, 10); - }); - }, - "-": function(a, b) { - if(b === undefined) { - return -a; - }else{ - return a - b; - } - }, - "/": function(a, b) { - return a / b; - }, - "min": function() { - return Math.min.apply(this, arguments); - }, - "max": function() { - return Math.max.apply(this, arguments); - }, - "merge": function() { - return Array.prototype.reduce.call(arguments, function(a, b) { - return a.concat(b); - }, []); - }, - "var": function(a, b) { - var not_found = (b === undefined) ? null : b; - var data = this; - if(typeof a === "undefined" || a==="" || a===null) { - return data; - } - var sub_props = String(a).split("."); - for(var i = 0; i < sub_props.length; i++) { - if(data === null) { - return not_found; - } - // Descending into data - data = data[sub_props[i]]; - if(data === undefined) { - return not_found; - } - } - return data; - }, - "missing": function() { - /* - Missing can receive many keys as many arguments, like {"missing:[1,2]} - Missing can also receive *one* argument that is an array of keys, - which typically happens if it's actually acting on the output of another command - (like 'if' or 'merge') - */ - - var missing = []; - var keys = Array.isArray(arguments[0]) ? arguments[0] : arguments; - - for(var i = 0; i < keys.length; i++) { - var key = keys[i]; - var value = jsonLogic.apply({"var": key}, this); - if(value === null || value === "") { - missing.push(key); - } - } - - return missing; - }, - "missing_some": function(need_count, options) { - // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. - var are_missing = jsonLogic.apply({"missing": options}, this); - - if(options.length - are_missing.length >= need_count) { - return []; - }else{ - return are_missing; - } - }, - "method": function(obj, method, args) { - return obj[method].apply(obj, args); - }, - - }; - - jsonLogic.is_logic = function(logic) { - return ( - typeof logic === "object" && // An object - logic !== null && // but not null - ! Array.isArray(logic) && // and not an array - Object.keys(logic).length === 1 // with exactly one key - ); - }; - - /* - This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. - - Spec and rationale here: http://jsonlogic.com/truthy - */ - jsonLogic.truthy = function(value) { - if(Array.isArray(value) && value.length === 0) { - return false; - } - return !! value; - }; - - - jsonLogic.get_operator = function(logic) { - return Object.keys(logic)[0]; - }; - - jsonLogic.get_values = function(logic) { - return logic[jsonLogic.get_operator(logic)]; - }; - - jsonLogic.apply = function(logic, data) { - // Does this array contain logic? Only one way to find out. - if(Array.isArray(logic)) { - return logic.map(function(l) { - return jsonLogic.apply(l, data); - }); - } - // You've recursed to a primitive, stop! - if( ! jsonLogic.is_logic(logic) ) { - return logic; - } - - data = data || {}; - - var op = jsonLogic.get_operator(logic); - var values = logic[op]; - var i; - var current; - var scopedLogic, scopedData, filtered, initial; - - // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} - if( ! Array.isArray(values)) { - values = [values]; - } - - // 'if', 'and', and 'or' violate the normal rule of depth-first calculating consequents, let each manage recursion as needed. - if(op === "if" || op == "?:") { - /* 'if' should be called with a odd number of parameters, 3 or greater - This works on the pattern: - if( 0 ){ 1 }else{ 2 }; - if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; - if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; - - The implementation is: - For pairs of values (0,1 then 2,3 then 4,5 etc) - If the first evaluates truthy, evaluate and return the second - If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) - given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) - given 0 parameters, return NULL (not great practice, but there was no Else) - */ - for(i = 0; i < values.length - 1; i += 2) { - if( jsonLogic.truthy( jsonLogic.apply(values[i], data) ) ) { - return jsonLogic.apply(values[i+1], data); - } - } - if(values.length === i+1) return jsonLogic.apply(values[i], data); - return null; - }else if(op === "and") { // Return first falsy, or last - for(i=0; i < values.length; i+=1) { - current = jsonLogic.apply(values[i], data); - if( ! jsonLogic.truthy(current)) { - return current; - } - } - return current; // Last - }else if(op === "or") {// Return first truthy, or last - for(i=0; i < values.length; i+=1) { - current = jsonLogic.apply(values[i], data); - if( jsonLogic.truthy(current) ) { - return current; - } - } - return current; // Last - - - - - }else if(op === 'filter'){ - scopedData = jsonLogic.apply(values[0], data); - scopedLogic = values[1]; - - if ( ! Array.isArray(scopedData)) { - return []; - } - // Return only the elements from the array in the first argument, - // that return truthy when passed to the logic in the second argument. - // For parity with JavaScript, reindex the returned array - return scopedData.filter(function(datum){ - return jsonLogic.truthy( jsonLogic.apply(scopedLogic, datum)); - }); - }else if(op === 'map'){ - scopedData = jsonLogic.apply(values[0], data); - scopedLogic = values[1]; - - if ( ! Array.isArray(scopedData)) { - return []; - } - - return scopedData.map(function(datum){ - return jsonLogic.apply(scopedLogic, datum); - }); - - }else if(op === 'reduce'){ - scopedData = jsonLogic.apply(values[0], data); - scopedLogic = values[1]; - initial = typeof values[2] !== 'undefined' ? values[2] : null; - - if ( ! Array.isArray(scopedData)) { - return initial; - } - - return scopedData.reduce( - function(accumulator, current){ - return jsonLogic.apply( - scopedLogic, - {'current':current, 'accumulator':accumulator} - ); - }, - initial - ); - - }else if(op === "all") { - scopedData = jsonLogic.apply(values[0], data); - scopedLogic = values[1]; - // All of an empty set is false. Note, some and none have correct fallback after the for loop - if( ! scopedData.length) { - return false; - } - for(i=0; i < scopedData.length; i+=1) { - if( ! jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) { - return false; // First falsy, short circuit - } - } - return true; // All were truthy - }else if(op === "none") { - filtered = jsonLogic.apply({'filter' : values}, data); - return filtered.length === 0; - - }else if(op === "some") { - filtered = jsonLogic.apply({'filter' : values}, data); - return filtered.length > 0; - } - - // Everyone else gets immediate depth-first recursion - values = values.map(function(val) { - return jsonLogic.apply(val, data); - }); - - - // The operation is called with "data" bound to its "this" and "values" passed as arguments. - // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments - if(typeof operations[op] === "function") { - return operations[op].apply(data, values); - }else if(op.indexOf(".") > 0) { // Contains a dot, and not in the 0th position - var sub_ops = String(op).split("."); - var operation = operations; - for(i = 0; i < sub_ops.length; i++) { - // Descending into operations - operation = operation[sub_ops[i]]; - if(operation === undefined) { - throw new Error("Unrecognized operation " + op + - " (failed at " + sub_ops.slice(0, i+1).join(".") + ")"); - } - } - - return operation.apply(data, values); - } - - throw new Error("Unrecognized operation " + op ); - }; - - jsonLogic.uses_data = function(logic) { - var collection = []; - - if( jsonLogic.is_logic(logic) ) { - var op = jsonLogic.get_operator(logic); - var values = logic[op]; - - if( ! Array.isArray(values)) { - values = [values]; - } - - if(op === "var") { - // This doesn't cover the case where the arg to var is itself a rule. - collection.push(values[0]); - }else{ - // Recursion! - values.map(function(val) { - collection.push.apply(collection, jsonLogic.uses_data(val) ); - }); - } - } - - return arrayUnique(collection); - }; - - jsonLogic.add_operation = function(name, code) { - operations[name] = code; - }; - - jsonLogic.rm_operation = function(name) { - delete operations[name]; - }; - - jsonLogic.rule_like = function(rule, pattern) { - // console.log("Is ". JSON.stringify(rule) . " like " . JSON.stringify(pattern) . "?"); - if(pattern === rule) { - return true; - } // TODO : Deep object equivalency? - if(pattern === "@") { - return true; - } // Wildcard! - if(pattern === "number") { - return (typeof rule === "number"); - } - if(pattern === "string") { - return (typeof rule === "string"); - } - if(pattern === "array") { - // !logic test might be superfluous in JavaScript - return Array.isArray(rule) && ! jsonLogic.is_logic(rule); - } - - if(jsonLogic.is_logic(pattern)) { - if(jsonLogic.is_logic(rule)) { - var pattern_op = jsonLogic.get_operator(pattern); - var rule_op = jsonLogic.get_operator(rule); - - if(pattern_op === "@" || pattern_op === rule_op) { - // echo "\nOperators match, go deeper\n"; - return jsonLogic.rule_like( - jsonLogic.get_values(rule, false), - jsonLogic.get_values(pattern, false) - ); - } - } - return false; // pattern is logic, rule isn't, can't be eq - } - - if(Array.isArray(pattern)) { - if(Array.isArray(rule)) { - if(pattern.length !== rule.length) { - return false; - } - /* - Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT) - */ - for(var i = 0; i < pattern.length; i += 1) { - // If any fail, we fail - if( ! jsonLogic.rule_like(rule[i], pattern[i])) { - return false; - } - } - return true; // If they *all* passed, we pass - }else{ - return false; // Pattern is array, rule isn't - } - } - - // Not logic, not array, not a === match for rule. - return false; - }; - - return jsonLogic; -})); diff --git a/package.json b/package.json index 201ef3b..ecf3074 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,57 @@ { - "name": "json-logic-js", + "name": "@axa-ch/json-logic-js", "version": "1.2.2", "description": "Build complex rules, serialize them as JSON, and execute them in JavaScript", - "main": "logic.js", + "main": "dist/jsonLogic.js", + "browser": "dist/jsonLogic.min.js", + "module": "lib/index.js", "directories": { "test": "tests" }, - "dependencies": {}, + "dependencies": { + "@babel/polyfill": "^7.2.5" + }, "devDependencies": { - "eslint": "^3.9.1", - "eslint-config-google": "^0.7.0", - "gulp": "^3.9.0", - "qunit": "^0.7.7", - "request": "^2.65.0" + "@babel/cli": "^7.2.3", + "@babel/core": "^7.3.3", + "@babel/plugin-proposal-export-default-from": "^7.2.0", + "@babel/preset-env": "^7.3.1", + "cross-env": "^5.2.0", + "eslint": "^5.14.1", + "eslint-config-airbnb": "^17.1.0", + "eslint-config-prettier": "^4.0.0", + "eslint-plugin-import": "^2.16.0", + "eslint-plugin-jsx-a11y": "^6.2.1", + "eslint-plugin-prettier": "^3.0.1", + "eslint-plugin-react": "^7.12.4", + "husky": "^1.3.1", + "lint-staged": "^8.1.4", + "prettier": "^1.16.4", + "qunit": "^2.9.2", + "request": "^2.65.0", + "rimraf": "^2.6.3", + "rollup": "^1.2.2", + "rollup-plugin-babel": "^4.3.2", + "rollup-plugin-filesize": "^6.0.1", + "rollup-plugin-node-resolve": "^4.0.1", + "rollup-plugin-optimize-js": "0.0.4", + "rollup-plugin-uglify": "^6.0.2" }, "scripts": { - "test": "gulp test" + "lint": "eslint src tests", + "lint-fix": "npm run lint -- --fix", + "test": "cross-env NODE_ENV=test qunit 'tests/**/*.js' -r tap", + "pretest": "npm run build-package", + "build": "cross-env NODE_ENV=production npm run build-lib", + "build-lib": "babel src --out-dir lib", + "prebuild-lib": "rimraf lib", + "postbuild-lib": "npm run build-package", + "build-package": "rollup --config", + "prebuild-package": "rimraf dist" }, "repository": { "type": "git", - "url": "git+https://github.com/jwadhams/json-logic-js.git" + "url": "git+https://github.com/axa-ch/json-logic-js.git" }, "keywords": [ "json", @@ -30,7 +62,7 @@ "author": "Jeremy Wadhams (http://jsonlogic.com)", "license": "MIT", "bugs": { - "url": "https://github.com/jwadhams/json-logic-js/issues" + "url": "https://github.com/axa-ch/json-logic-js/issues" }, - "homepage": "https://github.com/jwadhams/json-logic-js#readme" + "homepage": "https://github.com/axa-ch/json-logic-js#readme" } diff --git a/play.html b/play.html index f239440..f98a4b4 100644 --- a/play.html +++ b/play.html @@ -60,7 +60,7 @@

Test JsonLogic in your Browser

- +