@gorules/zen-engine 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +154 -32
  2. package/index.js +36 -1
  3. package/package.json +7 -7
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ZEN Engine is a cross-platform, Open-Source Business Rules Engine (BRE). It is written in **Rust** and provides native bindings for **NodeJS** and **Python**. ZEN Engine allows to load and execute [JSON Decision Model (JDM)](https://gorules.io/docs/rules-engine/json-decision-model) from JSON files.
6
6
 
7
- <img width="1258" alt="JSON Decision Model" src="https://user-images.githubusercontent.com/60513195/224425568-4a717e34-3d4b-4cc6-b031-8cd35f8ff459.png">
7
+ <img width="800" alt="Open-Source Rules Engine" src="https://gorules.io/images/jdm-editor.gif">
8
8
 
9
9
  An open-source React editor is available on our [JDM Editor](https://github.com/gorules/jdm-editor) repo.
10
10
 
@@ -13,7 +13,7 @@ An open-source React editor is available on our [JDM Editor](https://github.com/
13
13
  ZEN Engine is built as embeddable BRE for your **Rust**, **NodeJS** or **Python** applications.
14
14
  It parses JDM from JSON content. It is up to you to obtain the JSON content, e.g. from file system, database or service call.
15
15
 
16
- If you are looking for a **Decision-as-a-Service** (DaaS) over REST, take a look at [GoRules Cloud](https://gorules.io).
16
+ If you are looking for a complete **BRMS**, take a look at self-hosted [GoRules BRMS](https://gorules.io) or [GoRules Cloud](https://gorules.io).
17
17
 
18
18
  ### Installation
19
19
 
@@ -70,42 +70,93 @@ Similar to this example you can also utilise loader to load from different place
70
70
 
71
71
  ## JSON Decision Model (JDM)
72
72
 
73
- JDM is a modeling standard for business decisions and business rules and is stored in a JSON format. Decision models are represented by graphs. Graphs are built using nodes and edges. Edges are used to pass the data from one node to another (left-side to right-side).
73
+ GoRules JDM (JSON Decision Model) is a modeling framework designed to streamline the representation and implementation of decision models.
74
74
 
75
- An open-source version of the React Component is available on our [JDM Editor](https://github.com/gorules/jdm-editor) repo.
75
+ #### Understanding GoRules JDM
76
+ At its core, GoRules JDM revolves around the concept of decision models as interconnected graphs stored in JSON format.
77
+ These graphs capture the intricate relationships between various decision points, conditions, and outcomes in a GoRules Zen-Engine.
76
78
 
77
- You can try [Free Online Editor](https://editor.gorules.io) with built in Simulator.
79
+ Graphs are made by linking nodes with edges, which act like pathways for moving information from one node to another, usually from the left to the right.
78
80
 
79
- [JSON Example](https://github.com/gorules/zen/blob/master/test-data/credit-analysis.json)
81
+ The Input node serves as an entry for all data relevant to the context, while the Output nodes produce the result of decision-making process. The progression of data follows a path from the Input Node to the Output Node, traversing all interconnected nodes in between. As the data flows through this network, it undergoes evaluation at each node, and connections determine where the data is passed along the graph.
80
82
 
81
- Input node contains all data sent in the context, and Output node returns the data from the decision. Data flows from the Input Node towards Output Node evaluating all the Nodes in between and passing the data where nodes are connected.
83
+ To see JDM Graph in action you can use [Free Online Editor](https://editor.gorules.io) with built in Simulator.
82
84
 
83
- ### Decision Tables
85
+ There are 5 main node types in addition to a graph Input Node (Request) and Output Node (Response):
86
+ * Decision Table Node
87
+ * Switch Node
88
+ * Function Node
89
+ * Expression Node
90
+ * Decision Node
84
91
 
85
- Decision table is a node which allows business users to easily modify and add new rules in an intuitive way using spreadsheet like interface. The structure of decision table is defined by its schema. Schema consists of inputs and outputs.
92
+ ### Decision Table Node
86
93
 
87
- Decision tables are evaluated row by row from top to bottom, and depending on the hit policy a result is calculated.
94
+ #### Overview
88
95
 
89
- **Inputs**
96
+ Tables provide a structured representation of decision-making processes, allowing developers and business users to express complex rules in a clear and concise manner.
90
97
 
91
- Input fields are commonly (qualified) names for example `cart.total` or `customer.country`.
98
+ <img width="960" alt="Decision Table" src="https://gorules.io/images/decision-table.png">
99
+
100
+ #### Structure
101
+
102
+ At the core of the Decision Table is its schema, defining the structure with inputs and outputs. Inputs encompass business-friendly expressions using the ZEN Expression Language, accommodating a range of conditions such as equality, numeric comparisons, boolean values, date time functions, array functions and more. The schema's outputs dictate the form of results generated by the Decision Table.
103
+ Inputs and outputs are expressed through a user-friendly interface, often resembling a spreadsheet. This facilitates easy modification and addition of rules, enabling business users to contribute to decision logic without delving into intricate code.
104
+
105
+ #### Evaluation Process
106
+
107
+ Decision Tables are evaluated row by row, from top to bottom, adhering to a specified hit policy.
108
+ Single row is evaluated via Inputs columns, from left to right. Each input column represents `AND` operator. If cell is empty that column is evaluated **truthfully**, independently of the value.
109
+
110
+ If a single cell within a row fails (due to error, or otherwise), the row is skipped.
111
+
112
+ **HitPolicy**
113
+
114
+ The hit policy determines the outcome calculation based on matching rules.
115
+
116
+ The result of the evaluation is:
117
+
118
+ * **an object** if the hit policy of the decision table is `first` and a rule matched. The structure is defined by the output fields. Qualified field names with a dot (.) inside lead to nested objects.
119
+ * **`null`/`undefined`** if no rule matched in `first` hit policy
120
+ * **an array of objects** if the hit policy of the decision table is `collect` (one array item for each matching rule) or empty array if no rules match
121
+
122
+
123
+ #### Inputs
124
+
125
+ In the assessment of rules or rows, input columns embody the `AND` operator. The values typically consist of (qualified) names, such as `customer.country` or `customer.age`.
126
+
127
+ There are two types of evaluation of inputs, `Unary` and `Expression`.
128
+
129
+
130
+ **Unary Evaluation**
131
+
132
+ Unary evaluation is usually used when we would like to compare single fields from incoming context separately, for example `customer.country` and `cart.total` . It is activated when a column has `field` defined in its schema.
133
+
134
+ ***Example***
135
+
136
+ For the input:
92
137
 
93
138
  ```json
94
139
  {
95
- "cart": {
96
- "total": 1000
97
- },
98
140
  "customer": {
99
141
  "country": "US"
142
+ },
143
+ "cart": {
144
+ "total": 1500
100
145
  }
101
146
  }
102
147
  ```
103
148
 
104
- Inputs are using business-friendly ZEN Expression Language. The language is designed to follow these principles:
149
+ <img width="960" alt="Decision Table Unary Test" src="https://gorules.io/images/decision-table.png">
150
+
151
+ This evaluation translates to
152
+
153
+ ```
154
+ IF customer.country == 'US' AND cart.total > 1000 THEN {"fees": {"percent": 2}}
155
+ ELSE IF customer.country == 'US' THEN {"fees": {"flat": 30}}
156
+ ELSE IF customer.country == 'CA' OR customer.country == 'MX' THEN {"fees": {"flat": 50}}
157
+ ELSE {"fees": {"flat": 150}}
158
+ ```
105
159
 
106
- * Side-effect free
107
- * Dynamic types
108
- * Simple syntax for broad audiences
109
160
 
110
161
  List shows basic example of the unary tests in the Input Fields:
111
162
 
@@ -126,17 +177,45 @@ List shows basic example of the unary tests in the Input Fields:
126
177
 
127
178
  Note: For the full list please visit [ZEN Expression Language](https://gorules.io/docs/rules-engine/expression-language/).
128
179
 
180
+ **Expression Evaluation**
181
+
182
+ Expression evaluation is used when we would like to create more complex evaluation logic inside single cell. It allows us to compare multiple fields from the incoming context inside same cell.
183
+
184
+ It can be used by providing an empty `Selector (field)` inside column configuration.
185
+
186
+ ***Example***
187
+
188
+ For the input:
189
+
190
+ ```json
191
+ {
192
+ "transaction": {
193
+ "country": "US",
194
+ "createdAt": "2023-11-20T19:00:25Z",
195
+ "amount": 10000
196
+ }
197
+ }
198
+ ```
199
+
200
+ <img width="960" alt="Decision Table Expression" src="https://gorules.io/images/decision-table-expression.png">
201
+
202
+ ```
203
+ IF time(transaction.createdAt) > time("17:00:00") AND transaction.amount > 1000 THEN {"status": "reject"}
204
+ ELSE {"status": "approve"}
205
+ ```
206
+
207
+ Note: For the full list please visit [ZEN Expression Language](https://gorules.io/docs/rules-engine/expression-language/).
208
+
209
+
129
210
  **Outputs**
130
211
 
131
- The result of the decisionTableNode evaluation is:
212
+ Output columns serve as the blueprint for the data that the decision table will generate when the conditions are met during evaluation.
132
213
 
133
- * an object if the hit policy of the decision table is FIRST and a rule matched. The structure is defined by the output fields. Qualified field names with a dot (.) inside lead to nested objects.
134
- * `null`/`undefined` if no rule matched in FIRST hit policy
135
- * an array of objects if the hit policy of the decision table is COLLECT (one array item for each matching rule) or empty array if no rules match
214
+ When a row in the decision table satisfies its specified conditions, the output columns determine the nature and structure of the information that will be returned. Each output column represents a distinct field, and the collective set of these fields forms the output or result associated with the validated row. This mechanism allows decision tables to precisely define and control the data output.
136
215
 
137
- Example:
216
+ ***Example***
138
217
 
139
- <img width="860" alt="Screenshot 2023-03-10 at 22 57 04" src="https://user-images.githubusercontent.com/60513195/224436208-a2266cec-d0c6-42c7-8607-a4071e6a950b.png">
218
+ <img width="860" alt="Decision Table Output" src="https://gorules.io/images/decision-table-output.png">
140
219
 
141
220
  And the result would be:
142
221
 
@@ -151,20 +230,63 @@ And the result would be:
151
230
  }
152
231
  }
153
232
  ```
233
+ ### Switch Node (NEW)
234
+
235
+ The Switch node in GoRules JDM introduces a dynamic branching mechanism to decision models, enabling the graph to diverge based on conditions.
154
236
 
155
- ### Functions
237
+ Conditions are written in a Zen Expression Language.
156
238
 
157
- Function nodes are JavaScript lambdas that allow for quick and easy parsing, re-mapping or otherwise modifying the data. Inputs of the node are provided as function's arguments. Functions are executed on top of Google's V8 Engine that is built in into the ZEN Engine.
239
+ By incorporating the Switch node, decision models become more flexible and context-aware. This capability is particularly valuable in scenarios where diverse decision logic is required based on varying inputs. The Switch node efficiently manages branching within the graph, enhancing the overall complexity and realism of decision models in GoRules JDM, making it a pivotal component for crafting intelligent and adaptive systems.
240
+
241
+ The Switch node preserves the incoming data without modification; it forwards the entire context to the output branch(es).
242
+
243
+ <img width="960" alt="Switch / Branching" src="https://gorules.io/images/decision-graph.png">
244
+
245
+ #### HitPolicy
246
+ There are two HitPolicy options for the switch node, `first` and `collect`.
247
+
248
+ In the context of a first hit policy, the graph branches to the initial matching condition, analogous to the behavior observed in a table. Conversely, under a collect hit policy, the graph extends to all branches where conditions hold true, allowing branching to multiple paths.
249
+
250
+ Note: If there are multiple edges from the same condition, there is no guaranteed order of execution.
251
+
252
+ *Available from:*
253
+ * Python 0.16.0
254
+ * NodeJS 0.13.0
255
+ * Rust 0.16.0
256
+
257
+ ### Functions Node
258
+
259
+ Function nodes are JavaScript snippets that allow for quick and easy parsing, re-mapping or otherwise modifying the data using JavaScript. Inputs of the node are provided as function's arguments. Functions are executed on top of QuickJS Engine that is bundled into the ZEN Engine.
260
+
261
+ Function timeout is set to a 50ms.
158
262
 
159
263
  ```js
160
- const handler = (input) => {
161
- return input;
264
+ const handler = (input, {dayjs, Big}) => {
265
+ return {
266
+ ...input,
267
+ someField: 'hello'
268
+ };
162
269
  };
163
270
  ```
164
271
 
165
- ### Decision
272
+ There are two built in libraries:
273
+ * [dayjs](https://www.npmjs.com/package/dayjs) - for Date Manipulation
274
+ * [big.js](https://www.npmjs.com/package/big.js) - for arbitrary-precision decimal arithmetic.
275
+
276
+ ### Expression Node
277
+ The Expression node serves as a tool for transforming input objects into alternative objects using the Zen Expression Language. When specifying the output properties, each property requires a separate row. These rows are defined by two fields:
278
+ - Key - qualified name of the output property
279
+ - Value - value expressed through the Zen Expression Language
280
+
281
+ Note: Any errors within the Expression node will bring the graph to a halt.
282
+
283
+ <img width="960" alt="Decision Table" src="https://gorules.io/images/expression.png">
284
+
285
+ ### Decision Node
286
+
287
+ The "Decision" node is designed to extend the capabilities of decision models. Its function is to invoke and reuse other decision models during execution.
166
288
 
167
- Decision is a special node whose purpose is for decision model to have an ability to call other/re-usable decision models during an execution.
289
+ By incorporating the "Decision" node, developers can modularize decision logic, promoting reusability and maintainability in complex systems.
168
290
 
169
291
  ## Support matrix
170
292
 
@@ -172,4 +294,4 @@ Decision is a special node whose purpose is for decision model to have an abilit
172
294
  | :------------ | :-------------- | :--------- | :----------- | :------------- |
173
295
  | yes | yes | yes | yes | yes |
174
296
 
175
- We do not support linux-musl for now as we are relying on the GoogleV8 engine to run function blocks as isolates.
297
+ We do not support linux-musl for now.
package/index.js CHANGED
@@ -1,3 +1,9 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /* prettier-ignore */
4
+
5
+ /* auto-generated by NAPI-RS */
6
+
1
7
  const { existsSync, readFileSync } = require('fs')
2
8
  const { join } = require('path')
3
9
 
@@ -11,7 +17,7 @@ function isMusl() {
11
17
  // For Node 10
12
18
  if (!process.report || typeof process.report.getReport !== 'function') {
13
19
  try {
14
- const lddPath = require('child_process').execSync('which ldd').toString().trim();
20
+ const lddPath = require('child_process').execSync('which ldd').toString().trim()
15
21
  return readFileSync(lddPath, 'utf8').includes('musl')
16
22
  } catch (e) {
17
23
  return true
@@ -231,6 +237,35 @@ switch (platform) {
231
237
  loadError = e
232
238
  }
233
239
  break
240
+ case 'riscv64':
241
+ if (isMusl()) {
242
+ localFileExisted = existsSync(
243
+ join(__dirname, 'zen-engine.linux-riscv64-musl.node')
244
+ )
245
+ try {
246
+ if (localFileExisted) {
247
+ nativeBinding = require('./zen-engine.linux-riscv64-musl.node')
248
+ } else {
249
+ nativeBinding = require('@gorules/zen-engine-linux-riscv64-musl')
250
+ }
251
+ } catch (e) {
252
+ loadError = e
253
+ }
254
+ } else {
255
+ localFileExisted = existsSync(
256
+ join(__dirname, 'zen-engine.linux-riscv64-gnu.node')
257
+ )
258
+ try {
259
+ if (localFileExisted) {
260
+ nativeBinding = require('./zen-engine.linux-riscv64-gnu.node')
261
+ } else {
262
+ nativeBinding = require('@gorules/zen-engine-linux-riscv64-gnu')
263
+ }
264
+ } catch (e) {
265
+ loadError = e
266
+ }
267
+ }
268
+ break
234
269
  default:
235
270
  throw new Error(`Unsupported architecture on Linux: ${arch}`)
236
271
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gorules/zen-engine",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "main": "index.js",
5
5
  "types": "./index.d.ts",
6
6
  "license": "MIT",
@@ -79,12 +79,12 @@
79
79
  "prepublishOnly": "napi prepublish",
80
80
  "version": "napi version"
81
81
  },
82
- "gitHead": "dc4706dc95c7c21f16c824e8d238984f7948a6c5",
82
+ "gitHead": "b06c37aa94cc56a6355ae418c6e0f6efdaf0b94b",
83
83
  "optionalDependencies": {
84
- "@gorules/zen-engine-darwin-x64": "0.14.0",
85
- "@gorules/zen-engine-linux-x64-gnu": "0.14.0",
86
- "@gorules/zen-engine-win32-x64-msvc": "0.14.0",
87
- "@gorules/zen-engine-linux-arm64-gnu": "0.14.0",
88
- "@gorules/zen-engine-darwin-arm64": "0.14.0"
84
+ "@gorules/zen-engine-darwin-x64": "0.15.0",
85
+ "@gorules/zen-engine-linux-x64-gnu": "0.15.0",
86
+ "@gorules/zen-engine-win32-x64-msvc": "0.15.0",
87
+ "@gorules/zen-engine-linux-arm64-gnu": "0.15.0",
88
+ "@gorules/zen-engine-darwin-arm64": "0.15.0"
89
89
  }
90
90
  }