3.7 KiB
Lessons
Runtime-kompilierte React-Komponenten: Identität stabil halten
Kontext: Generierte Apps werden via new Function(...) zu einer App-Komponente
kompiliert und gerendert.
Fehler: Die App-Funktion wurde im Render-Body des Wrapper-Components per
factory(...) neu erzeugt. Jeder Render → neue Funktionsidentität → React unmountet/
remountet den Teilbaum → useState resettet, useEffect feuert erneut → Endlos-
Schleife ("Maximum update depth exceeded") bei jeder App mit State.
Regel: Dynamisch erzeugte Komponenten einmal memoisieren
(useMemo(() => factory(...), [stableDeps])), nie pro Render neu bauen. Galt hier
für die Sandbox-Runtime; gilt generell für jedes React.createElement mit einer
zur Laufzeit konstruierten Komponente.
Verifikation: innerText enthält keine Placeholder
Beim Headless-Test gegen Spotlight schlug die Assertion fehl, weil sie auf den
placeholder-Text eines <input> prüfte — document.body.innerText liefert
Placeholder/Attribut-Text nicht. Für UI-Smoke-Tests auf echte Text-Knoten oder
Selektoren (querySelector('input[placeholder*="…"]')) prüfen, nicht auf innerText.
Anthropic-kompatible Provider (z.ai): URL-Pfad + CORS beim Streaming
Zwei aufeinanderfolgende Stolpersteine beim Umstellen auf z.ai/GLM:
- URL ohne Pfad:
VITE_ANTHROPIC_API_URL=https://api.z.ai/api/anthropicist nur die Basis-URL → Request landete auf…/anthropicstatt…/anthropic/v1/messages(z.ai:{"code":500,"msg":"404 NOT_FOUND"}). Fix:resolveApiUrl()hängt/v1/messagesan, wenn nicht vorhanden (Basis- oder Voll-URL beide ok). - Doppelte CORS-Header beim Streaming: Die
stream:true-Antwort von z.ai enthält ZWEIaccess-control-allow-origin-Header (http://localhost:5173UND*) plusallow-credentials: true. Das ist laut CORS-Spec ungültig → Browser wirft "TypeError: Failed to fetch". Die Nicht-Stream-Antwort hat nur einen Header, deshalb ging App-Generierung, aber der Assistant nicht. Fix: Vite-Dev-Proxy (server.proxy["/__ai"]→ Upstream-Origin+Pfad aus env,changeOrigin:true). Browser ruft same-origin/__ai/v1/messages→ kein CORS, Streaming bleibt erhalten. Client nutzt im DEV den Proxy, im Prod-Build die absolute URL. Merke: curl testet CORS NICHT — Browser-Reproduktion ist Pflicht.
X-Frame-Options / CSP beim Einbetten externer Seiten (in-OS Browser)
X-Frame-Options und CSP frame-ancestors werden vom Browser anhand der
Ziel-Response erzwungen — client-seitig NICHT umgehbar (kein iframe-Trick, kein
Header-Override). Einzige saubere Lösung: Server-Proxy, der die Seite serverseitig
holt (dort sind die Header wirkungslos) und sie ohne Framing-Header zurückgibt.
Umsetzung hier: Vite-Dev-Middleware /__proxy?url=… → fetch upstream, droppt
x-frame-options/content-security-policy/content-encoding/content-length,
injiziert <base href> (relative Assets) + ein Klick-Interceptor-Script (Navigation
bleibt im Proxy). SDK-Helfer os.net.proxyUrl(url) + Generierungs-Prompt weisen
Browser-Apps an, NIE eine rohe URL in den iframe zu setzen. <iframe> im
Forbidden-Guard freigeben. End-to-end verifiziert: en.wikipedia/wiki/Cat lädt im
iframe (innerTitle "Cat - Wikipedia"). Prod-Build hat keinen Proxy → Fallback auf
rohe URL (dann wieder geblockt); für Prod bräuchte es einen echten Backend-Proxy.
Verifikation lohnt sich auch ohne externen Key
Die KI-App-Generierung braucht einen API-Key. Statt "kann ich nicht testen" wurde der konsumierende Runtime (Babel→Sandbox→Render→Error-Boundary→Forbidden-Guard) mit synthetischem KI-Output headless verifiziert. Das fand den Remount-Bug, den der echte API-Pfad sonst erst beim Nutzer gezeigt hätte.