Punto de Entrada del Facade
El módulo de backend wippy/facade es el punto de entrada que entrega el Web Host a los usuarios. Sirve una página HTML que carga el módulo JS del Web Host, gestiona las redirecciones de autenticación, expone un endpoint /facade/config y traslada la configuración específica del despliegue al bundle de frontend alojado en el CDN. No hay ninguna configuración integrada en el propio bundle: cada despliegue aporta la suya mediante este mecanismo.
La página HTML
Cuando un usuario navega a una aplicación de Wippy, wippy/facade sirve una página HTML. Esta página es ligera: carga un módulo JS del Web Host desde el CDN e inicializa el host con la configuración devuelta por /facade/config. El módulo se hace cargo de toda la página — incluido su historial de navegador — de modo que el host se ejecuta como la aplicación completa y no dentro de un iframe.
El facade carga uno de dos puntos de entrada de módulo JS según el fe_mode configurado:
module.js— el shell compat (por defecto): el layout estándar de barra lateral de navegación + área de página + panel derecho de chat.managed-layout.js— el shell managed (opt-in, acceso anticipado): el layout declarativo multipanel.
Una versión simplificada de la página tiene este aspecto:
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<div id="app"></div>
<script src="https://web-host.wippy.ai/<release-tag>/module.js"></script>
<script>
fetch('/facade/config')
.then(r => r.json())
.then(config => {
window.initWippyApp(config, '#app')
})
</script>
</body>
</html>
La página obtiene su configuración y se la entrega a la función de inicialización del módulo. El host se monta en la página, se hace cargo del enrutamiento y del historial del navegador, y continúa con la inicialización completa.
Nota sobre la ruta del fetch.
/facade/configes la ruta que el facade registra en el router público; la URL real que solicita su página incluye el prefijo de ese router. Con el prefijo de ejemplo/api/public, es/api/public/facade/config, exactamente lo que solicita la página del facade entregada. Los fragmentos inlinefetch('/facade/config')aquí están abreviados por legibilidad.
El flujo de configuración
El flujo de configuración tiene dos pasos:
- El JavaScript inline de la página llama a
GET /facade/configen el mismo origen que la página. Este endpoint lo registrawippy/facadeen el router público. - Al recibir la respuesta, la página pasa el objeto de configuración completo a la función de inicialización del módulo JS cargado (
window.initWippyApp(config, rootContainer?)).
El Web Host extrae el payload de AppConfig del objeto de configuración y continúa con la inicialización completa. A partir de este punto, el script de la página es pasivo: toda la interacción del usuario ocurre dentro del host montado.
Este patrón significa que el bundle alojado en el CDN nunca contiene URLs, tokens ni branding específicos del despliegue. El bundle es idéntico para todos los despliegues. Solo difiere el payload de configuración.
Campos del shell frente al
AppConfigdel hijo. La respuesta de/facade/configlleva ambos. Campos comofacade_url,iframe_origin,iframe_urlylogin_pathson campos de nivel de shell que consume la página embebedora para construirse a sí misma; no forman parte delAppConfigdel hijo. ElAppConfigcon el que el host se inicializa realmente sonauth,env,theming,hostConfig,contexty los demás campos documentados abajo.
La respuesta de /facade/config
El endpoint de configuración devuelve un objeto JSON que lleva tanto los campos de nivel de shell como el AppConfig del hijo. La página del facade se lo pasa a la función de inicialización del módulo del host; un embebido manual en iframe, en cambio, entrega la porción de AppConfig mediante PostMessage (vea abajo). Todos los campos los ensambla wippy/facade a partir de sus parámetros de módulo y del entorno en ejecución:
{
"$schema": "wippy-context-2.0",
"facade_url": "https://web-host.wippy.ai/<release-tag>",
"iframe_origin": "https://web-host.wippy.ai",
"iframe_url": "https://web-host.wippy.ai/<release-tag>/iframe.html?waitForCustomConfig",
"login_path": "/login.html",
"auth": {
"token": "eyJ...",
"expiresAt": "2026-06-01T12:00:00Z"
},
"env": {
"APP_API_URL": "https://api.example.com",
"APP_AUTH_API_URL": "https://api.example.com",
"APP_WEBSOCKET_URL": "wss://api.example.com"
},
"routePrefix": "https://api.example.com",
"axiosDefaults": {},
"apiRoutes": {},
"tanstack": { "lists": { "refetchOnWindowFocus": true } },
"theming": {
"global": {
"customCSS": "@import url('https://fonts.googleapis.com/...');",
"cssVariables": { "--p-primary": "#6366f1" },
"iconSets": {}
},
"host": {
"customCSS": ".wippy-host-app .chat-container { background: var(--p-content-background); }",
"cssVariables": {},
"iconSets": {},
"i18n": {
"app": {
"title": "My App",
"icon": "wippy:logo",
"appName": "My Application"
}
}
},
"children": {
"customCSS": "",
"cssVariables": {}
}
},
"hostConfig": {
// valores de ejemplo: los valores por defecto se muestran en la tabla de abajo
"session": { "type": "non-persistent" },
"history": "hash",
"showAdmin": true,
"allowSelectModel": false,
"startNavOpen": false,
"hideNavBar": false,
"disableRightPanel": false,
"hideSessionSelector": false,
"additionalNavItems": [],
"stateCache": {},
"allowAdditionalTags": [],
"chat": {}
},
"context": {
"resourceId": "root",
"resourceType": "page",
"route": "/"
}
}
Referencia de campos
Campos de nivel de shell: los consume la página embebedora para construirse a sí misma; no forman parte del AppConfig del hijo:
| Campo | Descripción |
|---|---|
facade_url |
URL base del CDN para el bundle del Web Host. Se usa para resolver el punto de entrada del módulo y los scripts de vendor. |
iframe_origin |
Valor de la cabecera Origin del CDN. Se usa como targetOrigin para PostMessage en embebidos manuales en iframe (vea abajo). |
iframe_url |
src completo del iframe, incluido ?waitForCustomConfig. Lo usan únicamente los embebidos manuales en iframe, sin facade (vea abajo). |
login_path |
Ruta en el origen de la página a la que redirigir a los usuarios no autenticados. |
Campos del AppConfig del hijo: se pasan a la función de inicialización del host y los consume el host en ejecución:
| Campo | Descripción |
|---|---|
$schema |
Versión del contrato de configuración ("wippy-context-2.0"). |
auth |
Bearer token de runtime y su caducidad, inyectados como AppConfig.auth. |
env |
URLs de runtime inyectadas como AppConfig.env de nivel superior. |
routePrefix |
Prefijo de URL de la API reenviado a las aplicaciones hijas. |
axiosDefaults |
Valores por defecto de la instancia de Axios reenviados a las aplicaciones hijas. |
apiRoutes |
Anula rutas individuales de endpoints de la API (campo de nivel superior de AppConfig). |
tanstack |
Valores por defecto de TanStack Query: globales + por categoría basada en rol (content/lists); campo de nivel superior de AppConfig. El valor por defecto del host es refetchOnWindowFocus:false. |
theming |
Personalización de CSS dividida en tres ámbitos. |
hostConfig |
Feature flags y configuración de UI del Web Host. |
context |
Contexto inicial de página o artefacto para el host. |
Campos de env:
| Campo | Origen | Descripción |
|---|---|---|
APP_API_URL |
Variable de entorno PUBLIC_API_URL |
URL base para todas las llamadas HTTP al backend |
APP_AUTH_API_URL |
Igual que APP_API_URL |
URL del endpoint de autenticación (puede diferir en configuraciones a medida) |
APP_WEBSOCKET_URL |
Derivada de APP_API_URL |
http:// → ws://, https:// → wss:// |
Ámbitos de theming:
| Ámbito | Se aplica a |
|---|---|
global |
Tanto el chrome del host como todos los iframes hijos |
host |
Solo el chrome del host. También lleva i18n.app para el título, el icono y el nombre de la aplicación mostrados en la barra lateral. |
children |
Solo los iframes hijos (inyectado por el script del proxy) |
Campos de hostConfig:
| Campo | Tipo | Por defecto | Descripción |
|---|---|---|---|
session.type |
"non-persistent" | "cookie" |
"non-persistent" |
Modo de almacenamiento del token |
history |
"hash" | "browser" |
"hash" |
Modo de historial de Vue Router |
showAdmin |
boolean | true |
Muestra las funciones de administración en la UI |
allowSelectModel |
boolean | false |
Muestra el selector de modelo LLM |
startNavOpen |
boolean | false |
Expande la barra lateral de navegación al cargar |
hideNavBar |
boolean | false |
Oculta por completo la barra lateral de navegación izquierda |
disableRightPanel |
boolean | false |
Deshabilita el panel derecho de artefactos |
hideSessionSelector |
boolean | false |
Oculta el selector de sesiones de chat |
additionalNavItems |
array | [] |
Elementos adicionales inyectados en la barra lateral |
stateCache |
object | {} |
Configuración de caché LRU para el estado de los iframes hijos |
allowAdditionalTags |
object | {} |
Lista blanca de etiquetas del sanitizador HTML (Record<string, string[]>, etiqueta → atributos permitidos) |
chat |
object | {} |
Anulaciones de la UI de chat (comportamiento de pegar como archivo, etc.) |
Flujo de autenticación
Si el usuario no está autenticado cuando carga la página, wippy/facade redirige a login_path antes de servir la página HTML. Tras un inicio de sesión correcto, se devuelve al usuario a la URL original. No se pasa ningún estado de autenticación a través de la propia configuración del Web Host: el Web Host confía en el token de autenticación embebido en auth/env por la respuesta de la página autenticada.
Como el endpoint de configuración lo sirve la misma sesión autenticada que sirvió la página HTML, APP_API_URL y la URL de WebSocket derivada reflejan automáticamente el backend correcto para ese usuario.
La función de inicialización del módulo
El punto de entrada del módulo JS registra window.initWippyApp en la página. La página del facade lo llama con el objeto de configuración obtenido de /facade/config. fe_mode selecciona qué módulo carga el facade — module.js para compat, managed-layout.js para managed — y ambos exponen la misma función de entrada initWippyApp. La elección del módulo determina qué shell se renderiza; es independiente del estilo de embebido (página con módulo JS frente a iframe manual).
initWippyApp(config, rootContainer?) devuelve un emisor de eventos sencillo:
const events = window.initWippyApp(config, '#app')
events.on('ready', () => console.log('Wippy loaded'))
events.on('error', err => console.error('Failed to load:', err))
Cuando se llama sin contenedor raíz, el host se monta en un elemento por defecto. A partir de ese momento, el host se hace cargo de la página y de su historial de navegador.
Embebido manual en iframe (sin facade)
La página con módulo JS anterior es la vía estándar y recomendada, y la que usa el facade actual. Existe además un segundo mecanismo de embebido para los casos en que quiera ejecutar el host completo dentro de un iframe, por ejemplo para ocupar solo una parte de una página con un aislamiento más fuerte respecto de la aplicación circundante. En este modo, usted embebe el host por su cuenta; el facade no produce esta página.
Aun así puede reutilizar el endpoint /facade/config del facade para obtener las URLs y la configuración: su iframe_url (el punto de entrada iframe.html del host, con ?waitForCustomConfig ya añadido) e iframe_origin (el targetOrigin para PostMessage) existen exactamente para esta vía. Después crea usted mismo el iframe y completa el handshake de configuración.
A diferencia de la vía del módulo JS, el host dentro del iframe solicita su configuración: arranca y envía un mensaje get-config al padre, y el padre responde con set-config. Así que el padre escucha la petición en lugar de enviar la configuración a ciegas en el evento load:
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<iframe id="wippy" style="width:100%;height:100vh;border:none"></iframe>
<script>
fetch('/facade/config')
.then(r => r.json())
.then(config => {
const iframe = document.getElementById('wippy')
// Escuchar la petición de configuración @gen2-chat del hijo y responderla.
window.addEventListener('message', (event) => {
if (event.origin !== config.iframe_origin) return
const msg = event.data
if (msg?.type === '@gen2-chat' && msg.action === 'get-config') {
iframe.contentWindow.postMessage(
{ type: '@gen2-chat', action: 'set-config', ...config },
config.iframe_origin
)
}
})
// iframe_url ya incluye ?waitForCustomConfig
iframe.src = config.iframe_url
})
</script>
</body>
</html>
El parámetro de query ?waitForCustomConfig (ya presente en iframe_url) es la señal clave. Indica al Web Host que pause la inicialización: la aplicación se monta, pero deliberadamente no intenta resolver la autenticación ni cargar rutas hasta que recibe un mensaje set-config. Sin él, el Web Host intentaría leer tokens de autenticación de parámetros de URL o de valores por defecto, lo que no es apropiado para despliegues embebidos.
El handshake usa el protocolo PostMessage @gen2-chat:
- El padre solicita
GET /facade/config(o suministra él mismo un payloadAppConfigequivalente) y crea el iframe apuntando aiframe_url. - El iframe en arranque envía
{ type: '@gen2-chat', action: 'get-config' }al padre. - El listener
messagedel padre responde con{ type: '@gen2-chat', action: 'set-config', ...config }, dirigido aiframe_origin.
El Web Host extrae el payload de AppConfig y continúa con la inicialización completa. Para el protocolo de mensajes completo (el sobre @gen2-chat y el enum IFrameMessageType), vea Proxy y Aislamiento. Este handshake SetConfig es específico del embebido manual sin facade; el módulo wippy/facade carga en su lugar el Web Host como módulo JS.
Configurar el módulo del facade
Los parámetros de wippy/facade que producen la respuesta de configuración anterior se establecen en su _index.yaml. Un ejemplo real de app-template:
- name: facade
kind: ns.dependency
component: wippy/facade
version: '>=v0.5.37'
parameters:
- name: server
value: app:gateway
- name: router
value: app:api.public
- name: app_title
value: Wippy App
- name: app_name
value: Wippy App
- name: app_icon
value: "wippy:logo"
- name: show_admin
value: "false"
- name: hide_nav_bar
value: "true"
- name: login_path
value: /app/login.html
- name: session_type
value: non-persistent
- name: history_mode
value: browser
- name: custom_css
value: "@import url('https://fonts.googleapis.com/css2?family=Poppins...');
body { font-family: 'Poppins', sans-serif; }"
- name: css_variables
value: '{"--p-primary":"#6366f1"}'
- name: host_custom_css
value: ".wippy-host-app .chat-container { background: var(--p-content-background); }"
- name: tanstack
value: '{"lists":{"refetchOnWindowFocus":true}}'
Para la lista completa de parámetros disponibles y sus valores por defecto, vea la referencia del módulo Facade.