Documentation Index
Fetch the complete documentation index at: https://docs.gorules.io/llms.txt
Use this file to discover all available pages before exploring further.
Install the ZEN Engine and evaluate your first decision in Node.js.
Installation
npm install @gorules/zen-engine
Basic usage
import { ZenEngine } from '@gorules/zen-engine';
import fs from 'fs';
const content = fs.readFileSync('./pricing-rules.json');
const engine = new ZenEngine();
const decision = engine.createDecision(content);
const response = await decision.evaluate({
customer: { tier: 'gold', yearsActive: 3 },
order: { subtotal: 150, items: 5 }
});
console.log(response.result);
// => { discount: 0.15, freeShipping: true }
engine.dispose();
Loader
The loader pattern enables dynamic decision loading from any storage backend. Combined with ZenDecisionContent for pre-compilation, this provides optimal performance for multi-decision applications.
File system
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';
import fs from 'fs/promises';
import path from 'path';
const cache = new Map();
const engine = new ZenEngine({
loader: async (key) => {
if (cache.has(key)) {
return cache.get(key);
}
const buffer = await fs.readFile(path.join('./rules', key));
const content = new ZenDecisionContent(buffer);
cache.set(key, content);
return content;
}
});
const response = await engine.evaluate('pricing.json', { amount: 100 });
console.log(response.result);
AWS S3
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import AdmZip from 'adm-zip';
const s3 = new S3Client({ region: 'us-east-1' });
const rules = new Map();
// Download and extract all decisions at startup
const command = new GetObjectCommand({
Bucket: 'my-rules-bucket',
Key: 'decisions.zip'
});
const s3Response = await s3.send(command);
const zipBuffer = Buffer.from(await s3Response.Body.transformToByteArray());
const zip = new AdmZip(zipBuffer);
for (const entry of zip.getEntries()) {
if (!entry.isDirectory) {
rules.set(entry.entryName, new ZenDecisionContent(entry.getData()));
}
}
const engine = new ZenEngine({
loader: async (key) => rules.get(key)
});
const response = await engine.evaluate('pricing.json', { amount: 100 });
Azure Blob Storage
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';
import { BlobServiceClient } from '@azure/storage-blob';
import AdmZip from 'adm-zip';
const blobService = BlobServiceClient.fromConnectionString(process.env.AZURE_STORAGE_CONNECTION);
const container = blobService.getContainerClient('rules');
const rules = new Map();
// Download and extract all decisions at startup
const blob = container.getBlobClient('decisions.zip');
const zipBuffer = await blob.downloadToBuffer();
const zip = new AdmZip(zipBuffer);
for (const entry of zip.getEntries()) {
if (!entry.isDirectory) {
rules.set(entry.entryName, new ZenDecisionContent(entry.getData()));
}
}
const engine = new ZenEngine({
loader: async (key) => rules.get(key)
});
const response = await engine.evaluate('pricing.json', { amount: 100 });
Google Cloud Storage
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';
import { Storage } from '@google-cloud/storage';
import AdmZip from 'adm-zip';
const storage = new Storage();
const bucket = storage.bucket('my-rules-bucket');
const rules = new Map();
// Download and extract all decisions at startup
const [zipBuffer] = await bucket.file('decisions.zip').download();
const zip = new AdmZip(zipBuffer);
for (const entry of zip.getEntries()) {
if (!entry.isDirectory) {
rules.set(entry.entryName, new ZenDecisionContent(entry.getData()));
}
}
const engine = new ZenEngine({
loader: async (key) => rules.get(key)
});
const response = await engine.evaluate('pricing.json', { amount: 100 });
Error handling
Using try-catch:
try {
const response = await decision.evaluate(input);
console.log(response.result);
} catch (error) {
console.error('Evaluation failed:', error.message);
}
Using safeEvaluate:
const response = await decision.safeEvaluate(input);
if (response.success) {
console.log(response.data.result);
} else {
console.error('Evaluation failed:', response.error);
}
Tracing
Enable tracing to inspect decision execution:
const response = await decision.evaluate(input, { trace: true });
console.log(response.trace);
// Each node's input, output, and performance timing
console.log(response.performance);
// Total evaluation time
Expression utilities
Evaluate ZEN expressions outside of a decision context:
import {
evaluateExpression,
evaluateUnaryExpression
} from '@gorules/zen-engine';
// Standard expressions
const sum = await evaluateExpression('a + b', { a: 5, b: 3 });
// => 8
const total = await evaluateExpression('sum(items)', { items: [1, 2, 3, 4] });
// => 10
// Unary expressions (comparison against $)
const isValid = await evaluateUnaryExpression('>= 5', { $: 10 });
// => true
const inList = await evaluateUnaryExpression('"US", "CA", "MX"', { $: 'US' });
// => true
Synchronous versions are also available:
import {
evaluateExpressionSync,
evaluateUnaryExpressionSync
} from '@gorules/zen-engine';
const result = evaluateExpressionSync('a * 2', { a: 10 });
// => 20
Best practices
Use ZenDecisionContent for caching. Pre-compiling decisions avoids repeated parsing overhead. Cache compiled content in a Map keyed by decision name.
Initialize the engine once. Create a single ZenEngine instance at application startup and reuse it for all evaluations.
Implement a loader for dynamic decisions. The loader pattern centralizes decision loading logic and enables caching at the source.
Call dispose() on shutdown. Release engine resources when your application terminates to prevent memory leaks.