Views
El módulo wippy/views proporciona un sistema de páginas y componentes virtuales con renderizado de plantillas, gestión de recursos y mapeo de variables de entorno. Las páginas vienen en dos variantes distintas:
- Páginas de plantilla Jet (
kind: template.jet) — HTML renderizado en el servidor. Los datos y recursos de la página se ensamblan e inyectan en el servidor, y luego el motor Jet renderiza el HTML final. Este es el modelo heredado de renderizado en servidor. Vea Páginas de Plantilla. - Frontends de entrada de registro (
kind: registry.entry) — dos tipos: aplicaciones micro frontend (view.page, SPAs completas) y componentes web reutilizables (view.component), servidos desde un CDN o un montaje estático. La entrada del registro contiene solo la política de enrutamiento y despliegue; la inyección de proxy/CSS se declara en elpackage.jsondel paquete frontend. Vea Páginas de Componente y Componentes de Vista.
Configuración
Agregue el módulo a su proyecto:
wippy add wippy/views
wippy install
Declare la dependencia:
version: "1.0"
namespace: app
entries:
- name: dep.views
kind: ns.dependency
component: wippy/views
version: "*"
parameters:
- name: api_router
value: app:api.public
- name: env_storage
value: app:env.storage
| Parámetro | Requerido | Predeterminado | Descripción |
|---|---|---|---|
api_router |
sí | — | Router HTTP para los endpoints de API de las views |
env_storage |
sí | — | Almacenamiento de entorno que respalda la variable PUBLIC_API_URL |
server |
no | app:gateway |
Servicio HTTP al que se enlaza el router auto-montado del gateway de Web Fragments (/@fragment). Anúlelo solo si el id de su http.service difiere de app:gateway. |
Páginas de Plantilla
Modelo renderizado en servidor. Las páginas de plantilla son el mecanismo heredado de renderizado en el servidor:
wippy/viewsensambla los datos y recursos de la página en el servidor y renderiza el HTML final con el motor de plantillas Jet. No hay proxy de iframe ni micro-frontend en el cliente — la respuesta es HTML plano. Para SPAs y componentes externos, vea Páginas de Componente.
Las páginas de plantilla se renderizan en el lado del servidor usando plantillas Jet. Los datos se inyectan mediante data.set, data.data_func y data.resources (inyección de recursos en el servidor):
entries:
- name: contact_page
kind: template.jet
meta:
type: view.page
name: contact
title: Contact Us
icon: mail
order: 5
group: main
group_icon: layout-grid
group_order: 1
announced: true
secure: false
data:
set: app.templates:default
data_func: app:contact_data
resources:
- contact_styles
Metadatos de Página
| Campo | Tipo | Predeterminado | Descripción |
|---|---|---|---|
meta.type |
string | — | Debe ser view.page |
meta.name |
string | nombre de la entrada | Identificador de la página |
meta.title |
string | — | Título de visualización |
meta.icon |
string | — | Identificador del icono |
meta.order |
number | 9999 |
Orden dentro del grupo |
meta.group |
string | — | Categoría del grupo |
meta.group_icon |
string | — | Icono del grupo |
meta.group_order |
number | 9999 |
Orden del grupo |
meta.group_placement |
string | "default" |
Ubicación: "default", "sidebar" |
meta.secure |
boolean | false |
Requiere autenticación |
meta.public |
boolean | false |
Hace que la página se anuncie cuando es true; no evita el control meta.secure |
meta.announced |
boolean | false |
Mostrar en navegación. El resolver usa announced or public, por lo que public: true vence a announced: false |
meta.inline |
boolean | false |
Se devuelve en /pages/list como marcador numérico hidden |
meta.content_type |
string | text/html |
Tipo MIME de la respuesta |
meta.parent |
string | — | ID de la página padre |
Datos de Plantilla
| Campo | Descripción |
|---|---|
data.set |
ID obligatorio del registro del conjunto de templates |
data.data_func |
ID de la función que retorna los datos de la página |
data.resources |
Array de IDs del registro de recursos |
La función data_func configurada mediante data.data_func recibe { params, query } y retorna una tabla que se convierte en el contexto data del template. Omitirla o devolver nil produce una tabla vacía. Una función configurada que no se pueda resolver o devuelva un error interrumpe el renderizado.
Pipeline de Renderizado
- Cargar la página desde el registro
- Verificar acceso (seguridad)
- Llamar a
data_funcsi está definido - Recolectar recursos: globales + recursos del conjunto de plantillas + recursos específicos de la página
- Cargar variables de entorno; los fallos de mapping se registran y producen una tabla
envvacía - Renderizar plantilla Jet con contexto:
{ data, resources, query_params, route_params, env }
Páginas de Componente
Las páginas de componente apuntan a aplicaciones de página única externas (SPAs, micro-frontends) cargadas por el Web Host dentro de un iframe. La entrada del registro contiene solo campos de enrutamiento del registro y de política de despliegue — servido de URL, control de acceso, ruta de montaje y anulaciones de configuración por página:
Forma requerida del registro: las páginas de componente son
kind: registry.entryconmeta.type: view.page.view.pagenunca es un valor dekind. Las anulaciones de despliegue del proxy residen enmeta.proxy, no endata.proxy.
entries:
- name: dashboard
kind: registry.entry
meta:
type: view.page
name: dashboard
title: Dashboard
icon: chart-bar
url: /app
base_path: app/dashboard
entry_point: index.html
mountRoute: /dashboard/:part(.*)*
secure: true
announced: true
config_overrides:
customization:
cssVariables:
"--p-primary": "#7c9ed9"
La API retorna un descriptor de componente con la URL base resuelta. El Web Host renderiza la SPA en un iframe y aplica las inyecciones de proxy que solicitó el paquete frontend.
Campos de Componente
| Campo | Tipo | Predeterminado | Descripción |
|---|---|---|---|
meta.url |
string | — | Prefijo de URL base donde se monta el bundle (origen CDN o ruta http.static) |
meta.base_path |
string | — | Subdirectorio dentro del montaje estático |
meta.entry_point |
string | index.html |
Archivo HTML de entrada; se combina como <url>/<base_path>/<entry_point> |
meta.mountRoute |
string | — | Reclama una ruta URL en el router del host; solo se permite la forma catch-all /:part(.*)* (raíz) o /<literal-prefix>/:part(.*)* — los patrones arbitrarios de Vue Router se rechazan (HTTP 500). Vea view-page.md / dynamic-routing.md |
meta.announced |
boolean | — | Mostrar en navegación y en pages/list |
meta.secure |
boolean | false |
Requiere autenticación |
meta.config_overrides |
object | — | Anulaciones de AppConfig por página (camelCase), fusionadas en profundidad sobre los valores predeterminados del bundle |
Al construir el descriptor, wippy/views solicita wippy-meta.json desde la raíz del bundle. El YAML vence campo por campo; los metadatos del bundle completan campos propios del frontend omitidos. Si no puede usar el archivo, recurre al descriptor YAML heredado. Mantenga meta.name y meta.title en YAML: /pages/list consume los campos raw del registro sin obtener los metadatos del bundle. config_overrides admite customization, axiosDefaults, routePrefix, apiRoutes y themeMode.
La inyección de proxy para páginas SPA se configura en el bloque wippy.proxy.injections del package.json del FE (camelCase) y se integra en wippy-meta.json en tiempo de compilación. También puede anularse por despliegue mediante un bloque proxy: en camelCase anidado bajo meta: en la entrada del registro (con la misma forma y el mismo envoltorio injections que el bloque wippy.proxy del package.json); el host lo fusiona en profundidad sobre el wippy.proxy del bundle, y el valor del YAML gana por cada clave anidada. No existe una forma en snake_case ni normalización de mayúsculas. Note que config_overrides solo fusiona en profundidad customization, axiosDefaults, routePrefix y apiRoutes — nunca afecta a proxy.injections. Vea Aplicaciones Micro Frontend (view.page) e Inyección de CSS.
Forma mínima correcta de anulación de despliegue:
entries:
- name: dashboard
kind: registry.entry
meta:
type: view.page
proxy:
enabled: true
injections:
css:
themeConfig: true
customCss: true
customVariables: true
tailwindConfig: false
Componentes de Vista
Los componentes de vista son elementos personalizados reutilizables (componentes web, micro-frontends) que el Web Host descubre y registra — no son páginas y no tienen entrada de navegación. Al igual que las páginas de componente, la entrada del registro solo lleva la política de enrutamiento y despliegue:
entries:
- name: reaction-bar
kind: registry.entry
meta:
type: view.component
name: reaction-bar
tag_name: example-reaction-bar
announced: true
auto_register: true
secure: false
url: /app/wc/reaction-bar
entry_point: index.js
Los componentes usan meta.type: view.component en lugar de view.page, se identifican por meta.tag_name y usan index.js como punto de entrada predeterminado. La inyección de proxy y el CSS de tema para los componentes se declaran igualmente en el package.json del FE (camelCase) y, para el CSS del shadow DOM, mediante hostCssKeys — no en el YAML del registro. Vea Componentes Web (view.component) e Inyección de CSS.
Recursos
Los recursos son archivos CSS, JS y de fuentes asociados con las páginas:
entries:
- name: global_styles
kind: registry.entry
meta:
type: view.resource
name: Global Styles
resource_type: style
global: true
order: 1
url: https://cdn.example.com/global.css
- name: app_script
kind: registry.entry
meta:
type: view.resource
name: App Script
resource_type: script
template_set: app.templates:default
order: 10
url: https://cdn.example.com/app.js
defer: true
Campos de Recurso
| Campo | Tipo | Descripción |
|---|---|---|
meta.type |
string | Debe ser view.resource |
meta.resource_type |
string | De elección libre (por defecto "other"); valores comunes son "style", "script", "font" |
meta.order |
number | Orden dentro del tipo |
meta.global |
boolean | Aplicado a todas las páginas |
meta.template_set |
string | Específico a un conjunto de plantillas |
meta.url |
string | URL del recurso |
meta.integrity |
string | Hash SRI |
meta.crossorigin |
string | "anonymous" o "use-credentials" |
meta.media |
string | Media query CSS |
meta.defer |
boolean | Carga diferida del script |
meta.async |
boolean | Carga asíncrona del script |
Recolección de Recursos
Los recursos se seleccionan acumulativamente de tres fuentes:
- Recursos globales —
global: true, aplicados a todas las páginas - Recursos del conjunto de plantillas — coincidentes por ID de
template_set - Recursos de página — listados en el array
data.resources
Después de reunirlos, se agrupan por resource_type y cada grupo se ordena por order. Las tres fuentes no establecen un orden de salida independiente.
Mapeo de Variables de Entorno
El cargador de entorno mapea variables de entorno a claves del contexto de plantilla mediante un sistema basado en prioridad.
Definición de Mapeos
entries:
- name: app_env
kind: registry.entry
meta:
type: view.env_mapping
priority: 20
data:
mappings:
api_endpoint: API_BASE_URL
app_title: APP_NAME
debug_mode: DEBUG_ENABLED
Cada entrada de mapeo asocia claves de contexto (usadas en plantillas como env.api_endpoint) con nombres de variables de entorno.
Sistema de Prioridad
| Rango | Categoría | Descripción |
|---|---|---|
| 0–9 | Predeterminados del framework | Mapeos integrados del framework |
| 10–19 | Anulaciones del sistema | Configuración a nivel de sistema |
| 20–29 | Mapeos de aplicación | Mapeos específicos de la aplicación |
| 30–100 | Anulaciones de entorno | Anulaciones en tiempo de ejecución |
La prioridad mayor gana. No defina la misma clave más de una vez con la misma prioridad: el orden entre mappings de igual prioridad no está definido.
Uso en Plantillas
Los valores de entorno resueltos están disponibles en el objeto de contexto env:
<script>
window.API_URL = "{{ env.api_endpoint }}";
document.title = "{{ env.app_title }}";
</script>
Endpoints de la API HTTP
El módulo views registra estos endpoints en el router configurado:
| Método | Ruta | Descripción |
|---|---|---|
| GET | /pages/list |
Listar páginas accesibles y anunciadas |
| GET | /components/list |
Listar componentes de vista accesibles y anunciados |
| GET | /pages/content/{id} |
Renderizar página o retornar descriptor de componente |
| GET | /pages/public/{id} |
Obtener URL base del componente |
| GET | /components/by-tag/{tag} |
Resolver un nombre de etiqueta de elemento personalizado a su descriptor view.component (usado por loadByTagName del host) |
| GET | /pages/routes |
Retorna el mapa mountRoute → pageId; HTTP 500 ante un mountRoute inválido o duplicado. No se filtra por announced (las páginas ocultas siguen necesitando resolución de URL); el control de acceso se aplica a las páginas seguras |
Respuesta de Renderizado
Para páginas de plantilla, retorna HTML renderizado con el content_type de la página.
Para páginas de componente, retorna un descriptor:
{
"name": "dashboard",
"version": "1.0.0",
"specification": "wippy-component-1.0",
"title": "Dashboard",
"baseUrl": "https://cdn.example.com/dashboard/",
"wippy": {
"type": "page",
"path": "index.html",
"proxy": {
"enabled": true,
"injections": {
"css": { "themeConfig": true, "iframe": true },
"tailwindConfig": false,
"resizeObserver": true,
"preventLinkClicks": true
}
}
}
}
Las banderas de inyección de css son themeConfig, iframe, primevue, markdown, customCss y customVariables. No existe una bandera fonts — las Google Fonts se entregan mediante theming.global.customCSS (una regla @import), inyectada por customCss.
Web Fragments Gateway
Cuando el Web Host renderiza una página con el motor de renderizado de fragmentos, la página se monta como <web-fragment src="/@fragment/{id}/">. wippy/views sirve ese contrato de reframing a través de un endpoint de gateway dedicado en /@fragment/{id}/{path...}.
A diferencia de la API de views (que se monta en el api_router del consumidor), el gateway es auto-provisto por wippy/views (≥ 0.5.9): el módulo declara internamente su propio http.router de nivel superior /@fragment, de modo que es enrutable por caché de CDN y está libre de token_auth — el gateway es agnóstico a la autenticación (el proxy de fragmento inyectado negocia la autenticación con el host en el cliente). Un consumidor no necesita ningún cableado de fragmentos — ni entrada de router ni parámetro fragment_router. La aplicación arranca normalmente con el motor de iframe, estén o no habilitados los fragmentos.
El router auto-montado se enlaza a un requisito server que por defecto es app:gateway. La única anulación opcional: si la entrada http.service de su aplicación tiene un id distinto de app:gateway, establezca el parámetro server de wippy/views para que coincida:
entries:
- name: dep.views
kind: ns.dependency
component: wippy/views
version: "*"
parameters:
- name: api_router
value: app:api.public
- name: env_storage
value: app:env.storage
- name: server # opcional — solo si el id de su http.service ≠ app:gateway
value: app:my_http_service
Sin cableado de fragmentos, sin riesgo de arranque. Como
wippy/viewses dueño del router/@fragmenty lo enlaza aserver(por defectoapp:gateway), un consumidor que actualice el módulo arranca normalmente con el motor de iframe sin ninguna configuración de fragmentos. Una página que opta por fragmentos página por página (wippy.renderEngine: "fragment") en un despliegue por lo demás basado en iframe está protegida por un sondeo de capacidad en tiempo de ejecución que la mantiene silenciosamente en el motor de iframe cuando el gateway oproxy-fragment.jsno están disponibles. El interruptor globalrender_engine: fragmentconfía en el operador y no sondea.
Contrato de reframing
El gateway responde a la misma URL /@fragment/{id}/ de tres maneras, discriminadas por la cabecera Sec-Fetch-Dest de la petición y por el subpath:
| Petición | Respuesta |
|---|---|
Carga del iframe del realm (Sec-Fetch-Dest: iframe) |
Un pequeño stub reframed que lleva el import map del host + loading.js + proxy-fragment.js. |
| Fetch de documento (subpath vacío) | El HTML de la aplicación de la página, transformado para el realm (<base>, enlaces de CSS del host, renombrado de <html>/<head>/<body> → <wf-*>). |
| Asset (subpath no vacío) | Redirigido por proxy al base_url real de la página + subpath. |
Las respuestas llevan Cache-Control: el stub es cacheable de forma compartida (public, max-age=300); el documento y los assets con acceso restringido son private (pasan una comprobación can_access por usuario, así que una caché compartida filtraría datos entre usuarios). Los errores en tiempo de ejecución son respuestas HTTP explícitas — 400 Missing fragment id, 404 Fragment page not found, 401 Access denied, 502 Fragment document fetch failed: … (url: …).
El FE selecciona el motor y monta el fragmento — vea Motores de Renderizado.
Control de Acceso
Las páginas con secure: true requieren autenticación. El registro de páginas verifica security.can("view", "page:<page_id>") contra el actor y el ámbito actuales.
Las páginas no seguras siempre son accesibles. La bandera announced controla la visibilidad en los listados de navegación sin afectar el acceso.
Calificación de IDs
Los IDs relativos en las definiciones de página se califican con el namespace de la entrada:
# In namespace "app"
data:
data_func: my_data_func # resolves to app:my_data_func
set: templates:default # stays as templates:default (already qualified)
resources:
- page_styles # resolves to app:page_styles
Véase También
- Facade - Facade de iframe del frontend y barra lateral de navegación
- Template - Motor de plantillas Jet
- Security - Actores de seguridad y control de acceso
- Environment - Almacenamiento de variables de entorno
- Resumen del Framework - Uso del módulo del framework
- Aplicaciones Micro Frontend (view.page) - Referencia completa de metadatos e inyección de proxy de view.page
- Componentes Web (view.component) - Referencia completa de autocarga y props de view.component
- Motores de Renderizado - Renderizado de páginas por iframe vs Web Fragment (el consumidor del gateway
/@fragment)