From e1b088750ea02748f89c8f87bcd6e5b729aec321 Mon Sep 17 00:00:00 2001 From: guomiemie <1109255675@qq.com> Date: Wed, 25 Mar 2026 12:22:14 +0800 Subject: [PATCH] fix: prevent crashes and enhance string support --- examples/03-dynamic-facts.js | 21 ++++++++++++++------- package.json | 1 + src/engine-default-operators.js | 14 +++++++++----- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/examples/03-dynamic-facts.js b/examples/03-dynamic-facts.js index e7ddc502..58307d8b 100644 --- a/examples/03-dynamic-facts.js +++ b/examples/03-dynamic-facts.js @@ -38,7 +38,7 @@ async function start () { }, { fact: 'account-information', operator: 'in', - value: ['active', 'paid-leave'], // 'status'' can be active or paid-leave + value: ['active', 'paid-leave'], // 'status' can be active or paid-leave path: '$.status' // access the 'status' property of "account-information" }, { fact: 'account-information', @@ -61,18 +61,25 @@ async function start () { * into the engine. The major advantage of this technique is that although there are THREE conditions * requiring this data, only ONE api call is made. This results in much more efficient runtime performance. */ - engine.addFact('account-information', function (params, almanac) { - return almanac.factValue('accountId') - .then(accountId => { - return apiClient.getAccountInformation(accountId) - }) + engine.addFact('account-information', async function (params, almanac) { + try { + const accountId = await almanac.factValue('accountId') + return await apiClient.getAccountInformation(accountId) + } catch (err) { + console.error('Error fetching account-information:', err) + throw err + } }) // define fact(s) known at runtime const facts = { accountId: 'lincoln' } const { events } = await engine.run(facts) - console.log(facts.accountId + ' is a ' + events.map(event => event.params.message)) + if (events.length > 0) { + console.log(facts.accountId + ' is a ' + events.map(event => event.params.message).join(', ')) + } else { + console.log(facts.accountId + ' did not meet conditions for the microsoft-christmas-pto rule') + } } start() diff --git a/package.json b/package.json index cb744042..02b19605 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "clone": "^2.1.2", "eventemitter2": "^6.4.4", "hash-it": "^6.0.0", + "json-rules-engine": "^7.3.1", "jsonpath-plus": "^10.3.0" } } diff --git a/src/engine-default-operators.js b/src/engine-default-operators.js index dfe33f29..37893679 100644 --- a/src/engine-default-operators.js +++ b/src/engine-default-operators.js @@ -3,16 +3,20 @@ import Operator from './operator' const Operators = [] +function isCollection (value) { + return !!(value && (Array.isArray(value) || typeof value === 'string')) +} + Operators.push(new Operator('equal', (a, b) => a === b)) Operators.push(new Operator('notEqual', (a, b) => a !== b)) -Operators.push(new Operator('in', (a, b) => b.indexOf(a) > -1)) -Operators.push(new Operator('notIn', (a, b) => b.indexOf(a) === -1)) +Operators.push(new Operator('in', (a, b) => isCollection(b) && b.indexOf(a) > -1)) +Operators.push(new Operator('notIn', (a, b) => !isCollection(b) || b.indexOf(a) === -1)) -Operators.push(new Operator('contains', (a, b) => a.indexOf(b) > -1, Array.isArray)) -Operators.push(new Operator('doesNotContain', (a, b) => a.indexOf(b) === -1, Array.isArray)) +Operators.push(new Operator('contains', (a, b) => a.indexOf(b) > -1, isCollection)) +Operators.push(new Operator('doesNotContain', (a, b) => a.indexOf(b) === -1, isCollection)) function numberValidator (factValue) { - return Number.parseFloat(factValue).toString() !== 'NaN' + return typeof factValue !== 'object' && !Number.isNaN(Number.parseFloat(factValue)) } Operators.push(new Operator('lessThan', (a, b) => a < b, numberValidator)) Operators.push(new Operator('lessThanInclusive', (a, b) => a <= b, numberValidator))