@edgerules/node 0.0.0-alpha.202607061032 → 0.0.0-alpha.202607061957

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.
package/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # @edgerules/node
2
+
3
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=rimvydasb_edgerules&metric=alert_status&token=98a392e505393c255f7de845e8b74a32911f2435)](https://sonarcloud.io/summary/new_code?id=rimvydasb_edgerules)
4
+ [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=rimvydasb_edgerules&metric=bugs&token=98a392e505393c255f7de845e8b74a32911f2435)](https://sonarcloud.io/summary/new_code?id=rimvydasb_edgerules)
5
+ [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=rimvydasb_edgerules&metric=code_smells&token=98a392e505393c255f7de845e8b74a32911f2435)](https://sonarcloud.io/summary/new_code?id=rimvydasb_edgerules)
6
+ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=rimvydasb_edgerules&metric=coverage&token=98a392e505393c255f7de845e8b74a32911f2435)](https://sonarcloud.io/summary/new_code?id=rimvydasb_edgerules)
7
+ [![Duplicated Lines](https://sonarcloud.io/api/project_badges/measure?project=rimvydasb_edgerules&metric=duplicated_lines_density&token=98a392e505393c255f7de845e8b74a32911f2435)](https://sonarcloud.io/summary/new_code?id=rimvydasb_edgerules)
8
+
9
+ EdgeRules decision service for **Node.js**. Ships two builds:
10
+
11
+ | Export | Description |
12
+ |---|---|
13
+ | `@edgerules/node` | Production build — execution only (`DecisionService`) |
14
+ | `@edgerules/node/mutable` | Development build — execution + CRUD (`MutableDecisionService`) |
15
+
16
+ ---
17
+
18
+ ## Installation
19
+
20
+ ```sh
21
+ npm install @edgerules/node @edgerules/portable
22
+ ```
23
+
24
+ > Requires Node.js 18 or later (ESM only).
25
+
26
+ ---
27
+
28
+ ## Production Usage
29
+
30
+ ### Load from an EdgeRules DSL string
31
+
32
+ ```typescript
33
+ import { DecisionService } from '@edgerules/node';
34
+
35
+ const service = new DecisionService(`{
36
+ applicant: {
37
+ age: <number, required: true>
38
+ income: <number, default: 0>
39
+ }
40
+ isEligible: applicant.age >= 18 and applicant.income >= 1000
41
+ }`);
42
+
43
+ // Execute the whole model — inputs are bound via a model-shaped request object
44
+ const result = service.execute('*', { applicant: { age: 25, income: 2500 } });
45
+ // { applicant: { age: 25, income: 2500 }, isEligible: true }
46
+ ```
47
+
48
+ ### Load from a Portable JSON model
49
+
50
+ ```typescript
51
+ import { DecisionService } from '@edgerules/node';
52
+ import type { PortableRootContext } from '@edgerules/portable';
53
+
54
+ const model: PortableRootContext = {
55
+ '@version': '1.0',
56
+ '@model-name': 'Tax Calculator',
57
+ taxRate: 0.21,
58
+ calculateTax: {
59
+ '@kind': 'function',
60
+ '@parameters': { amount: 'number' },
61
+ '@body': {
62
+ '@kind': 'context',
63
+ result: 'amount * taxRate',
64
+ },
65
+ },
66
+ };
67
+
68
+ const service = new DecisionService(model);
69
+
70
+ // Execute a specific function — pass its arguments as request bindings
71
+ const tax = service.execute('calculateTax', { amount: 100 });
72
+ // { result: 21 }
73
+ ```
74
+
75
+ ### Executing a ruleset
76
+
77
+ ```typescript
78
+ const service = new DecisionService(`{
79
+ ruleset risk(age: number, income: number): {
80
+ hitPolicy: "first-match"
81
+ rules: [
82
+ { when: { age: 18..25, income: < 30000 }, then: { level: "high", limit: 1000 } }
83
+ { when: { age: 18..25, income: >= 30000 }, then: { level: "medium", limit: 5000 } }
84
+ { when: { age: > 25 }, then: { level: "low", limit: 20000 } }
85
+ ]
86
+ default: { level: "none", limit: 0 }
87
+ }
88
+ decision: risk(age: applicant.age, income: applicant.income)
89
+ }`);
90
+
91
+ const result = service.execute('decision', { applicant: { age: 23, income: 25000 } });
92
+ // { level: "high", limit: 1000 }
93
+ ```
94
+
95
+ ### Error handling
96
+
97
+ Every execution or model error is a structured `PortableError` — never a plain string.
98
+
99
+ ```typescript
100
+ import { DecisionService } from '@edgerules/node';
101
+ import type { PortableError } from '@edgerules/portable';
102
+
103
+ try {
104
+ service.execute('unknownField');
105
+ } catch (err) {
106
+ const error = err as PortableError;
107
+ console.error(error.type); // "EntryNotFound"
108
+ console.error(error.message); // human-readable, safe to surface
109
+ console.error(error.path); // "unknownField"
110
+ }
111
+ ```
112
+
113
+ | `error.type` | Meaning |
114
+ |---|---|
115
+ | `EntryNotFound` | Path does not exist in the model |
116
+ | `WrongFieldPath` | Invalid path, out-of-bounds array index |
117
+ | `Parse` | Expression string failed to parse |
118
+ | `Linking` | Type mismatch or broken reference |
119
+ | `Execution` | Runtime error during evaluation |
120
+
121
+ ---
122
+
123
+ ## Mutable / Development Usage
124
+
125
+ Use the `./mutable` subpath import to access `MutableDecisionService`, which adds CRUD operations on top of execution.
126
+ This build is intended for IDEs, rule editors, and test tooling — **not** for production traffic.
127
+
128
+ ```typescript
129
+ import { MutableDecisionService } from '@edgerules/node/mutable';
130
+
131
+ const service = new MutableDecisionService(`{
132
+ taxRate: 0.21
133
+ tax: amount * taxRate
134
+ }`);
135
+
136
+ // Read a node enriched with linked type information
137
+ const node = service.get('taxRate');
138
+ // { "@kind": "expression", "expression": 0.21, "type": "number", "readOnly": true }
139
+
140
+ // Update a field at runtime — the engine relinks lazily on the next execute
141
+ service.set('taxRate', 0.25);
142
+
143
+ // Read the entire model snapshot
144
+ const model = service.get('*');
145
+
146
+ // Add a new typed input
147
+ service.set('amount', { '@kind': 'type', type: 'number', required: true });
148
+
149
+ // Remove a field
150
+ service.remove('taxRate');
151
+
152
+ // Rename a field
153
+ service.rename('tax', 'vat');
154
+ ```
155
+
156
+ ### `get` filters
157
+
158
+ ```typescript
159
+ // Default FIELDS view — returns expressions, typed holes, and function schemas
160
+ service.get('isEligible');
161
+
162
+ // Full function definition (body included)
163
+ service.get('isEligible', 'FUNCTION_DEFINITIONS');
164
+
165
+ // Type definition
166
+ service.get('Customer', 'TYPE_DEFINITIONS');
167
+
168
+ // Everything at once
169
+ service.get('*', 'ALL');
170
+ ```
171
+
172
+ ---
173
+
174
+ ## API Reference
175
+
176
+ ### `DecisionService`
177
+
178
+ | Method | Description |
179
+ |---|---|
180
+ | `new DecisionService(model)` | Create from DSL string or `PortableRootContext` |
181
+ | `execute(method, args?)` | Evaluate `method` (field path, function, or `"*"`) against optional request bindings |
182
+
183
+ ### `MutableDecisionService` (dev build)
184
+
185
+ Inherits `execute` and adds:
186
+
187
+ | Method | Description |
188
+ |---|---|
189
+ | `get(path, filter?)` | Read a node enriched with inferred types |
190
+ | `set(path, node)` | Write an expression, typed hole, function, type, or context |
191
+ | `remove(path)` | Delete an entry |
192
+ | `rename(oldPath, newPath)` | Rename an entry within its context |
193
+
194
+ ---
195
+
196
+ ## Related packages
197
+
198
+ - [`@edgerules/web`](https://www.npmjs.com/package/@edgerules/web) — same API for the browser
199
+ - [`@edgerules/portable`](https://www.npmjs.com/package/@edgerules/portable) — Portable JSON type definitions
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@edgerules/node",
3
- "version": "0.0.0-alpha.202607061032",
3
+ "version": "0.0.0-alpha.202607061957",
4
4
  "type": "module",
5
5
  "description": "EdgeRules decision service for Node.js.",
6
6
  "dependencies": {
7
- "@edgerules/portable": "0.0.0-alpha.202607061032"
7
+ "@edgerules/portable": "0.0.0-alpha.202607061957"
8
8
  },
9
9
  "devDependencies": {
10
10
  "@edgerules/core": "*",