# Navegação e composição

Conecte a URL e as intenções de navegação do shell à montagem, desmontagem e estado das suas aplicações.

<span id="el-shell-es-dueño-de-la-navegación"></span>

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

## O shell é responsável pela navegação

O shell decide qual aplicação corresponde a cada rota e usa montagem, desmontagem, eventos e estado para compô-la. Astro pode resolver páginas; um roteador React/Vue pode gerenciar rotas internas de uma aplicação. O core WU não inclui `wu.router` nem `wu.navigate()`: conecte o mecanismo de navegação usado pelo seu shell ao ciclo de vida.

`metadata.routes` em `wu.json` é informação descritiva. O parser valida strings, duplicados e limites, mas não instala handlers da History API. `wu.routes` não vazio é rejeitado no Framework 4 e não deve ser usado como receita de roteamento.

<span id="dos-niveles-de-rutas"></span>

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

## Dois níveis de rotas

| Nível | Exemplo | Responsável |
| --- | --- | --- |
| Página do portal | `/docs/framework/state` | Roteador de arquivos Astro |
| Seção dinâmica do shell | `/workspace/catalog` | Controlador/roteador do shell |
| Vista interna de uma app | `/products/:id` dentro de catalog | Roteador dessa app |

A URL base de download, como `/apps/catalog`, é um recurso técnico e não precisa corresponder à rota de navegação. Defina separadamente a base do bundle, a base do roteador interno e a rota vista pelo usuário.

<span id="conectar-una-intención-de-navegación"></span>

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

## Conectar uma intenção de navegação

Este exemplo implementa uma convenção do shell; `shell:navigate` não ativa um roteador integrado:

```js
const routeTable = new Map([
  ['/workspace/catalog', 'catalog'],
  ['/workspace/reports', 'reports'],
]);

let activeApp = null;
let transition = Promise.resolve();

function showRoute(path) {
  transition = transition.catch(() => {}).then(async () => {
    const nextApp = routeTable.get(path);
    if (!nextApp || nextApp === activeApp) return;
    if (activeApp) await wu.unmount(activeApp, { force: true });
    activeApp = null;
    await wu.mount(nextApp, '#workspace-slot');
    activeApp = nextApp;
  });
  return transition;
}

const stop = wu.on('shell:navigate', ({ data }) => {
  if (!routeTable.has(data?.path)) return;
  history.pushState(null, '', data.path);
  showRoute(data.path).catch(console.error);
});

const onPopState = () => showRoute(location.pathname).catch(console.error);
window.addEventListener('popstate', onPopState);
await showRoute(location.pathname);

// Al retirar el controlador del shell:
// stop();
// window.removeEventListener('popstate', onPopState);
```

A tabela limita quais rotas e apps são aceitas. A fila mantém a ordem quando o usuário navega várias vezes antes de um carregamento terminar. O core também coordena a propriedade do contêiner; manter um estado claro no shell evita que uma promise antiga atualize controles de navegação de uma nova rota.

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

## Elementos dentro de Shadow DOM

`wu.mount(name, selector)` resolve o seletor com `document.querySelector`. Esse seletor não atravessa ShadowRoots e a assinatura atual espera uma string, não um HTMLElement. Para que o shell localize o slot, coloque-o no light DOM ou use uma composição que exponha um nó acessível por seletor.

Dentro de um ciclo de vida, use o `container` entregue para manipular a raiz local; não a procure globalmente por ID outra vez. O adaptador Vue do repositório ilustra por que uma raiz estável deve ser irmã do indicador de carregamento, e não seu contêiner gerenciado pelo shell.

<span id="keep-alive-y-cambio-de-ruta"></span>

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

## Keep-alive e mudança de rota

`hide` preserva estado e listeners; funciona quando você mantém o contêiner da app. Trocar muitas apps em um único slot exige uma decisão explícita: desmontar o proprietário anterior ou manter contêineres separados para cada app que continuar viva. Não use keep-alive como sinônimo de manter módulos em cache; são recursos diferentes.

Se Astro substituir páginas por navegação cliente, o bootstrap deve acompanhar esse ciclo de vida: libere listeners e mounts da página anterior e conecte os novos slots. Uma importação de script deduplicada não significa que todos os nós futuros serão montados automaticamente.

<span id="roles-de-ui"></span>

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

## Papéis de UI

`wu.setPrincipal({ id: 'u1', roles: ['catalog-reader'] })` alimenta `wu.can('catalog')`. A montagem consulta os papéis exigidos pelo manifesto e pode rejeitar com `WU_ACCESS_DENIED`. Isso ajuda a manter a navegação e as vistas coerentes; as APIs de dados devem autorizar cada solicitação no servidor.

<span id="referencia-de-implementación"></span>

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

## Referência de implementação

Validação de rotas descritivas: `src/core/wu-manifest.js`; acesso: `src/core/wu-core.js` e `src/core/wu-core.js`; seletor: `src/core/wu-core.js`; transição de contêiner: `src/core/wu-core.js`; convenção usada pelo MCP: `src/core/wu-mcp-bridge.js`. Testes: `tests/unit/shared-container-navigation.test.js`, `tests/unit/sandbox-container-ownership.test.js`, `tests/unit/rbac.test.js`.