Files
hemx/README.md
T
slhx agent 78aff362ff docs(recipes): add pwa offline boundary
Document optional PWA/offline support as an adapter boundary: cached shell, domain-command queues, server-canonical replay, conflict/fingerprint behavior, and sync-crate shape without adding a client state graph or service worker to core.

req: canonical_authoring/008

req: runtime/003

req: runtime/004

req: auth/002

req: auth/004

req: failure/004

req: failure/005

req: sync/001

req: sync/007
2026-06-05 10:03:39 +02:00

144 lines
8.0 KiB
Markdown

# hemx
hemx is checked hypermedia for Rust: write hemplate templates, write typed Rust
handlers, and return generated UI commands. The browser receives checked UI
commands; ordinary server-first apps do not need a frontend framework,
handwritten UI JavaScript, selector targeting, or raw runtime primitives. req: pitch/001 req: canonical_authoring/001
Status: the repository currently has a strong v0 example/API path, a compile-tested
SaaS tutorial skeleton, an advanced Kanban milestone sketch, and a full techdemo.
The polished v1 product story is still being filled out through docs, recipes,
diagnostics, and stability guidance. See `docs/v1-readiness.md` for the current
close-gap audit.
## The normal path
For beginner and production-shaped app code, stay on this path. req: public_api/001 req: public_api/005
1. **Templates declare the surface.** `.heml` files declare roots, slots,
forms, handles, keys, page targets, optional pending states, and explicit
leaf islands with `data-hemx-*` attributes.
2. **Build generates typed helpers.** `hemx_build::app().run()` consumes the
hemplate Surface and emits generated Rust helpers for slots, forms, handles,
page targets, classes, and events. req: ceremony/003 req: build/001
3. **Handlers are plain Rust.** App code uses `#[hemx::handler]` functions with
ordinary domain types, framework extractors, generated `Form<T>` inputs, and
`impl IntoEffect` or `Result<impl IntoEffect, E>` returns. req: derive_handler/004
4. **Handlers return generated commands.** Common handlers return helpers such
as `todos.append(todo)`, `todo_row.replace(row)`, `new_todo.error("title",
"Required")`, `new_todo.clear()`, `page.replace(view)`, or tuples of those
commands. req: dx/006 req: dx/007
5. **The runtime only applies effects.** The JavaScript runtime is a small,
root-scoped effect interpreter: no VDOM, no hydration framework, no client
expression engine, no selector retargeting, and no app state store. req: runtime/003 req: invariant/002
## Mental model: render → slot/key → effect → runtime
- **Render:** hemplate renders Rust view structs into checked HTML. App code
normally reaches rendering through generated helpers or `hemx::page(...)` at a
server page boundary, not raw HTML construction. req: html_safety/002 req: view/001
- **Slot/key:** a generated slot names the target, and a generated keyed slot
also carries the stable row key. A list target inside `h-for` must have a
stable `h-key`, so row updates are addressable without CSS selectors. req: list/001
- **Effect:** handlers return typed commands that become a checked effect
response. Tuple composition is the normal batch syntax.
- **Runtime:** the browser checks the build fingerprint, resolves targets within
the current `data-hemx-root`, and applies compatible batches. Mismatched
server/runtime builds fail closed instead of silently mutating the wrong DOM.
req: abi/002 req: check/004 req: failure/005
## Forms and errors
Forms remain HTML forms. hemx checks the generated form contract against a
user-authored Rust form type, so domain newtypes such as `Email`, `TodoId`, and
`Title` parse through ordinary Rust traits rather than generated DTOs. req: form/001
Use validation effects for expected user mistakes, and `Result<impl IntoEffect,
E>` for fallible domain, database, or infrastructure work. Integration crates map
`E` to generated UI effects, redirects, events, or HTTP responses; the canonical
app code still uses the same handler shape for plain and fallible handlers. See
`docs/diagnostics.md` for common compile/build/runtime mistakes and fixes.
req: failure/004 req: derive_handler/004
## Pages, push, CSS, and islands
- Page navigation is a specialized generated page/slot effect around real
anchors and ordinary HTTP routes. hemx does not own routing. req: modes/002 req: axum_integration/002
- Server push streams send checked effect responses over framework-managed
transports such as SSE; auth and connection policy stay in the server
integration. req: wire/004 req: push/002
- Appearance is plain CSS. Generated class tokens can make dynamic classes
checked, but hemx does not introduce a styling runtime.
- Custom JavaScript belongs at explicit opaque leaf boundaries: charts, maps,
editors, Web Components, or similar widgets. Islands communicate through
generated handles/events and do not create a second UI model. req: canonical_authoring/007 req: interop/003
## Production boundary
hemx is not a SaaS platform. Production concerns stay in normal Rust/web crates
and integrate at explicit boundaries. req: laws/002 req: auth/001
- **Persistence:** use SQLx or another storage adapter in your application
state/handlers. hemx should see ordinary domain values and generated UI
commands, not own the database layer. See `docs/recipes/sqlx-persistence.md`.
- **Auth/session:** use Axum/Tower extractors and middleware. Handlers may accept
typed auth/session context and return ordinary HTTP failures or generated UI
failures. See `docs/recipes/auth-session-csrf.md`. req: auth/002
- **CSRF:** keep CSRF policy in middleware/extractors with hidden form fields,
cookies, and normal SameSite/browser semantics. hemx preserves submitted form
fields and credentials semantics. See `docs/recipes/auth-session-csrf.md`. req: auth/004 req: auth/005
- **Observability, feature flags, killswitches, deploy:** use explicit platform
integrations around handlers, routes, and runtime assets. Core hemx must not
vendor providers or add framework-specific magic. See `docs/recipes/observability-flags.md` and `docs/recipes/deploy-versioning.md`.
- **PWA/offline/sync:** optional adapters may reuse generated targets/effects,
but core hemx must not gain a mandatory client state graph or local app
runtime. See `docs/recipes/pwa-offline.md`. req: canonical_authoring/008
## Escape hatches
Advanced APIs are named and isolated. Raw effects, low-level ids, manual
registries, raw HTML/render/target construction, runtime hooks, SSE internals,
and island internals are for integration crates, tests, migrations, or explicit
leaf boundaries. They should not appear in beginner examples or ordinary handler
docs. req: public_api/002 req: public_api/005
## Versioning and deploy compatibility
The generated API, symbols, effect wire schema, and JavaScript runtime carry
schema/ABI versions. Deploy a matching server, generated output, and runtime
asset together. Build fingerprints are derived from the generated surface and ABI
parts; the runtime refuses incompatible effect responses and integrations should
fall back to a full page reload when possible. See `docs/recipes/deploy-versioning.md`. req: abi/001 req: abi/002 req: failure/005
Before a v1 release, the semver policy and upgrade notes should explicitly state
which surfaces are stable: beginner generated helpers and handler shapes; the
wire/runtime ABI; and advanced escape hatches that may remain integration-level.
See `docs/versioning.md`.
## Examples
- `examples/v0`: canonical beginner path covering counter, typed todo CRUD,
form wizard, auth action, page swaps, SSE notifications, and keyed list
updates. Start here.
- `examples/saas`: compile-tested v1 tutorial skeleton covering auth/session,
CSRF-safe mutation, local persistence, generated swaps, page/push shape, plain
CSS, and one explicit island without provider-heavy platform scope. Read the
walkthrough in `docs/tutorial-saas.md`; the SQLx persistence recipe in
`docs/recipes/sqlx-persistence.md` shows the provider boundary without moving
SQL into core.
- `examples/kanban`: advanced / north-star milestone boundary sketch. It may
expose manual registry or render escape hatches while exploring product limits.
- `examples/techdemo`: advanced integration demo with a leaf island and broader
product interactions.
## Local checks
```sh
cargo run -p hemx-xtask -- test
cargo check --workspace
redgate health --strict
```
Use `cargo run -p hemx-xtask -- test` for the full local verification path so
jobs stay capped for local CPU and memory. req: test/004