sppl 0.0.2

Embed static Svelte (and other static) apps into Rust servers.
Documentation
# sppl

**sppl** _(supple)_ — embed static Svelte apps into your Rust binary.

A Svelte/SvelteKit app, built with `adapter-static`, is just a tree of
HTML/CSS/JS files. `sppl` bakes that tree into your Rust binary at compile
time and hands it to your web framework as a single `Router` (or a generic
asset lookup) that already knows how to:

- serve every file with the correct `Content-Type`,
- resolve SvelteKit `adapter-static` `<route>.html` files for prerendered
  routes,
- fall back to `index.html` for client-side SPA routes,
- store **one gzipped copy** of each compressible asset and serve it as-is
  to clients that send `Accept-Encoding: gzip`, decompressing on the fly
  for clients that don't,
- ship as a single self-contained binary — no extra files to deploy.

## Compression

Run [`sppl::build::gzip_assets`](crates/sppl/src/build.rs) from your
`build.rs` once, after your Svelte build, and `sppl` takes care of the rest
at request time. Because only the gzipped bytes live in the binary, you pay
the storage cost once and the wire cost only when the client can't accept
gzip (essentially never, in practice).

## Layout

```
crates/sppl/         # the library
examples/app/        # SvelteKit + adapter-static demo (built with deno)
examples/server/     # axum server that embeds the demo
```

## Usage

```toml
# Cargo.toml
[dependencies]
sppl  = "0.0.1"
axum  = "0.7"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```

```rust
use axum::{routing::get, Router};

#[derive(sppl::RustEmbed)]
#[folder = "$CARGO_MANIFEST_DIR/../app/build"]
#[crate_path = "sppl::rust_embed"]
struct App;

#[tokio::main]
async fn main() {
    let api = Router::new()
        .route("/api/hello", get(|| async { "hello from rust" }));

    // Serve the embedded Svelte app on every other path.
    let app = api.fallback_service(sppl::axum::router::<App>());

    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}
```

If you'd rather wire things up yourself, `sppl::resolve::<App>(path)` returns
the matching `(path, EmbeddedFile)` using the same lookup rules and is
framework-agnostic.

## Running the example

```bash
# from the repo root — the server's build.rs runs `deno task build` for you:
cargo run -p sppl-example-server

# …or build the svelte app yourself:
deno task --cwd=examples/app build
SPPL_SKIP_SVELTE_BUILD=1 cargo run -p sppl-example-server
```

Set `SPPL_SKIP_SVELTE_BUILD=1` to skip the build-script step (useful in CI
when the build is produced upstream).

Then open <http://127.0.0.1:3000>.

## Testing

```bash
cargo test -p sppl
```

Covers `accepts_gzip` header parsing and the `resolve` lookup rules
(exact path, `.html` extension, trailing-slash `index.html`, SPA fallback,
and `.gz` preference). Fixture files live under
`crates/sppl/tests/fixtures/static/`.

## Requirements

- Rust (1.75+ recommended) — `cargo`
- [Deno]https://deno.com 2.x — drives the SvelteKit build via `deno task`

## License

MIT