Navegación y composición
Conecta la URL y las intenciones de navegación del shell con el montaje, desmontaje y estado de tus aplicaciones.
El shell es dueño de la navegación
El shell decide qué aplicación corresponde a cada ruta y utiliza montaje, desmontaje, eventos y estado para componerla. Astro puede resolver páginas; un router React/Vue puede gestionar rutas internas de una aplicación. El core WU no incluye wu.router ni wu.navigate(): conecta el mecanismo de navegación que utilice tu shell con su lifecycle.
metadata.routes en wu.json es información descriptiva. El parser valida strings, duplicados y límites, pero no instala handlers de History API. wu.routes no vacío se rechaza en Framework 4 y no debe usarse como receta de routing.
Dos niveles de rutas
| Nivel | Ejemplo | Responsable |
|---|---|---|
| Página del portal | /docs/framework/state |
Router de archivos Astro |
| Sección dinámica del shell | /workspace/catalog |
Controlador/router del shell |
| Vista interna de una app | /products/:id dentro de catalog |
Router de esa app |
La URL base de descarga, como /apps/catalog, es un recurso técnico y no tiene por qué coincidir con la ruta de navegación. Define por separado el base del bundle, el base del router interno y la ruta que el usuario ve.
Conectar una intención de navegación
Este ejemplo implementa una convención del shell; shell:navigate no activa un router integrado:
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);
La tabla limita qué rutas y apps acepta. La cola conserva orden cuando el usuario navega varias veces antes de terminar una carga. El core también coordina ownership del contenedor; mantener un estado claro en el shell evita que una promesa vieja actualice controles de navegación de una ruta nueva.
Elementos dentro de Shadow DOM
wu.mount(name, selector) resuelve el selector con document.querySelector. Ese selector no cruza ShadowRoots y la firma actual espera un string, no un HTMLElement. Para que el shell localice el slot, colócalo en su light DOM o utiliza una composición que exponga un nodo accesible por selector.
Dentro de un lifecycle, usa el container entregado para manipular la raíz local; no vuelvas a buscarla globalmente por ID. El adapter Vue del repositorio ilustra por qué una raíz estable debe ser un hermano del indicador de loading y no su contenedor gestionado por el shell.
Keep-alive y cambio de ruta
hide preserva estado y listeners; funciona cuando mantienes el contenedor de la app. Cambiar muchas apps sobre un único slot requiere una decisión explícita: desmontar el dueño anterior o conservar contenedores separados para cada app que permanezca viva. No uses keep-alive como sinónimo de cachear módulos; son recursos distintos.
Si Astro reemplaza páginas mediante navegación cliente, el bootstrap debe acompañar ese lifecycle: libera listeners y mounts de la página anterior y conecta los nuevos slots. Una importación de script deduplicada no significa que todos los nodos futuros queden montados automáticamente.
Roles de UI
wu.setPrincipal({ id: 'u1', roles: ['catalog-reader'] }) alimenta wu.can('catalog'). El montaje consulta los roles requeridos por el manifest y puede rechazar con WU_ACCESS_DENIED. Esto ayuda a mantener navegación y vistas coherentes; las APIs de datos deben autorizar cada petición en el servidor.
Referencia de implementación
Validación de rutas descriptivas: src/core/wu-manifest.js; acceso: src/core/wu-core.js y src/core/wu-core.js; selector: src/core/wu-core.js; transición de contenedor: src/core/wu-core.js; convención usada por MCP: src/core/wu-mcp-bridge.js. Pruebas: tests/unit/shared-container-navigation.test.js, tests/unit/sandbox-container-ownership.test.js, tests/unit/rbac.test.js.