API reference
Public API of @nodora/js
The package exports four named entries: compile, createEvaluator,
registerFunction, and the Evaluator class.
import {
compile,
createEvaluator,
registerFunction,
Evaluator,
} from "@nodora/js";compile(src)
compile(src: string): Promise<string>Compiles a ruleset source string and returns the NIR program as a JSON
string. Rejects with an Error if parsing or semantic analysis fails;
the message preserves the position reported by the compiler.
const nir = await compile("rule R { out ok = true }");createEvaluator(programJSON)
createEvaluator(programJSON: string): Promise<Evaluator>Loads a compiled NIR JSON string and returns an Evaluator bound to
it. The first call also initializes the WebAssembly module; subsequent
calls reuse it.
Pass either the output of compile or NIR JSON produced ahead of time
by the CLI.
Evaluator
interface Evaluator {
getId(): number;
evaluate(ruleName: string, input?: Record<string, any>): EvaluationResult;
evaluateAsync(ruleName: string, input?: Record<string, any>): Promise<EvaluationResult>;
on(signalName: string, callback: (...args: any[]) => void): Evaluator;
destroy(): void;
}
type EvaluationResult = {
outputs: Record<string, any>;
emitted_signals: { name: string; args: any[] }[];
};evaluate(ruleName, input?)
Runs the named rule against input and returns the result
synchronously. Throws if ruleName is not declared in the program.
evaluateAsync(ruleName, input?)
Same semantics as evaluate, returned as a Promise. Both forms run
the rule to completion on the calling stack; the async variant exists
purely for ergonomics when you want to mix it with other awaited work.
on(signalName, callback)
Registers a callback for emissions of a given signal. The signal's positional arguments are spread into the callback in declaration order. Returns the evaluator for chaining.
evaluator
.on("BlockAccount", (user_id) => { /* ... */ })
.on("Audit", (user_id) => { /* ... */ });If a callback throws, the error is swallowed and the evaluation continues. This is to keep rule output stable in the face of buggy listeners.
getId()
Returns the integer handle the WASM runtime uses to track this evaluator. Useful only for debugging.
destroy()
Releases the evaluator handle on the WASM side. Call it when you're
done. After destroy, all other methods throw.
registerFunction(options)
registerFunction(options: {
name: string;
namespace?: string;
args?: { name: string; type: Type; required?: boolean }[];
returnType: Type;
fn: (...args: any[]) => any;
}): Promise<void>Adds a function to the runtime registry so rulesets can call it.
The registration is consulted at both compile time and evaluation time:
- The compiler resolves the function's namespace and name when it sees
the call expression, so
registerFunctionmust run beforecompile. - The evaluator looks the function up again on every call, so the same
registration must also exist in any process that calls
evaluateorevaluateAsyncfor a ruleset that uses it.
If you compile in one process and evaluate in another (for example,
build-time compile in Node and run-time evaluate in a browser), call
registerFunction in both. NIR does not embed the function body,
only its qualified name; the host has to provide the implementation.
await registerFunction({
namespace: "js",
name: "is_corporate_email",
args: [{ name: "email", type: "string", required: true }],
returnType: "bool",
fn: (email) => /@(corp|company)\./.test(email),
});Type strings
The type and returnType fields accept these strings:
| Form | Meaning |
|---|---|
"string" | String value. |
"number" | Numeric value (stored as float64 internally). |
"bool" | Boolean. |
"object" | Object (string-keyed map). |
"any" | Top type, accepts anything. |
"array<T>" | Array of T. T may be any of the other forms. |
"array" | Shorthand for array<any>. |
"A|B|..." | Union of two or more of the above. |
Rules
- The function must be synchronous. Returning a
Promisemakes the evaluation that calls it reject withasync functions are not supported. - If
fnthrows, the evaluator surfaces the error to the caller ofevaluate/evaluateAsyncwith a message containing the function's fully qualified name. - A name may only be registered once per namespace. Re-registering the same name rejects.
- Omitting
namespaceregisters the function under the core namespace, callable by bare name from rulesets.
Lifecycle notes
- The WebAssembly module is loaded lazily on the first
compileorcreateEvaluatorcall and cached for the lifetime of the process. - The Node and browser entry points are wired through the package's conditional exports, so bundlers and Node both pick the right loader without any configuration on your part.
- Each
Evaluatorkeeps its own registry of signal listeners. Listeners registered on one evaluator do not fire for emissions from another.