Boam.Runtime
(boam v0.1.4)
Copy Markdown
GenServer wrapper around a single Boa runtime.
Each Boam.Runtime process owns one Rust resource, and that
resource owns one dedicated worker thread where the Boa engine actually runs.
This keeps the JavaScript engine isolated from BEAM scheduler threads while
still allowing synchronous eval/2 calls from Elixir.
Options
:name- optionalGenServername for the runtime process:dispatcher- existing dispatcher server to receivebeam.call(...)messages; accepts anyGenServer.server/0. This is the primary way to integrate Boam into a supervised application.:exports- map or keyword list of dispatch handlers when Boam starts its own convenience dispatcher:js_expose- nested tree of JavaScript shims to install onglobalThisduring startup:expose- legacy combined tree that configures both:exportsand:js_expose:prelude- JavaScript source string or list of strings to evaluate during startup, after any generated exposure shims:fallback- optional(name, args -> result)function used when a dispatch name is not present in:exports:dispatch_timeout- timeout for a singlebeam.call(...)round-trip; defaults to30_000, accepts:infinity
Dispatch Contract
JavaScript code may call:
beam.call("name", arg1, arg2)The dispatcher receives the function name and arguments as JSON-compatible Elixir values and may reply with:
- any JSON-compatible value
{:ok, value}{:error, reason}
Returning {:error, reason} raises a JavaScript error with reason as the
message.
Exposing Functions
In most applications you will manage your own dispatch process and pass it in
through :dispatcher:
children = [
{MyApp.JsDispatcher, name: MyApp.JsDispatcher},
{Boam.Runtime,
dispatcher: MyApp.JsDispatcher,
js_expose: %{console: %{log: true}}}
]The built-in dispatcher exists as convenience sugar. Register Elixir handlers
with :exports and install JavaScript shims with :js_expose:
exports: %{
"console.log" => fn [message] -> "logged: #{message}" end,
"logger.warn" => fn [message] -> "warn: #{message}" end
},
js_expose: %{
console: %{
log: true,
warn: {:dispatch, "logger.warn"}
}
}This creates console.log(...) and console.warn(...) automatically.
Dispatch handlers can also queue JS exposure at runtime by sending:
send(runtime, {:boam_expose_js, %{console: %{debug: true}}})The message is applied after the current runtime operation completes, which
avoids deadlocking a beam.call(...) handler.
Notes
- Avoid synchronously calling back into the same runtime from a dispatch
handler before replying, or the call will deadlock. Use
notify_expose_js/2orsend(runtime, {:boam_expose_js, tree})from dispatch handlers instead. - If you pass a named sibling dispatcher, use a supervisor strategy that restarts the runtime after the dispatcher restarts, because the runtime resolves the dispatcher to a pid during startup.
- Top-level JavaScript
undefinedis surfaced as{:ok, :undefined}.
Summary
Functions
Returns a specification to start this module under a supervisor.
Returns the dispatcher pid currently attached to the runtime.
Evaluates a JavaScript string inside the runtime.
Installs JavaScript shim functions on globalThis.
Queues JavaScript shim exposure through an asynchronous message.
Starts a runtime process.
Types
@type dispatch_timeout() :: timeout()
@type start_option() :: {:name, GenServer.name()} | {:dispatcher, GenServer.server()} | {:exports, %{optional(export_name()) => Boam.Dispatcher.handler()} | keyword(Boam.Dispatcher.handler())} | {:js_expose, Boam.JS.export_tree()} | {:expose, legacy_expose()} | {:prelude, prelude()} | {:fallback, (String.t(), list() -> term())} | {:dispatch_timeout, dispatch_timeout()}
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec dispatcher(GenServer.server()) :: pid()
Returns the dispatcher pid currently attached to the runtime.
@spec eval(GenServer.server(), String.t()) :: {:ok, term()} | {:error, String.t()}
Evaluates a JavaScript string inside the runtime.
The return value is either:
{:ok, value}for a JSON-compatible JavaScript result{:ok, :undefined}for top-level JavaScriptundefined{:error, message}if evaluation or dispatch fails
Example
{:ok, runtime} =
Boam.Runtime.start_link(
exports: %{
greet: fn [name] -> "hello #{name}" end
}
)
Boam.Runtime.eval(runtime, "beam.call('greet', 'Ada')")
#=> {:ok, "hello Ada"}
@spec expose_js(GenServer.server(), Boam.JS.export_tree()) :: :ok | {:error, String.t()}
Installs JavaScript shim functions on globalThis.
This call is synchronous and should be used only when the runtime is idle.
Do not call it from a beam.call(...) dispatch handler; use
notify_expose_js/2 instead.
@spec notify_expose_js(GenServer.server(), Boam.JS.export_tree()) :: :ok
Queues JavaScript shim exposure through an asynchronous message.
This is safe to use from dispatch handlers because it does not wait for the runtime process to finish its current evaluation.
@spec start_link([start_option()]) :: GenServer.on_start()
Starts a runtime process.
See the module documentation for the supported options and dispatch contract.