# Typed bindings

> Declare the state a document reads and the events it raises, and have both checked at load.

By default a document can read any host state name and raise any event. Declaring them makes the document **strict**: the contract is written at the top of the file, and loading refuses a document that breaks it.

## Declaring the contract

```masque
host_state { volume: float, muted: bool, title: string };
events { set_volume(float), toggle_mute(), rename(string) };

let main = fn () {
  Text { text: title }
};
```

- `host_state` lists the names the document reads and their types.
- `events` lists the events it may raise and their argument types.

## What gets checked

When a strict document loads, Masque checks the document against its own declarations. An `event("menu")` that `events` doesn't list fails to load. The error names the cause and says what to write: `events { menu(...) }`.

Each module's `events` belong to that module. A shared file imported by two documents declares the union of what it raises, and each importing document declares only its own.

## The host side

Register a handler for each declared event, and supply each declared field:

```rust
let config = RuntimeConfig::new()
    .with_host_state(initial_state)
    .with_event_handler("set_volume", |args| {
        if let Some(Value::Float(v)) = args.first() {
            audio.set_volume(*v);
        }
        true
    });
```

The handler receives the event's arguments as `&[Value]` and returns whether it handled the event.

TODO: Rust-side derives for state and message types, so the host half of the contract is checked too. The design exists; the macros are not in the current tree.
