WU / DOCSUNIVERSAL MICROFRONTENDS
EN

Remote apps and capability ports

Connect an app on another origin through an iframe and a MessagePort with permissions and revocation.

This page describes WU Framework 4.0.0. Read the Framework 4.0 and CLI 1.2 migration guide before upgrading an existing project.

Configure the 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' });

Create #widget-slot in the shell and serve the app from the configured origin. WU creates the iframe and transfers its port to the exact URL origin. The browser enforces cross-origin isolation; port messages execute through the widget facade with its requested and granted permissions.

Connect the remote document

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 });

The remote document uses its own renderer and must accept only the actual shell origin in hostOrigin. Calls to the host are asynchronous: await store.get, store.set, emit and RPC. onProps receives updates; props holds the latest values. resize requests a height and whenClosed observes closure. Data must support structured clone.

The iframe defaults to allow-scripts allow-same-origin allow-forms; configure it with iframe.sandbox and iframe.allow. wu.remoteOf(name) returns its window. hide retains the iframe; unmount removes it and revokes the port. Connection failures are WU_REMOTE_TIMEOUT and WU_REMOTE_LOAD_FAILED; a remote handler exceeding its deadline produces WU_PORT_CALL_TIMEOUT.

Headers on both origins

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

In WU CLI 1.0.0, security.frameOrigins adds these origins to frame-src, not to script-src or connect-src. The remote response must allow the shell through frame-ancestors and must not block it with X-Frame-Options. Configure its renderer and the wu-framework/remote entry in the remote build too.

Create a port manually

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');

The host creates worker and registers widget with grants beforehand; the receiver must connect the remote protocol. The port can be transferred and closed. close revokes that port; revoke closes the app ports. Pending calls reject with WU_PORT_CLOSED. Message communication does not directly share the store object between documents.

Origin validation in 4.0

connect(options) and waitForPort(options) require hostOrigin; omitting it produces WU_REMOTE_HOST_ORIGIN_REQUIRED. "*" knowingly accepts any origin. In an iframe or popup, the sender must also be parent or opener; source can specify another window. The connect(port) overload accepts an already received port.

The app’s origin is the origin of its url. An entry on another origin fails with WU_REMOTE_ORIGIN_MISMATCH. A remote on the shell’s origin fails with WU_REMOTE_SAME_ORIGIN, unless iframe.allowSameOrigin: true; that exception loses cross-origin isolation. wu.detach() rejects remotes with WU_WINDOW_REMOTE_UNSUPPORTED.

The host keeps offering the port after load until it receives a response, so the remote can await work before connecting. Over the port, consume() permits only methods in the declared shape; without a shape, it excludes Object.prototype methods. Unmount and destroy() revoke the core’s ports.

BUILD WU WITH US

Found a bug? Have an idea?

Your experience improves WU. Tell us what went wrong or help support its development.