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 antes de actualizar un proyecto existente.
Configurar el host
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.
Conectar el documento remoto
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.
Cabeceras de ambos orígenes
{ "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.
Crear un puerto manualmente
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.
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.