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.
The ZEN Engine compiles to WebAssembly, allowing you to evaluate decisions entirely in the browser with no backend required.
Installation
npm install @gorules/zen-engine --cpu=wasm32
For yarn v4, configure supportedArchitectures in .yarnrc.yml:supportedArchitectures:
cpu:
- current
- wasm32
Then install:yarn add @gorules/zen-engine
For yarn v1, use --ignore-engines as there is no other effective way to install wasm32 packages:yarn add @gorules/zen-engine --ignore-engines
Configure supportedArchitectures in pnpm-workspace.yaml:supportedArchitectures:
cpu:
- current
- wasm32
Then install:pnpm add @gorules/zen-engine
Server requirements
WASM requires SharedArrayBuffer support, which needs these HTTP headers:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Example Vite configuration:
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
{
name: 'configure-response-headers',
enforce: 'pre',
configureServer: (server) => {
server.middlewares.use((_req, res, next) => {
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
next()
})
},
},
],
})
Basic usage
import { ZenEngine } from '@gorules/zen-engine';
const res = await fetch('/rules/pricing.json');
const content = await res.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 remote sources. Combined with ZenDecisionContent for pre-compilation, this provides optimal performance for multi-decision applications.
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';
const cache = new Map();
const engine = new ZenEngine({
loader: async (key) => {
if (cache.has(key)) {
return cache.get(key);
}
const response = await fetch(`/rules/${key}`);
const buffer = await response.arrayBuffer();
const content = new ZenDecisionContent(new Uint8Array(buffer));
cache.set(key, content);
return content;
}
});
const response = await engine.evaluate('pricing.json', { amount: 100 });
console.log(response.result);
HTTP handler
When decisions make HTTP requests to external APIs, use httpHandler to proxy requests through your backend. This is necessary when the frontend cannot directly access services behind a firewall or private network:
const engine = new ZenEngine({
httpHandler: async (request) => {
const response = await fetch('/api/proxy', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request)
});
const data = await response.json();
return {
status: response.status,
headers: Object.fromEntries(response.headers.entries()),
data
};
}
});
Your backend proxy can forward requests to internal services, add authentication headers, or handle IAM credentials.
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 cleanup. Release engine resources when the application terminates to prevent memory leaks.