Expose wasm engine clone and add prepare API

Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/58419b20-1586-4a23-85fb-5aed5f5d5961

Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-05-13 20:16:02 +00:00
committed by GitHub
parent 839933c933
commit 72515f6d4c
5 changed files with 97 additions and 0 deletions

View File

@@ -21,3 +21,6 @@ Run `cargo xtask build-wasm` to invoke wasm-pack with sensible defaults, or `car
## Usage
See [test.js](https://github.com/microsoft/regorus/blob/main/bindings/wasm/test.js) for example usage.
For best performance with large policies, call `engine.prepare()` after loading
policy/data, then use `engine.clone()` to create per-request engines.

View File

@@ -138,6 +138,14 @@ impl Engine {
self.engine.set_rego_v0(enable)
}
/// Clone this engine.
///
/// Useful for creating per-request engines after loading policy/data once.
#[wasm_bindgen(js_name = "clone")]
pub fn cloneEngine(&self) -> Engine {
Clone::clone(self)
}
/// Add a policy
///
/// The policy is parsed into AST.
@@ -158,6 +166,14 @@ impl Engine {
self.engine.add_data(data).map_err(error_to_jsvalue)
}
/// Prepare the engine for evaluation.
///
/// This initializes internal evaluation structures so a cloned engine can
/// evaluate without requiring an initial "dummy" evaluation.
pub fn prepare(&mut self) -> Result<(), JsValue> {
self.engine.prepare().map_err(error_to_jsvalue)
}
/// Get the list of packages defined by loaded policies.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
@@ -487,6 +503,9 @@ mod tests {
)?;
assert_eq!(pkg, "data.test");
// Prepare before first evaluation.
engine.prepare()?;
let results = engine.evalQuery("data".to_string())?;
let r = regorus::Value::from_json_str(&results).map_err(error_to_jsvalue)?;

View File

@@ -40,6 +40,13 @@ engine.addDataJson(`
}
`);
// Prepare internal evaluation structures once.
engine.prepare();
// Clone a prepared template engine for reuse.
var template = engine.clone();
engine = template.clone();
// Set policy input
engine.setInputJson(`
{

View File

@@ -505,6 +505,34 @@ impl Engine {
self.add_data(Value::from_json_str(data_json)?)
}
/// Prepare the engine for evaluation without executing a query or rule.
///
/// This parses and initializes internal evaluation data structures so that
/// subsequent evaluations (or cloned engines) can run without paying the
/// one-time preparation cost during the first evaluation call.
///
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let mut engine = Engine::new();
/// engine.add_policy("test.rego".to_string(), r#"
/// package test
/// import rego.v1
/// allow if input.user == "alice"
/// "#.to_string())?;
///
/// engine.prepare()?;
/// let mut cloned = engine.clone();
///
/// cloned.set_input_json(r#"{"user":"alice"}"#)?;
/// assert_eq!(cloned.eval_rule("data.test.allow".to_string())?, Value::from(true));
/// # Ok(())
/// # }
/// ```
pub fn prepare(&mut self) -> Result<()> {
self.prepare_for_eval(false, false)
}
/// Set whether builtins should raise errors strictly or not.
///
/// Regorus differs from OPA in that by default builtins will

View File

@@ -102,6 +102,46 @@ fn extension_with_state() -> Result<()> {
Ok(())
}
#[test]
fn prepare_then_clone_without_initial_eval() -> Result<()> {
let mut engine = Engine::new();
engine.add_policy(
"test.rego".to_string(),
r#"package test
import rego.v1
default allow := false
allow if {
input.user in data.allowed_users
}
"#
.to_string(),
)?;
engine.add_data(Value::from_json_str(
r#"{"allowed_users":["alice","bob"]}"#,
)?)?;
// Prepare once and clone without running an initial evaluation.
engine.prepare()?;
let mut alice_engine = engine.clone();
alice_engine.set_input_json(r#"{"user":"alice"}"#)?;
assert_eq!(
alice_engine.eval_rule("data.test.allow".to_string())?,
Value::from(true)
);
let mut mallory_engine = engine.clone();
mallory_engine.set_input_json(r#"{"user":"mallory"}"#)?;
assert_eq!(
mallory_engine.eval_rule("data.test.allow".to_string())?,
Value::from(false)
);
Ok(())
}
#[test]
#[cfg(feature = "azure_policy")]
#[cfg_attr(docsrs, doc(cfg(feature = "azure_policy")))]