Proxy y Aislamiento
El Web Host ejecuta cada micro-frontend hijo en un contexto sandbox y lo conecta con el host a través de la API del Proxy. Tanto las aplicaciones micro frontend como los web components alcanzan el host importando desde @wippy-fe/proxy.
La API del Proxy
La API del Proxy es su punto de entrada al host. Un runtime — proxy.js — la entrega: coloca la API y el AppConfig actual en la página y los expone mediante el módulo @wippy-fe/proxy.
- Para una aplicación micro frontend (
view.page), el host inyectaproxy.jsen elsrcdocde la página. - Para un web component (
view.component), el runtime ya está presente en la página del host: el componente se monta en el DOM del host, no en un iframe separado.
Su código lo consume mediante los getters síncronos exportados por @wippy-fe/proxy:
import { host, api, on, config } from '@wippy-fe/proxy'
host.navigate('/dashboard')
const data = await api.get('/api/v1/agents') // api es una instancia de axios; el await es la llamada HTTP
on('@visibility', (visible) => { /* pausar o reanudar el trabajo */ })
El enrutamiento portable de Vue es la excepción: @wippy-fe/router consume @history e informa de la navegación local por usted. No añada suscripciones manuales de enrutamiento a su alrededor.
Estos getters son síncronos: host, api, on, config y el resto están listos en el momento en que se ejecuta su código — la configuración está en su sitio antes de que el runtime se inicialice (vea abajo), así que no hay ningún handshake que esperar. Marque @wippy-fe/proxy como external en su build de Vite: el host lo proporciona mediante el import map. Vea API del Proxy para la superficie completa.
Cómo llega la configuración al iframe de una aplicación
Cuando el host carga un view.page, construye un srcdoc e inyecta, en este orden, antes del script de su aplicación:
<!-- 1. El AppConfig del hijo, establecido de forma síncrona antes de que cargue el runtime -->
<script>window.__WIPPY_APP_CONFIG__ = { /* auth, env, theming, hostConfig, context */ }</script>
<!-- 2. Los flags de inyección de CSS para esta página -->
<script>window.__WIPPY_PROXY_CONFIG__ = { injections: { css: { themeConfig: true, primevue: true /* … */ } } }</script>
<!-- 3. El runtime (precedido por loading.js) -->
<script src="/.../loading.js"></script>
<script src="/.../proxy.js"></script>
Como la global de configuración se establece antes de que se ejecute proxy.js, el runtime se inicializa de forma síncrona y los getters de @wippy-fe/proxy funcionan inmediatamente: sin handshake. Las páginas no referencian estos scripts directamente; el host reemplaza el marcador <script data-role="@wippy/scripts"> por las etiquetas correctas y ordenadas. Las anulaciones por página llegan como window.__WIPPY_CONFIG_OVERRIDES__ (vea API del Proxy — Anulaciones de configuración).
Un web component ve las mismas globales porque se ejecuta en la página del host, donde el runtime ya las estableció antes de que se dispare el connectedCallback del componente.
En qué se diferencian las aplicaciones y los web components
Ambos importan la misma API de @wippy-fe/proxy. Se diferencian en el contexto de ejecución y en cómo se entregan los estilos:
Aplicación micro frontend (view.page) |
Web Component (view.component) |
|
|---|---|---|
| Se ejecuta en | su propio iframe srcdoc |
el DOM de la página del host (Shadow DOM) |
| Entrega del runtime | proxy.js inyectado en el iframe |
el runtime ya está presente en la página del host |
| CSS | pipeline de inyección completo (themeConfig, primevue, …); vea Inyección de CSS |
hostCssKeys hacia el Shadow DOM; vea Temas: Web Components |
Composición y anidamiento
Los hijos se componen. Una aplicación micro frontend o un web component pueden alojar a su vez hijos — de nuevo aplicaciones micro frontend o web components — que pueden alojar los suyos, hasta cualquier profundidad. Cada nivel usa la misma API @wippy-fe/proxy.
Cómo aloja un nodo a un hijo depende del tipo de hijo:
- Un hijo en iframe — una aplicación micro frontend, un artefacto o HTML arbitrario de Wippy — pasa por
<w-iframe>,<w-artifact>ohtml.inject. Estos inyectan el runtime completo (URL base, import map,loading.js,proxy.jsy configuración) en elsrcdocdel hijo, de modo que obtiene la API del Proxy exactamente igual que una aplicación de nivel superior. Su proxy se conecta hacia arriba a través del padre hasta el host. - Un hijo que es web component no necesita nada de eso. Renderice su etiqueta — o cárguelo con
loadWebComponent/loadByTagName— y se ejecuta en el mismo DOM, importando la API del Proxy directamente.
El código propio del hijo es idéntico tanto si se ejecuta en el nivel superior como si está anidado varios niveles: importe de @wippy-fe/proxy y úselo. No hay reglas especiales de anidamiento.
Vea <w-iframe>, <w-artifact> y Inyección Avanzada de HTML más abajo para conocer la mecánica.
Internos: no leer ni sobrescribir
proxy.js instala las siguientes globales para su propio uso. El código de aplicaciones y componentes nunca debe leerlas ni asignarlas: use @wippy-fe/proxy en su lugar. Se documentan únicamente para que no las sobrescriba por accidente:
| Global | Qué es |
|---|---|
window.$W |
Objeto accesor asíncrono ($W.host(), $W.api(), …). Interno; @wippy-fe/proxy es la superficie soportada. |
window.getWippyApi / window.initWippyApi |
Funciones asíncronas de "resolver la instancia". Internas (initWippyApi está obsoleta). |
window.__WIPPY_APP_API__ |
La instancia de proxy resuelta. |
window.__WIPPY_APP_CONFIG__ |
El snapshot del AppConfig del hijo. |
window.__WIPPY_PROXY_CONFIG__ / window.__WIPPY_CONFIG_OVERRIDES__ |
Flags de inyección de CSS y anulaciones por página. |
window.__WIPPY_WEB_COMPONENT_CACHE__ |
Caché de componentes cargados. |
Dos puntos de entrada componen la API pública de JavaScript: initWippyApp(config, rootContainer?) monta el Web Host completo (el punto de entrada de embebido por módulo que usa el facade; vea Punto de Entrada del Facade), y @wippy-fe/proxy es la API síncrona para aplicaciones y componentes hijos. Todo lo de la tabla anterior es interno.
Protocolo PostMessage (IFrameMessageType): transporte interno
Este es el protocolo de cable que el runtime usa internamente; el código de la aplicación nunca envía ni recibe estos mensajes: @wippy-fe/proxy los gestiona por usted.
La vía estándar inyectada por el host no necesita handshake para arrancar: la configuración ya está presente de forma síncrona como window.__WIPPY_APP_CONFIG__ antes de que se ejecute proxy.js, así que el runtime construye su instancia inmediatamente. El intercambio get-config/set-config sigue ocurriendo en esta vía, pero solo como canal no bloqueante de resincronización y actualización en vivo: después de construir la instancia síncrona, el runtime del iframe siempre envía get-config, el host responde con set-config y vuelve a enviar set-config en cada actualización posterior de la configuración. Los hijos anidados <w-iframe> se comportan igual. Su código nunca espera nada de esto: los getters síncronos ya están activos.
El handshake es la única fuente de configuración, y bloqueante, en exactamente un escenario: el embebido manual en iframe sin facade (iframe.html?waitForCustomConfig), donde no hay un window.__WIPPY_APP_CONFIG__ preinyectado, así que la inicialización se bloquea hasta el primer set-config y el padre debe responder a la petición get-config (vea Punto de Entrada del Facade § Embebido manual en iframe).
Cada mensaje es un sobre JSON con la forma { type: '@gen2-chat', action: IFrameMessageType.*, ...payload }. El campo type es configurable mediante APP_CONFIG_IFRAME_EVENT_TYPE, pero por defecto es '@gen2-chat'.
Todos los tipos de mensaje se definen en el enum IFrameMessageType:
| Miembro del enum | Valor de cable | Dirección | Descripción |
|---|---|---|---|
GetConfig |
get-config |
Hijo → Host | Handshake inicial: el hijo solicita su AppConfig |
SetConfig |
set-config |
Host → Hijo | El host entrega el AppConfig en respuesta a GetConfig |
UrlWasUpdatedInParent |
url-was-updated-in-parent |
Host → Hijo | La URL del host cambió; dispara el evento @history del hijo |
VisibilityWasUpdatedInParent |
visibility-was-updated-in-parent |
Host → Hijo | La visibilidad del iframe cambió; dispara el evento @visibility del hijo |
TopicWasReceivedInParent |
topic-was-received-in-parent |
Host → Hijo | Entrega un evento de topic de WebSocket a los hijos suscritos |
CmdRouteChanged |
cmd-route-changed |
Hijo → Host | La ruta interna del hijo cambió; el host actualiza la URL del navegador |
CmdTitleChanged |
cmd-title-changed |
Hijo → Host | El document.title del hijo cambió; el host actualiza el título de la página |
CmdStartChat |
cmd-start-chat |
Hijo → Host | Abre una nueva sesión de chat |
CmdOpenSession |
cmd-open-session |
Hijo → Host | Navega a una sesión de chat existente |
CmdOpenArtifact |
cmd-open-artifact |
Hijo → Host | Abre un artefacto en la barra lateral o en un modal |
CmdNavigate |
cmd-navigate |
Hijo → Host | Petición de navegación SPA |
CmdShowToast |
cmd-show-toast |
Hijo → Host | Muestra una notificación toast |
CmdShowConfirm |
cmd-show-confirm |
Hijo → Host | Muestra un diálogo de confirmación |
OnConfirmResult |
on-confirm-result |
Host → Hijo | Entrega el resultado del diálogo de confirmación |
CmdSetContext |
cmd-set-context |
Hijo → Host | Envía contexto a una sesión de chat |
CmdHandleError |
cmd-handle-error |
Hijo → Host | Informa de un error al host |
CmdLogout |
cmd-logout |
Hijo → Host | Dispara el cierre de sesión |
CmdSubscribe |
cmd-subscribe |
Hijo → Host | Se suscribe a un topic de WebSocket |
CmdUnSubscribe |
cmd-unsubscribe |
Hijo → Host | Cancela la suscripción a un topic |
OnSubscription |
on-subscription |
Host → Hijo | Entrega los datos de un evento de suscripción |
CmdStateGet |
cmd-state-get |
Hijo → Host | Lee una clave de estado persistido |
CmdStateSet |
cmd-state-set |
Hijo → Host | Escribe una clave de estado persistido |
CmdStateRemove |
cmd-state-remove |
Hijo → Host | Elimina una clave de estado persistido |
CmdStateClear |
cmd-state-clear |
Hijo → Host | Limpia todo el estado de esta página |
CmdStateGetAll |
cmd-state-get-all |
Hijo → Host | Lee todo el estado persistido |
OnStateResult |
on-state-result |
Host → Hijo | Entrega el resultado de una lectura de estado |
OnStateError |
on-state-error |
Host → Hijo | Informa del fallo de una operación de estado |
CmdWsSend |
cmd-ws-send |
Hijo → Host | Reenvía un comando de WebSocket a través de la conexión del host |
CmdBodySize |
cmd-body-size |
Hijo → Host | Informa del tamaño del body para auto-height |
CmdBridgePost |
cmd-bridge-post |
Hijo ↔ Padre | Mensaje de canal sin respuesta mediante host.bridge |
CmdBridgeRequest |
cmd-bridge-request |
Hijo ↔ Padre | Mensaje de canal petición/respuesta mediante host.bridge |
CmdClaimNavOwner |
cmd-claim-nav-owner |
Hijo → Host | Reclama la propiedad de la navegación (modo nav-owner) |
CmdReleaseNavOwner |
cmd-release-nav-owner |
Hijo → Host | Libera la propiedad de la navegación |
CmdLayoutSubscribe |
cmd-layout-subscribe |
Hijo → Host | Se suscribe a las actualizaciones de managed-layout |
CmdLayoutUpdatePanel |
cmd-layout-update-panel |
Hijo → Host | Parchea una definición de panel |
CmdLayoutBroadcast |
cmd-layout-broadcast |
Hijo ↔ Host | Mensaje del bus de layout dentro de la pestaña |
OnLayoutChange |
on-layout-change |
Host → Hijo | Actualización completa del snapshot del layout |
OnLayoutPanelChanged |
on-layout-panel-changed |
Host → Hijo | Delta de estado en vivo por panel |
OnLayoutBroadcast |
on-layout-broadcast |
Host → Hijo | Entrega de una difusión del bus de layout |
El código de la aplicación nunca envía ni recibe estos mensajes directamente. El proxy gestiona el protocolo de forma transparente y expone únicamente la superficie de la API @wippy-fe/proxy.
Elemento personalizado <w-iframe>
<w-iframe> es la primitiva de iframe de bajo nivel integrada en proxy.js. Acepta HTML fuente en bruto, inyecta el runtime completo de Wippy (URL base, import map, loading.js, proxy.js, configuración del hijo) y renderiza el resultado como un iframe srcdoc en sandbox.
Use <w-iframe> cuando tenga HTML fuente y quiera el mismo comportamiento de runtime que obtienen automáticamente las aplicaciones micro frontend de Wippy: API autenticada, relé de estado, relé de WebSocket, enrutamiento nav-owner y mensajería puente padre-hijo.
Atributos y propiedades
| Atributo / propiedad | Obligatorio | Por defecto | Descripción |
|---|---|---|---|
src |
No | — | URL que se solicitará como HTML fuente en bruto a través del api del proxy. |
srcdoc |
No | — | HTML fuente en bruto. También asignable como element.srcdoc = html para cadenas grandes. |
base-url |
No | Derivado de src o de document.baseURI |
<base href> inyectado para la resolución de assets relativos. |
resource-id |
No | El id del elemento y, si no, src |
Identificador de contexto del hijo; establece el estado por defecto y el ámbito de log. |
resource-type |
No | page |
Tipo de contexto del hijo: page o artifact. |
sub-path |
No | La ruta del padre | Ruta inicial del hijo. Se reenvía como config.context.route en el handshake GetConfig. |
auto-height |
No | false |
Redimensiona la altura del iframe para ajustarse a los informes CmdBodySize del hijo. |
nav-owner |
No | false |
Intercepta el CmdRouteChanged del hijo y despacha eventos DOM nav-owner-route en lugar de mutar la URL del host. |
Propiedades JS aceptadas en el elemento:
const frame = document.querySelector('w-iframe')
frame.proxyConfig = { injections: { css: { markdown: false } } }
frame.configOverrides = { customization: { customCSS: ':root { --brand: red }' } }
frame.srcdoc = sourceHtml
Eventos y métodos
| Evento | Detalle | Descripción |
|---|---|---|
loading |
— | Se dispara antes de que empiece el fetch/procesado/renderizado. |
load |
— | Se dispara después de que cargue el iframe en sandbox. |
error |
Error original | Se dispara cuando falla el fetch, la inyección o la carga. |
nav-owner-route |
{ path: string, navId?: number } |
Cambio de ruta del hijo cuando nav-owner está establecido. El evento hace bubbling y es composed. |
wippy-message |
{ channel, payload, requestId?, respond?, reject? } |
Mensaje puente procedente del hijo. |
| Método | Descripción |
|---|---|
post(channel, payload?) |
Mensaje puente sin respuesta hacia el hijo. |
request<T>(channel, payload?, { timeoutMs }?) |
Mensaje puente de petición/respuesta; se resuelve con el valor devuelto por el manejador. |
Shadow parts: loader, error, frame.
Cuando nav-owner está establecido, el ciclo por defecto de sincronización de rutas queda completamente suprimido: el host no actualiza su propia barra de URL y no envía UrlWasUpdatedInParent de vuelta al hijo. La propiedad de la navegación se delega por completo en el código del padre que escucha nav-owner-route. El path del detalle del evento es la ruta interna en bruto del hijo, exactamente como el hijo la pasó a host.onRouteChanged(internalRoute, navId?): no lleva el prefijo de montaje (a diferencia de la vía por defecto CmdRouteChanged, donde el host antepone el prefijo de montaje de la página). El padre embebedor es responsable de cualquier prefijado o mapeo de router:
const frame = document.querySelector('w-iframe')
frame.addEventListener('nav-owner-route', (event) => {
const { path, navId } = event.detail
myRouter.push(path)
})
Puente padre-hijo
El puente usa canales con nombre, de modo que ninguna de las partes necesita sobres postMessage en bruto.
Lado del padre:
const frame = document.querySelector('w-iframe')
frame.addEventListener('wippy-message', async (event) => {
const { channel, payload, respond, reject } = event.detail
if (channel === 'pick-file') {
try {
respond({ id: 'file-1', name: 'data.csv' })
} catch (error) {
reject(error)
}
}
})
frame.post('refresh', { reason: 'parent-click' })
const result = await frame.request('get-selection', undefined, { timeoutMs: 5000 })
Lado del hijo:
import { host } from '@wippy-fe/proxy'
host.bridge.post('ready', { value: 1 })
const file = await host.bridge.request('pick-file', { accept: '.csv' })
const off = host.bridge.on('refresh', async (payload) => {
console.log('refresh requested', payload)
return { ok: true }
})
host.bridge.on() devuelve una función para cancelar la suscripción (() => void). Un canal = un manejador activo. Si se registran varios manejadores para el mismo canal, gana el registrado más recientemente y gestiona todos los mensajes entrantes de ese canal, tanto los post() sin respuesta como los request(). on() no es aditivo: los manejadores anteriores quedan ensombrecidos (no eliminados) y no se ejecutan mientras exista un manejador más nuevo, y el proxy registra un console.warn ante un registro duplicado. Si el manejador más nuevo cancela su suscripción, el manejador anterior de ese canal vuelve a estar activo. Use nombres de canal distintos si necesita varios listeners independientes.
Si omite options.timeoutMs, host.bridge.request() (y el frame.request() del lado del padre) usan un plazo por defecto de 10 segundos (10000 ms). Al agotarse el plazo, la Promise devuelta se rechaza con un Error cuyo mensaje es Bridge request <id> timed out after <ms>ms. Una petición a un canal para el que la otra parte no tiene manejador se rechaza inmediatamente con No handler registered for channel "<channel>" en lugar de esperar a que expire el plazo.
Elemento personalizado <w-artifact>
<w-artifact> resuelve los metadatos y el contenido de un artefacto o página, y luego delega internamente en <w-iframe> los tipos respaldados por iframe. Gestiona la detección del tipo de contenido (HTML, Markdown, paquetes de página web, paquetes ESM, componentes por etiqueta directa) y proporciona una API de más alto nivel que un <w-iframe> en bruto.
Atributos
| Atributo | Obligatorio | Valores | Por defecto | Descripción |
|---|---|---|---|---|
id |
Sí | UUID de artefacto / página | — | Identificador del contenido. |
type |
No | artifact | page |
artifact |
Determina el endpoint REST invocado: /api/v1/artifact/<id>/content o /api/public/pages/content/<id>. |
auto-height |
No | flag booleano | false |
Se reenvía al <w-iframe> interno para la sincronización de altura con CmdBodySize. |
url |
No | Cualquier URL | — | Obtiene el contenido directamente de esta URL; ignora id/type. |
sub-path |
No | Cadena de ruta | — | Se reenvía al <w-iframe> interno como ruta inicial del hijo. |
nav-owner |
No | flag booleano | false |
Se reenvía al <w-iframe> interno; los cambios de ruta del hijo despachan nav-owner-route. |
Eventos
| Evento | Cuándo | Detalle |
|---|---|---|
loading |
Antes de que empiece el fetch | — |
load |
Después de que cargue el iframe | — |
error |
Falla el fetch o el renderizado | Error original |
nav-owner-route |
Cambia la ruta de un hijo nav-owner | { path: string, navId?: number } |
wippy-message |
Mensaje puente desde el iframe anidado | { channel, payload, requestId?, respond?, reject? } |
Estado CSS y parts
El elemento establece un atributo status (loading, ready, error) y expone shadow parts:
w-artifact[status="loading"] { opacity: 0.5; }
w-artifact[status="error"] { border: 1px solid var(--p-danger-color); }
w-artifact::part(loader) { font-size: 1rem; }
w-artifact::part(frame) { border: 0; }
<w-iframe> frente a <w-artifact> frente a un <iframe> en bruto
| Característica | <w-iframe> |
<w-artifact> |
<iframe> en bruto |
|---|---|---|---|
| Inyecta el runtime de Wippy | Sí | Sí (mediante <w-iframe>) |
No |
| Resuelve metadatos de artefacto/página | No | Sí | No |
| Fetch autenticado de contenido | Sí (HTML en bruto) | Sí (resolvedor completo) | No |
| Relé de estado | Sí | Sí | No |
| Relé de WebSocket | Sí | Sí | No |
| Puente padre-hijo | Sí | Sí (reenviado) | No |
| Soporte de nav-owner | Sí | Sí | No |
| Detección del tipo de contenido | No | Sí | No |
| Shadow parts CSS | loader, error, frame |
loader, error, frame |
— |
Atributo status |
Sí | Sí | No |
Use <w-artifact> cuando tenga un UUID de artefacto de Wippy o un ID de página y quiera que la plataforma gestione toda la resolución. Use <w-iframe> cuando ya tenga HTML fuente y quiera inyección directa del runtime. Use un <iframe> en bruto solo para contenido completamente externo que no necesite la API de Wippy.
Inyección avanzada de HTML
Para los casos en que necesite la transformación de HTML fuente a srcdoc sin montar un elemento, el proxy expone html.inject(...):
import { html } from '@wippy-fe/proxy'
const processed = await html.inject(sourceHtml, {
baseUrl: 'https://example.com/app/',
resourceId: 'child-id',
resourceType: 'page',
route: '/initial',
})
La misma función es accesible como instance.html.inject, $W.html e import { html } from '@wippy-fe/proxy'. Prefiera <w-iframe> para el montaje normal; use html.inject(...) solo cuando construya infraestructura de alojamiento a medida.