WU / DOCSUNIVERSAL MICROFRONTENDS
EN

Core, subsystems and manifests

Choose optional entry points and declare each app contract in a file, the shell or its module.

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

Choose the runtime

wu-framework loads optional subsystems on first use. wu-framework/core requires explicit imports in the shell. CLI 1.0.0 shares the core entry between the shell and apps in both development and production.

import { wu } from 'wu-framework/core';
import 'wu-framework/timeline';
import 'wu-framework/ports';

await wu.init({ apps: [] });
const timeline = await wu.timelineReady();
Feature Shell import
AI wu-framework/ai
Timeline wu-framework/timeline
Devtools wu-framework/devtools
MCP wu-framework/mcp
strict wu-framework/sandbox/iframe
eval wu-framework/sandbox/html-parser + wu-framework/sandbox/iframe
Store sync wu-framework/store-sync
Shadow wu-framework/shadow
Windows wu-framework/windows
Drag and drop wu-framework/dnd
Ports / remote wu-framework/ports
.wuapp wu-framework/wuapp

When an import is missing, WU_OPTIONAL_NOT_BUNDLED identifies the subsystem and entry to add. Add it once in the shell and rebuild. Importing an entry registers the subsystem; it does not grant app permissions.

Three manifest sources

manifest: "fetch" is the default and loads wu.json. A manifest object declares the same information in the shell. With manifest: "define", the module calls wu.declare or passes { manifest } as the third argument to wu.define; configure entry because no earlier file identifies the module.

// shell.js
import { wu } from 'wu-framework/core';
await wu.init({ apps: [{
  name: 'greeting', url: '/apps/greeting/',
  manifest: 'define', entry: 'entry.js', grants: [],
}] });
await wu.mount('greeting', '#greeting-slot');
// apps/greeting/entry.js
import { wu } from 'wu-framework/core';
wu.define('greeting', {
  mount(container) { container.textContent = 'Hello from WU'; },
  unmount(container) { container.replaceChildren(); },
}, { manifest: { wu: { permissions: [] } } });

The shell HTML must contain #greeting-slot; the app module must import the same WU instance as the shell. All three modes validate the manifest and apply host grants. Declaring it in JavaScript does not make the code secret or create a security boundary.

Identity during loading

definesAs registers another instance of a module that keeps its original name. During loading, WU redirects define and declare to the new registration. Only one load can be active per redirected name: WU_DEFINE_REDIRECT_BUSY. If a module defines the wrong name, WU_DEFINE_NAME_MISMATCH reports the observed names without repeating a timeout that cannot fix the mismatch.

Mount inside another Shadow DOM

mount(name, target) accepts a selector or an HTMLElement. Pass the element when it belongs to a Shadow DOM: document selectors do not cross that boundary. Recovery and unmount retain the same host; inspect() returns containerSelector: null for an element mount.

// The shell has already registered and initialized the editor app.
const shell = document.querySelector('my-shell');
const slot = shell.shadowRoot.querySelector('[data-editor-slot]');
await runtime.mount('editor', slot);
// Later, when the shell releases this view:
await runtime.unmount('editor', { force: true });

Instances own their resources

Each new WuCore() has its own store, memory cache, timing entries and resource hints. destroy() releases those resources while preserving other instances. A cache injected with new WuCore({ cache }) belongs to the caller and is not automatically cleared. For persistence, create a WuCache with explicit storage and namespace, and decide when to clear it.

Register lifecycles with runtime.define(...) on the appropriate instance. Importing the global wu singleton registers against the shared host. On an independent instance, await runtime.timelineReady() before recording changes. This pattern composes trusted demos in the same page; it does not create a security boundary for arbitrary code.

See supervision, recovery and replay for superviseApp, createReplaySession and the scoped reportError, now and random methods.

destroy() revokes host-minted ports, returns apps from detached windows and releases that instance’s drag and drop, shadow, health, replay and timeline resources. Drag offers and targets are also released when an app’s lease is revoked, including unmount or quarantine.

BUILD WU WITH US

Found a bug? Have an idea?

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