Boam (boam v0.1.4)

Copy Markdown

High-level entrypoint for running JavaScript through Boa from Elixir.

Boam wraps a dedicated Boa runtime behind a GenServer. Each runtime owns a single Rust worker thread because Boa Context values are not thread-safe.

JavaScript code can call back into Elixir with beam.call(name, ...args). The dispatch itself happens through a separate BEAM process, which keeps the runtime process focused on driving the JavaScript engine.

You can either:

  • pass your own dispatcher with :dispatcher
  • install JavaScript shims with :js_expose
  • register Elixir handlers with :exports when Boam manages the dispatcher
  • use legacy combined config with :expose
  • run startup JavaScript with :prelude
  • generate shim code manually with Boam.JS.export_prelude/1

Quick Start

{:ok, _dispatcher} =
  MyApp.JsDispatcher.start_link(name: MyApp.JsDispatcher)

{:ok, runtime} =
  Boam.start_link(
    dispatcher: MyApp.JsDispatcher,
    js_expose: %{math: %{sum: true}}
  )

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

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

The built-in dispatcher is available for simpler cases:

{:ok, runtime} =
  Boam.start_link(
    exports: %{
      "console.log" => fn [message] -> "logged: #{message}" end
    },
    js_expose: %{
      console: %{
        log: true
      }
    }
  )

Boam.eval(runtime, "console.log('hello')")
#=> {:ok, "logged: hello"}

Value Bridge

Values crossing between JavaScript and Elixir are intentionally restricted to JSON-compatible data:

  • null maps to nil
  • booleans, numbers, strings, arrays, and objects round-trip normally
  • top-level JavaScript undefined is returned as :undefined
  • undefined cannot be passed through beam.call/1

Returned JavaScript objects are serialized through Boa's JSON conversion, so object keys come back to Elixir as strings.

Architecture

  • Boam.Runtime owns the Rust runtime resource
  • Boam.Dispatcher receives beam.call(...) requests
  • Boam.JS generates JavaScript shim preludes
  • Boam provides the small public entrypoint for the common case

Summary

Functions

Returns the dispatcher pid attached to a runtime.

Evaluates JavaScript source code inside a running runtime.

Installs JavaScript shim functions on globalThis.

Queues JavaScript shim installation through an asynchronous message.

Starts a Boa runtime process.

Types

start_option()

@type start_option() ::
  {:name, GenServer.name()}
  | {:dispatcher, GenServer.server()}
  | {:exports, map() | keyword()}
  | {:js_expose, Boam.JS.export_tree()}
  | {:expose, map() | keyword()}
  | {:prelude, String.t() | [String.t()]}
  | {:fallback, (String.t(), list() -> term())}
  | {:dispatch_timeout, timeout()}

Functions

dispatcher(runtime)

@spec dispatcher(GenServer.server()) :: pid()

Returns the dispatcher pid attached to a runtime.

eval(runtime, source)

@spec eval(GenServer.server(), String.t()) :: {:ok, term()} | {:error, String.t()}

Evaluates JavaScript source code inside a running runtime.

This is a convenience wrapper around Boam.Runtime.eval/2.

expose_js(runtime, tree)

@spec expose_js(GenServer.server(), Boam.JS.export_tree()) ::
  :ok | {:error, String.t()}

Installs JavaScript shim functions on globalThis.

notify_expose_js(runtime, tree)

@spec notify_expose_js(GenServer.server(), Boam.JS.export_tree()) :: :ok

Queues JavaScript shim installation through an asynchronous message.

start_link(opts \\ [])

@spec start_link([start_option()]) :: GenServer.on_start()

Starts a Boa runtime process.

This is a convenience wrapper around Boam.Runtime.start_link/1.

See Boam.Runtime for the full option contract.