> ## 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.

# WASM Rules Engine

> Run the GoRules engine entirely in the browser using WebAssembly.

The ZEN Engine compiles to WebAssembly, allowing you to evaluate decisions entirely in the browser with no backend required.

## Installation

<Tabs>
  <Tab title="npm">
    ```bash theme={null}
    npm install @gorules/zen-engine --cpu=wasm32
    ```
  </Tab>

  <Tab title="yarn">
    For yarn v4, configure `supportedArchitectures` in `.yarnrc.yml`:

    ```yaml .yarnrc.yml theme={null}
    supportedArchitectures:
      cpu:
        - current
        - wasm32
    ```

    Then install:

    ```bash theme={null}
    yarn add @gorules/zen-engine
    ```

    For yarn v1, use `--ignore-engines` as there is no other effective way to install wasm32 packages:

    ```bash theme={null}
    yarn add @gorules/zen-engine --ignore-engines
    ```
  </Tab>

  <Tab title="pnpm">
    Configure `supportedArchitectures` in `pnpm-workspace.yaml`:

    ```yaml pnpm-workspace.yaml theme={null}
    supportedArchitectures:
      cpu:
        - current
        - wasm32
    ```

    Then install:

    ```bash theme={null}
    pnpm add @gorules/zen-engine
    ```
  </Tab>
</Tabs>

## 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:

```javascript vite.config.ts theme={null}
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

```javascript theme={null}
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.

```javascript theme={null}
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:

```javascript theme={null}
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:

```javascript theme={null}
try {
  const response = await decision.evaluate(input);
  console.log(response.result);
} catch (error) {
  console.error('Evaluation failed:', error.message);
}
```

Using `safeEvaluate`:

```javascript theme={null}
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:

```javascript theme={null}
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:

```javascript theme={null}
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:

```javascript theme={null}
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.
