napi-async-runtime 0.2.0

Shared pluggable async runtime for napi-rs addons: a tokio-free async/CPU/blocking scheduler plus an optional napi AsyncRuntime SPI adapter
Documentation
# napi-async-runtime

A shared, tokio-free async runtime for napi-rs addons: a pluggable
async/CPU/blocking scheduler plus an optional adapter that registers it as the
addon's `AsyncRuntime` backend (napi's `async-runtime` SPI).

## Layers

- **Scheduler** (always compiled, napi-free). Exposed at the crate root:
  `spawn`, `try_spawn`, `spawn_detached`, `spawn_blocking`, `block_on`,
  `sleep_until`, `configure`/`configure_partial`/`configured_options`,
  `start`/`shutdown`, metrics (`metrics`/`reset_metrics`), and the host driver
  SPIs (`CurrentThreadTaskDriver`, `TimerDriver`).
- **napi adapter** (cargo feature `napi`, on by default). `install(options)`
  configures the scheduler and calls `register_async_runtime`; the
  JavaScript-facing host protocol is exported under stable names
  (`reserveCurrentThreadHostRegistration`, `registerCurrentThreadTaskHost`,
  `registerTimerHost`, `unregisterCurrentThreadTaskHost`,
  `unregisterTimerHost`, `isCurrentThreadHostRegistrationActive`,
  `getCurrentThreadTaskHostContractVersion`, `configureAsyncRuntime`,
  `getAsyncRuntimeConfig`, `getAsyncRuntimeMetrics`,
  `resetAsyncRuntimeMetrics`).

## Usage

```rust
use napi_async_runtime::{RuntimeOptions, install};

#[napi_derive::module_init]
fn init() {
  // Resolve your own configuration (env vars, defaults) FIRST; the scheduler
  // never reads the environment itself.
  install(RuntimeOptions::default()).expect("failed to install the shared async runtime");
}
```

This crate deliberately ships **no `module_init` of its own**: the host stays
in charge of configuration resolution order. `@napi-rs/async-runtime` provides
the matching JavaScript host installers (task host + timer host) for
CurrentThread builds.

## Implementation notes

- **Flavors.** `MultiThread` (native only) runs futures on a Rayon-backed
  worker pool; `CurrentThread` (the only flavor on WebAssembly) never creates
  threads and instead publishes _host turns_ through registered
  `CurrentThreadTaskDriver`s — on Node that driver is a native threadsafe
  function installed by `registerCurrentThreadTaskHost` (contract version
  **4**: a registration capability is reserved and validated before host
  installation performs side effects).
- **Timers.** MultiThread owns a timer heap serviced by a timekeeper thread.
  CurrentThread delegates each timer to the host event loop through the JS
  relay installed by `registerTimerHost`
  (`(relayId, ms) => Promise<void>` paired with `(relayId) => void`
  cancellation). Schedule and cancel share ONE per-relay health record (a
  single strike); three consecutive live-host failures evict the host;
  eviction is decided only by `Status::Closing` or the liveness probe, never
  by message text; relay ids are never reused while referable.
- **Blocking admission.** The blocking cap is `worker_threads - 1`: one
  execution lane always stays available for runnable futures and timer
  service. There is no hidden reserve worker, and napi never creates fallback
  threads when `spawn_blocking` declines.
- **Lifecycle.** `shutdown` closes admission, waits for the scheduler
  generation to quiesce, JOINS native workers (TLS destructors retire inside
  the barrier), and releases active resources before returning; generation
  identities fail closed before u64 reuse. Detached-task semantics match
  tokio: dropping a `JoinHandle` detaches, and shutdown may cancel accepted
  work by dropping futures.
- **Threadless wasm** (`wasm32-wasip1`, `wasm32-unknown-unknown`): no threads,
  no `Atomics.wait`. A `block_on` park that provably can never be woken fails
  loudly with the typed `BlockOnDeadlock` panic instead of hanging the JS
  event loop. The `wasm32-wasip1-threads` target is discriminated by this
  crate's build.rs (`napi_runtime_wasi_threads` cfg) because rustc exposes
  identical cfg sets for both WASI targets.

The scheduler and adapter were extracted from rolldown's shared async runtime
(rolldown#9977/#9978) and generalized; the wire behavior of the host protocol
is byte-compatible with the rolldown hosts at contract version 4.