swr-runtime-web 0.1.0

Browser (wasm32) implementation of the swr-core Runtime trait with focus/online event sources
Documentation
# swr-rs

UI-agnostic stale-while-revalidate async cache for Rust, aligned with the
semantics of [SWR](https://swr.vercel.app) and TanStack Query. The core is a
headless `SwrClient` over a pure synchronous state machine (sans-io); async
runtimes and UI frameworks plug in as thin layers.

## Crates

- `swr` — the batteries-included entry point: re-exports the full `swr-core`
  API and picks the default runtime per platform (`swr::client()`).
- `swr-core` — state machine, cache, and the public client API. Compiles on
  native and `wasm32-unknown-unknown`.
- `swr-runtime-tokio` — tokio `Runtime` implementation for native targets.
- `swr-reqwest` — reqwest fetchers: `JsonFetcher` maps cache keys to HTTP
  requests with JSON decoding (`reqwest::Error` as the query error type).
- `swr-ureq` — the same for the blocking ureq client, bridged onto
  per-request worker threads (runtime-agnostic; `ureq::Error` as the query
  error type). This worker-thread + oneshot bridge is the general answer for
  any blocking fetcher — `Runtime` deliberately has no
  `spawn_blocking`, which would tie the core to tokio.
- `swr-gpui`[GPUI]https://crates.io/crates/gpui adapter: `GpuiRuntime`
  runs fetches/timers on GPUI's executors (no tokio needed; virtual-clock
  test support via `advance_clock`), and `Query` bridges watch changes into
  an entity that views observe and read lock-free during render. See
  `crates/swr-gpui/examples/status.rs`.
- `swr-runtime-web` — browser `Runtime` (`spawn_local` + `setTimeout` timers)
  plus a reference-counted focus/online event source (`WebEventSource::attach`
  forwards `focus`/`visibilitychange`/`online` to `SwrClient::broadcast`).
  wasm32-only; an empty crate on native targets.

## Usage

```rust
use swr::ReadPolicy;

#[tokio::main]
async fn main() {
    let client = swr::client();

    let user = client
        .fetch(
            ("user", 1u64),
            |(_, id): (&str, u64)| async move { load_user(id).await },
            ReadPolicy::StaleWhileRevalidate,
        )
        .await
        .unwrap();
    println!("{user}");
}

async fn load_user(id: u64) -> Result<String, String> {
    Ok(format!("user-{id}"))
}
```

To supply your own [`Runtime`] (clock/spawn/timers), depend on `swr-core`
plus a runtime crate directly and use `SwrClient::builder()`.

Fetchers are plain closures, so any HTTP client works inline. For reqwest
(`swr-reqwest`) and ureq (`swr-ureq`, same shape), the integration crates
remove the boilerplate:

```rust
use swr_reqwest::JsonFetcher;

let users: JsonFetcher<(&str, u64), User> =
    JsonFetcher::get(http, |(_, id)| format!("https://api.example.com/users/{id}"));
let user = client.fetch(("user", 1u64), users.clone(), ReadPolicy::StaleWhileRevalidate).await?;
```

A fuller walkthrough (caching, background refresh, optimistic mutation, prefix
invalidation) is in `crates/swr-runtime-tokio/examples/bff.rs`:

```sh
cargo run -p swr-runtime-tokio --example bff
```

## Development

```sh
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo check -p swr-core -p swr-runtime-web --target wasm32-unknown-unknown

# wasm smoke tests; needs wasm-bindgen-cli matching the locked
# wasm-bindgen version, plus Node for smoke.rs / a browser for browser.rs
CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner \
  cargo test -p swr-runtime-web --target wasm32-unknown-unknown --test smoke
```