# Orama JS Pool

[![Rust](https://github.com/oramasearch/orama-js-pool/actions/workflows/ci.yml/badge.svg)](https://github.com/oramasearch/orama-js-pool/actions/workflows/ci.yml)

Orama JS Pool provides a pool of JavaScript engines (using the Deno runtime via [deno_core](https://crates.io/crates/deno_core)) for running JavaScript code from Rust. It is designed for high-throughput, parallel, and optionally sandboxed execution of JS functions, supporting both sync and async workflows.

## Quickstart

Here's how to run an async JavaScript function using a pool of JS engines:

```rust
use orama_js_pool::{ExecOptions, Pool, RuntimeError};
use std::time::Duration;

static CODE_ASYNC_SUM: &str = r#"
async function async_sum(a, b) {
    await new Promise(resolve => setTimeout(resolve, 100));
    return a + b
}
export default { async_sum };
"#;

#[tokio::main]
async fn main() -> Result<(), RuntimeError> {
    // Create a pool with 10 JS workers, with the module loaded
    let pool = Pool::builder()
        .max_size(10) // number of workers in the pool
        .with_evaluation_timeout(Duration::from_millis(200)) // module load timeout
        .with_execution_timeout(Duration::from_secs(30)) // default execution timeout
        .add_module("async_calculator", CODE_ASYNC_SUM.to_string())
        .build()
        .await?;

    let params = vec![1, 2];
    let result: u8 = pool
        .exec(
            "async_calculator", // module name
            "async_sum",        // function name
            &params,             // input parameters
            ExecOptions::new().with_timeout(Duration::from_millis(200)), // override pool timeout
        )
        .await?;

    println!("async_sum(1, 2) == {result}");
    assert_eq!(result, 3);
    Ok(())
}

```

## API Overview

### `Pool`

Main entry point. Manages a pool of JS workers, each with loaded modules.

- `Pool::builder()` — creates a new pool builder
- `PoolBuilder::max_size(n)` — sets worker pool size
- `PoolBuilder::with_evaluation_timeout(duration)` — module load timeout (default: 5 seconds)
- `PoolBuilder::with_execution_timeout(duration)` — default execution timeout for all workers (default: 30 seconds)
- `PoolBuilder::with_max_executions(limit)` — recycle workers after n executions (prevents memory leaks)
- `PoolBuilder::with_domain_permission(permission)` — restrict network access at pool level
- `PoolBuilder::add_module(name, code)` — loads a module into all workers
- `exec(module_name, function_name, params, ExecOptions)` — executes a function

### `ExecOptions`

Per-execution configuration (all optional, overrides pool defaults):

- `with_timeout(duration)`: Override pool execution timeout for this specific call
- `with_domain_permission(permission)`: Override pool domain permission for this specific call
- `with_stdout_sender(sender)`: Capture console.log/error output

### `RuntimeError`

All errors (startup, execution, JS exceptions) are reported as `RuntimeError`.

## Features

- **Parallel execution**: Multiple requests handled concurrently
- **Async and sync JS support**: Run both types of JS functions
- **Sandboxing**: Restrict network access via domain permissions (allow/deny lists)
- **Flexible timeouts**: Set default timeouts at pool level, override per execution
  - `evaluation_timeout`: Controls how long module loading can take (default: 5s)
  - `execution_timeout`: Controls how long function execution can take (default: 30s)
- **Typed input/output**: Use Rust types for parameters and results (via serde)
- **Worker recycling**: Prevent memory leaks with configurable max executions per worker

## Example: Streaming (if supported)

If your JS function is an async generator, you can use streaming APIs (see crate docs for details).

## License

Licensed under the Affero GPL v3 license. See the LICENSE file for details.
