diff --git a/functions/package-lock.json b/functions/package-lock.json index 5a8fb99c..168ab70f 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -2822,11 +2822,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==" }, - "complex.js": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.11.tgz", - "integrity": "sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw==" - }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -3135,11 +3130,6 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" }, - "decimal.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" - }, "decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -3525,11 +3515,6 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "escape-latex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -4357,6 +4342,11 @@ } } }, + "expr-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz", + "integrity": "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==" + }, "express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -4750,11 +4740,6 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, - "fraction.js": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz", - "integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA==" - }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -6591,11 +6576,6 @@ "html-escaper": "^2.0.0" } }, - "javascript-natural-sort": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" - }, "jest": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", @@ -7665,21 +7645,6 @@ "object-visit": "^1.0.0" } }, - "mathjs": { - "version": "5.10.3", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-5.10.3.tgz", - "integrity": "sha512-ySjg30BC3dYjQm73ILZtwcWzFJde0VU6otkXW/57IjjuYRa3Qaf0Kb8pydEuBZYtqW2OxreAtsricrAmOj3jIw==", - "requires": { - "complex.js": "2.0.11", - "decimal.js": "10.2.0", - "escape-latex": "1.2.0", - "fraction.js": "4.0.12", - "javascript-natural-sort": "0.7.1", - "seed-random": "2.2.0", - "tiny-emitter": "2.1.0", - "typed-function": "1.1.0" - } - }, "md5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", @@ -9405,11 +9370,6 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" }, - "seed-random": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", - "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==" - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -10434,11 +10394,6 @@ "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==" }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -10606,11 +10561,6 @@ "is-typed-array": "^1.1.9" } }, - "typed-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.0.tgz", - "integrity": "sha512-TuQzwiT4DDg19beHam3E66oRXhyqlyfgjHB/5fcvsRXbfmWPJfto9B4a0TBdTrQAPGlGmXh/k7iUI+WsObgORA==" - }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", diff --git a/functions/package.json b/functions/package.json index a4b20a18..c91f7741 100644 --- a/functions/package.json +++ b/functions/package.json @@ -24,12 +24,12 @@ "bespoken-tools": "^2.6.7", "dashbot": "^12.1.0", "debug": "^4.3.4", + "expr-eval": "^2.0.2", "firebase-admin": "^8.9.0", "firebase-functions": "^3.3.0", "glob": "^7.1.6", "hirestime": "^4.0.0", "lodash": "^4.17.21", - "mathjs": "^5.10.3", "mustache": "^2.3.2", "raven": "^2.6.4", "uuid": "^9.0.0" diff --git a/functions/src/configurator/selectors/condition-selector.js b/functions/src/configurator/selectors/condition-selector.js index 1de091de..600117c3 100644 --- a/functions/src/configurator/selectors/condition-selector.js +++ b/functions/src/configurator/selectors/condition-selector.js @@ -1,24 +1,28 @@ -const math = require('mathjs'); +const { debug, timer, warning } = require('../../utils/logger')('ia:selectors:condition-selector'); +const equal = require('../../mathjs/equal'); +const includes = require('../../mathjs/includes'); +const Parser = require('expr-eval').Parser; +const parser = new Parser(); const util = require('util'); -const { debug, timer, warning } = require('../../utils/logger')('ia:selectors:condition-selector'); +parser.functions.equal = equal(); +parser.functions.includes = includes(); -function safieMathEval (condition, context) { +function safieEval (condition, context) { try { - return math.eval(condition, context); + return parser.evaluate(condition, context); } catch (error) { - debug('Get error from Math.js:', error && error.message); + debug('Get error from eval:', error && error.message); return false; } } /** - * Choose one which satisfies context. - * Or get default which doesn't have condition + * Choose one option that satisfies the context or get the default option that doesn't have a condition. * * @param options {Array.} * @param context {Object} - * @returns {string} + * @returns {Object|null} */ function find (options, context) { debug('select option by condition'); @@ -30,14 +34,12 @@ function find (options, context) { const stopFindOptionTimer = timer.start('find option'); const option = options .filter(({ condition }) => condition) - .find( - ({ condition }) => { - const stopMathEvalTimer = timer.start('math.eval'); - const res = safieMathEval(condition, context); - stopMathEvalTimer(); - return res; - } - ); + .find(({ condition }) => { + const stopEvalTimer = timer.start('eval'); + const res = safieEval(condition, context); + stopEvalTimer(); + return res; + }); stopFindOptionTimer(); diff --git a/functions/src/mathjs/equal.js b/functions/src/mathjs/equal.js index f61782b8..f4b01e2f 100644 --- a/functions/src/mathjs/equal.js +++ b/functions/src/mathjs/equal.js @@ -1,7 +1,4 @@ -const math = require('mathjs'); - const { debug } = require('../utils/logger')('ia:mathjs:equal'); - /** * Support equal command: * @@ -10,7 +7,8 @@ const { debug } = require('../utils/logger')('ia:mathjs:equal'); */ module.exports = () => { debug('support'); - math.import({ - equal: (a, b) => a === b, - }, { override: true }); + // Define the equal function + const equal = (a, b) => a === b; + // Export the equal function + return equal; }; diff --git a/functions/src/mathjs/includes.js b/functions/src/mathjs/includes.js index a13c5182..fa59e5ce 100644 --- a/functions/src/mathjs/includes.js +++ b/functions/src/mathjs/includes.js @@ -1,7 +1,4 @@ -const math = require('mathjs'); - -const { debug } = require('../utils/logger')('ia:mathjs:equal'); - +const { debug } = require('../utils/logger')('ia:mathjs:includes'); /** * Support includes command: * @@ -10,15 +7,14 @@ const { debug } = require('../utils/logger')('ia:mathjs:equal'); */ module.exports = () => { debug('support'); - math.import({ - includes: (a, b) => { - let array; - if (typeof a.toArray === 'function') { - array = a.toArray(); - } else { - array = a; - } - return array.indexOf(b) >= 0; - }, - }, { override: true }); + const includes = (a, b) => { + let array; + if (typeof a.toArray === 'function') { + array = a.toArray(); + } else { + array = a; + } + return array.indexOf(b) >= 0; + }; + return includes; }; diff --git a/functions/src/mathjs/index.js b/functions/src/mathjs/index.js index ab44a0cf..8242678e 100644 --- a/functions/src/mathjs/index.js +++ b/functions/src/mathjs/index.js @@ -1,16 +1,13 @@ -/** - * All extensions for Math.js - */ - -const builder = require('../extensions/builder'); - -const extensions = builder.build({ root: __dirname }); +const includes = require('./includes'); +const equal = require('./equal'); +const Parser = require('expr-eval').Parser; +const parser = new Parser(); +// FIXME: Need to improve this code to avoid duplications module.exports = { - /** - * Get all extensions and apply patch - */ + parser, // Export the parser instance patch: () => { - extensions.all().forEach(({ ext }) => ext()); + parser.functions.equal = equal; + parser.functions.includes = includes; }, }; diff --git a/functions/src/setup.js b/functions/src/setup.js index cdf6d760..5e1f86eb 100644 --- a/functions/src/setup.js +++ b/functions/src/setup.js @@ -9,7 +9,9 @@ const packageJSON = require('../package.json'); const appConfig = require('./config'); const env = require('./config/env'); const errors = require('./errors'); -const mathjsExtensions = require('./mathjs'); +const equal = require('../src/mathjs/equal'); +const includes = require('../src/mathjs/includes'); +const Parser = require('expr-eval').Parser; const axiosProfile = require('./performance/axios'); const { debug, warning } = require('./utils/logger')('ia:axio:interceptions'); @@ -33,9 +35,9 @@ const errorHandler = (error) => { module.exports = ({ platform }) => { // turn-off escaping in MustacheJS mustache.escape = v => v; - - mathjsExtensions.patch(); - + const parser = new Parser(); + parser.functions.equal = equal(); + parser.functions.includes = includes(); const userAgent = mustache.render( appConfig.request.userAgent, Object.assign({}, packageJSON, { platform }) diff --git a/functions/tests/actions/music-query.spec.js b/functions/tests/actions/music-query.spec.js index 477fc0f9..7d340995 100644 --- a/functions/tests/actions/music-query.spec.js +++ b/functions/tests/actions/music-query.spec.js @@ -4,9 +4,10 @@ const rewire = require('rewire'); const sinon = require('sinon'); const action = rewire('../../src/actions/music-query'); -const mathjsExtensions = require('../../src/mathjs'); +const equal = require('../../src/mathjs/equal'); +const includes = require('../../src/mathjs/includes'); +const Parser = require('expr-eval').Parser; const query = require('../../src/state/query'); - const mockApp = require('../_utils/mocking/platforms/app'); const mockDialog = require('../_utils/mocking/dialog'); const mockAlbumsFeeder = require('../_utils/mocking/feeders/albums'); @@ -88,7 +89,9 @@ describe('actions / music query', () => { describe('multiple slot schemes', () => { beforeEach(() => { handler = action.build(slotSchemeWithMultipleCases).handler; - mathjsExtensions.patch(); + const parser = new Parser(); + parser.functions.equal = equal(); + parser.functions.includes = includes(); }); it('should get slot scheme without condition', () => { diff --git a/functions/tests/mathjs/patches.spec.js b/functions/tests/mathjs/patches.spec.js index 4df747e4..bd2fc20f 100644 --- a/functions/tests/mathjs/patches.spec.js +++ b/functions/tests/mathjs/patches.spec.js @@ -1,37 +1,38 @@ const { expect } = require('chai'); -const mathjsExtensions = require('../../src/mathjs'); - -const math = require('mathjs'); +const equal = require('../../src/mathjs/equal'); +const includes = require('../../src/mathjs/includes'); +const Parser = require('expr-eval').Parser; +const parser = new Parser(); describe('mathjs', () => { - beforeEach(() => { - // TODO: for clear test we should have - // mathjsExtensions.unpatch(); - mathjsExtensions.patch(); - }); - - describe('includes', () => { - it('should be true when collection includes value', () => { - expect(math.eval('includes([1,2], 1)')).to.be.true; - expect(math.eval('includes(["a","b"], "a")')).to.be.true; - expect(math.eval('includes(options, "a")', { - options: ['a', 'b'], - })).to.be.true; + describe('equal', () => { + beforeEach(() => { + parser.functions.equal = equal(); + parser.functions.includes = includes(); }); - it('should be false when collection includes value', () => { - expect(math.eval('includes([1,2], 3)')).to.be.false; - expect(math.eval('includes(["a","b"], "c")')).to.be.false; - }); - }); + describe('includes', () => { + it('should be true when collection includes value', () => { + expect(parser.evaluate('includes([1,2], 1)')).to.be.true; + expect(parser.evaluate('includes(["a","b"], "a")')).to.be.true; + expect(parser.evaluate('includes(options, "a")', { + options: ['a', 'b'], + })).to.be.true; + }); - describe('equal', () => { - it('should "abc" == "abc"', () => { - expect(math.eval('equal("abc", "abc")')).to.be.true; + it('should be false when collection includes value', () => { + expect(parser.evaluate('includes([1,2], 3)')).to.be.false; + expect(parser.evaluate('includes(["a","b"], "c")')).to.be.false; + }); }); - it('should "abc" != "cba"', () => { - expect(math.eval('equal("abc", "cba")')).to.be.false; + describe('equal', () => { + it('should "abc" == "abc"', () => { + expect(parser.evaluate('equal("abc", "abc")')).to.be.true; + }); + it('should "abc" != "cba"', () => { + expect(parser.evaluate('equal("abc", "cba")')).to.be.false; + }); }); }); });