diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index f22caef..d721681 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -102,6 +102,11 @@ Global exports are opt-in only. ### req: component/005 005 An optional `#[slhx::component]` macro may validate that every handle declared in the template Surface has a corresponding `#[slhx::handler]` within the annotated module. This is the only macro with cross-handler visibility inside a single module; it remains strictly local. Missing handlers without `#[slhx::component]` are caught at app mount or test time, not `cargo check`. +### req: component/006 +006 `#[derive(Hemplate)]` structs are natural component boundaries. slhx_build +generates APIs per component/template namespace and may merge component +Surfaces only at explicit app/root boundaries. + --- ## codegen @@ -147,7 +152,8 @@ Global exports are opt-in only. 001 hemplate does not expose a slhx API. It exposes a stable, generic Template Surface IR. slhx is one consumer; a11y tools, test generators, and documentation generators are others. ### req: boundary/002 -002 hemplate never interprets `data-slhx-*`, `slhx-*`, or any other tool-prefixed attribute. It records them as raw `name: value` pairs in the Surface. +002 hemplate never interprets `data-slhx-*` or any other tool-prefixed +attribute. It records them as raw `name: value` pairs in the Surface. ### req: boundary/003 003 slhx never parses `.heml` directly. It consumes `hemplate.surface.postcard` emitted by `hemplate_build`. slhx interprets tool-specific conventions (`data-slhx-handle`, `data-slhx-slot`, etc.) from the generic Surface. @@ -177,6 +183,22 @@ Global exports are opt-in only. ### req: surface/007 007 `hemplate-derive` does not write Surface files. Surface generation is a `build.rs` / `hemplate_build` concern, proc-macro side-effect free. +### req: surface/008 +008 The Surface records hemplate structural directives as first-class facts: +`h-for`, `h-key`, `h-if`, `h-else-if`, `h-else`, dynamic `+attr` bindings, +and interpolated attr/text expressions. slhx consumes these facts; it never +parses `.heml` source directly. + +### req: surface/009 +009 Raw/pre-rendered HTML insertions are opaque Surface holes. The parent +Surface records the insertion point and source span, but nodes inside inserted +HTML belong to the child component Surface or remain invisible to tools. + +### req: surface/010 +010 Attribute values preserve their origin: static literal, dynamic `+attr` +expression, or interpolated template string. slhx param inference consumes +these from Surface and never reparses `.heml`. + --- ## build @@ -191,7 +213,7 @@ Global exports are opt-in only. 003 `slhx-derive` (`#[slhx::handler]`) reads `slhx.syms` at expansion time to validate handle names, slot names, and form signatures. It generates only local glue (static fn-table entry) plus compile errors. ### req: build/004 -004 `#slhx::surface]` reads `slhx.generated.rs` from `$OUT_DIR` and expands it into the annotated module. It is a pure include/bridge macro with no semantic analysis of its own. +004 `#[slhx::surface]` reads `slhx.generated.rs` from `$OUT_DIR` and expands it into the annotated module. It is a pure include/bridge macro with no semantic analysis of its own. ### req: build/005 005 A `build.rs` failure (missing Surface, version mismatch, stale hash) is a hard error before proc-macro expansion. @@ -232,10 +254,15 @@ Global exports are opt-in only. ## state ### req: state/001 -001 Typed atoms with `Atom`: read via `atom.get()`, subscribe via `Effect::set`. No hidden global proxy / reactive graph. Atoms are explicit values in `struct App`. +001 Typed atoms with `Atom` are explicit addressable state resources. +Read via `atom.get()`, mutate via `atom.set()` / `atom.update()`, publish to +the runtime via generated atom commands or `Effect::set(atom, value)`. +No hidden global proxy / reactive graph. Atoms are explicit values in `struct App`. ### req: state/002 -002 Subscription is explicit: `Effect::set(atoms::FOO, 42)` pushes the new value to all consumers. No automatic component re-render graph. +002 Atoms are not reactive by default. Updating an atom does not re-render +anything unless the handler returns an effect targeting consumers. +Subscriptions are explicit runtime/store APIs. No automatic component re-render graph. ### req: state/003 003 The JS runtime maintains a client-side atom store keyed by `AtomId`. Runtime values are type-erased postcard bytes. Types are compile-time only. A deterministic `TypeHash` may be generated by `slhx_build` for diagnostics, but the JS runtime does not depend on Rust `TypeId`. @@ -297,14 +324,22 @@ Global exports are opt-in only. ## scope ### req: scope/001 -001 `Scope` is a first-class primitive. Keyed loops (`h-for`), conditional branches (`h-if`), component instances, modals, tabs, nested forms — all are scopes. A slhx-addressable node inside any dynamic scope must carry a stable `ScopeKey`. Composite identity is `(ResourceId, ScopeKey)`. [north_star] +001 `Scope` is a first-class primitive. Keyed loops (`h-for`), conditional +branches (`h-if`), component instances, modals, tabs, nested forms — all are +scopes. A slhx-addressable node inside any dynamic scope must carry a stable +`ScopeKey`. Concrete runtime targets are addressed through `ResourceRef` +`{ resource: ResourceId, scope: Option }`. [north_star] --- ## wire ### req: wire/001 -001 Authoring HTML uses symbolic `data-slhx-*` attributes. Rendered runtime HTML lowers these to compact numeric metadata: `data-hid`, `data-sid`, optional `data-key`, optional atom/bootstrap ids. The browser never sees handler or slot names. `data-slhx-root` marks a scoped root boundary. +001 Authoring HTML uses symbolic `data-slhx-*` attributes. Rendered runtime +HTML lowers these to compact numeric metadata: `data-hid`, `data-sid`, +optional `data-key`, optional atom ids, optional form/control ids, and +`data-slhx-st` for state bootstrap. The browser never sees handler or slot +names. `data-slhx-root` marks a scoped root boundary. ### req: wire/002 002 POST bodies carry `application/x-www-form-urlencoded` with distinguished field `__h` (handle id). Server routes by numeric id, not by URL path. @@ -345,7 +380,10 @@ Global exports are opt-in only. 002 No build step, no virtual DOM, no diffing, no scheduler. Receiving an effect = apply ops immediately in declared order. ### req: js/003 -003 The runtime consists of an Op interpreter (`ReplaceHtml`, `SetText`, `PatchAtom`, `Navigate`, `Focus`, `AddClass`, `RemoveClass`, `RemoveKeyed`, `CustomOp`) reading from a postcard byte stream. +003 The runtime implements the canonical `EffectBatch` opcode schema from +`slhx-core`. DOM-specific operations such as `ReplaceHtml`, `SetText`, +`AddClass`, or `RemoveKeyed` are lowering details generated from canonical +resource ops. --- @@ -538,7 +576,10 @@ Global exports are opt-in only. 004 Three execution modes supported: server-first (request/response), client-local WASM (requestAnimationFrame, no round-trip), and hybrid sync (local + remote via `slhx-sync`). Modes are opt-in per handler, not global. ### req: misc/005 -005 The only user-facing proc-macro is `#[slhx::handler]`. No `!` call-syntax macros. Attribute macros only. +005 The only required user-facing proc-macro in slhx core is `#[slhx::handler]`. +Optional ergonomic macros may exist: `#[slhx::surface]`, `#[slhx::component]`, +`#[slhx::app]`, and integration-crate macros such as `#[slhx::island]` or +`#[slhx_sync::presence]`. No `!` call-syntax macros. ### req: misc/006 006 Source spans are present on every Surface node, attribute, and scope. Error messages cite file, line, and column. This is non-negotiable for DX.