WAP LabsProject atlas

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

  1. 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.
  1. runtime/
  • Holds deck/card graph, active card, history, variable store (later), and task bindings.
  1. nav/
  • Focus traversal, link activation, URL resolution, and navigation intents.
  1. layout/
  • Converts active card nodes to flow lines and draw commands.
  1. render/
  • Defines serializable output model consumed by host canvas renderer.
  1. 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_diagnostics and WASM lastWmlLoadDiagnostics expose the same ordered malformed | invalid | unsupported | recoverable taxonomy, 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::Break
  • InlineNode::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() -> RenderList
  • lastWmlLoadDiagnostics() -> WmlLoadDiagnostic[]
  • future: drainEvents() -> EngineEvent[] for nav/error/task events

Compatibility policy:

  • Existing methods remain available.
  • New methods/fields are additive.

5. Navigation Pipeline

  1. key input -> focus/action intent
  2. resolve active interactable
  3. if #fragment: transition card
  4. else: emit host navigation request (future explicit event queue)
  5. 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 DOCTYPE before building the deck:
    • the canonical identity is -//WAPFORUM//DTD WML 1.3//EN with http://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.
  • expose deterministic WML load outcomes:
    • malformed XML and invalid WML reject the load;
    • unsupported optional constructs are ignored with an unsupported diagnostic;
    • content errors with an explicit recovery rule are ignored with a recoverable diagnostic;
    • 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