# Receitas de servidor e SSR

Renderize HTML, React e Vue com WU no Node; verifique escape, estado, CSP, erros e respostas HTTP reais.

<span id="de-un-renderer-a-una-respuesta-http"></span>

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

## De um renderizador a uma resposta HTTP

O servidor do WU recebe um módulo com `render(props)` e retorna o markup de uma microapp. O host HTTP mantém a responsabilidade de carregar dados, escolher módulos confiáveis e construir a resposta. Você não precisa inicializar o runtime do navegador para usar `wu-framework/server`.

O projeto inclui uma receita completa em `examples/ssr/render-page.mjs`. Ela compõe três renderizadores reais — HTML, React e Vue — e gera um documento com Declarative Shadow DOM, estado de hidratação e cabeçalhos CSP.

Você pode [baixar as receitas executáveis](/examples/wu-server-recipes.zip), extrair o ZIP e executar `npm install`. Ele inclui o código, os testes e um manifesto independente para reproduzi-los fora do portal.

```sh
node examples/ssr/render-page.mjs Ada
node examples/ssr/server.mjs
```

O primeiro comando escreve HTML em stdout. O segundo abre `http://127.0.0.1:4310/?name=Ada`. Os comandos são executados na raiz do projeto com suas dependências instaladas. O servidor aceita a variável `PORT` e é fechado com `Ctrl+C`.

<span id="un-renderer-html-pequeño"></span>

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

## Um renderizador HTML pequeno

```js
import { escapeHtml, renderApp } from 'wu-framework/server';

const greeting = {
  render({ name }) {
    return {
      html: `<p>Hola, ${escapeHtml(name)}.</p>`,
      styles: 'p { color: #b7d900; }',
      state: { name },
    };
  },
};

const result = await renderApp('greeting', {
  module: greeting,
  props: { name: 'Ada' },
});
```

O contêiner completo está em `result.html`. Seu formato é:

```html
<div id="wu-app-greeting" data-wu-app="greeting" data-wu-ssr="open">
  <template shadowrootmode="open">
    <style>p { color: #b7d900; }</style>
    <div data-wu-root><p>Hola, Ada.</p></div>
  </template>
</div>
```

`result.inner` contém o interior do contêiner; `result.containerAttrs` permite ao Astro ou a outro motor construir o elemento externo. `state` contém apenas o que foi retornado explicitamente pelo renderizador. Renderizadores HTML personalizados decidem quais dados publicar; os helpers React e Vue retornam as props como estado.

<span id="react-y-vue-sin-compilar-jsx-ni-sfc"></span>

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

## React e Vue sem compilar JSX nem SFC

Esta receita usa `React.createElement` e `h` para que o Node possa executá-la diretamente. Em um projeto de componentes, você pode importar o resultado do build de servidor equivalente.

```js
import * as React from 'react';
import { renderToString as reactToString } from 'react-dom/server';
import { createSSRApp, h } from 'vue';
import { renderToString as vueToString } from '@vue/server-renderer';
import { renderReact, renderVue, renderApps } from 'wu-framework/server';

const reactApp = renderReact(
  ({ name }) => React.createElement('p', null, `Hola, ${name}`),
  { react: React, renderToString: reactToString },
);
const vueApp = renderVue(
  { props: ['name'], setup: props => () => h('p', `Hola, ${props.name}`) },
  { createSSRApp, renderToString: vueToString },
);

const result = await renderApps([
  { name: 'react-greeting', module: reactApp, props: { name: 'Ada' } },
  { name: 'vue-greeting', module: vueApp, props: { name: 'Ada' } },
]);
```

A injeção do renderizador mantém a mesma instância de React ou Vue usada pelo host. Os imports públicos dos helpers são `wu-framework/server` e `wu-framework/server/adapters`; não existem subcaminhos `wu-framework/server/react` ou `wu-framework/server/vue` no mapa de exportações do pacote.

<span id="estado-y-csp-por-respuesta"></span>

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

## Estado e CSP por resposta

```js
import { createNonce, renderApps, renderStateScript, securityHeaders } from 'wu-framework/server';

const nonce = createNonce();
const rendered = await renderApps(apps, { nonce, failFast: true });
const headers = securityHeaders({ nonce });
const body = rendered.html + renderStateScript(rendered.state, { nonce });
```

Use o mesmo nonce nos estilos, no script de estado e no cabeçalho CSP dessa resposta; gere um novo para a próxima. `renderStateScript` publica `window.__WU_SSR_STATE__` por padrão. Sua opção `merge: true` mantém entradas de outras apps quando cada integração emite seu próprio script.

