README.md

# Boam

Boam is an Elixir wrapper around the [Boa](https://boajs.dev/) JavaScript
engine, implemented as a Rustler NIF.

The library starts a dedicated JavaScript runtime per Elixir process and lets
JavaScript call back into the BEAM through an explicit dispatch bridge.

## Features

- Boa-backed JavaScript evaluation from Elixir
- Dedicated runtime thread per engine, matching Boa's thread-safety model
- `beam.call(name, ...args)` bridge from JavaScript into Elixir
- JSON-compatible value round-tripping between JS and Elixir
- Configurable dispatcher process for custom routing and supervision setups

## Installation

Add `boam` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:boam, "~> 0.1.0"}
  ]
end
```

## Quick Start

```elixir
{:ok, runtime} =
  Boam.start_link(
    exports: %{
      sum: fn [left, right] -> left + right end,
      greet: fn [name] -> "hello #{name}" end
    }
  )

Boam.eval(runtime, "1 + 2")
#=> {:ok, 3}

Boam.eval(runtime, "beam.call('sum', 2, 3)")
#=> {:ok, 5}

Boam.eval(runtime, "beam.call('greet', 'Ada')")
#=> {:ok, "hello Ada"}
```

## Value Model

Boam intentionally restricts the bridge to JSON-compatible values:

- JavaScript `null` maps to Elixir `nil`
- booleans, numbers, strings, arrays, and objects round-trip normally
- top-level JavaScript `undefined` becomes `{:ok, :undefined}`
- `undefined` is rejected when passed through `beam.call(...)`
- JavaScript object keys come back as strings

For predictable round-tripping, return Elixir maps with string keys from
dispatch handlers.

## Dispatching Into Elixir

JavaScript code can call:

```javascript
beam.call("name", arg1, arg2)
```

That request is delivered to a `Boam.Dispatcher` process on the BEAM side. A
handler can return:

- any JSON-compatible value
- `{:ok, value}`
- `{:error, reason}`

If a handler crashes, the JavaScript caller receives an error instead of
hanging forever.

## Architecture

- `Boam` is the small public entrypoint
- `Boam.Runtime` owns the NIF resource and runtime lifecycle
- `Boam.Dispatcher` resolves and executes `beam.call(...)` handlers
- the internal NIF bridge module is intentionally hidden from the generated docs

## Generating Docs

Generate the docs locally with:

```bash
mix docs
```

The generated site will be written to `doc/`.