dioxus-clerk 0.6.0

Clerk integration for Dioxus: components, hooks, and SSR initial state for web and fullstack apps
Documentation
# Working on dioxus-clerk

Clerk authentication for Dioxus 0.7. One crate, two build targets: a browser
(wasm) client that drives clerk-js, and a native server half with Axum
middleware that verifies Clerk session tokens.

Read [CONTEXT.md](CONTEXT.md) first — it defines the domain vocabulary (Clerk
lifecycle, action dispatch, auth state, SSR seed, JS bridge layer, verification
outcome). Use those terms; the code and comments already do.

## The target split is the main constraint

Most mistakes here come from forgetting that code compiles for two very
different targets.

- `src/components`, `src/hooks`, `src/core` — both targets.
- `src/bindings.rs`, `src/bridge.rs`, `src/handle.rs`, `src/lifecycle.rs`,
  `src/loader.rs` — browser only, gated on `cfg(clerk_client)` (emitted by
  `build.rs`: wasm32 *without* the `worker` feature).
- `src/server/` — native only, behind the `server` feature.
- `src/testing.rs` — behind the `testing` feature, independent of `server` so a
  harness that only mints tokens does not pull in axum and reqwest.

A change that compiles for one target routinely breaks the other. Check both
before claiming done.

## Checks

```bash
cargo fmt
cargo clippy --all-features --all-targets   # must be warning-free
cargo test --all-features                   # native + integration tests
cargo build --target wasm32-unknown-unknown # the browser half
cargo doc --no-deps --features server,testing
wasm-pack test --headless --chrome          # browser tests (slow)
```

`dagger check` runs all of it in pinned containers and is exactly what CI runs.
Prefer it before opening a PR; prefer the individual commands while iterating.

Some tests only exist under a feature or target: `tests/*.rs` files start with a
`#![cfg(...)]` gate, so a bare `cargo test` silently runs almost none of the
server or testing coverage. Use `--all-features`.

## Conventions the code already follows

Match these; they are visible in any file you open.

- **Comments explain *why*, not *what*.** The non-obvious reasoning — why a
  redirect is refused, why the cache is locked through poison recovery, why a
  claim is rejected — belongs next to the code. Do not narrate mechanics.
- **Security reasoning lives at the decision point.** See
  `src/server/config.rs` (claim acceptance) and `src/server/verification.rs`
  (redirects, origin checks, response size limits) for the register.
- **No key material in the repository.** The `testing` feature generates keys at
  runtime; nothing is committed, and nothing should be.
- **Public API is curated.** `src/lib.rs` re-exports an explicit list rather than
  glob-re-exporting modules, so a new `pub` item does not silently land at the
  crate root. Add to the list deliberately.
- **`#![warn(missing_docs)]` and `#![forbid(unsafe_code)]` are on.** Every public
  item needs a doc comment.
- **`#[non_exhaustive]` on public enums and structs** that mirror Clerk concepts,
  so new Clerk fields are not breaking changes.

## Tests

Integration tests drive the real code path rather than asserting on
intermediate representations — `tests/testing_issuer.rs` runs minted tokens
through an actual `ClerkAuthLayer` instead of checking claim JSON, so the
helpers stay honest when the verifier moves.

Browser tests (`tests/wasm_*.rs`) install a fake `window.Clerk` and must be
order-independent; page-scoped state is reset through
`dioxus_clerk::__reset_load_state()`.

## Docs

Markdown in `docs/` is the source of truth for long-form guides. Each is also
included into rustdoc via `src/lib.rs`'s `guides` module, so it ships versioned
on docs.rs. Two consequences when editing them:

- Use absolute `https://` links, not relative paths to other Markdown files —
  relative links break in the rustdoc rendering.
- Non-Rust fences need a language tag (`js`, `toml`, `bash`); untagged fences
  are treated as Rust and compiled as doctests.

`README.md` is for people arriving from GitHub or crates.io; keep it a summary
that points at the guides rather than a second copy of them.

## Publishing

`Cargo.toml` has no `include`/`exclude`, so **everything git-tracked ships** —
CI workflows, devenv and dagger config, and these agent instructions included.
Check what a release would carry with:

```bash
cargo package --list          # what would ship
cargo package                 # also runs a verification build
```

Narrowing this to an allowlist is planned (see Known gaps). The working recipe,
for whenever it lands:

```toml
exclude = ["*", "!src", "!src/**", "!docs", "!docs/**", "!build.rs",
           "!CONTEXT.md", "!LICENSE-APACHE", "!LICENSE-MIT", "!README.md"]
```

Two traps, both verified the hard way:

- A directory needs *two* entries (`!docs` and `!docs/**`). Gitignore rules
  cannot re-include a file whose parent is still excluded, so `!docs` alone
  re-admits the directory entry and none of its contents — `cargo package`
  succeeds and publishes a crate with no source.
- Do not spell it as `include = [...]`. It produces an identical package, but
  makes cargo enumerate the filesystem instead of asking git for the file list,
  which trips over symlink loops in devenv's Nix profile and prints a dozen
  warnings on every `cargo check`.

`docs/` must stay in whatever list is used: `src/lib.rs`'s `guides` module
`include_str!`s it, so docs.rs fails to build the crate without it.

## Known gaps

- `README.md` links to `docs/react-migration.md`, which has never existed.
- The published package carries contributor tooling and these instructions;
  see Publishing for the allowlist that would fix it.