@lemmabase/lemma-engine
v0.8.22
Published
A pure, declarative language for business rules.
Downloads
475
Maintainers
Readme
@lemmabase/lemma-engine
Lemma is a declarative language for business rules. This package is the engine, compiled to WebAssembly - runs in the browser, on Node, Bun, Deno, Cloudflare Workers, Vercel Edge, etc.
Pricing tiers, tax brackets, leave entitlement, eligibility checks, discount stacks: the rules that change, that auditors ask about, that legal writes in PDFs and engineers re-implement in operational code... Lemma is a language built specifically for your business rules. It is readable by stakeholders, executable anywhere, and impossible to drift out of sync.
spec pricing 2026-01-01
data money: measure
-> unit eur 1.00
-> decimals 2
data quantity : number
data is_vip : false
rule unit_price:
20 eur
unless quantity >= 10 then 18 eur
unless quantity >= 50 then 16 eur
unless is_vip then 15 eur
rule total:
unit_price * quantityimport { Lemma } from '@lemmabase/lemma-engine';
const engine = await Lemma();
await engine.load(pricing, 'pricing.lemma');
const response = engine.run(null, 'pricing', null, { quantity: 50, is_vip: false }, null);
// response.results.unit_price → 16 eur
// response.results.total → 800 eurThe Response carries every rule's value (or veto if no result could be computed), the input snapshot, and the source location of every rule that fired, allowing you to render an audit trail in your UI.
Why use it from JavaScript?
- Deterministic.
(spec, data, effective_date) → result. No DB, no clock, no ambient state. Same inputs → same outputs, every time. - Explainable. The
Responsetells you which rules contributed and why; pair it with the CLI for a full reasoning trace. - Time-aware. Multiple versions of the same spec coexist. Pass an
effectivedate and the engine resolves the version in force on that day. - Statically checked. Type errors, missing data, cycles, measure-family mismatches - all caught at
load()time. Bad specs never reachrun(). - Runs anywhere V8 does. ~2 MB WASM, no native binary, no postinstall script.
- Editor in a tab. Includes an in-process language server and a Monaco adapter, so you can build a real Lemma editor experience client-side - diagnostics, completion, formatting... even without setting up a server.
Install
npm install @lemmabase/lemma-engineBrowser
import { Lemma } from '@lemmabase/lemma-engine';
const engine = await Lemma();Lemma() initializes the WASM module once and returns an Engine. Serve over http(s), not file://. For manual control: init() then new Engine().
If your bundler emits IIFE, can't resolve import.meta.url, or refuses to ship lemma_bg.wasm as a separate asset, use the inlined entry - it embeds the wasm bytes in the JS bundle:
import { Lemma } from '@lemmabase/lemma-engine/iife';esbuild users get an auto-rewriting plugin:
import { lemmaEngineEsbuildPlugin } from '@lemmabase/lemma-engine/esbuild';
esbuild.build({ /* ... */ plugins: [lemmaEngineEsbuildPlugin()] });Node
Identical to the browser path:
import { Lemma } from '@lemmabase/lemma-engine';
const engine = await Lemma();For zero-fetch startup with a preloaded module: initSync({ module }) then new Engine().
In-process LSP + Monaco
import { init } from '@lemmabase/lemma-engine';
import { LspClient } from '@lemmabase/lemma-engine/lsp-client';
await init();
const client = new LspClient(monaco);
await client.start();
await client.initialize();
client.onDiagnostics((uri, diagnostics) => { /* render */ });
client.didOpen('file:///pricing.lemma', 'lemma', 1, source);A pre-wired Monaco adapter ships at @lemmabase/lemma-engine/monaco.
API
Engine (returned by Lemma() or new Engine()):
| Method | Description |
|--------|-------------|
| load(code, attribute?) | Parse and validate a .lemma spec set. Resolves on success; rejects with EngineError[]. |
| load_batch(sources, dependency?) | Load many sources in one planning pass (see lemma.d.ts). |
| fetch(name) | Download registry source only; resolves with { source, id }. Does not load. Rejects with EngineError[]. |
| list() | JSON array of ResolvedRepository: each has repository and specs (spec sets). Always includes embedded lemma / spec units. |
| format_repository(repo) | Canonical Lemma source for a loaded repository, formatted from the in-engine AST. Use "lemma" for the embedded units stdlib. |
| schema(repo, name, effective?) | SpecSchema; repo null for workspace. |
| run(repo, name, ruleNames, data, effective?, explain?) | Evaluate. Omit/null ruleNames for all rules; pass a non-empty array to scope. [] errors. Returns a Response. |
| format(code, attribute?) | Canonical formatting; throws EngineError on parse error. |
Full TypeScript types are bundled - see lemma.d.ts.
Registry dependencies
Specs that reference uses … @org/pkg need that package available. fetch only downloads; call load_batch to load the dependency, then load your workspace:
import { Lemma } from '@lemmabase/lemma-engine';
const engine = await Lemma();
const { source, id } = await engine.fetch('@iso/countries');
await engine.load_batch({ '': source }, id);
await engine.load(sourceThatUsesStd, 'app.lemma');In the browser, the registry must allow your origin (CORS). Use https or http://localhost when using fetch.
Status
Lemma is pre-1.0. The WASM API is stable for most use cases, but breaking changes may occur between minor versions. Pin your dependency version and review the changelog before upgrading.
WASM panic behavior
Rust panics cannot unwind on the wasm32 target, so an internal invariant violation (a bug) traps the WASM instance. The call throws a RuntimeError which you can catch with try/catch to fail gracefully, but the module's linear memory is poisoned — constructing a new Engine() from the same initialized module is not safe. To recover, re-initialize the WASM module (init() again) or, for robust containment, run the engine in a Web Worker and respawn the worker on trap. The panic message (prefixed BUG: ...) is logged to the console before the trap. All domain-level failures (invalid specs, bad data, impossible rules) are reported as EngineError[] or vetoes and never cause traps.
Related
lemmabase.com: public database for Lemma Specslemma: REPL, HTTP server, MCP server, formatterlemma-engine: same engine as a Rust cratelemma_engineon Hex: Elixir bindings via Rustler- VS Code / Cursor extension: search "Lemma Language" in the marketplace
License
Apache-2.0
