diff --git a/package-lock.json b/package-lock.json index 3af0c60..7f6e784 100644 --- a/package-lock.json +++ b/package-lock.json @@ -593,6 +593,23 @@ "private": "^0.1.8", "slash": "^1.0.0", "source-map": "^0.5.7" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "babel-eslint": { @@ -1418,6 +1435,23 @@ "globals": "^9.18.0", "invariant": "^2.2.2", "lodash": "^4.17.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "babel-types": { @@ -2115,12 +2149,11 @@ "integrity": "sha1-ywVLx7vB9EDLxhNSe4oFtX4Z4as=" }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } }, "debug-log": { @@ -2719,6 +2752,23 @@ "requires": { "debug": "^2.6.9", "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "eslint-module-utils": { @@ -2731,6 +2781,15 @@ "pkg-dir": "^2.0.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "find-up": { "version": "2.1.0", "resolved": "http://artifactory.shuttercorp.net/artifactory/api/npm/npm-composite/find-up/-/find-up-2.1.0.tgz", @@ -2750,6 +2809,12 @@ "path-exists": "^3.0.0" } }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "p-limit": { "version": "1.3.0", "resolved": "http://artifactory.shuttercorp.net/artifactory/api/npm/npm-composite/p-limit/-/p-limit-1.3.0.tgz", @@ -2822,6 +2887,15 @@ "resolve": "^1.11.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "doctrine": { "version": "1.5.0", "resolved": "http://artifactory.shuttercorp.net/artifactory/api/npm/npm-composite/doctrine/-/doctrine-1.5.0.tgz", @@ -2831,6 +2905,12 @@ "esutils": "^2.0.2", "isarray": "^1.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -5457,10 +5537,9 @@ } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "mute-stream": { "version": "0.0.8", @@ -6334,6 +6413,16 @@ "to-regex": "^3.0.1" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -6572,6 +6661,13 @@ "snapdragon": "^0.8.1", "to-regex": "^3.0.2" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true, + "optional": true } } }, @@ -7013,6 +7109,16 @@ "use": "^3.1.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -7032,6 +7138,13 @@ "requires": { "is-extendable": "^0.1.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true, + "optional": true } } }, diff --git a/package.json b/package.json index 8be97f4..2115bae 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ }, "dependencies": { "clone": "^2.1.2", + "debug": "^4.2.0", "events": "^3.2.0", "hash-it": "^4.0.5", "jsonpath-plus": "^4.0.0", diff --git a/src/almanac.js b/src/almanac.js index 30b4625..539f606 100644 --- a/src/almanac.js +++ b/src/almanac.js @@ -2,11 +2,13 @@ import Fact from './fact' import { UndefinedFactError } from './errors' -import debug from './debug' +import Debug from './debug' import { JSONPath } from 'jsonpath-plus' import isObjectLike from 'lodash.isobjectlike' +const debug = Debug('json-rules-engine:almanac') + /** * Fact results lookup * Triggers fact computations and saves the results @@ -27,7 +29,7 @@ export default class Almanac { } this._addConstantFact(fact) - debug(`almanac::constructor initialized runtime fact:${fact.id} with ${fact.value}<${typeof fact.value}>`) + debug('constructor initialized runtime fact:%s with %o <%t>', fact.id, fact.value, fact.value) } } @@ -70,7 +72,7 @@ export default class Almanac { * @param {Mixed} value - constant value of the fact */ addRuntimeFact (factId, value) { - debug(`almanac::addRuntimeFact id:${factId}`) + debug('addRuntimeFact id:%s', factId) const fact = new Fact(factId, value) return this._addConstantFact(fact) } @@ -100,24 +102,24 @@ export default class Almanac { const cacheVal = cacheKey && this.factResultsCache.get(cacheKey) if (cacheVal) { factValuePromise = Promise.resolve(cacheVal) - debug(`almanac::factValue cache hit for fact:${factId}`) + debug('factValue cache hit for fact:%s', factId) } else { - debug(`almanac::factValue cache miss for fact:${factId}; calculating`) + debug('factValue cache miss for fact:%s; calculating', factId) factValuePromise = this._setFactValue(fact, params, fact.calculate(params, this)) } } if (path) { // selectn supports arrays and strings as a 'path' // strings starting with '$' denotes json path. otherwise fall back to deprecated 'selectn' syntax if (typeof path === 'string' && path.startsWith('$')) { - debug(`condition::evaluate extracting object property ${path}`) + debug('condition::evaluate extracting object property %s', path) return factValuePromise .then(factValue => { if (isObjectLike(factValue)) { const pathValue = JSONPath({ path, json: factValue, wrap: false }) - debug(`condition::evaluate extracting object property ${path}, received: ${pathValue}`) + debug('condition::evaluate extracting object property %s, received: %o', path, pathValue) return pathValue } else { - debug(`condition::evaluate could not compute object path(${path}) of non-object: ${factValue} <${typeof factValue}>; continuing with ${factValue}`) + debug('condition::evaluate could not compute object path(%s) of non-object: %s <%t>; continuing with %s', path, factValue, factValue, factValue) return factValue } }) @@ -136,10 +138,10 @@ export default class Almanac { .then(factValue => { if (isObjectLike(factValue)) { const pathValue = selectn(path)(factValue) - debug(`condition::evaluate extracting object property ${path}, received: ${pathValue}`) + debug('condition::evaluate extracting object property %s, received: %o', path, factValue) return pathValue } else { - debug(`condition::evaluate could not compute object path(${path}) of non-object: ${factValue} <${typeof factValue}>; continuing with ${factValue}`) + debug('condition::evaluate could not compute object path(%s) of non-object: %s <%t>; continuing with %s', path, factValue, factValue, factValue) return factValue } }) diff --git a/src/condition.js b/src/condition.js index cd6f428..035533f 100644 --- a/src/condition.js +++ b/src/condition.js @@ -1,8 +1,11 @@ 'use strict' -import debug from './debug' +import Debug from './debug' + import isObjectLike from 'lodash.isobjectlike' +const debug = Debug('json-rules-engine:condition') + export default class Condition { constructor (properties) { if (!properties) throw new Error('Condition: constructor options required') @@ -101,7 +104,7 @@ export default class Condition { return almanac.factValue(this.fact, this.params, this.path) .then(leftHandSideValue => { const result = op.evaluate(leftHandSideValue, rightHandSideValue) - debug(`condition::evaluate <${leftHandSideValue} ${this.operator} ${rightHandSideValue}?> (${result})`) + debug('evaluate <%s %s %s?> (%s)', leftHandSideValue, this.operator, rightHandSideValue, result) return { result, leftHandSideValue, rightHandSideValue, operator: this.operator } }) }) diff --git a/src/debug.js b/src/debug.js index 7bd5477..581a260 100644 --- a/src/debug.js +++ b/src/debug.js @@ -1,10 +1,7 @@ -export default function debug (message) { - try { - if ((typeof process !== 'undefined' && process.env && process.env.DEBUG && process.env.DEBUG.match(/json-rules-engine/)) || - (typeof window !== 'undefined' && window.localStorage && window.localStorage.debug && window.localStorage.debug.match(/json-rules-engine/))) { - console.log(message) - } - } catch (ex) { - // Do nothing - } -} +'use strict' + +import Debug from 'debug' + +Debug.formatters.t = o => typeof o + +export default Debug diff --git a/src/engine.js b/src/engine.js index 4a949ac..1be7a05 100644 --- a/src/engine.js +++ b/src/engine.js @@ -7,8 +7,9 @@ import Almanac from './almanac' import { EventEmitter } from 'events' import { SuccessEventFact } from './engine-facts' import defaultOperators from './engine-default-operators' -import debug from './debug' +import Debug from './debug' +const debug = Debug('json-rules-engine:engine') export const READY = 'READY' export const RUNNING = 'RUNNING' export const FINISHED = 'FINISHED' @@ -81,7 +82,7 @@ class Engine extends EventEmitter { } else { operator = new Operator(operatorOrName, cb) } - debug(`engine::addOperator name:${operator.name}`) + debug('addOperator name:%s', operator.name) this.operators.set(operator.name, operator) } @@ -116,7 +117,7 @@ class Engine extends EventEmitter { } else { fact = new Fact(id, valueOrMethod, options) } - debug(`engine::addFact id:${factId}`) + debug('addFact id:%s', factId) this.facts.set(factId, fact) return this } @@ -185,11 +186,11 @@ class Engine extends EventEmitter { evaluateRules (ruleArray, almanac) { return Promise.all(ruleArray.map((rule) => { if (this.status !== RUNNING) { - debug(`engine::run status:${this.status}; skipping remaining rules`) + debug('run status:%s; skipping remaining rules', this.status) return } return rule.evaluate(almanac).then((ruleResult) => { - debug(`engine::run ruleResult:${ruleResult.result}`) + debug('run ruleResult: %o', ruleResult) if (ruleResult.result) { this.emit('success', rule.event, almanac, ruleResult) this.emit(rule.event.type, rule.event.params, almanac, ruleResult) @@ -204,12 +205,11 @@ class Engine extends EventEmitter { /** * Runs the rules engine * @param {Object} runtimeFacts - fact values known at runtime - * @param {Object} runOptions - run options * @return {Promise} resolves when the engine has completed running */ run (runtimeFacts = {}) { - debug('engine::run started') - debug('engine::run runtimeFacts:', runtimeFacts) + debug('run started') + debug('run runtimeFacts: %o', runtimeFacts) runtimeFacts['success-events'] = new Fact('success-events', SuccessEventFact(), { cache: false }) this.status = RUNNING const almanac = new Almanac(this.facts, runtimeFacts, { allowUndefinedFacts: this.allowUndefinedFacts }) @@ -226,7 +226,7 @@ class Engine extends EventEmitter { }) cursor.then(() => { this.status = FINISHED - debug('engine::run completed') + debug('run completed') return almanac.factValue('success-events') }).then(events => { resolve({ diff --git a/src/rule.js b/src/rule.js index d34dac0..86c7c92 100644 --- a/src/rule.js +++ b/src/rule.js @@ -3,7 +3,9 @@ import Condition from './condition' import RuleResult from './rule-result' import { EventEmitter } from 'events' -import debug from './debug' +import Debug from './debug' + +const debug = Debug('json-rules-engine:rule') class Rule extends EventEmitter { /** @@ -192,7 +194,7 @@ class Rule extends EventEmitter { return Promise.all(conditions.map((condition) => evaluateCondition(condition))) .then(conditionResults => { - debug('rule::evaluateConditions results', conditionResults) + debug('evaluateConditions results: %o', conditionResults) return method.call(conditionResults, (result) => result === true) }) }