Tyrex (Tyrex v0.3.0)

Copy Markdown View Source

Embedded Deno JS/TS runtime for Elixir.

Tyrex wraps the Deno runtime as a GenServer, allowing you to evaluate JavaScript and TypeScript code directly from Elixir. Each runtime is an isolated V8 instance with full access to Deno APIs.

Quick Start

{:ok, pid} = Tyrex.start()
{:ok, 3} = Tyrex.eval("1 + 2", pid: pid)
Tyrex.stop(pid: pid)

Named Runtime

# In your supervision tree
{Tyrex, name: MyApp.JS, main_module_path: "priv/js/app.js"}

# Then anywhere
{:ok, result} = Tyrex.eval("processData()", name: MyApp.JS)

Calling Elixir from JavaScript

JavaScript code can call Elixir functions via Tyrex.apply():

Tyrex.eval(~s|(async () => await Tyrex.apply("Enum", "sum", [[1,2,3]]))()|, pid: pid)
# => {:ok, 6}

Supervision and error handling

Tyrex.start_link/1 follows the standard OTP shape, so a Tyrex runtime (and Tyrex.Pool) can be added directly to a supervisor's child list. Runtime errors are returned as {:error, %Tyrex.Error{}} from the run/eval API; see the Error handling section of the README for a full breakdown of the possible :name values and how to pattern-match them.

Summary

Functions

Returns a specification to start this module under a supervisor.

Same as eval/2, but it assumes that there is a process with the name Tyrex (the default if you don't provide a name to start_link/1).

Run the given JavaScript code and return the result. If a promise is returned, it will be awaited.

Same as eval/1, but raises Tyrex.Error if the result isn't successful.

Same as eval/2, but raises Tyrex.Error if the result isn't successful.

Start a Tyrex process without any main module.

Start a Tyrex process.

Start a Tyrex process linked to the current process.

Same as stop/1, but it assumes that there is a process with the name Tyrex (the default if you don't provide a name to start_link/1).

Stop a Tyrex process.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

eval(code)

@spec eval(binary()) :: {:ok, term()} | {:error, Tyrex.Error.t()}

Same as eval/2, but it assumes that there is a process with the name Tyrex (the default if you don't provide a name to start_link/1).

Examples

iex> Tyrex.eval("1 + 2")
{:ok, 3}

eval(code, opts)

@spec eval(binary(), Keyword.t()) :: {:ok, term()} | {:error, Tyrex.Error.t()}

Run the given JavaScript code and return the result. If a promise is returned, it will be awaited.

Options

  • :blocking - Indicates whether the NIF call should block until the JavaScript execution finishes or not. Blocking is more performant, but it can also cause problems if the call takes too long. The default is false.
  • :name - The name of the Tyrex process. The default is Tyrex. Can't be provided if :pid is provided.
  • :pid - The pid of the Tyrex process. Can't be provided if :name is provided.
  • :timeout - See GenServer.call/3.

Examples

iex> Tyrex.eval("1 + 2")
{:ok, 3}

iex> Tyrex.eval("1 + 2", blocking: true)
{:ok, 3}

iex> {:ok, pid} = Tyrex.start()
iex> Tyrex.eval("1 + 2", pid: pid)
{:ok, 3}

eval!(code)

@spec eval!(binary()) :: term() | no_return()

Same as eval/1, but raises Tyrex.Error if the result isn't successful.

Use this when you'd rather treat runtime errors as exceptions than handle them in a case block. See Tyrex.Error for the possible :name values.

eval!(code, opts)

@spec eval!(binary(), Keyword.t()) :: term() | no_return()

Same as eval/2, but raises Tyrex.Error if the result isn't successful.

Use this when you'd rather treat runtime errors as exceptions than handle them in a case block. See Tyrex.Error for the possible :name values.

start()

@spec start() :: GenServer.on_start()

Start a Tyrex process without any main module.

See start/1 for more information.

Examples

iex> {:ok, pid} = Tyrex.start()
iex> Tyrex.eval("1 + 2", pid: pid)
{:ok, 3}

start(opts)

@spec start(Keyword.t()) :: GenServer.on_start()

Start a Tyrex process.

Options

  • :main_module_path - Path to the main JavaScript module. The default is to start the runtime without a main module.
  • :permissions - Runtime permissions. Defaults to :allow_all. See "Permissions" section below.
  • :startup_timeout - Maximum time in milliseconds to wait for the NIF to acknowledge runtime startup. Defaults to 30_000. If the NIF does not respond in time, init/1 returns {:stop, :nif_startup_timeout}.

Permissions

Control what the JavaScript runtime can access:

  • :allow_all — Full access to everything (default)
  • :none — No permissions (JS can only compute, no I/O)
  • Keyword list — Granular control per permission type

Each permission key accepts true (allow all), false (deny all), or a list of specific allowed values:

  • :allow_net / :deny_net — Network access (true, false, or ["host:port", ...])
  • :allow_read / :deny_read — File read access (true, false, or ["/path", ...])
  • :allow_write / :deny_write — File write access
  • :allow_env / :deny_env — Environment variables (true, false, or ["VAR", ...])
  • :allow_run / :deny_run — Subprocess execution
  • :allow_ffi / :deny_ffi — Foreign function interface
  • :allow_sys / :deny_sys — System info (hostname, OS, etc.)
  • :allow_import / :deny_import — Dynamic ES module imports

Examples

iex> Tyrex.start(main_module_path: "path/to/main.js")

# No permissions (pure computation only)
iex> Tyrex.start(permissions: :none)

# Only allow network and reading from /tmp
iex> Tyrex.start(permissions: [allow_net: true, allow_read: ["/tmp"]])

start_link(opts)

@spec start_link(Keyword.t()) :: GenServer.on_start()

Start a Tyrex process linked to the current process.

Options

  • :name - The name of the process. The default is Tyrex.

See start/1 for more options.

Examples

iex> Tyrex.start_link(name: MyApp.Tyrex)
iex> Tyrex.eval("1 + 2", name: MyApp.Tyrex)
{:ok, 3}

stop()

@spec stop() :: :ok

Same as stop/1, but it assumes that there is a process with the name Tyrex (the default if you don't provide a name to start_link/1).

stop(opts)

@spec stop(Keyword.t()) :: :ok

Stop a Tyrex process.

Options

  • :name - The name of the Tyrex process. The default is Tyrex. Can't be provided if :pid is provided.
  • :pid - The pid of the Tyrex process. Can't be provided if :name is provided.
  • :reason - See GenServer.stop/3.
  • :timeout - See GenServer.stop/3.