# `Denox.Run.Base`
[🔗](https://github.com/gsmlg-dev/denox/blob/v0.9.0/lib/denox/run/base.ex#L1)

Shared GenServer dispatch logic for Run modules.

This module provides a `__using__` macro that injects GenServer boilerplate,
a common public API, and stdout dispatch logic for both the NIF-backed
`Denox.Run` and the CLI-backed `Denox.CLI.Run`.

## Public API (injected into using modules)

**Lifecycle:** `start_link/1`, `stop/1`

**I/O:** `send/2`, `recv/2`, `send_and_recv/3`

**Pub/Sub:** `subscribe/1`, `unsubscribe/1`

**Introspection:** `alive?/1`, `os_pid/1`

**Convenience (one-shot):**
  - `capture/1` — start, collect all lines, stop; returns `{:ok, [String.t()]}`
  - `stream/1` — start + lazy `Stream` (auto-stops on halt)
  - `stream_from/2` — lazy `Stream` from an existing PID (caller manages lifecycle)
  - `with_runtime/2` — start + user function + guaranteed stop via `after`

## Implementing a Backend

Modules that `use Denox.Run.Base, backend: :my_backend` must implement four
callbacks:

  * `c:init_backend/1` — start the backend (NIF resource or Port)
  * `c:send_backend/2` — write data to the backend's stdin
  * `c:stop_backend/1` — shut down the backend
  * `c:alive_backend?/1` — check if the backend is still running

See `Denox.Run` (NIF backend) and `Denox.CLI.Run` (subprocess backend) for
concrete implementations.

# `alive_backend?`

```elixir
@callback alive_backend?(backend_state :: term()) :: boolean()
```

# `init_backend`

```elixir
@callback init_backend(keyword()) :: {:ok, backend_state :: term()} | {:error, term()}
```

# `send_backend`

```elixir
@callback send_backend(backend_state :: term(), String.t()) :: :ok | {:error, term()}
```

# `stop_backend`

```elixir
@callback stop_backend(backend_state :: term()) :: :ok
```

# `resolve_specifier`

```elixir
@spec resolve_specifier(String.t()) :: String.t()
```

Resolve a specifier to a form suitable for the Deno runtime.

## Rules

  * Specifiers already prefixed with `npm:`, `jsr:`, `http://`, `https://`,
    or `file://` are passed through unchanged.
  * Scoped package names starting with `@` are prefixed with `npm:`.
  * Everything else is returned as-is (treated as a file path by the backend).

## Examples

    iex> Denox.Run.Base.resolve_specifier("npm:cowsay")
    "npm:cowsay"

    iex> Denox.Run.Base.resolve_specifier("@scope/pkg")
    "npm:@scope/pkg"

    iex> Denox.Run.Base.resolve_specifier("server.ts")
    "server.ts"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
