QuickBEAM-ng is intended to feel like an Elixir library first: runtimes are started from keyword options, host APIs are ordinary Elixir modules, and embedded JavaScript can be validated at Elixir compile time.
Use QuickBEAM.sandbox/2 when you want inspectable options:
opts = QuickBEAM.sandbox(:strict, memory_limit: 8 * 1024 * 1024)
{:ok, rt} = QuickBEAM.start(opts)Or use QuickBEAM.new/1 as a concise runtime constructor:
{:ok, rt} = QuickBEAM.new(sandbox: :strict)
{:ok, browser_rt} = QuickBEAM.new(sandbox: :browser)
{:ok, node_rt} = QuickBEAM.new(sandbox: :node)Presets are plain keyword options. You can override any value.
Import QuickBEAM to use ~JS:
import QuickBEAM
source = ~JS"1 + 2"
chunk = ~JS"1 + 2"cThe sigil accepts only string literals and validates JavaScript while Elixir code
is compiled. The c modifier returns a source-only %QuickBEAM.Chunk{}.
Chunks are first-class scripts:
{:ok, chunk} = QuickBEAM.parse_chunk("1 + 2")
{:ok, compiled} = QuickBEAM.compile_chunk(rt, "1 + 2")
{:ok, 3} = QuickBEAM.eval(rt, compiled)Bang helpers are available when exceptions are preferable:
chunk = QuickBEAM.parse_chunk!("1 + 2")
compiled = QuickBEAM.compile_chunk!(rt, "1 + 2")
3 = QuickBEAM.eval!(rt, compiled)If eval/3 receives a compiled chunk with options such as vars: or timeout:,
it evaluates the source path so option semantics stay identical to normal eval.
Define host functions with js:
defmodule MyApp.Tools do
use QuickBEAM.API, scope: "tools.math"
js double(n), do: n * 2
js add(a, b), runtime do
QuickBEAM.set_global(runtime, "lastCall", "add")
a + b
end
@variadic true
js join(args), do: Enum.join(args, ":")
def install(%QuickBEAM.API.Context{}) do
~JS"globalThis.toolsInstalled = true"c
end
end
{:ok, rt} = QuickBEAM.new(sandbox: :strict)
:ok = QuickBEAM.load_api(rt, MyApp.Tools)
{:ok, 10} = QuickBEAM.eval(rt, "tools.math.double(5)")defjs remains as a compatibility alias for js.
Supported host API features:
- nested scopes:
scope: "tools.math"orscope: [:tools, :math] - load-time scope override:
QuickBEAM.load_api(rt, MyAPI, scope: "custom") - install data:
QuickBEAM.load_api(rt, MyAPI, data: value) - multi-clause functions with guards
- multi-arity functions exported under one JavaScript name
- variadic functions with
@variadic true - runtime-aware functions via the three-argument form
- structured JS errors with
raise_js!/2
Example structured error:
js read(path) do
if forbidden?(path), do: raise_js!("TypeError", "forbidden path")
File.read!(path)
endFrom JavaScript this throws a TypeError.
QuickBEAM.Value exposes public guards/helpers for low-level BEAM-mode values:
import QuickBEAM.Value
is_object(value)
is_function(value)
is_symbol(value)
is_bigint(value)
QuickBEAM.Value.bigint(123)Most NIF-mode calls convert to ordinary Elixir data. These helpers are intended for BEAM-mode integrations, tests, and advanced host APIs.
QuickBEAM can inspect native QuickJS bytecode and generated BEAM code:
{:ok, rt} = QuickBEAM.start()
{:ok, bytecode} = QuickBEAM.compile(rt, "function add(a, b) { return a + b }")
{:ok, js_bc} = QuickBEAM.disasm(bytecode)
{:ok, beam_rt} = QuickBEAM.start(mode: :beam, apis: false)
{:ok, beam_disasm} = QuickBEAM.disasm(beam_rt, "function fib(n) { return n < 2 ? n : fib(n-1) + fib(n-2) }")This keeps embedded JavaScript inspectable instead of opaque.