docs/wml-engine/architecture.md
WASM Engine Architecture (Maintainable, MVP-First)
1. Design Constraints
- Keep module boundaries explicit.
- Avoid over-modeling unsupported spec areas in MVP.
- Keep public API stable while internal parser/runtime evolve.
2. Runtime Components
parser/
- Converts WML XML to AST/runtime nodes.
- Classifies malformed and invalid input without guessing author intent.
- Preserves recognized content inside recoverable alternate-DTD extensions and reports ignored unsupported/recoverable constructs through stable load diagnostics.
runtime/
- Holds deck/card graph, active card, history, variable store (later), and task bindings.
nav/
- Focus traversal, link activation, URL resolution, and navigation intents.
layout/
- Converts active card nodes to flow lines and draw commands.
render/
- Defines serializable output model consumed by host canvas renderer.
api (lib.rs core + adapters)
- Stable runtime methods in native Rust (
load_deck,load_deck_context,render,handle_key,navigate_to_card, metadata getters). - WASM adapter exports JS-compatible names (
loadDeck,loadDeckContext,render,handleKey,navigateToCard, metadata getters). - Native
last_wml_load_diagnosticsand WASMlastWmlLoadDiagnosticsexpose the same orderedmalformed | invalid | unsupported | recoverabletaxonomy, codes, outcomes, and messages. - Target-specific serialization/binding logic must remain adapter-only.
3. Data Model (Incremental)
MVP core:
Deck { cards: Vec<Card> }Card { id, nodes }Node::Paragraph(Vec<InlineNode>) | Node::BreakInlineNode::Text | InlineNode::Link { text, href }
Phase 2 additions:
CardMeta { title, access_domain, access_path, ordered, newcontext }TaskBinding { kind, label, target, method, postfields }VariableStore
Phase 3 additions:
EventBinding- timer runtime state
- form controls model
4. API Boundary
Input boundary:
loadDeckContext(wmlXml, baseUrl, contentType, rawBytesBase64?, referringUrl?)
Output boundary:
render() -> RenderListlastWmlLoadDiagnostics() -> WmlLoadDiagnostic[]- future:
drainEvents() -> EngineEvent[]for nav/error/task events
Compatibility policy:
- Existing methods remain available.
- New methods/fields are additive.
5. Navigation Pipeline
- key input -> focus/action intent
- resolve active interactable
- if
#fragment: transition card - else: emit host navigation request (future explicit event queue)
- refresh render state
6. Performance and Safety
- No heap-heavy DOM mirrors; keep compact node model.
- Cache parsed deck and precomputed interactables per card.
- Deterministic rendering for snapshot tests.
- Never panic across wasm boundary; convert to structured error.
- Reject malformed/invalid loads atomically without replacing the last successfully loaded deck.
7. Future-Proofing Without Overengineering
Do now:
- keep clear module seams
- preserve source metadata (
baseUrl,contentType, root/card language) - enforce destination access against an optional host-supplied referring URI before replacing the active deck
- define stable event/error enums
- classify an external WML
DOCTYPEbefore building the deck:- the canonical identity is
-//WAPFORUM//DTD WML 1.3//ENwithhttp://www.wapforum.org/DTD/wml13.dtd; - alternate external DTDs are accepted under WML 1.3 section 12.4, with unknown tags/attributes ignored and recognized nested content retained;
- a mismatched root, canonical-public-ID/system-ID conflict, duplicate or misplaced declaration, and internal subset produce deterministic parse errors;
- no DTD is fetched, and transport-decoded WBXML remains normalized textual WML before engine ingestion.
- the canonical identity is
- expose deterministic WML load outcomes:
- malformed XML and invalid WML reject the load;
- unsupported optional constructs are ignored with an
unsupporteddiagnostic; - content errors with an explicit recovery rule are ignored with a
recoverablediagnostic; - a clean successful load clears diagnostics from the prior attempt.
Defer now:
- mandatory-prologue enforcement and full DTD content-model validation
- exhaustive per-element error-condition coverage and host fetch/access failure atomicity
- full WMLScript execution
- full event/timer matrix
- pixel-perfect vendor quirks