Rendering-Pipeline
Wie aus Definitionen und einem Stück JSON eine fertige, interaktive Seite wird — in zwei strikt getrennten Zeiten.
Teil 1: Build-Zeit — aus Quelle wird Registry
npm run build läuft in drei Stufen, jede blockiert bei Fehlern (Exit ≠ 0):
- 1a. Token-Compiler (build/tokens.mjs): prüft die komplette Token-Kette streng und emittiert
tokens.css(die eine Laufzeit-Wahrheit),tokens.tailwind.css(@theme-Mapping für den Bausteine-Build) undtokens.json(Modell für PHP). - 1b. Registry-Build (build/registry.mjs): scannt
definitions/*/, validiert Manifest gegen Schema, ID-Namensraum, Entrypoint-Existenz, Targets, Props-Schemas, Dependencies (zyklenfrei) →registry.cache.php— die Laufzeit liest nie Verzeichnisse. - 1c. Asset-Pipeline (build/assets.mjs): CSS wird mit Tailwind kompiliert (jedes
@applyvollständig aufgelöst — zur Laufzeit läuft kein Tailwind), Client-Controller mit esbuild gebündelt, Dateinamen content-gehasht. Ein Token-Verwendungs-Check macht Tippfehler in Token-Namen zu Build-Fehlern.
Teil 2: Anfrage-Zeit — aus JSON wird HTML
Laden mit Limits: Größenlimit (256 kB) VOR dem Parsen, JSON mit Tiefenbegrenzung. Der Node-Walker geht rekursiv durch den Baum (max. 500 Nodes, Tiefe 20 — danach Fallback statt Absturz). Pro Node:
nodeId/definitionvalidieren → Definition aus der Registry (unbekannt → sichtbarer Fallback-Kasten + Trace-Warnung)- Props aufbereiten: nicht deklarierte Props verwerfen (Whitelist), Defaults einsetzen, ungültige Enum-Werte auf Default zurückfallen, responsive Objekte normalisieren, maxLength kappen
- Slots zuerst: Kinder rekursiv rendern, Ergebnis als fertiges HTML in
$ctx['slots'] - Render-Vertrag ausführen — Escaping passiert IM Renderer; wirft er, gibt es einen Fallback für DIESEN Node, nie einen Fatal für die Seite
// Der komplette Render-Vertrag einer Definition:
return function (array $props, array $ctx = []): string {
return '<div class="invisible-card">'
. ($ctx['slots']['default'] ?? '')
. '</div>';
};
Die Profil-Hülle: Das Target-Profil baut das Dokument um den gerenderten Baum: tokens.css, dann NUR die Assets der tatsächlich verwendeten Definitionen — invisible_require() sammelt sie während des Walkens, löst die Dependency-Kette auf (FAQ zieht Accordion zieht Icon) und dedupliziert.
Render-Cache: Publizierte Surfaces werden als fertiges HTML abgelegt. Der Cache-Key enthält Surface-ID + Version + Target + Token-Build-Stand + Asset-Stand — ein Token-Update invalidiert automatisch alle betroffenen Seiten. Niemand leert je einen Cache von Hand.
Einbettung in bestehende Systeme
| Andockpunkt | Aufruf | Typischer Einsatz |
|---|---|---|
| Einzelner Baustein | invisible_render_id($id, $props, ['slots' => …]) | Invisible-Bausteine mitten in einer Bestandsseite — schrittweise Migration |
| Ganze Surface | invisible_render_surface($surface, $options) | Host-System erzeugt JSON selbst (Workflows, Agenten, Generatoren) |
| Publizierte Seite | invisible_render_published($id) | Produktion: Gates garantiert bestanden, Cache aktiv |
Jeder Render erzeugt einen Trace (Request-ID, Node-Zahl, Fallbacks, Warnungen, Dauer, Cache-Status). Faustregel: „sauber" heißt null Fallbacks, null Warnungen.