# Apps remotas y puertos de capacidades

Conecta una app de otro origen mediante un iframe y un MessagePort con permisos y revocación.

Esta página describe WU Framework `4.0.0`. Consulta la [migración a Framework 4.0 y CLI 1.2](/es/docs/guides/releases/) antes de actualizar un proyecto existente.

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

## Configurar el host

```js
import { wu } from 'wu-framework/core';
import 'wu-framework/ports';
await wu.init({ apps: [{
  name: 'widget', url: 'https://widgets.example/app/', sandbox: 'remote',
  manifest: { entry: 'index.html', wu: { permissions: ['events.emit:widget:*'] } },
  grants: ['events.emit:widget:*'],
  iframe: { title: 'Remote widget', timeout: 15000, callTimeout: 10000 },
}] });
await wu.mount('widget', '#widget-slot');
await wu.update('widget', { theme: 'light' });
```

Crea `#widget-slot` en el shell y sirve la app desde el origen indicado. WU crea el iframe y entrega su puerto al origen exacto de la URL. El navegador aplica el aislamiento entre orígenes; los mensajes del puerto ejecutan operaciones a través de la facade de `widget`, con sus permisos solicitados y concedidos.

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

## Conectar el documento remoto

```js
import { connect } from 'wu-framework/remote';

const remote = await connect({ hostOrigin: 'https://shell.example' });
const off = remote.onProps(props => {
  document.documentElement.dataset.theme = props.theme;
});
await remote.emit('widget:ready', { ready: true });
await remote.resize(document.body.scrollHeight);

window.addEventListener('pagehide', () => {
  off();
  remote.close();
}, { once: true });
```

El documento remoto utiliza su propio renderer y debe aceptar únicamente el origen real del shell en `hostOrigin`. Las llamadas al host son asíncronas: espera `store.get`, `store.set`, `emit` y RPC. `onProps` recibe actualizaciones; `props` conserva las últimas. `resize` solicita altura y `whenClosed` permite observar el cierre. Los datos deben admitir structured clone.

El iframe usa por defecto `allow-scripts allow-same-origin allow-forms`; `iframe.sandbox` y `iframe.allow` permiten configurarlo. `wu.remoteOf(name)` consulta su ventana. `hide` conserva el iframe; `unmount` retira el iframe y revoca el puerto. Los fallos de conexión son `WU_REMOTE_TIMEOUT` y `WU_REMOTE_LOAD_FAILED`; un handler remoto que no responde a tiempo produce `WU_PORT_CALL_TIMEOUT`.

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

## Cabeceras de ambos orígenes

```json
{ "security": { "frameOrigins": ["https://widgets.example"] } }
```

En WU CLI `1.0.0`, `security.frameOrigins` añade esos orígenes a `frame-src`, no a `script-src` ni a `connect-src`. La respuesta remota debe permitir al shell en `frame-ancestors` y no bloquearlo con `X-Frame-Options`. Configura también el renderer y la entrada `wu-framework/remote` en el build remoto.

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

## Crear un puerto manualmente

```js
const capability = await wu.ports.mint('widget', { props: { theme: 'dark' } });
worker.postMessage({ wu: 1, type: 'port', app: 'widget' }, [capability.port]);
await capability.ready;
capability.push({ theme: 'light' });
capability.close('finished');
const active = wu.ports.list();
const revoked = wu.ports.revoke('widget');
```

El host crea `worker` y registra previamente `widget` con sus grants; el receptor debe conectar el protocolo remoto. El puerto es transferible y cerrable. `close` revoca ese puerto; `revoke` cierra los de la app. Lo pendiente rechaza con `WU_PORT_CLOSED`. Esta comunicación por mensajes no comparte directamente el objeto store entre documentos.

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

## Validación de origen en 4.0

`connect(options)` y `waitForPort(options)` exigen `hostOrigin`; omitirlo produce `WU_REMOTE_HOST_ORIGIN_REQUIRED`. `"*"` acepta conscientemente cualquier origen. En iframe o popup también se valida que el emisor sea `parent` u `opener`; `source` permite indicar otra ventana. El overload `connect(port)` acepta un puerto ya recibido.

El origen de la app es el de su `url`. Un `entry` en otro origen falla con `WU_REMOTE_ORIGIN_MISMATCH`. Un remoto en el origen del shell falla con `WU_REMOTE_SAME_ORIGIN`, salvo `iframe.allowSameOrigin: true`; esa excepción pierde el aislamiento entre orígenes. `wu.detach()` rechaza remotos con `WU_WINDOW_REMOTE_UNSUPPORTED`.

El host vuelve a ofrecer el puerto después de `load` hasta recibir respuesta, por lo que el remoto puede esperar antes de conectar. Sobre el puerto, `consume()` solo permite métodos de la `shape` declarada; sin `shape`, excluye los métodos de `Object.prototype`. El unmount y `destroy()` revocan los puertos del core.