Skip to content

Type-checked wires

Wires between blocks are checked once, at load time, by generating a synthetic TypeScript file that imports every block module in the flow and asserts each wire’s source-output type is assignable to its destination-input type — then running tsc --noEmit --incremental over it.

If the check fails, the old flow keeps running and the error is reported; nothing partially starts. Warm checks take a few hundred milliseconds.

Compile-time checking covers wires between your own blocks. Anything crossing a genuine trust boundary — Home Assistant event payloads, third-party API responses — gets validated at runtime with Zod instead, inside the boundary block. Calling an arbitrary external API with fetch and Zod-parsing the response is meant to be a first-class, pleasant thing to do in a block, not a workaround.

The coordinator runs the typecheck gate itself, before ever touching a running flow-host process:

  1. A data/blocks/*.ts or data/wiring/*.json edit lands (debounced fs.watch).
  2. Every loaded flow re-typechecks together through the generated assertion file.
  3. On failure, every existing flow process is left completely alone.
  4. Only a passing check triggers an actual restart of the affected flow.

This is verified behavior, not aspiration: a deliberately broken edit leaves every flow’s pid unchanged.