diff --git a/README.md b/README.md index 188b406..eb93149 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,9 @@ user-authored Rust form type, so domain newtypes such as `Email`, `TodoId`, and Use validation effects for expected user mistakes, and `Result` 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. req: failure/004 req: derive_handler/004 +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 diff --git a/docs/diagnostics.md b/docs/diagnostics.md new file mode 100644 index 0000000..790ebd4 --- /dev/null +++ b/docs/diagnostics.md @@ -0,0 +1,76 @@ +# Diagnostics guide + +hemx diagnostics should tell a Rust developer which template fact, generated +helper, or handler shape is wrong, and what to change next. They should not teach +raw ids, selector targeting, runtime opcodes, or Cargo internals in the normal +path. req: diagnostics/001 req: diagnostics/002 req: diagnostics/003 + +Use this guide as the v1 checklist for common mistakes in beginner and +production-shaped apps. + +## Where errors happen + +- **Template/build diagnostics** come from `hemx_build::app().run()` while reading + `.heml` files and CSS. Fix the template or generated-surface convention. +- **Derive/compile diagnostics** come from `#[hemx::surface]`, `#[hemx::form]`, + `#[hemx::handler]`, `#[hemx::component]`, and `#[hemx::app]`. Fix Rust code so + it matches the generated surface. +- **Runtime diagnostics** come from the tiny browser runtime when a deployed page + and response are incompatible or a target cannot be applied. Fix deployment or + recover with a full page response. req: failure/005 + +## Common mistakes and fixes + +| Mistake | Diagnostic shape | Fix | +| --- | --- | --- | +| Handler has no matching template handle | `unknown hemx handle \`save\`; add \`data-hemx-handle="save"\`` | Add the handle to the template, rename the function, or put the handler in the matching component. | +| Component is missing a generated handler | `#[hemx::component] missing handler implementation(s): delete` | Add a `#[hemx::handler] fn delete(...)` in that component, or remove the template handle. | +| Handler name is ambiguous across components | `ambiguous generated handle name(s): save` | Scope the component with `#[hemx::component("todos")]` or rename handles so the generated path is unique. | +| Handler misses generated params | `hemx handler \`show\` is missing generated param argument(s): mode` | Add typed handler arguments for every `data-hemx-param-*` fact generated by the template. | +| Form handler omits the form argument | `handles a generated form and must accept a typed form argument` | Accept `Form`/`hemx::Form`/integration equivalent and derive `#[hemx::form("...")]` for the type. | +| Form struct misses a control | `hemx form \`new_todo\` is missing field \`title\`` | Add a Rust field matching the form control name, or rename the template control. | +| Required/multiple form control has wrong Rust shape | `required ... must not be Option<_>` or `accepts multiple values and must be Vec<_>` | Match HTML required/multiple semantics with `T`, `Option`, or `Vec` as appropriate. | +| Form field type cannot parse submitted values | compiler mentions `T: FormValue` / `T: hemx::FormValue` | Implement `FromStr`/the expected form value trait for the domain newtype, or use a parseable domain type. | +| Generated resources are unavailable | `could not find generated hemx module` / `could not find generated hemx symbols` plus `add hemx_build::app().run()? to build.rs` | Add or fix `build.rs`, then rerun `cargo check`; do not copy `$OUT_DIR` paths into app code. | +| Unknown `data-hemx-*` attribute | `unknown hemx attribute ... check the spelling or use a non-hemx data-* attribute` | Fix the spelling, use the supported hemx attribute, or rename app metadata to a non-hemx `data-*` attribute. | +| Selector-style targeting | ``data-hemx-target` is selector-style targeting; hemx uses generated resources` | Put `data-hemx-slot` on the local target and return a generated slot/page/form effect. | +| Generated target appears in a loop without a stable key | `inside an h-for without h-key; add a stable h-key="item.id"` | Add a stable `h-key` to the owning loop; use generated keyed helpers for row updates. | +| Page/SSE attributes are on the wrong element | `expected a real ` / `expected placement on the same element as data-hemx-root` | Keep page navigation on anchors and put root-scoped runtime attributes on the root element. | +| Result handler error type is not mappable | compiler reports the error type does not satisfy `IntoHandlerFailure` | Implement `IntoHandlerFailure` for the app error, or keep expected validation as generated UI effects instead of `Err`. req: failure/004 | +| Old page talks to a new server/runtime | runtime refuses the partial update on fingerprint mismatch | Serve a self-consistent release or fall back to full page reload/navigation. See `docs/recipes/deploy-versioning.md`. req: abi/004 | +| Missing runtime target | runtime emits a missing-target diagnostic in development and fails/no-ops according to target kind | Fix the template/generated helper mismatch; do not retarget with selectors. req: failure/001 | + +## What a good diagnostic should include + +A v1-quality diagnostic should include: + +- the user-facing name: handle, form, slot, key, param, class, event, or template +- the source area: template path, Rust item, or deployment/runtime boundary +- the concrete expected shape, not an internal representation +- one next action that preserves generated helpers and the tiny runtime + +Avoid beginner-facing messages that suggest `ResourceId`, `ResourceRef`, raw +`Effect`, manual registries, selector strings, or runtime opcodes. If an advanced +escape hatch is genuinely required, say that it is advanced and name the safer +normal path first. req: public_api/002 req: public_api/005 + +## Verification anchors + +Current recurring checks cover the most common classes: + +- `cargo test -p hemx-build` covers template/build diagnostics such as unknown + hemx attributes, selector-style targeting, invalid runtime attribute values, + missing keys, and invalid page/SSE placement. +- `cargo test -p hemx-derive --test compile_fail` covers derive/compile + diagnostics for missing handlers, form mismatch, params, missing generated + files, unknown scoped components, ambiguous handles, and generated resource + lookup. +- `cargo test -p hemx-js` covers root-scoped runtime behavior, selectorless + targeting, fingerprint mismatch refusal, SSE application, and recoverable + runtime events. +- `cargo test -p hemx-test --test examples_contract` keeps public examples from + teaching forbidden normal-path constructs. + +Before claiming the diagnostics story is closed for v1, run those gates plus +`cargo run -p hemx-xtask -- test`, `cargo check --workspace`, and +`redgate health --strict` on a clean tree. req: test/003 req: test/004