Host-less-Modus
Maßgeblicher Leitfaden zum standalone-fähigen Design-Vertrag, dank dem jede Wippy-Micro-Frontend-App und Web Component bauen, laufen und testen kann, ohne dass der Wippy Web Host sie umschließt.
Standardzustand der Injections: Das Dev-Overlay startet mit
themeConfig,primevue,markdownundiframedeaktiviert, aber mitcustomCssundcustomVariablesaktiviert. Eine App, die sich nur auf eigene Overrides stützt, scheint also zu funktionieren, während eine App, die die Plattform-Theme-Variablen oder PrimeVue-Styles erwartet, ungestylt rendert, bis Sie diese Injections aktivieren. Öffnen Sie den Overlay-FAB → aktivieren Sie die benötigten Injections → setzen Sie "Auto-accept on reload", damit die Auswahl Reloads überdauert.
Inhaltsverzeichnis
- Denkmodell — Apps und WCs sind absichtlich standalone-fähig
- Der
@wippy/scripts-Umschaltpunkt — ein Tag, zwei Boot-Pfade - Was
dev-proxy.jstatsächlich tut - Das Dev-Overlay (Konfigurationsdialog)
- Host-Stubs — die Standalone-
host-API - Web Components — Host-less-Playground und Tests
- Häufige Abweichungen und wie man sie erkennt
- Fehlersuche
- Verwandte Dokumente
Denkmodell — Apps und WCs sind absichtlich standalone-fähig
Jede Wippy-Micro-Frontend-App und jede Web Component ist um eine kleine, bewusste Einschränkung herum gebaut:
Der Laufzeitvertrag ist die Oberfläche der Proxy-API. Sonst nichts.
Was das in der Praxis bedeutet:
- Das Einzige, was eine App oder WC zur Laufzeit berührt, ist die Oberfläche der Proxy-API: die synchronen Getter, die aus
@wippy-fe/proxyimportiert werden (host,api,on,config,state,ws,logger). Apps und WCs nutzen dieselben Imports; darunter lösen sie auf dieselbeProxyApiInstanceauf, die die Runtime als interne Globals installiert (window.$W,window.__WIPPY_APP_API__— lesen Sie diese niemals direkt). - Apps und WCs importieren keinen Code aus benachbarten Apps, aus der
Lua-Seite des übergeordneten Moduls, aus dem Wippy Web Host oder aus einem
anderen Projektmodul. Sie leben in ihrem eigenen Ordner. Vite leitet jedes
Rollup-External aus der gepinnten
import-map.jsondes Ziel-Hosts ab;package.jsondeklariert nur die npm-Abhängigkeiten und Peer-Roots, die das Artefakt tatsächlich importiert. - Dieselbe
app.ts(bzw.index.tseiner WC) bootet in zwei Umgebungen korrekt:- Hosted — innerhalb eines Wippy Web Hosts, der
proxy.js, AppConfig, Importmap und CSS injiziert. - Host-less — mit direkt ausgeführter
app.htmlüber den Vite-Dev-Server, file://, eine Unit-Test-Seite, einen Storybook-artigen Playground usw.
- Hosted — innerhalb eines Wippy Web Hosts, der
Sie können sich jede App/WC als "kleines Programm mit einer winzigen standardisierten I/O-Oberfläche" vorstellen. Der Host ist eine mögliche Laufzeitumgebung; Standalone eine andere. Der App-Code weiß nicht, in welcher er steckt.
Das ist kein Zufall und kein nachträglicher Einfall. Es ermöglicht:
- Lokale FE-Iteration ohne ein vollständiges Wippy-Backend.
- WCs, die isoliert unter vitest + jsdom unit-testbar sind.
- Apps, die zwischen Wippy-Modulen teilbar sind — jede Micro-Frontend-App und Web Component baut mit derselben Toolchain, unabhängig davon, welches Modul sie ausliefert.
- Kundenspezifische Overlays — Betreiber patchen Metadaten (Theming, Importmap, Env), ohne das FE-Bundle neu zu bauen.
Der @wippy/scripts-Umschaltpunkt — ein Tag, zwei Boot-Pfade
Die app.html jeder kanonischen App liefert ein Script-Tag, das den Boot-Pfad zur Ladezeit entscheidet:
Dies ist ein gekürztes Body-/Boot-Beispiel. Fügen Sie die vollständige gültige Import-Map-Antwort ein, die der Algorithmus für den Import-Map-Snapshot beschreibt, und aktualisieren Sie sie, wenn sich das gepinnte Web-Host-Tag ändert.
<!-- Die URL MUSS ein Release-Tag-Segment enthalten: https://web-host.wippy.ai/<release-tag>/dev-proxy.js -->
<script
src="https://web-host.wippy.ai/<release-tag>/dev-proxy.js"
data-role="@wippy/scripts"
></script>
Vollständiges app.html-Scaffold in Micro Frontend App.
Zwei Attribute auf diesem einen Tag tragen den gesamten Dual-Mode-Vertrag:
| Attribut | Rolle | Genutzt von |
|---|---|---|
data-role="@wippy/scripts" |
Marker für den Host. Wenn vorhanden, entfernt der Host dieses <script>-Element, bevor er den iframe ausliefert, und injiziert seine eigenen loading.js + proxy.js + Importmap + AppConfig vor dem Marker. Im Hosted-Modus verschwindet das Element. |
Wippy Web Host |
src="…/dev-proxy.js" |
Fallback-URL. Wird verwendet, wenn kein Host vorhanden ist — der Browser lädt dev-proxy.js direkt, und dieses Skript bootet die Seite. Das Attribut src= ist im Hosted-Modus irrelevant (das <script>-Element existiert dann nicht mehr). |
Standalone-Laden im Browser |
Wählen Sie eine URL, die zu Ihrer Umgebung passt. Beachten Sie: Die Web-Host-URL verlangt immer ein Release-Tag-Segment im Pfad — /dev-proxy.js direkt am Host-Root ist NICHT gültig; Sie müssen einen bestimmten Build adressieren (/<release-tag>/dev-proxy.js). Das garantiert, dass jeder Dev-Modus-Boot an ein bekanntes, reproduzierbares Bundle gepinnt ist, und vermeidet Überraschungen der Klasse "das Host-CDN wurde über Nacht aktualisiert, meine Vorschau ist kaputt".
| Umgebung | Beispielwert für src= |
|---|---|
| Öffentliches CDN (Standard) | https://web-host.wippy.ai/<release-tag>/dev-proxy.js |
| Selbst gehostetes Wippy-Deployment | https://<your-wippy-host>/<release-tag>/dev-proxy.js |
Das Tag muss zur Release-Version passen, die fe_facade_url der Facade verwendet. Pinnen Sie es explizit — /dev-proxy.js ohne Tag-Segment ist nicht gültig. Dasselbe Bundle funktioniert für lokale Iteration, CI und teilbare Vorschau-Links.
So ist dieselbe HTML-Zeile zugleich der Anker "injiziere deine Skripte hier" für den Host und der Host-less-Fallback-Boot — ganz ohne bedingte Logik.
Was gehört in die Importmap?
Holen Sie die vollständige Map einmal während der Entwicklung, mit demselben Tag wie fe_facade_url und dev-proxy.js:
curl.exe -fsS "https://web-host.wippy.ai/<release-tag>/import-map.json" -o import-map.json
Setzen Sie den Text des <script type="importmap">-Elements in app.html auf
die geholte JSON-Antwort, wortgetreu. Setzen Sie keine Kommentare,
Auslassungsplatzhalter oder handgeschriebenen Ersetzungen in dieses JSON. Der
Build- und Abhängigkeitsvertrag
definiert die Anforderungen an Snapshot und Herkunft; die geholte Release-Antwort
liefert das exakte imports-Objekt.
Konventionen:
- Setzen Sie jeden geholten Key in die Rollup-Externals, auch aktuell ungenutzte Keys.
- Behalten Sie dasselbe vollständige Key/Value-Objekt in
app.html; rekonstruieren Sie es nicht mitesm.sh. - Bundeln Sie einen importierten Specifier nur, wenn sein exakter Key fehlt.
- Holen Sie sie erneut, wenn sich das Web-Host-Tag ändert oder eine neue Abhängigkeit hinzukommt, um zu prüfen, ob genau dieser Specifier extern sein kann.
Die Standalone-app.html löst die vollständige kopierte Map auf. Der Hosted-Modus verwendet die Map, die dasselbe gepinnte Release ausliefert.
package.json für dev-proxy verfügbar machen (kanonisches Scaffold)
Die package.json jeder Wippy-App trägt Metadaten, die Laufzeit-Defaults bestimmen — Proxy-Injections (wippy.proxy.injections.css.*), Theming-Overrides pro Page (wippy.configOverrides.customization), Iconify-Icon-Sammlungen usw. Im Hosted-Modus liest der Host diese aus der Registry. Im Host-less-Modus braucht dev-proxy dieselben Daten, um dieselben Defaults anzuwenden.
Das kanonische Muster ist wippyPagePlugin() aus der aktuellen kohärenten @wippy-fe/vite-plugin-Familie (0.0.46 zum Publikationszeitpunkt), einmalig in Ihre vite.config.ts eingetragen. Das Plugin liest Ihre package.json zur Build-Zeit und tut zwei Dinge:
- Löst
file://-Referenzen imwippy-Block auf (jeder String-Wert der Form"file://<relative>"wird durch den UTF-8-Inhalt der referenzierten Datei ersetzt — siehe die Namenskonvention*.do-not-link.<ext>in build-system.md). - Erzeugt zwei Ausgaben mit dem aufgelösten JSON:
- In
<head>injiziertes<script type="application/json" data-role="@wippy/package">für den Host-less-/dev-proxy-Boot. wippy-meta.jsonim tatsächlichen Vite-Ausgabeverzeichnis für den Wippy-Hosted-Modus.
- In
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { wippyPagePlugin } from '@wippy-fe/vite-plugin'
export default defineConfig({
plugins: [
vue(),
wippyPagePlugin(),
],
// …
})
Für Web Components (view.component, nur ESM — kein HTML-Entry, in das injiziert werden könnte) verwenden Sie wippyComponentPlugin() aus demselben Package. Es erzeugt nur wippy-meta.json im tatsächlichen Ausgabeverzeichnis; kein transformIndexHtml-Schritt.
// vite.config.ts für eine Web Component
import { wippyComponentPlugin } from '@wippy-fe/vite-plugin'
export default defineConfig({ plugins: [wippyComponentPlugin()] })
wippyPackagePluginbleibt ein veralteter Kompatibilitäts-Alias. Neuer Page-Code verwendetwippyPagePlugin(); reine Komponenten-Builds verwendenwippyComponentPlugin().
Das Plugin erzeugt dies am Anfang von <head> in der gebauten app.html:
<script type="application/json" data-role="@wippy/package">
{ "name": "@wippy/your-app", "version": "1.0.0", "wippy": { "proxy": {...}, "configOverrides": {...} } }
</script>
dev-proxy.js liest das beim Boot synchron über
document.querySelector('script[data-role="@wippy/package"]') und nutzt wippy.proxy.injections, um die Proxy-Konfigurations-Defaults zu setzen, sowie wippy.configOverrides.customization, um appConfig.theming.global zu setzen. Der data-role-String @wippy/package wird als WIPPY_PACKAGE_DATA_ROLE aus @wippy-fe/shared exportiert, damit beide Seiten der Grenze dieselbe Konstante teilen.
Warum diese Form:
- Keine Duplikation.
package.jsonist die einzige Wahrheitsquelle — das Plugin liest sie zur Build-Zeit, nichts in Ihremsrc/referenziert sie. - Kein Fetch. Inline im ausgelieferten HTML — synchron lesbar durch
dev-proxy.js, bevor irgendein App-Code läuft. - Richtige Reihenfolge. Am Anfang von
<head>vor jedem Script-Tag injiziert, sodass es im DOM ist, wenn der dev-proxy ausgeführt wird (dev-proxy ist ein synchrones UMD-Skript; Modul-Skripte sind deferred und laufen später). - Kein Bearbeiten von
app.html. Das Template bleibt sauber; das Plugin besitzt die Injection. - Konstante aus dem Shared-Package. Der String
'@wippy/package'lebt an genau einer Stelle (@wippy-fe/shared→WIPPY_PACKAGE_DATA_ROLE); Apps referenzieren ihn nicht direkt, dev-proxy und das Plugin importieren ihn beide von dort. - Unter einem echten Host sauber ignoriert. Das
processWebPagedes Hosts liestpackage.jsonserverseitig aus der Registry; das Inline-JSON-Tag ist harmlose Metadaten.
dev-proxy liest das JSON während resolveDevConfig() und nutzt es, um die Defaults des Dev-Overlays zu füllen. Fehlt das Script-Tag (ältere App, Plugin noch nicht ergänzt), fällt dev-proxy auf getDefaultProxyConfig() zurück. Das Hinzufügen des Plugins ist also rein additiv — Apps ohne es funktionieren mit den generischen Defaults weiter.
Warum ein Plugin und kein Laufzeit-
window-Global? dev-proxy.js ist ein synchrones Nicht-Modul-Skript, das früh beim Parsen von<head>läuft — bevor irgendein Modul-Skript (einschließlich Ihrerapp.ts) geladen ist.app.tskann ein Global also nicht setzen, bevor dev-proxy es liest. Eine HTML-Transformation zur Build-Zeit legt die Daten von vornherein ins DOM, verfügbar in dem Moment, in dem dev-proxy ausgeführt wird.
Warum ein Tag und nicht zwei? Ein zweiter
<script>-Block (z. B. einif (!window.__WIPPY__) load dev-proxy) liefe erst, nachdem die Injection des Hosts abgeschlossen ist; ist der Marker weg, hat die Bedingung nichts, woran sie sich hängen könnte. Das Ein-Tag-Muster bedeutet, dass der Marker immer im Quell-HTML steht und die Aufgabe des Hosts genau lautet: "lösche diesen Marker und ersetze ihn". Der Standalone-Fall tritt genau dann ein, wenn ihn niemand gelöscht hat.
Der Host-Vertrag verlangt, dass die in wippy.path angegebene HTML-Datei ein Element <script type="text/javascript" data-role="@wippy/scripts"> enthalten MUSS, in das zusätzliche Skripte automatisch injiziert werden.
Die kanonischen App-Template-Apps werden mit gesetztem src="…/dev-proxy.js" ausgeliefert. Das ist die empfohlene Form: binden Sie immer den src=-Fallback ein, es sei denn, Ihre App kann nicht host-less laufen (selten und begründungswürdig).
Was dev-proxy.js tatsächlich tut
dev-proxy.js ist das Host-less-Boot-Bundle, ausgeliefert vom Wippy-Web-Host-CDN unter https://web-host.wippy.ai/<release-tag>/dev-proxy.js.
Seine Aufgabe ist es, die @wippy-fe/proxy-Getter ohne jeden Host korrekt auflösen zu lassen — indem es dieselben internen Globals installiert (window.$W, window.__WIPPY_APP_API__), die der echte Host installieren würde. App- und WC-Code berührt diese Globals nie; er importiert einfach aus @wippy-fe/proxy, und die Getter funktionieren. dev-proxy tut das in grob fünf Schritten:
- History-Guard installieren (
installHistoryGuard()) — stubtpushState/replaceState, damit vue-router nicht versucht, die Browser-History außerhalb eines iframe-srcdoc-Kontexts zu verändern. - Eine Konfiguration auflösen (
resolveDevConfig()insrc/proxy/dev/resolve-dev.ts):- Liest
localStorage['@wippy-dev/config']undlocalStorage['@wippy-dev/proxy-config']. - Wenn
localStorage['@wippy-dev/auto-accept'] === 'true'UND eine gespeicherte Konfiguration existiert → sofort verwenden, Overlay im Monitoring-Modus rendern. - Andernfalls → Overlay im Warte-Modus rendern (FAB pulsiert blau, Sprechblase "Accept config to continue loading") und den Boot blockieren, bis der Entwickler auf Accept klickt.
- Liest
- Eine unechte
ProxyApiInstancebauen, verdrahtet mit:- Der akzeptierten
ChildAppConfig(das, wasconfigaus@wippy-fe/proxyzurückgibt). - Einem nanoevents-Emitter für
on(...)-Subscriptions und@history-/@visibility-Simulationen. host-Stubs, die jede Methode in die Konsole loggen (createDevHostAPI()insrc/proxy/dev/host-stubs.ts).- Einer echten axios-Instanz hinter
apiaus@wippy-fe/proxy, konfiguriert gegen die vom Entwickler eingegebene URL (env.APP_API_URLhat den Default${location.origin}/api). - Einem Logger-/State-/ws-Stub, der die Form des Produktions-Proxys spiegelt.
- Der akzeptierten
- CSS-Injection anwenden, basierend auf der vom Entwickler gewählten Proxy-Konfiguration:
themeConfig: true→ injizierttheme-config.cssaus@wippy-fe/theme.iframe,primevue,markdown→ ebenso, die Inline-CSS-Bundles aussrc/proxy/dev/css-inline.ts.customCss/customVariables→ wendetappConfig.theming.global.customCSS/cssVariablesan (einschließlich der@dark/@light-Blöcke, beschrieben in micro-frontend-app-theming.md).
- Die internen Proxy-Globals installieren mit derselben Form wie
entry.iframe.ts, damit die@wippy-fe/proxy-Getter (config,host,api,on,logger,state,ws,loadWebComponent) auflösen. Jeder App- oder WC-Code, der aus@wippy-fe/proxyimportiert, funktioniert unverändert. (Die Globals selbst —window.$Wund Co. — sind intern; siehe Proxy & Isolation § Internals.)
Standard-ChildAppConfig (aus getDefaultConfig() in config-store.ts):
{
$schema: '<built schema URL>',
auth: { token: 'dev-token', expiresAt: '' },
env: {
APP_API_URL: `${location.origin}/api`,
APP_AUTH_API_URL: `${location.origin}/api`,
APP_WEBSOCKET_URL: `${location.origin.replace(/^http/, 'ws')}/ws`,
},
theming: { global: {} },
context: { resourceId: '', resourceType: 'page' },
}
Sie überschreiben all das im Dialog (oder indem Sie localStorage['@wippy-dev/config'] bearbeiten).
Das Dev-Overlay (Konfigurationsdialog)
Visuell ist das Dev-Overlay eine winzige Shadow-DOM-Web-Component (<wippy-dev-overlay>), die rendert:
- Einen FAB (Floating Action Button) in der unteren rechten Ecke — bis zum Klick die einzige sichtbare Affordanz.
- Eine Sprechblase im Wartemodus: "Accept config to continue loading."
- Ein Panel, das sich beim Klick auf den FAB öffnet. Das Panel hat drei Abschnitte:
- Monitor — Live-Anzeige von aktuellem Pfad, Dokumenttitel und Viewport-Größe; Button "Trigger Refresh", der
@visibility(true)auslöst, damit die App neu laden kann. - Configuration (einklappbar):
App Config (JSON)— vollständigeChildAppConfigals bearbeitbares JSON. Wird bei Accept validiert.Proxy Injections— Checkboxen für jedes Proxy-Injection-Flag (themeConfig,iframe,primevue,markdown,customCss,customVariables,tailwindConfig,resizeObserver,preventLinkClicks,iconifyIcons,refreshWhenVisible,historyPolyfill,errorCapture).Options— Checkbox "Auto-accept on reload" (schreibt das Auto-Accept-Flag in den localStorage).
- Footer — Reset (löscht alle
@wippy-dev/*-localStorage-Keys), Accept (speichert die Konfiguration und löst das Boot-Promise auf).
- Monitor — Live-Anzeige von aktuellem Pfad, Dokumenttitel und Viewport-Größe; Button "Trigger Refresh", der
Verwendete localStorage-Keys (definiert in src/proxy/dev/config-store.ts):
| Key | Was er speichert |
|---|---|
@wippy-dev/config |
Die akzeptierte ChildAppConfig als JSON |
@wippy-dev/proxy-config |
Die akzeptierte partielle ProxyConfig (Injection-Flags) |
@wippy-dev/auto-accept |
'true', um den manuellen Accept-Schritt beim Reload zu überspringen |
Auto-Accept lässt "gegen einen Host-less-Build iterieren" nahezu nativ wirken: Aktualisieren, die App bootet sofort mit der zuletzt bekannten Konfiguration, der FAB bleibt sichtbar, sodass Sie beobachten oder nachjustieren können.
Host-Stubs — die Standalone-host-API
Die host-API (import { host } from '@wippy-fe/proxy') ist die Oberfläche, über die die App den Host um Dinge bittet — Toast, Navigation, eine Session öffnen, Kontext setzen, URLs formatieren usw. Ohne echten Host setzt dev-proxy eine Stub-Schicht in src/proxy/dev/host-stubs.ts ein:
| Methode | Standalone-Verhalten |
|---|---|
host.toast(message) |
Nur Konsolen-Log |
host.confirm({ message }) |
Browser-window.confirm() |
host.startChat(token, options) |
Konsolen-Log |
host.openSession(uuid, options) |
Konsolen-Log |
host.openArtifact(uuid, options) |
Konsolen-Log |
host.navigate(url) |
Konsolen-Log + löst @history aus, damit der Child-Router es aufnimmt, + aktualisiert die Pfadanzeige im Overlay |
host.onRouteChanged(path) |
Konsolen-Log + aktualisiert die Pfadanzeige im Overlay |
host.handleError(code, error) |
console.error |
host.setContext(context, sessionUUID, source) |
Konsolen-Log |
host.formatUrl(rel) |
Gibt `${appConfig.routePrefix |
host.classifyLink(href) |
Echte Implementierung — nutzt mountRoutes / routePrefix aus der akzeptierten Konfiguration |
host.layout.* |
No-op-Stubs, die den Typvertrag erfüllen |
Die Stubs sind absichtlich gesprächig: Die Konsolenausgabe ersetzt die echten Seiteneffekte des Hosts, damit eine Entwicklerin sehen kann, was passiert wäre, ohne den Host tatsächlich zu verdrahten. Hängt die Korrektheit Ihrer App vom Seiteneffekt ab (z. B. dass host.openSession tatsächlich eine Session öffnet), testen Sie diesen Pfad unter einem Host; die Stubs leisten das nicht.
Web Components — Host-less-Playground und Tests
Web Components teilen dasselbe Dual-Mode-Design, werden aber als ES-Module statt als iframes geladen. Der Proxy-Vertrag für WCs ist import { api, host, on, ... } from '@wippy-fe/proxy' — und dieser Import löst zur Laufzeit auf, indem er window.__WIPPY_APP_API__ liest (gesetzt entweder vom echten Proxy oder von dev-proxy).
Playground-/Demo-HTML-Seite
<!-- demo.html in Ihrem WC-Projekt -->
<!DOCTYPE html>
<html>
<head>
<!-- Das erforderliche vollständige importmap-Skript ist in diesem gekürzten Beispiel weggelassen. -->
<script src="https://web-host.wippy.ai/webcomponents-1.0.44/dev-proxy.js" data-role="@wippy/scripts"></script>
</head>
<body>
<my-component prop1="value"></my-component>
<script type="module" src="./src/index.ts"></script>
</body>
</html>
Derselbe Umschaltpunkt, dasselbe Dev-Overlay. Die index.ts Ihrer WC ruft define(import.meta.url, ...) auf, und das Element registriert sich selbst; dev-proxy stellt die Host-Stubs bereit.
Wenn dev-proxy.js nicht lädt (oder Sie vergessen, es einzubinden), wirft entry.web-component.ts einen expliziten Fehler:
@wippy-fe/proxy: Proxy globals not found. For dev/testing without the Wippy host, add <script src="dev-proxy.js"></script> to your HTML.
Dieser Fehler ist das kanonische Signal dafür, dass Ihnen das Host-less-Boot-Skript fehlt.
Vitest-/jsdom-Tests
Für Unit-Tests ist das Dev-Overlay unnötig — Tests haben keine UI zum Interagieren. Das Muster ist, den Host-Kontext direkt zu fälschen, indem man das Wrapper-Objekt anhängt, das der Host anhängen würde:
import { describe, expect, it } from 'vitest'
import { WippyElement } from './base-element'
class TestEl extends WippyElement {
static get wippyConfig() {
return { propsSchema: { properties: {} }, hostCssKeys: [] }
}
protected onMount(): void {}
protected onUnmount(): void {}
}
const TAG = 'wippy-test-el'
customElements.define(TAG, TestEl)
it('reads host wrapper attached by resolver as __wippyHost', () => {
const el = document.createElement(TAG) as TestEl
const fakeHost = { layout: { broadcast: () => {} } }
;(el as any).__wippyHost = fakeHost
expect(el.host).toBe(fakeHost)
})
Die Eigenschaft __wippyHost ist der Vertrag, den der Managed-Layout-Host verwendet. Tests, die API- oder Proxy-Globals benötigen, können entweder dev-proxy über eine vitest-Setup-Datei einbinden oder window.__WIPPY_APP_API__ selbst stubben:
// vitest.setup.ts
;(window as any).__WIPPY_APP_API__ = {
api: mockApi,
host: mockHost,
on: mockOn,
// ...weitere ProxyApiInstance-Felder
}
Beide Ansätze sind "host-less" im selben Sinne wie der Browser-dev-proxy: Der Proxy-Vertrag wird von Code erfüllt, den der Test besitzt, statt von einem echten Wippy-Server.
Häufige Abweichungen und wie man sie erkennt
Wenn eine App oder WC vom standalone-fähigen Vertrag abgedriftet ist, sind die Symptome vorhersehbar:
| Symptom | Wahrscheinliche Ursache | Behebung |
|---|---|---|
app.html hat <script data-role="@wippy/scripts"></script> ohne src= |
Die Page kann nicht host-less booten. Das direkte Laden der Datei ergibt eine leere Seite — die Proxy-Runtime wird nie installiert, also lassen sich @wippy-fe/proxy-Imports nicht auflösen. |
Fügen Sie dem Tag src="https://web-host.wippy.ai/<release-tag>/dev-proxy.js" hinzu — die URL verlangt immer ein Release-Tag-Segment. |
app.html hat das dev-proxy-<script src=…>, aber kein <script type="importmap"> darüber |
Der Browser kann externe Bare Specifier nicht auflösen. Der erste Modul-Skript-Ladevorgang scheitert mit Failed to resolve module specifier. |
Holen Sie <release-tag>/import-map.json, kopieren Sie dessen vollständiges imports-Objekt vor dev-proxy in <head> und verwenden Sie alle Keys als Rollup-Externals. |
Der Body von app.html hat einen eigenen SVG-Spinner / <div>Loading…</div> statt <wippy-loading title="…"> |
Der Pre-Bootstrap-Loader entspricht nicht dem kanonischen Wippy-Idiom. Das eigene Markup bleibt sichtbar, während das WC-Ökosystem (das einen gestylten, theme-fähigen Loader rendern würde) vollständig hochfährt. | Ersetzen Sie es durch <wippy-loading title="Loading..."></wippy-loading>. Die Web Component <wippy-loading> wird von dev-proxy.js registriert (es importiert @wippy-fe/loading synchron), bevor der <body> geparst wird, sodass das Element auch sehr früh im Seitenaufbau korrekt auflöst. |
import aus den Quelldateien einer Schwester-App |
Gemeinsamer Code wird über Modulgrenzen hinweg kopiert. | Extrahieren Sie ihn in ein Workspace-Package oder duplizieren Sie bewusst; greifen Sie nie über App-Ordner hinweg. |
Fest verdrahtete fetch('/api/…')-Aufrufe |
Umgeht die axios-Instanz, die der Proxy bereitstellt; nimmt env.APP_API_URL-Overrides nicht auf. |
Verwenden Sie useApi() (Apps) oder import { api } from '@wippy-fe/proxy' (WCs). |
new EventSource(...) für Live-Daten |
Umgeht die Auth-/Relay-Brücke des Hosts; im Standalone-Modus gibt es kein Äquivalent. | Verwenden Sie on('your.topic', cb) — funktioniert in beiden Modi (im Standalone-Modus feuert das Topic nur nicht, wenn Sie es nicht simulieren). |
document.documentElement.setAttribute('data-theme', ...) zum Theme-Wechsel |
data-theme ist nicht das Wippy-Theme-Protokoll. |
Verwenden Sie den Auto-Modus oder die vom Host verwalteten Klassen .w-theme-light / .w-theme-dark. Konfigurierte @light-/@dark-Werte unterstützen beide Wege. Siehe micro-frontend-app-theming.md. |
import '@wippy-fe/theme/theme-config.css' in app.ts |
Redundant — der Host injiziert theme-config über die Proxy-Injection themeConfig: true. Im Host-less-Modus injiziert dev-proxy es ebenfalls. |
Entfernen Sie den Import. |
| Fest verdrahtete API-Basis-URLs in api/-Modulen | Funktioniert im Host-less-Modus gegen eine andere Umgebung nicht. | Lesen Sie sie über useApi() aus appConfig.env.APP_API_URL. |
Fehlersuche
Fehler "Proxy globals not found".
Das WC-Bundle lief, aber weder der echte Proxy noch dev-proxy hat window.__WIPPY_APP_API__ initialisiert. Prüfen Sie, ob <script src=".../dev-proxy.js" data-role="@wippy/scripts"> in der Seite steht und die URL erreichbar ist. Im Produktions-Host-Modus bedeutet dieser Fehler, dass der Host proxy.js nicht injizieren konnte — prüfen Sie die Host-Logs.
Das Dev-Overlay erscheint nie.
Das Overlay ist ein Shadow-DOM-Custom-Element, das nach DOMContentLoaded an document.body angehängt wird. Wenn Sie dev-proxy.js aus <head> laden und der Body fehlt oder display: none hat, kann das Overlay nicht rendern. Verschieben Sie das Skript ans Ende des Bodys oder blenden Sie den Body ein.
Auto-Accept "hängt" mit schlechter Konfiguration.
Ist die gespeicherte Konfiguration defekt und Auto-Accept aktiv, rendert das Overlay dennoch (im Monitoring-Modus); klicken Sie den FAB → Reset, um alle @wippy-dev/*-localStorage-Keys zu löschen, und laden Sie neu.
Das Theme ist im Dev-Modus falsch.
Standardmäßig aktiviert getDefaultProxyConfig() customCss und customVariables, deaktiviert aber themeConfig, iframe, primevue, markdown. Wenn Ihre App das theme-config-CSS von PrimeVue erwartet, schalten Sie diese Checkboxen im Panel ein. Auto-Accept merkt es sich.
Importmap-Abweichung zwischen Hosted und Standalone.
Holen Sie die import-map.json des gepinnten Releases erneut, ersetzen Sie das vollständige Host-less-imports-Objekt und generieren Sie die Rollup-External-Keys daraus neu. Patchen Sie keine Einzeleinträge und pflegen Sie keine kuratierte Teilmenge.
WC-Test scheitert mit "host getter returned null".
Tests müssen el.__wippyHost = fakeWrapper setzen, bevor connectedCallback feuert. Setzen Sie es entweder vor document.body.appendChild(el) oder fälschen Sie den Wrapper über das Resolver-Muster, das Ihre Suite verwendet.
Verwandte Dokumente
- proxy-api.md — vollständige
@wippy-fe/proxy-Referenz (funktioniert im Hosted- und Host-less-Modus identisch) - micro-frontend-app.md — Micro-Frontend-Apps bauen (der Boot-Pfad ist das Dual-Mode-
app.html-Muster, das dieses Dokument beschreibt) - web-component.md — Web Components bauen (
WippyVueElement,define(), Host-less-Playground/Tests) - theming.md — Theme-Overrides pro Page über
config_overrides(speisen auch dev-proxy übertheming.global.cssVariables/customCSS) - compliance-checklist.md — §9 Host-less-Modus-Checkliste mit vollständigen REJECT-Regeln