README.md

# Javex

Compile JavaScript to WebAssembly with [Javy](https://github.com/bytecodealliance/javy)
and run it on [wasmtime](https://wasmtime.dev/), from Elixir.

Javex uses **dynamic linking by default**: each compiled module imports
QuickJS from a shared Javy plugin that is instantiated once per
`Javex.Runtime`. Compiled modules are tiny (a few KB) and cold starts
are fast enough to spin up a fresh instance per call.

```elixir
js = ~S"""
function readInput() {
  const chunks = [];
  let total = 0;
  while (true) {
    const buf = new Uint8Array(1024);
    const n = Javy.IO.readSync(0, buf);
    if (n === 0) break;
    total += n;
    chunks.push(buf.subarray(0, n));
  }
  const out = new Uint8Array(total);
  let o = 0;
  for (const c of chunks) { out.set(c, o); o += c.length; }
  return JSON.parse(new TextDecoder().decode(out));
}

function writeOutput(value) {
  Javy.IO.writeSync(1, new TextEncoder().encode(JSON.stringify(value)));
}

const input = readInput();
writeOutput({ sum: input.a + input.b });
"""

{:ok, mod} = Javex.compile(js)
{:ok, %{"sum" => 3}} = Javex.run(mod, %{a: 1, b: 2})
```

Javy's default I/O surface is `Javy.IO.readSync(fd, buf)` and
`Javy.IO.writeSync(fd, buf)`; the `readInput` / `writeOutput` helpers
above are the convention from
[Javy's README](https://github.com/bytecodealliance/javy#example).

## Installation

```elixir
def deps do
  [{:javex, "~> 0.1"}]
end
```

Add a runtime to your application's supervision tree:

```elixir
# lib/my_app/application.ex
children = [
  Javex.Runtime
  # or: {Javex.Runtime, default_fuel: 1_000_000}
]
```

`Javex.compile/2` works without any running process — it reads the
bundled provider plugin from `priv/` directly — so scripts and tests
can compile modules without starting a runtime.

Javex ships a Rust NIF (built with
[`rustler_precompiled`](https://github.com/philss/rustler_precompiled))
that wraps `javy-codegen` and `wasmtime`. Precompiled artifacts are
published as GitHub release assets, so consumers do not need a Rust
toolchain. Set `JAVEX_BUILD=1` to force a local source build. The Javy
plugin Wasm is bundled in `priv/`.

## API

- `Javex.compile/2` — compile a JS source string into a `Javex.Module`.
- `Javex.run/3` — run a compiled module with JSON or raw byte I/O.
- `Javex.Runtime.start_link/1` — start additional runtimes with custom
  fuel, memory, or timeout defaults.

`%Javex.Module{}` is a plain struct; if you need to persist one,
`:erlang.term_to_binary/1` round-trips it.

## Design

See `lib/javex.ex` for the full module docs. A few highlights:

- Starting a `Javex.Runtime` is the consumer's responsibility — add it
  to your own supervision tree. wasmtime's `Engine` is `Send + Sync`,
  so one runtime handles the whole BEAM. Spin up extra runtimes when
  you need different resource tiers (e.g. a tight fuel/memory cap for
  untrusted code).
- Each call creates a fresh Store and instance. This is viable
  precisely because dynamic linking keeps per-call cost low (the
  provider is already live).
- Modules track the SHA-256 of the provider plugin they were compiled
  against. Running on a runtime with a mismatched provider returns
  `{:error, %Javex.IncompatibleProviderError{}}` instead of a cryptic
  link-time trap.

## License

MIT.