Referencia de renderizado y seguridad HTTP
Contratos completos de SSR, adaptadores del servidor, serialización y cabeceras.
Servidor y navegador tienen responsabilidades distintas
Importa estas funciones desde wu-framework/server, wu-framework/server/adapters o wu-framework/server/security. El renderizado recibe módulos de servidor explícitos; no monta componentes del navegador ni descarga automáticamente el entry de un manifest.
Cada petición debe conservar su propio estado y nonce. El HTML del renderer es contenido de confianza: utiliza escapeHtml para datos externos y evita enviar secretos en renderStateScript. La receta de servidor muestra cómo componer la respuesta y su hidratación.
Los nombres RenderResult y RenderOutput de esta página describen la forma de los resultados; consulta las declaraciones del subpath server para importar tipos efectivamente exportados.
renderApp
Renderiza una aplicación con un módulo de servidor y produce el contenedor que reconoce la hidratación de WU.
renderApp(appName, options): Promise<RenderResult>
- Parámetros: Nombre no vacío; options.module obligatorio, props, styles, containerId, className, shadowMode y declarative; nonce opcional.
- Retorno: Objeto con html, inner, containerAttrs, state, appName y containerId.
- Errores: Propaga errores de entrada o del renderer; maneja el rechazo antes de emitir la respuesta HTTP.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { renderApp, renderHtml, escapeHtml } from 'wu-framework/server';
const card = renderHtml(({ title }) => `<h2>${escapeHtml(title)}</h2>`);
const result = await renderApp('card', { module: card, props: { title: 'Resumen' } });
console.log(result.html);
El módulo debe producir HTML confiable. El helper escapa atributos y estado, pero no sanea el HTML que devuelve render. No importa automáticamente una URL de navegador.
Referencia: src/server/wu-server-render.js.
renderApps
Renderiza una lista en paralelo y reúne HTML, estado y errores por aplicación.
renderApps(apps, options?): Promise<RenderAppsResult>
- Parámetros: Lista de {name, module, …opciones}; failFast false por defecto; nonce compartido opcional.
- Retorno: {html, byApp, state, errors}; con failFast false conserva resultados parciales.
- Errores: Por defecto registra errores por entrada; failFast:true rechaza la operación. No cancela renderers que ya estén ejecutándose.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { renderApps, renderHtml } from 'wu-framework/server';
const result = await renderApps([
{ name: 'summary', module: renderHtml(() => '<p>Resumen</p>') },
{ name: 'activity', module: renderHtml(() => '<p>Actividad</p>') },
]);
console.log(result.errors);
Referencia: src/server/wu-server-render.js.
renderStateScript
Serializa estado en un script que el cliente puede recuperar durante hidratación.
renderStateScript(state, options?): string
- Parámetros: state serializable; varName identificador JavaScript, merge boolean, nonce opcional.
- Retorno: Etiqueta script completa.
- Errores: Rechaza varName inválido, nonce inválido o valores que JSON.stringify no pueda serializar.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { renderStateScript } from 'wu-framework/server';
const script = renderStateScript({ card: { title: 'Resumen' } }, { merge: true });
No introduzcas secretos: este estado es visible en el HTML de la respuesta.
Referencia: src/server/wu-server-render.js.
declarativeShadowDomPolyfill
Genera un script de compatibilidad para templates con shadowrootmode cuando no hay soporte nativo.
declarativeShadowDomPolyfill(options?): string
- Parámetros: options.nonce opcional.
- Retorno: HTML de un script; no lo ejecuta al llamar al helper.
- Errores: Un nonce inválido falla en la construcción del atributo.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { declarativeShadowDomPolyfill } from 'wu-framework/server';
const script = declarativeShadowDomPolyfill({ nonce });
Emite el script después del contenido que deba convertir; no es un observador de templates futuros.
Referencia: src/server/wu-server-render.js.
escapeHtml
Escapa ampersand, signos de etiqueta y comillas para texto o atributos HTML.
escapeHtml(value): string
- Parámetros: Cualquier valor; null y undefined se convierten en cadena vacía.
- Retorno: Cadena escapada.
- Errores: La coerción a string puede propagar un error del propio valor.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { escapeHtml } from 'wu-framework/server';
escapeHtml('<etiqueta>'); // '<etiqueta>'
No valida URLs ni convierte HTML arbitrario en contenido seguro.
Referencia: src/server/wu-server-render.js.
serializeState
Codifica JSON e impide cerrar el script mediante caracteres de etiqueta.
serializeState(value): string
- Parámetros: Valor compatible con JSON; null/undefined raíz se representan como null.
- Retorno: JSON con <, > y separadores Unicode escapados.
- Errores: Referencias cíclicas, BigInt o serializaciones no admitidas pueden lanzar.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { serializeState } from 'wu-framework/server';
serializeState({ title: '</script>' });
Referencia: src/server/wu-server-render.js.
SSR_MARKER_ATTR / SSR_ROOT_ATTR
Expone los nombres de atributos que coordinan renderer e hidratación.
SSR_MARKER_ATTR: 'data-wu-ssr'; SSR_ROOT_ATTR: 'data-wu-root'
- Parámetros: Constantes exportadas desde wu-framework/server.
- Retorno: Dos strings.
- Errores: La lectura de las constantes no valida el contenido del contenedor.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { SSR_MARKER_ATTR, SSR_ROOT_ATTR } from 'wu-framework/server';
console.log(SSR_MARKER_ATTR, SSR_ROOT_ATTR);
Referencia: src/server/wu-server-render.js.
createNonce
Genera un nonce de 128 bits con crypto.getRandomValues.
createNonce(): string
- Parámetros: Sin parámetros.
- Retorno: String base64url sin padding.
- Errores: Necesita Web Crypto; lanza si no está disponible.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { createNonce } from 'wu-framework/server';
const nonce = createNonce();
Referencia: src/server/wu-security-headers.js.
buildCsp
Compone una política CSP para scripts, estilos, imágenes, conexiones y frames.
buildCsp(options?): string
- Parámetros: nonce; listas scriptSrc/styleSrc/connectSrc/imgSrc/frameSrc; unsafeInlineStyle; https true por defecto.
- Retorno: Valor completo de Content-Security-Policy.
- Errores: Rechaza un nonce inválido; las listas de orígenes deben provenir de configuración confiable.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { buildCsp } from 'wu-framework/server';
const policy = buildCsp({ nonce, connectSrc: ['https://api.example.com'] });
Sin nonce y sin styleSrc, la política incluye unsafe-inline para estilos; connect-src también incorpora scriptSrc. https:false omite upgrade-insecure-requests.
Referencia: src/server/wu-security-headers.js.
securityHeaders
Construye cabeceras CSP, HSTS, X-Frame-Options, MIME, referrer, permissions y CORS opcional.
securityHeaders(options?): Record<string, string>
- Parámetros: Opciones CSP más hsts, frameOptions, referrerPolicy, permissionsPolicy, nosniff, corsOrigin y csp.
- Retorno: Mapa de cabeceras; no modifica una respuesta.
- Errores: Propaga validación de CSP cuando se genera la política.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { securityHeaders } from 'wu-framework/server';
const headers = securityHeaders({ nonce, hsts: false, corsOrigin: 'https://shell.example.com' });
HSTS está activo por defecto; desactívalo explícitamente para una respuesta HTTP de desarrollo. csp:false omite CSP.
Referencia: src/server/wu-security-headers.js.
applySecurityHeaders
Aplica las cabeceras a una respuesta Node compatible con setHeader.
applySecurityHeaders(res, options?): response
- Parámetros: res con setHeader; getHeader permite combinar Vary existente.
- Retorno: La misma respuesta.
- Errores: Lanza si res no tiene setHeader o la política es inválida.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { applySecurityHeaders } from 'wu-framework/server';
applySecurityHeaders(res, { nonce });
Referencia: src/server/wu-security-headers.js.
wuSecurityMiddleware
Crea middleware de Express o Connect con nonce por petición.
wuSecurityMiddleware(options?): middleware
- Parámetros: Opciones de cabeceras;
noncedebe ser una función por petición. Omitirla genera un nonce nuevo automáticamente; un string fijo se rechaza al construir el wrapper. - Retorno: Función (req,res,next) que escribe req.wuNonce y res.locals.nonce si existe locals.
- Errores: Errores de generación del nonce o cabeceras se propagan al ejecutar middleware.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { wuSecurityMiddleware } from 'wu-framework/server';
app.use(wuSecurityMiddleware());
Referencia: src/server/wu-security-headers.js.
withSecurityHeaders
Envuelve un handler Fetch y añade cabeceras conservando cuerpo y estado HTTP.
withSecurityHeaders(handler, options?): (request) => Promise<Response>
- Parámetros: handler(request,{nonce}) debe devolver Response.
- Retorno: Handler asíncrono con una Response nueva.
- Errores: Propaga el error del handler y exige un resultado compatible con Response.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { withSecurityHeaders } from 'wu-framework/server';
const GET = withSecurityHeaders(async (_request, { nonce }) => {
return new Response(`<p>Listo</p>`, { headers: { 'Content-Type': 'text/html' } });
});
Referencia: src/server/wu-security-headers.js.
renderReact
Adapta el renderer de React al contrato de renderApp.
renderReact(ComponentOrFn, options?): { render(props): Promise<RenderOutput> }
- Parámetros: Component React; styles, strictMode, react y renderToString inyectables.
- Retorno: Módulo de servidor con render que devuelve html, styles y state.
- Errores: Propaga errores de entrada o del renderer; maneja el rechazo antes de emitir la respuesta HTTP.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { renderReact, renderApp } from 'wu-framework/server';
import React from 'react';
import { renderToString } from 'react-dom/server';
const module = renderReact(Card, { react: React, renderToString });
const result = await renderApp('card', { module, props: { count: 2 } });
Inyecta la misma instancia de React que utiliza el componente para evitar duplicar su runtime.
Referencia: src/server/adapters.js.
renderVue
Adapta el renderer de Vue al contrato de renderApp.
renderVue(ComponentOrFn, options?): { render(props): Promise<RenderOutput> }
- Parámetros: Component Vue; styles, setup(app,props), createSSRApp y renderToString inyectables.
- Retorno: Módulo de servidor con render que devuelve html, styles y state.
- Errores: Propaga errores de entrada o del renderer; maneja el rechazo antes de emitir la respuesta HTTP.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { renderVue, renderApp } from 'wu-framework/server';
const module = renderVue(Card, { setup(app) { /* app.use(plugin) */ } });
const result = await renderApp('card', { module, props: { count: 2 } });
Necesita vue y @vue/server-renderer o ambas funciones inyectadas.
Referencia: src/server/adapters.js.
renderPreact
Adapta el renderer de Preact al contrato de renderApp.
renderPreact(ComponentOrFn, options?): { render(props): Promise<RenderOutput> }
- Parámetros: Component Preact; styles, h y renderToString inyectables.
- Retorno: Módulo de servidor con render que devuelve html, styles y state.
- Errores: Propaga errores de entrada o del renderer; maneja el rechazo antes de emitir la respuesta HTTP.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { renderPreact, renderApp } from 'wu-framework/server';
const module = renderPreact(Card, { styles: ['h2{color:navy}'] });
const result = await renderApp('card', { module, props: { count: 2 } });
Necesita preact y preact-render-to-string si no se inyectan funciones.
Referencia: src/server/adapters.js.
renderSvelte
Adapta el renderer de Svelte al contrato de renderApp.
renderSvelte(ComponentOrFn, options?): { render(props): Promise<RenderOutput> }
- Parámetros: Componente compilado para servidor y styles opcionales.
- Retorno: Módulo de servidor con render que devuelve html, styles y state.
- Errores: Propaga errores de entrada o del renderer; maneja el rechazo antes de emitir la respuesta HTTP.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { renderSvelte, renderApp } from 'wu-framework/server';
const module = renderSvelte(Card);
const result = await renderApp('card', { module, props: { count: 2 } });
Svelte 4 usa Component.render; Svelte 5 importa svelte/server. El wrapper no agrega out.head al documento.
Referencia: src/server/adapters.js.
renderSolid
Adapta el renderer de Solid al contrato de renderApp.
renderSolid(ComponentOrFn, options?): { render(props): Promise<RenderOutput> }
- Parámetros: Componente compilado para SSR y styles opcionales.
- Retorno: Módulo de servidor con render que devuelve html, styles y state.
- Errores: Propaga errores de entrada o del renderer; maneja el rechazo antes de emitir la respuesta HTTP.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { renderSolid, renderApp } from 'wu-framework/server';
const module = renderSolid(Card);
const result = await renderApp('card', { module, props: { count: 2 } });
Resuelve solid-js/web en el entorno servidor; no acepta un renderer inyectado como React.
Referencia: src/server/adapters.js.
renderHtml
Adapta el renderer de Html al contrato de renderApp.
renderHtml(ComponentOrFn, options?): { render(props): Promise<RenderOutput> }
- Parámetros: Función (props) => string|Promise
y styles string/lista/función. - Retorno: Módulo de servidor con render que devuelve html, styles y state.
- Errores: Propaga errores de entrada o del renderer; maneja el rechazo antes de emitir la respuesta HTTP.
- Permisos: No utiliza grants del navegador; la aplicación servidor controla módulos, datos y cabeceras.
- Limpieza: No crea listeners ni un runtime cliente persistente.
import { renderHtml, renderApp } from 'wu-framework/server';
const module = renderHtml(({ count }) => `<p>${Number(count)}</p>`);
const result = await renderApp('card', { module, props: { count: 2 } });
El HTML retornado no se sanea. Escapa contenido externo antes de interpolarlo.
Referencia: src/server/adapters.js.
createServerRequest
createServerRequest(options?): WuServerRequest
Crea un contexto privado por petición con signal, locals, renderApp, renderApps, stream y dispose. Acepta las opciones de lote, incluido request; combina su señal con options.signal. No crees este objeto en el scope global del módulo. dispose(reason) aborta, libera listeners y vacía locals.
import { createServerRequest, renderStateScript, escapeHtml, withSecurityHeaders } from 'wu-framework/server';
export const GET = withSecurityHeaders(async (request, { nonce }) => {
const scope = createServerRequest({ request, nonce, timeout: 2000, concurrency: 2 });
try {
scope.locals.set('requestId', crypto.randomUUID());
const rendered = await scope.renderApps([{
name: 'greeting',
load: async ({ signal }) => {
signal.throwIfAborted();
return { props: { label: 'Hello' }, publicState: { label: 'Hello' } };
},
module: { render: props => '<p>' + escapeHtml(props.label) + '</p>' },
}]);
return new Response(rendered.html + renderStateScript(rendered.state, { nonce }), {
headers: { 'Content-Type': 'text/html; charset=utf-8' },
});
} finally {
scope.dispose();
}
});
load(context) recibe { appName, signal, request, locals } y devuelve { props?, publicState? }. Si hay loader, solo publicState sale como estado de hidratación; ni las props privadas ni el state devuelto por el renderer se publican automáticamente. Sin loader se conserva el state del renderer. Esto no impide que el propio renderer escriba un secreto en el HTML.
Cancelación, plazos y concurrencia SSR
renderApp, renderApps y renderAppsStream aceptan signal y timeout por app, incluido el loader; timeout: 0 desactiva el plazo. Los lotes y streams aceptan concurrency, ilimitada por defecto. El aborto conserva signal.reason; sin razón propia usa WU_SSR_ABORTED. Vencer el plazo produce WU_SSR_TIMEOUT. Los renderers y loaders deben cooperar con context.signal para detener su trabajo.
Sin failFast, un fallo por app permite degradación parcial; con failFast: true se aborta el lote. Cancelar el stream propaga el aborto al trabajo pendiente. Al devolver un stream HTTP, conserva el contexto hasta que termine o se cancele su consumo; no llames dispose() inmediatamente después de construir la respuesta.
// Reusable wrappers: a function generates a new nonce for each request.
wuSecurityMiddleware({ nonce: () => createNonce() });
withSecurityHeaders(handler, { nonce: () => createNonce() });
// Low-level helpers still accept the nonce of this response.
const nonce = createNonce();
const headers = securityHeaders({ nonce });