# Referencia de renderizado y seguridad HTTP

Contratos completos de SSR, adaptadores del servidor, serialización y cabeceras.

<span class="section-anchor" id="section-1"></span>

## 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](/es/docs/framework/server-recipes/) 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.

<span class="section-anchor" id="section-2"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-3"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-4"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-5"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-6"></span>

## 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.

```js
import { escapeHtml } from 'wu-framework/server';
escapeHtml('<etiqueta>'); // '&lt;etiqueta&gt;'
```

No valida URLs ni convierte HTML arbitrario en contenido seguro.

Referencia: `src/server/wu-server-render.js`.

<span class="section-anchor" id="section-7"></span>

## 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.

```js
import { serializeState } from 'wu-framework/server';
serializeState({ title: '</script>' });
```

Referencia: `src/server/wu-server-render.js`.

<span class="section-anchor" id="section-8"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-9"></span>

## 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.

```js
import { createNonce } from 'wu-framework/server';
const nonce = createNonce();
```

Referencia: `src/server/wu-security-headers.js`.

<span class="section-anchor" id="section-10"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-11"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-12"></span>

## 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.

```js
import { applySecurityHeaders } from 'wu-framework/server';
applySecurityHeaders(res, { nonce });
```

Referencia: `src/server/wu-security-headers.js`.

<span class="section-anchor" id="section-13"></span>

## wuSecurityMiddleware

Crea middleware de Express o Connect con nonce por petición.

`wuSecurityMiddleware(options?): middleware`

- **Parámetros:** Opciones de cabeceras; `nonce` debe 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.

```js
import { wuSecurityMiddleware } from 'wu-framework/server';
app.use(wuSecurityMiddleware());
```

Referencia: `src/server/wu-security-headers.js`.

<span class="section-anchor" id="section-14"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-15"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-16"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-17"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-18"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-19"></span>

## 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.

```js
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`.

<span class="section-anchor" id="section-20"></span>

## renderHtml

Adapta el renderer de Html al contrato de renderApp.

`renderHtml(ComponentOrFn, options?): { render(props): Promise<RenderOutput> }`

- **Parámetros:** Función (props) => string|Promise<string> 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.

```js
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`.

<span class="section-anchor" id="section-21"></span>

## 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`.

```js
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.

<span class="section-anchor" id="section-22"></span>

## 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.

```js
// 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 });
```