FMIR binary format parser, walker, and HTML generator for the Forma Stack. Parses compiled component IR and generates server-rendered HTML with hydration markers — no JavaScript runtime needed.
Install#
These examples target the published, coordinated 3.0.0 package set. Request that version explicitly:
[dependencies]
forma-ir = "=3.0.0"What It Does#
- Parses
.irbinary files (FMIR format) into a validatedIrModule - Walks the opcode stream and generates HTML with proper escaping
- Fills dynamic values (text, attributes, conditionals, lists) from typed
SlotData - Emits hydration markers (
<!--f:t0-->,<!--f:s0-->, etc.) for @getforma/core client-side hydration, except insidetextarea,title,styleandscript, whose content HTML reads as text - Writes form-control values as HTML reads them: a
textarea'svalueas its text, aselect's asselectedon the matching option - Renders a list in O(rows), with a committed regression gate that fails the build if that changes (
tests/list_scaling.rs)
The on-disk layout and its stability contract are documented in
docs/FMIR-FORMAT.md.
Low-level walker example#
Applications normally use typed page rendering,
which binds declared props and returns a complete response. SlotData is the
low-level walker input; arbitrary JSON slot names do not replace that contract.
use forma_ir::{IrModule, SlotData, walker};
// Parse a compiled .ir binary
let module = IrModule::parse(&ir_bytes)?;
// Fill slots with runtime data
let slots = SlotData::from_json(r#"{"count": 42}"#, &module)?;
// Generate HTML
let html = walker::walk_to_html(&module, &slots)?;SlotData::from_json resolves known slot names from a JSON object and leaves
missing values at their defaults. Its numeric values are IEEE-754 f64: decimal
parsing is correctly rounded and preserves serialized finite f64 bits,
including negative zero. It does not provide arbitrary-precision integers;
integers beyond the exactly representable range may round. Declared page props
use the typed bounds below. The island-state Serialize9 contract subsequently
normalizes negative zero to zero.
walk_to_html_counted(&module, &slots, limits) writes the same HTML and also
returns what it wrote (WalkCounts: nodes, bytes, list rows); on failure it
returns a WalkFailure with the counts reached and the component path, the
islands open where the walk stopped. It catches a panic inside the walk as
IrError::Panicked, while the application unwinds on panic.
FMIR v3#
The 3.0.0 package set implements FMIR v3. Modules carry a 24-byte signature/version/length/CRC
header and a variable section table; the reader accepts writers of its own major up to its own minor; patch is ignored.
Package prerelease suffixes are absent from the numeric wire triple.
V2 modules require rebuilding. IrModule::parse validates every bytecode string reference,
props schemas, key paths and optional source positions before rendering; IrError::code()
provides stable LOAD- diagnostics. See FMIR-FORMAT.md
for the maintained contract and CHANGELOG.md for the breaking changes.
dump::dump_ir is a lossless canonical text representation shared with the compiler. It
includes every section, including unknown optional bytes and island extensions.
The wasm feature and its JavaScript exports were removed in 3.0.0. Browser
islands adopt the compiled native markup with @getforma/core; explicit
browserOnly islands construct their client content. walk_island remains internal and is
not a supported API; server callers use the full-page walker.
Declared page props#
#[derive(forma_ir::PageProps)] describes named Rust structs and unit enums using
FMIR's actual props schema. props::BoundProps::<P>::bind(module, route) checks
that contract by field name once and retains the immutable module. Its encode
method checks each request's values and emits only declared fields. Extra Rust
fields warn and never reach the page; every declared TypeScript field must exist
in Rust, including optional fields. Errors include route, property path and both
source declarations where available.
The derive reference documents supported types,
renames, optional direction, integer bounds and signed epoch-millisecond dates.
props::encode_props is a one-shot convenience that also performs the comparison;
servers should retain a binding and rebind only when the module changes.
When You Need This#
- Building your own server (not Axum) — parse IR and generate HTML yourself
- Build tools — inspect, transform, or generate IR files
If you're using Axum, use forma-server instead — it wraps forma-ir with page rendering, asset serving, and CSP headers.
Security#
FMIR is a trusted input. This crate validates structure and bounds, returning errors for malformed structures. Those checks do not recover allocation failure or a panic compiled with panic = "abort"; the typed forma-server request boundary catches unwinding faults. Structural validation does not sanitise the IR's content: an .ir file chooses what markup your page contains. In 3.0, only admit modules from the application's own trusted build; there is no untrusted-module admission mechanism. See the security policy.
- All dynamic text and attribute values are HTML-escaped; tag and attribute names are validated against
[A-Za-z_:][-A-Za-z0-9_:.]* - URL-bearing attributes are scheme-checked and
on*handlers dropped, matching@getforma/coreso SSR and CSR agree - Recursion depth limited to 64; list nesting limited to 4 levels
- Per-render output cap (default 8 MiB) and LIST iteration budget (default 50,000), both configurable via
WalkLimits— a small module plus a large request array cannot force a multi-gigabyte allocation - Parser rejects declared table counts the section cannot hold, and structurally validates the opcode stream once at load
- Island props JSON is escaped so it cannot terminate or re-open its
<script>element — read it withJSON.parse, never by string-matching #![forbid(unsafe_code)]on the crate root — zerounsafeis compiler-enforced, not asserted- XSS payload verification and malformed-input handling covered by the test suite
Part of the Forma Stack#
Frontend (TypeScript)#
| Package | Description |
|---|---|
| @getforma/core | Reactive DOM library — signals, h(), islands, SSR hydration |
| @getforma/compiler | FMIR compiler and esbuild SSR plugin |
| @getforma/build | Production pipeline — bundling, hashing, compression, manifest |
Backend (Rust)#
| Package | Description |
|---|---|
| forma-ir | This crate — FMIR parser and native HTML walker |
| forma-server | Axum middleware — SSR, asset serving, CSP headers |
Full Framework#
| Package | Description |
|---|---|
| @getforma/create-app | npx @getforma/create-app — scaffolds Rust server + TypeScript frontend |
License#
MIT