Skip to main content

rig_boa/
lib.rs

1//! Lightweight execution of [Boa](https://github.com/boa-dev/boa) JavaScript programs.
2//!
3//! `BoaExecutor` creates a fresh JavaScript context for each call, keeping
4//! globals from separate application or agent requests isolated.
5
6use boa_engine::{Context, JsError, JsValue, Source};
7
8/// Errors returned while evaluating JavaScript.
9#[derive(Debug, thiserror::Error)]
10#[error(transparent)]
11pub struct BoaError(#[from] JsError);
12
13/// Executes JavaScript in a fresh Boa context.
14#[derive(Clone, Copy, Debug, Default)]
15pub struct BoaExecutor;
16
17impl BoaExecutor {
18    /// Evaluates `source` and returns its completion value.
19    ///
20    /// A fresh Boa context is used on each call, preventing globals from leaking
21    /// between independent application or agent requests.
22    pub fn eval(&self, source: impl AsRef<[u8]>) -> Result<JsValue, BoaError> {
23        let mut context = Context::default();
24        Ok(context.eval(Source::from_bytes(source.as_ref()))?)
25    }
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31
32    #[test]
33    fn evaluates_javascript() {
34        let value = BoaExecutor.eval("const x = 20; x + 22").unwrap();
35
36        assert_eq!(value.display().to_string(), "42");
37    }
38
39    #[test]
40    fn does_not_share_globals_between_calls() {
41        let executor = BoaExecutor;
42        executor.eval("globalThis.answer = 42").unwrap();
43
44        assert_eq!(
45            executor
46                .eval("typeof answer")
47                .unwrap()
48                .display()
49                .to_string(),
50            "\"undefined\""
51        );
52    }
53}