@benrogmans/lemma-engine 0.5.2 → 0.6.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 +274 -33
  2. package/lemma_bg.wasm +0 -0
  3. package/package.json +6 -8
package/README.md CHANGED
@@ -1,55 +1,296 @@
1
- # Lemma Engine
1
+ # @benrogmans/lemma-engine
2
2
 
3
- **Logic for man and machine**
3
+ Lemma is a declarative programming language for expressing business rules. This WebAssembly build for JavaScript and TypeScript provides an embeddable runtime, so that Lemma code can be evaluated anywhere.
4
4
 
5
- A declarative logic language engine for expressing rules, facts, and business logic in a way that is both human-readable and machine-executable.
5
+ **New to Lemma?** Check out the [language introduction](https://github.com/benrogmans/lemma#quick-start) and [examples](https://github.com/benrogmans/lemma/tree/main/docs/examples).
6
6
 
7
- ## Features
7
+ ## Installation
8
8
 
9
- - **Declarative syntax** - Express rules naturally
10
- - **Type-safe units** - Built-in support for money, mass, length, time, etc.
11
- - **Rule composition** - Reference other rules and documents
12
- - **Conditional logic** - Unless clauses for business rules
13
- - **Date arithmetic** - Native datetime operations
14
- - **WebAssembly** - Run in the browser
9
+ ```bash
10
+ npm install @benrogmans/lemma-engine
11
+ ```
12
+
13
+ ## JavaScript API Reference
14
+
15
+ ### Initialization
16
+
17
+ #### Browser
18
+ ```javascript
19
+ import init, { WasmEngine } from '@benrogmans/lemma-engine';
20
+
21
+ // Async initialization (recommended for browsers)
22
+ await init();
23
+ const engine = new WasmEngine();
24
+ ```
25
+
26
+ #### Node.js
27
+ ```javascript
28
+ import { readFileSync } from 'fs';
29
+ import { WasmEngine, initSync } from '@benrogmans/lemma-engine';
30
+
31
+ // Synchronous initialization for Node.js
32
+ const wasmBytes = readFileSync('node_modules/@benrogmans/lemma-engine/lemma_bg.wasm');
33
+ initSync(wasmBytes);
34
+ const engine = new WasmEngine();
35
+ ```
36
+
37
+ #### Bundlers (Webpack, Vite, etc.)
38
+ ```javascript
39
+ import init, { WasmEngine } from '@benrogmans/lemma-engine';
40
+ import wasmUrl from '@benrogmans/lemma-engine/lemma_bg.wasm?url';
41
+
42
+ // Initialize with URL
43
+ await init(wasmUrl);
44
+ const engine = new WasmEngine();
45
+ ```
46
+
47
+ ### Core Methods
15
48
 
16
- ## Quick Start
49
+ #### `addLemmaCode(code: string, filename: string): string`
17
50
 
18
- ```rust
19
- use lemma::{Engine, LemmaResult};
51
+ Adds a Lemma document to the engine.
20
52
 
21
- fn main() -> LemmaResult<()> {
22
- let mut engine = Engine::new();
53
+ ```javascript
54
+ const result = engine.addLemmaCode(`
55
+ doc employee_contract
23
56
 
24
- engine.add_lemma_code(r#"
25
- doc pricing
26
- fact base_price = 100 USD
27
- fact quantity = 5
28
- rule total = base_price * quantity
29
- "#, "pricing.lemma")?;
57
+ fact salary = 5000 eur/month
58
+ fact start_date = 2024-01-15
59
+ fact vacation_days = 25
30
60
 
31
- let response = engine.evaluate("pricing", vec![])?;
32
- println!("{:#?}", response);
61
+ rule annual_salary = salary * 12
62
+ rule daily_rate = salary / 21
63
+ `, 'employee.lemma');
33
64
 
34
- Ok(())
65
+ const response = JSON.parse(result);
66
+ if (response.success) {
67
+ console.log('Document loaded:', response.data);
68
+ } else {
69
+ console.error('Error:', response.error);
35
70
  }
36
71
  ```
37
72
 
38
- ## Documentation
73
+ #### `evaluate(docName: string, facts: string): string`
39
74
 
40
- - **API docs**: Run `cargo doc --open` or visit [docs.rs](https://docs.rs/lemma)
41
- - **Language guide**: [Lemma language documentation](https://github.com/benrogmans/lemma/tree/main/docs)
42
- - **Examples**: [Complete examples](https://github.com/benrogmans/lemma/tree/main/docs/examples)
75
+ Evaluates a document with optional runtime facts.
43
76
 
44
- ## WebAssembly
77
+ ```javascript
78
+ // Evaluate with default facts
79
+ const result1 = engine.evaluate('employee_contract', '{}');
45
80
 
46
- Compile to WASM for use in browsers (from project root):
81
+ // Evaluate with runtime fact overrides (as JSON object)
82
+ const result2 = engine.evaluate('employee_contract', JSON.stringify({
83
+ salary: 6000,
84
+ vacation_days: 30
85
+ }));
47
86
 
48
- ```bash
49
- wasm-pack build lemma --target web --out-dir target/wasm
87
+ const response = JSON.parse(result2);
88
+ if (response.success) {
89
+ console.log('Document:', response.data.document);
90
+ console.log('Rules:', response.data.rules);
91
+ // Access specific rule results directly:
92
+ // response.data.rules.annual_salary.value
93
+ }
94
+ ```
95
+
96
+ #### `listDocuments(): string`
97
+
98
+ Lists all loaded documents in the engine.
99
+
100
+ ```javascript
101
+ const result = engine.listDocuments();
102
+ const response = JSON.parse(result);
103
+
104
+ if (response.success) {
105
+ console.log('Loaded documents:', response.data);
106
+ // response.data is an array of document names
107
+ }
108
+ ```
109
+
110
+ ### Response Format
111
+
112
+ All methods return a JSON string with this structure:
113
+
114
+ ```typescript
115
+ interface WasmResponse {
116
+ success: boolean;
117
+ data?: any; // Success data (varies by method)
118
+ error?: string; // Error message if success is false
119
+ warnings?: string[]; // Optional warnings
120
+ }
121
+ ```
122
+
123
+ ### Complete Example
124
+
125
+ ```javascript
126
+ import init, { WasmEngine } from '@benrogmans/lemma-engine';
127
+
128
+ async function calculatePricing() {
129
+ // Initialize WASM
130
+ await init();
131
+ const engine = new WasmEngine();
132
+
133
+ // Define pricing rules
134
+ const pricingDoc = `
135
+ doc product_pricing
136
+
137
+ fact base_price = 100 usd
138
+ fact quantity = 1
139
+ fact is_member = false
140
+ fact promo_code = ""
141
+
142
+ rule bulk_discount = 0%
143
+ unless quantity >= 10 then 5%
144
+ unless quantity >= 50 then 10%
145
+ unless quantity >= 100 then 15%
146
+
147
+ rule member_discount = 0%
148
+ unless is_member then 10%
149
+
150
+ rule promo_discount = 0%
151
+ unless promo_code == "SAVE20" then 20%
152
+ unless promo_code == "SAVE10" then 10%
153
+
154
+ rule best_discount = max(bulk_discount, member_discount, promo_discount)
155
+ rule final_price = base_price * quantity * (1 - best_discount)
156
+ `;
157
+
158
+ // Load the document
159
+ const loadResult = JSON.parse(
160
+ engine.addLemmaCode(pricingDoc, 'pricing.lemma')
161
+ );
162
+
163
+ if (!loadResult.success) {
164
+ throw new Error(loadResult.error);
165
+ }
166
+
167
+ // Calculate for different scenarios
168
+ const scenarios = [
169
+ { quantity: 25, is_member: false, promo_code: "" },
170
+ { quantity: 10, is_member: true, promo_code: "" },
171
+ { quantity: 5, is_member: false, promo_code: "SAVE20" }
172
+ ];
173
+
174
+ for (const scenario of scenarios) {
175
+ const result = JSON.parse(
176
+ engine.evaluate('product_pricing', JSON.stringify(scenario))
177
+ );
178
+
179
+ if (result.success) {
180
+ console.log(`Scenario:`, scenario);
181
+ // Access rule results directly by name
182
+ const finalPrice = result.data.rules.final_price.value;
183
+ const bestDiscount = result.data.rules.best_discount.value;
184
+ console.log(`Final price:`, finalPrice);
185
+ console.log(`Discount applied:`, bestDiscount);
186
+ console.log('---');
187
+ }
188
+ }
189
+ }
190
+
191
+ calculatePricing().catch(console.error);
50
192
  ```
51
193
 
194
+ ### TypeScript Support
195
+
196
+ The package includes TypeScript definitions. For better type safety:
197
+
198
+ ```typescript
199
+ import init, { WasmEngine } from '@benrogmans/lemma-engine';
200
+
201
+ interface PricingFacts {
202
+ quantity: number;
203
+ is_member: boolean;
204
+ promo_code: string;
205
+ }
206
+
207
+ interface PricingResults {
208
+ base_price: string;
209
+ final_price: string;
210
+ best_discount: string;
211
+ // ... other fields
212
+ }
213
+
214
+ interface EvaluationResponse {
215
+ success: boolean;
216
+ data?: {
217
+ document: string;
218
+ rules: {
219
+ [ruleName: string]: {
220
+ value: any; // The computed value (e.g., {Number: "100"}, {Unit: "50 EUR"})
221
+ veto?: string; // Present if rule was vetoed
222
+ missing_facts?: string[]; // Present if rule couldn't be evaluated
223
+ operations?: Array<{ // Operation records (always present if rule was evaluated)
224
+ type: string; // "fact_used", "operation_executed", "final_result", etc.
225
+ // Additional fields depend on type
226
+ }>;
227
+ };
228
+ };
229
+ };
230
+ error?: string;
231
+ warnings?: string[]; // Document-level warnings
232
+ }
233
+
234
+ async function typedExample() {
235
+ await init();
236
+ const engine = new WasmEngine();
237
+
238
+ // ... load document
239
+
240
+ const facts = {
241
+ quantity: 10,
242
+ is_member: true,
243
+ promo_code: "SAVE10"
244
+ };
245
+
246
+ const result: EvaluationResponse = JSON.parse(
247
+ engine.evaluate('product_pricing', JSON.stringify(facts))
248
+ );
249
+
250
+ if (result.success && result.data) {
251
+ const price = result.data.rules.final_price?.value;
252
+ // price might be {Unit: "100 USD"} or {Number: "100"}
253
+ // depending on the rule's result type
254
+ }
255
+ }
256
+ ```
257
+
258
+ ### Error Handling
259
+
260
+ ```javascript
261
+ try {
262
+ const result = JSON.parse(
263
+ engine.addLemmaCode('invalid syntax', 'bad.lemma')
264
+ );
265
+
266
+ if (!result.success) {
267
+ console.error('Lemma error:', result.error);
268
+ // Handle parse/semantic errors
269
+ }
270
+ } catch (e) {
271
+ // Handle JSON parse errors or WASM exceptions
272
+ console.error('System error:', e);
273
+ }
274
+ ```
275
+
276
+ ### Performance Tips
277
+
278
+ 1. **Initialize once**: The WASM module should be initialized once per application
279
+ 2. **Reuse engines**: Create one `WasmEngine` and load multiple documents
280
+ 3. **Batch operations**: Load all documents before evaluating
281
+ 4. **Cache results**: Evaluation results can be cached if facts don't change
282
+
283
+ ### Compatibility
284
+
285
+ Works in modern browsers with WebAssembly support and Node.js with ES module support.
286
+
52
287
  ## License
53
288
 
54
- Apache 2.0
289
+ Apache-2.0
290
+
291
+ ## Links
55
292
 
293
+ - [GitHub Repository](https://github.com/benrogmans/lemma)
294
+ - [Lemma Language Guide](https://github.com/benrogmans/lemma/tree/main/docs)
295
+ - [Examples](https://github.com/benrogmans/lemma/tree/main/docs/examples)
296
+ - [Report Issues](https://github.com/benrogmans/lemma/issues)
package/lemma_bg.wasm CHANGED
Binary file
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@benrogmans/lemma-engine",
3
+ "version": "0.6.0",
4
+ "description": "The programming language that means business.",
3
5
  "type": "module",
4
- "version": "0.5.2",
5
- "description": "Lemma: A declarative programming language for business rules. Run in browsers with WebAssembly.",
6
6
  "main": "lemma.js",
7
7
  "types": "lemma.d.ts",
8
8
  "files": [
@@ -13,13 +13,15 @@
13
13
  "lemma_bg.wasm.d.ts"
14
14
  ],
15
15
  "keywords": [
16
- "business-rules",
17
16
  "logic",
17
+ "rules",
18
18
  "declarative",
19
+ "dsl",
20
+ "rust",
19
21
  "wasm",
20
22
  "webassembly"
21
23
  ],
22
- "author": "Ben Rogmans <ben@amrai.nl>",
24
+ "author": "benrogmans <ben@amrai.nl>",
23
25
  "license": "Apache-2.0",
24
26
  "repository": {
25
27
  "type": "git",
@@ -28,9 +30,5 @@
28
30
  "homepage": "https://github.com/benrogmans/lemma",
29
31
  "bugs": {
30
32
  "url": "https://github.com/benrogmans/lemma/issues"
31
- },
32
- "scripts": {
33
- "test": "node test-wasm.js"
34
33
  }
35
34
  }
36
-