resuma 0.2.3

Resuma β€” SSR + Resumability + Islands + Server Actions + JS Bridge for Rust
Documentation
<div align="center">

# 🌊 Resuma

[![Crates.io](https://img.shields.io/crates/v/resuma.svg)](https://crates.io/crates/resuma)
[![docs.rs](https://img.shields.io/docsrs/resuma)](https://docs.rs/resuma)
[![License](https://img.shields.io/crates/l/resuma.svg)](https://github.com/GolfredoPerezFernandez/resuma)

**The first Rust web framework with SSR + Resumability + Islands + Server Actions + a friendly JS Bridge.**

*Zero hydration, true resumability, native islands, automatic Rust→JS handler compilation.*

**Install:** [`cargo install resuma`](https://crates.io/crates/resuma) Β· **Docs:** [resuma-docs.fly.dev](https://resuma-docs.fly.dev) Β· **API:** [docs.rs/resuma](https://docs.rs/resuma) Β· **Repo:** [GitHub](https://github.com/GolfredoPerezFernandez/resuma)

</div>

---

## What is this?

Resuma is a from-scratch Rust framework for building modern web apps with **resumability** instead of hydration:

## Resumability vs hydration

| Aspect | Classic SSR + hydration | **Resuma** |
| --- | --- | --- |
| Client work after load | Re-run components to attach listeners | **Resume** serialized state and handlers |
| Initial JS | Grows with app size | ~3KB runtime + lazy chunks |
| Interactive boundaries | Often manual | First-class `#[island]` |
| Server RPC | Custom wiring | `#[server] async fn` + built-in endpoint |
| Handler code on client | Ship framework runtime + app logic | Compile handlers to small JS via rs2js |
| Templates | Varies | JSX-like `view!{}` β€” no extra sigils |

The mental model: **components only run on the server**. The browser never re-executes them. Instead, the SSR pass serialises every signal, handler reference and island into the HTML, and the tiny client runtime *resumes* execution lazily β€” exactly when the user clicks something.

## Hello, Resuma

```rust
use resuma::prelude::*;

#[component]
fn Counter() -> View {
    let count = use_signal(0);
    view! {
        <main>
            <h1>"Count: " {count}</h1>
            <button onClick={ move |_| count.update(|c| *c += 1) }>"+"</button>
        </main>
    }
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    ResumaApp::new()
        .with_title("Counter")
        .page("/", || Counter::render(CounterProps::default()))
        .serve(ServeOptions::default())
        .await
}
```

That single click handler is **automatically translated to JavaScript** by `resuma-macros` (rs2js), lazy-loaded on first interaction, and runs against the resumed signal state. No hydration, no re-execution, no WASM bundle.

## Server actions

```rust
#[server]
async fn search(q: String) -> Vec<String> {
    db::search(&q).await
}

#[component]
fn LiveSearch() -> View {
    let query   = use_signal(String::new());
    let results = use_signal::<Vec<String>>(vec![]);

    view! {
        <input
            onInput={ js! {
                state.query.set(event.target.value);
                const r = await __resuma.action('search', [event.target.value]);
                state.results.set(r);
            }}
        />
        <ul>{format!("{} results", results.peek().len())}</ul>
    }
}
```

`#[server]` registers an RPC endpoint at `/_resuma/action/search`. The handler is dispatched there transparently.

## Islands

```rust
#[island]
fn LiveCounter() -> View {
    let count = use_signal(0);
    view! {
        <button onClick={ move |_| count.update(|c| *c += 1) }>{count}</button>
    }
}
```

Mark any component with `#[island]` and Resuma will package its handlers into an isolated chunk that ships only when the island scrolls into view (or immediately, configurable).

## Resuma Flow (full-stack layer)

**One crate** β€” `resuma` includes core + Flow in a single dependency.

| Resuma Flow | Purpose |
|-------------|---------|
| `FlowApp` | App builder with page registry |
| `#[load]` | Server data before render |
| `#[submit]` | Form mutations |
| `src/pages/` | File-based pages |

See [`docs/PACKAGE.md`](docs/PACKAGE.md) and [`docs/FLOW.md`](docs/FLOW.md).

**Live docs site:** https://resuma-docs.fly.dev Β· or `cargo run -p example-website` β†’ http://127.0.0.1:3000

```bash
resuma new my-app                    # static SSR (default)
resuma new my-app --template todo    # full Resuma showcase
```

## Architecture

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   resuma crate (v0.2)                    β”‚
β”‚                                                          β”‚
β”‚   core ──► ssr ──► server (axum)                         β”‚
β”‚     β”‚              GET  /_resuma/runtime.js              β”‚
β”‚     β”‚              POST /_resuma/action/:name            β”‚
β”‚     └──► flow + router (pages, loads, submits)           β”‚
β”‚                                                          β”‚
β”‚   resuma-macros (separate crate)                         β”‚
β”‚     view! / #[component] / rs2js β†’ JS handlers           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚ HTTP
                         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Browser (~3KB)                         β”‚
β”‚   parse resuma/state Β· delegate events Β· lazy handlers   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for a deep dive.

**Security:** [`docs/SECURITY.md`](docs/SECURITY.md) β€” CSRF, headers, rate limits, production checklist.

**Backend patterns:** [`docs/BACKEND.md`](docs/BACKEND.md) β€” live in `examples/todo`.

**All docs:** [`docs/README.md`](docs/README.md) Β· `cargo run -p example-website`

**Publishing:** [`docs/PUBLISHING.md`](docs/PUBLISHING.md) β€” crates.io release checklist

## Project layout

```
Resuma/
β”œβ”€β”€ crates/
β”‚   β”œβ”€β”€ resuma/             # single runtime crate (core, ssr, server, flow, cli)
β”‚   └── resuma-macros/      # proc-macros + rs2js (required separate crate)
β”œβ”€β”€ runtime/                # TypeScript source for the ~3KB client runtime
└── examples/
    β”œβ”€β”€ counter/
    β”œβ”€β”€ todo/
    β”œβ”€β”€ flow-demo/
    β”œβ”€β”€ flow-pages/
    └── website/            # docs site
```

**Docs:** [`docs/README.md`](docs/README.md) Β· live site: `cargo run -p example-website`

## Getting started

> **Pre-requisites:** Rust 1.91+ ([rustup]https://rustup.rs).

### Install from crates.io (recommended)

```sh
cargo install resuma
resuma new my-app --template todo
cd my-app
resuma dev
```

- **Crate:** [crates.io/crates/resuma]https://crates.io/crates/resuma
- **API docs:** [docs.rs/resuma/0.2.3]https://docs.rs/resuma/0.2.3
- **Proc-macros:** [docs.rs/resuma-macros/0.2.3]https://docs.rs/resuma-macros/0.2.3

Library only (no CLI binary):

```toml
[dependencies]
resuma = { version = "0.2", default-features = false }
tokio = { version = "1", features = ["full"] }
```

### From source (development)

```sh
git clone https://github.com/GolfredoPerezFernandez/resuma
cd resuma
cargo install --path crates/resuma --features cli

# Examples
cargo run -p example-counter   # http://127.0.0.1:3000
cargo run -p example-todo      # full-stack + security showcase
cargo run -p example-website   # docs site
```

## What works in v0.2

βœ… `Signal<T>`, `use_signal`, `use_effect`, `use_computed`
βœ… `view!{}` macro with JSX-like syntax (no `$` noise)
βœ… `#[component]` with auto-generated props builder
βœ… `#[server]` async actions with JSON-RPC endpoint
βœ… `#[island]` interactive component boundary
βœ… `js!{}` escape hatch for raw JS handlers
βœ… Rust β†’ JS compiler for common handler patterns
βœ… SSR with resumability payload embedded in HTML
βœ… ~3KB client runtime (lazy event delegation + signals + RPC)
βœ… axum-based server with built-in `/_resuma/*` routes
βœ… File-based routing scanner (`src/routes/[id].rs` β†’ `/users/:id`)
βœ… `resuma` CLI: `new`, `dev`, `build`, `routes`

## Roadmap (v0.3+)

- [ ] Hot Module Reload via `resuma` CLI + websocket bridge
- [ ] Build-time pre-rendering for static sites
- [ ] Partial pre-rendering (PPR) β€” server shell + dynamic islands
- [ ] `#[island(load = "visible")]` lazy load policies
- [ ] Devtools extension for resumability payload inspection
- [ ] First-class TypeScript bindings for `js!{}` blocks
- [ ] WASM-backed islands for compute-heavy code (opt-in)

Already shipped in v0.2: single-crate layout, streaming SSR (Flow), layouts, file-based routing, security defaults, [crates.io publish](https://crates.io/crates/resuma).

## Why "Resuma"?

Spanish for both *resumes* (continues) and *summary* β€” fitting because the framework's superpower is **resuming** execution from a serialised summary of the server-side render.

## License

MIT OR Apache-2.0