A receita HTTP local usa `securityHeaders({ nonce, https: false, hsts: false })` porque escuta por HTTP em loopback. Para um servidor HTTPS, configure a política de transporte correspondente. O helper não inicia TLS nem administra certificados.

Não publique segredos no estado: o HTML da resposta e as props de hidratação são visíveis ao cliente.

<span id="escapar-no-es-sanitizar-un-documento"></span>

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

## Escapar não é sanitizar um documento

`escapeHtml` converte os caracteres especiais de um valor de texto. `serializeState` mantém JSON válido e neutraliza `<`, `>`, U+2028 e U+2029 para impedir que um dado feche o script de estado. O teste introduz `</script><img ...>` como nome e verifica que nenhuma tag executável aparece.

O HTML retornado por `render()` é inserido como está. Se receber HTML externo, sanitize-o antes de entregá-lo ao renderizador, com uma política adequada ao produto; `renderApp` não remove tags nem atributos perigosos desse HTML. O CSS do renderizador também pertence ao código confiável.

O estado deve ser serializável como JSON. Referências circulares e `BigInt` falham em `serializeState`; funções e propriedades `undefined` não constituem estado de hidratação portátil.

<span id="fallos-parciales-y-failfast"></span>

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

## Falhas parciais e failFast

```js
import { renderApps } from 'wu-framework/server';

const result = await renderApps([
  { name: 'healthy', module: { render: () => '<p>Disponible</p>' } },
  { name: 'optional', module: { render() { throw new Error('Sin datos'); } } },
]);

console.log(result.errors[0].app); // optional
console.log(result.byApp.optional);
// <div id="wu-app-optional" data-wu-app="optional"></div>
```

`renderApps` mantém a ordem das entradas mesmo renderizando em paralelo. Sem `failFast`, retorna o HTML das apps saudáveis, contêineres vazios para as falhas e uma lista `errors`. O host decide se responde 200 com uma degradação visível ou altera o status HTTP.

Com `failFast: true`, a promise rejeita. Isso não cancela automaticamente os outros renderizadores já iniciados; evite trabalho com efeitos externos dentro de `render`.

| Falha | Resultado |
| --- | --- |
| Nome inválido | Rejeição antes de renderizar; máximo de 50 caracteres, início alfanumérico e restante alfanumérico, `_` ou `-` |
| Falta `module` | Erro que solicita o renderizador |
| Módulo sem `render` | Erro de resolução |
| `render` lança erro | Erro com `code: WU_SSR_RENDER_FAILED`, `appName` e `cause` |
| `shadowMode` diferente de `open`/`closed` | Erro de opções |
| Nonce ou `varName` inválidos | Erro antes de emitir o atributo ou script correspondente |
| Resultado de `render` não aceito | Erro de normalização; não tem necessariamente o código de falha do renderizador |

<span id="qué-falta-para-hidratar"></span>

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

## O que falta para hidratar

A receita produz HTML real sem baixar JavaScript das apps. Para torná-lo interativo, registre as mesmas apps no cliente e monte-as nos contêineres SSR. Um ciclo de vida com `hydrate` adota o DOM existente; sem ele, o WU usa `mount`. Os adaptadores React e Vue devem receber a mesma árvore e as mesmas props do servidor.

`shadowMode: 'closed'` serve para markup inerte: a hidratação normal não pode acessar essa raiz. `declarative: false` produz markup plano com `data-wu-ssr="flat"`, útil quando você não precisa de encapsulamento declarativo.

Consulte [SSR e hidratação](/pt/docs/framework/ssr/) e [o ciclo de vida](/pt/docs/framework/lifecycle/) para conectar os dois lados. Um módulo WASM/WASI pode fornecer lógica portátil ao renderizador por um runner do host; sozinho, não substitui o servidor HTTP, a resolução de dados nem a política de resposta. O guia [Um serviço conectado com WU](/pt/docs/guides/browser-backend/) percorre solicitações, contratos e cancelamento entre aplicações no navegador.

<span id="ejecutar-las-comprobaciones"></span>

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

## Executar as verificações

```sh
node scripts/verify-server-examples.mjs
```

A suíte verifica os três renderizadores, a correspondência entre nonce e CSP, estado, escape, opções inválidas, código/contexto de falhas, degradação parcial e saída plana. Os testes usam as exportações do pacote instalado, não cópias das suas funções.