@hyperfrontend/features/host

Host

Host-side SDK for embedding hyperfrontend features: shell factory, display modes, iframe utilities, and lifecycle.

import { builtInDisplayModes, createShell, DisplayMode } from '@hyperfrontend/features/host'

const shell = createShell({
  modes: builtInDisplayModes,
  url: 'https://features.example.com/clock',
  container: '#clock-slot',
  displayMode: DisplayMode.Embedded,
})

shell.on('open', () => console.log('feature connected'))
shell.on('tick', (time) => console.log('feature said', time))

shell.open()
shell.send('set-timezone', { tz: 'UTC' })

API

ExportPurpose
createShellBuild a shell handle from an explicit modes map: only the mounts you pass ship.
builtInDisplayModesThe all-modes map, for hosts that want every mode available.
mountEmbedded …The four mount functions (mountEmbedded, mountDialog, mountPopup, mountStandalone) for the modes map.
DisplayModeThe four built-in modes: Embedded, Dialog, Popup, Standalone.
ShellHandleType of the handle returned by createShell.
CreateShellOptionsOptions accepted by createShell (ShellOptions plus the modes map).
ExperiencePluginOpt-in extension point for layering transitions/animations onto display modes.

The modes map is how unused mode code stays out of bundles: a generated shell passes exactly the modes its feature declared, and a hand-written host that only ever embeds can pass { embedded: mountEmbedded } and ship one mode. Opening a mode outside the map throws, naming the supported set.

The shell wraps a @hyperfrontend/nexus broker: send emits a contract action to the feature, and on subscribes to feature messages and the open/closing/close/error/status/dirty-state/reopen/dismiss lifecycle events. close disconnects the channel politely (the feature gets a closing flush window, and isDirty reports declared unsaved work first); destroy also releases the DOM.

Opening is asynchronous: isOpen stays false and sends queue until the wire handshake with the feature completes, flushing on the open event. If the feature never completes the handshake within openTimeoutMs (default 10 s), the shell tears the mount down and emits error with reason: 'open-timeout'.

Every shell-originated error payload carries a reason discriminator:

reasonEmitted whenPayload beyond reason
'open-timeout'The handshake does not complete within openTimeoutMs.elapsedMs, displayMode
'open-failed'The display mode cannot produce a feature window at all (e.g. a blocked popup).displayMode
'target-lost'A window the shell opened stops being reachable before the handshake completes.elapsedMs, displayMode
'unresponsive'The heartbeat watchdog trips under the emit, unmount, or reopen onUnresponsive policy.missedBeats, lastBeatAt, displayMode, frame
'reopen-exhausted'The reopen policy spent its attempts and the feature is still silent; the shell has torn it down.attempts, displayMode

'target-lost' has two possible causes and the browser gives the host no way to tell them apart: the visitor closed the window, or the feature document carried Cross-Origin-Opener-Policy: same-origin and the browser severed the opener as it loaded. The elapsedMs is what separates them in practice. A severed opener is reported as the document commits, typically within a few hundred milliseconds of open(); a visitor reaching for the close control takes far longer. A short elapsedMs on a popup or standalone session is worth reading as an isolation problem on the feature origin rather than a dismissal.

The watchdog cannot tell a frame that died from one that stalled: a renderer process the browser or the operating system killed (routine on phones) goes silent without any signal the embedding page can observe, exactly like a frame whose main thread is starved. The 'unresponsive' error says what the host can see through frame: 'gone' when the iframe left the page or the window was closed, 'present' otherwise. Only time separates the two 'present' cases, and the 'reopen' policy spends that time for you:

const shell = createShell({
  modes: builtInDisplayModes,
  container: '#clock',
  url: 'https://clock.example.com',
  onUnresponsive: {
    reopen: { graceMs: 4000, backoff: 3, attempts: 3, stableMs: 60_000 },
  },
})
shell.on('reopen', (data) => console.info('reviving the clock', data))

A frame that beats again within graceMs was only stalled and keeps its session. One that stays silent has its mount (the browser's crash placeholder included) replaced by a fresh one opened with the same options, announced by a 'reopen' event carrying { attempt, attempts, displayMode }. Each further death in the episode waits backoff times longer, and a reopened session that stays open for stableMs restores the whole budget. 'reopen' alone takes every default shown above.

Nothing is reopened into a hidden page: the attempt waits for the visitor to return, then allows a fresh grace. A reopened session that never completes its handshake counts as another death. The policy stands down for a 'gone' frame (the shell tears it down instead, since your page or the visitor removed it), when you call close, destroy, or open yourself, and when the feature closes the session.

A frame whose renderer died is painted over by the browser's own crash placeholder, which your styles cannot reach. Set concealUnresponsive to hide the frame from the verdict until its next beat or the next session opens. The trade-off: a feature that merely stalls past the miss budget also disappears until it beats again.

A feature that reloads itself (a refresh, an in-frame navigation, a dev-server rebuild) ends its session but keeps its mount: close fires with { reason: 'peer-reload' }, then open fires again once the new document completes its own handshake, and the shell re-announces the presentation to it. Treat the pair as a session boundary: pending requests reject, isDirty resets, and anything you sent the previous document needs sending again. To refuse the reload instead, destroy() on that reason.

Display modes and sizing

The host owns presentation. It picks the display mode (from the set the feature's contract declares), announces it to the feature once per mount (the announcement already carries the frame's initial dimensions, so the feature lays itself out without waiting for a second message), and is the single authority on frame geometry: every dimension crosses the boundary as an exact pixel value, host to feature, never the other way.

Mounted is not displayed: the frame mounts hidden and is revealed only once the session opens, so the user never sees (or clicks into) a frame whose feature is not ready. A dialog pane cannot intercept the page while it is still connecting; an embedded frame reserves its box without painting.

Embedded mounts the frame inline in your container and the frame fills the container's content box: measured before the frame is inserted, then observed with a ResizeObserver; every later change is reported to the feature. While the container has no measurable size (hidden, not yet laid out, or nothing gives it a height), the SDK applies a dynamic viewport-derived fallback so the embed is never invisible by accident; the fallback retires as soon as your layout takes over.

A feature with intrinsic dimensions can bake fixed embedWidth/embedHeight instead (or you can pass them), then the frame gets exactly those pixels and you place the container somewhere they fit; the SDK never distorts a fixed agreement.

Dialog is a full-viewport transparent pane layered above your page. The feature draws its dialog box (and any backdrop paint) inside the pane; dialogWidth/dialogHeight set the inner box (viewport-derived when unset) and dialogPosition places it: center by default, or any edge/corner (top-left … bottom-right). The feature detects backdrop clicks and in-frame Escape presses and reports them as a dismiss signal; dialogBackdrop decides what the shell does: close (default) starts the polite teardown, event emits a dismiss event for you to handle, none ignores it. closeOnEscape covers Escape from both documents.

shell.open({
  displayMode: DisplayMode.Dialog,
  dialogWidth: 480,
  dialogPosition: 'top-center',
  dialogBackdrop: 'event',
})
shell.on('dismiss', ({ source }) => console.log('backdrop interaction', source))

Popup opens a separate window at popupWidth/popupHeight (viewport-derived when unset), placed on the screen per popupPosition (center by default, or any edge/corner). After that the window belongs to the browser and the user: they move and resize it freely, and no frame geometry crosses the boundary. The window's title and chrome are not the host's to set: the title comes from the loaded document (the feature sets its own document.title), and browsers ignore chrome flags like resizability for window.open. Standalone is a plain new tab: the simplest mode, deliberately free of presentation coordination.

Both windowed modes depend on the opener relationship surviving the feature document's load, and a cross-origin isolated feature origin ends it. crossOriginIsolated (the precondition for SharedArrayBuffer and performance.measureUserAgentSpecificMemory) requires Cross-Origin-Opener-Policy: same-origin, and a window opened onto such an origin keeps its opener only when the opener is same-origin and itself isolated. Every other pairing puts the new window in its own browsing-context group: window.opener is null inside it, messages in both directions are discarded without error, and the session times out while the feature renders perfectly on screen.

Note that same-origin-allow-popups does not widen this, because it governs popups the document opens, not the opener it keeps when it is opened. Declare isolation in feature.config.* and the toolchain composes only the modes the origin can serve; the type narrowing means declaring isolation alongside popup or standalone does not compile.

Transparency is on by default in both iframe modes (allowtransparency plus a matched color-scheme pin on both sides; a mismatch would force an opaque canvas), so features can render non-rectangular designs, blend with your UI, and paint dialog backdrops. Want an opaque embed? Give your container a background.

What the SDK deliberately does not do

  • Content-driven growth (the embed grows with its content) is not built in: it would hand geometry authority to the feature, which is the inversion this design exists to avoid. The recipe is one contract action: have the feature emit its content height as product data, and set it on the container you own (shell.on('contentHeight', (px) => setContainerHeight(px))); the SDK's container observation propagates the change back down.
  • Clipping and scrolling are not managed. The feature owns its document's overflow behaviour; the host owns the container's. Neither needs a protocol.
  • Draggable/resizable dialog boxes are not built in, but the full-viewport pane makes them a pure feature-side concern: the pane never moves, so the feature can drag or resize its inner box with ordinary CSS/pointer code and nothing needs to cross the boundary. If your host UI needs to know, carry position as an ordinary contract action.

Browser capabilities

Capability is one of the two axes in the Security Model: read it for where these options sit relative to what the browser enforces and what stays the operator's job.

Two ShellOptions fields govern what the feature frame may do with the browser around it, both applied before the frame loads (the only moment they take effect) and both scoped to the iframe modes: popup and standalone open top-level windows, which ask the user for permissions directly.

permissions delegates Permissions-Policy features (camera, fullscreen, clipboard, …) to the frame via the iframe allow attribute. Browsers deny these to cross-origin frames by default, so a feature that needs one only works when the host delegates it. A generated shell bakes the needs the feature declared at build time (also disclosed in its README and metadata.json); a host-supplied list replaces the baked one entirely.

sandbox is the host's containment lever and is never baked by a build. true (or an opt-in object) starts the frame from the browser's deny-all sandbox, and the SDK manages the two hazardous tokens itself:

  • allow-scripts is always granted, because the feature runtime is JavaScript.
  • allow-same-origin is granted only to cross-origin feature URLs, since a same-origin frame holding both tokens could remove its own sandbox. A sandboxed same-origin feature therefore runs with an opaque origin (no cookies or storage) while the messaging protocol still connects.
  • Everything else (forms, popups, modals, downloads, topNavigationByUserActivation) is denied unless opted in.

Requesting a sandbox on popup or standalone throws, because no containment can apply to a top-level window.

shell.open({
  permissions: ['fullscreen', 'clipboard-write'],
  sandbox: { downloads: true },
})

API Reference§

ƒ Functions

§function

createShell(options: CreateShellOptions): ShellHandle

Creates a host-side shell for embedding a feature.
Provisions a nexus broker and returns a handle whose open mounts the feature in the requested display mode. The shell is built from an explicit modes map: pass the mounts this host supports (which is how generated shells exclude undeclared modes from their bundles) or builtInDisplayModes for all of them; opening a mode outside the map throws, naming the supported set. The contract option takes the feature's contract exactly as the feature authored it; the shell derives the host-side orientation itself, so the handle sends what the feature accepts and receives what the feature emits.
Parameters
NameTypeDescription
§options
CreateShellOptionsCreate-time options including the modes map; overridable per open call.
Returns
ShellHandle
A handle exposing open, close, destroy, send, on, and isOpen.
Example

Embedding a clock feature with every built-in mode available

const clock = createShell({ modes: builtInDisplayModes, container: '#clock', url: 'https://clock.example.com' })
clock.open({ displayMode: DisplayMode.Dialog, dialogWidth: 530 })
clock.on('timeUpdated', (data) => console.log(data))

◈ Interfaces

§interface

CreateShellOptions

Options accepted by createShell: the shell options plus the display modes this shell is built from.
Properties
§closeOnEscape?:booleanWhether Escape closes the dialog; defaults to true. Enforced on both sides of the boundary: the host listens in its own document, and the feature reports an Escape pressed inside its frame as a dismiss signal the host acts on (dialog mode only).
§concealUnresponsive?:booleanWhether the shell hides the feature's frame on the unresponsive verdict; defaults to false. The frame stays mounted with its session open, and its next beat or the next session to open shows it again. This keeps the browser's crash placeholder for a dead frame off your page, but a frame that merely stalls past the miss budget also disappears until it beats again. Applies to the iframe modes (embedded, dialog) with any UnresponsivePolicy.
§container?:string | HTMLElementAnchor element (or CSS selector) the embedded feature mounts into; required by (and only meaningful for) embedded mode.
§contract?:FeatureContractThe feature's contract exactly as the feature authored it (emitted = what the feature sends, accepted = what the feature handles). The shell derives the host-side orientation itself: hand it the feature's contract, never a pre-swapped copy. Replaces the generic default when provided.
§dialogBackdrop?:BackdropBehaviorHow the host reacts to a pointer interaction on the dialog backdrop, the transparent area around the feature's dialog box; defaults to close. See BackdropBehavior.
§dialogHeight?:numberHeight in pixels of the feature's inner dialog box; see ShellOptions.dialogWidth.
§dialogPosition?:BoxPositionWhere the inner dialog box sits inside the pane (dialog mode only); defaults to center.
§dialogWidth?:numberWidth in pixels of the feature's inner dialog box (dialog mode only). Crosses the boundary at open and is applied by the hostee SDK inside the full-viewport dialog pane; when absent, the hostee derives a size from the viewport and its aspect ratio.
§displayMode?:DisplayModeHow the feature should be surfaced; defaults to DisplayMode.Embedded.
§embedHeight?:numberFixed embedded height in pixels; see ShellOptions.embedWidth.
§embedWidth?:numberFixed embedded width in pixels. When both embedWidth and embedHeight are set, the embedded iframe receives exactly those dimensions instead of filling its container, and the host application is responsible for placing the container somewhere the feature fits: the SDK never distorts or reinterprets fixed dimensions. Setting only one of the pair throws.
§modes:DisplayModeMapThe display modes this shell supports, mode name to mount function.
§name?:stringStable identifier for the feature; seeds the broker name surfaced in debug logs.
§onUnresponsive?:UnresponsivePolicyHow the host reacts when the feature stops responding; defaults to emit.
§openTimeoutMs?:numberMilliseconds the shell waits for the feature to complete the connection handshake before emitting an error with reason: 'open-timeout' and tearing the mount down; defaults to 10000.
Opening is asynchronous: isOpen stays false and the open event fires only once the wire handshake completes. send/request calls issued in between queue on the channel and flush on open.
§permissions?:unknownPermissions-Policy features delegated to the feature frame, applied as the iframe allow attribute scoped to the frame's own origin. Shell builds bake the feature's declared needs here; a host-supplied list replaces the baked one entirely. Only the iframe modes (embedded, dialog) apply it: popup and standalone open top-level windows, which request these permissions from the user directly.
§plugins?:unknownExperience plugins wrapped around each mount/unmount; onMount runs in registration order, onUnmount in reverse.
§popupHeight?:numberPopup window height in pixels (popup mode only); when absent, derived from the viewport.
§popupPosition?:BoxPositionWhere the popup window sits on the screen (popup mode only); defaults to center.
§popupWidth?:numberPopup window width in pixels (popup mode only); when absent, derived from the viewport.
§protocol?:SecurityProtocolSecurity envelope to negotiate; defaults to none.
§sandbox?:boolean | SandboxOptionsContainment posture for the feature frame. true (or an opt-in object) starts the frame from the browser's deny-all sandbox; the SDK always returns allow-scripts, grants allow-same-origin only to cross-origin feature URLs, and denies everything else unless opted in; see SandboxOptions for the managed tokens and why. Host-decreed and never baked by a shell build. Only meaningful for the iframe modes: opening popup or standalone with a sandbox set throws, because no containment can apply to a top-level window.
§sharedKey?:stringPre-shared key the v4 protocol binds the session to; at least 16 characters, never given with another protocol.
§url?:stringURL of the feature app to load.
§interface

HeartbeatStatus

Snapshot of the watchdog's judgement.
Properties
§lastBeatAt:numberTimestamp of the last received beat, or null if none arrived.
§missedBeats:numberConsecutive watchdog ticks without a beat.
§state:HeartbeatStateCurrent liveness state.
§interface

MountContext

Context handed to a display-mode mount function.
Properties
§options:ShellOptionsThe fully merged shell options for this open.
§interface

MountResult

Outcome of mounting a display mode: the window to message plus a teardown hook.
Properties
§element?:HTMLElementIn-document root the mode mounted (the feature iframe); unset when the feature opens in a separate window.
§present:PresentPayloadPresentation announcement the shell sends the feature once per mount: the mode, the frame's initial dimensions, and any agreed dialog box geometry.
§target:WindowWindow the host messages, or null when a popup/standalone was blocked.
§viewport?:ViewportReporterReporter of the frame's exact pixel space, when the mode observes an iframe; the shell forwards its change reports once the channel opens.
§interface

ShellHandle

Public handle returned by createShell.
Properties
§readonly isDirty:booleanWhether the feature has declared unsaved work (true between setDirty(true) and setDirty(false); resets when the channel closes).
§readonly isOpen:booleanWhether the feature channel is currently open (true while connected).
§interface

ViewportReporter

Reports the exact pixel dimensions of the space a feature frame occupies.
Created by a display-mode mount seeded with a synchronous initial measurement: current() feeds the presentation announcement, and once the shell calls start, only changes relative to what was already announced are forwarded: the initial size never crosses twice.
Properties
§interface

ActionDescription

Description of a single action a feature can emit or accept.
Structurally compatible with nexus's channel contract action shape so the same contract can drive both messaging and the shell type generator.
Properties
§description?:stringHuman-readable explanation of the action, surfaced in tooling.
§required?:booleanMarks an accepted action as essential for correct operation: the connection is denied at handshake time unless the counterpart emits this type. Only meaningful on accepted entries. Unflagged actions never gate the connection, so additive contract evolution stays non-breaking.
§respondsWith?:stringWhen this action is used as a request, the type of the action in the other direction that answers it.
§schema?:objectOptional JSON-schema-like shape describing the action payload.
§type:stringWire type string that identifies the action.
§interface

ExperiencePlugin

Opt-in extension that decorates a feature's mount lifecycle (e.g. transitions, animations).
Register plugins through ShellOptions.plugins. After each successful mount the shell calls onMount on every plugin in registration order; before each unmount it calls onUnmount one plugin at a time in reverse registration order, awaiting any returned promise, then runs the teardowns returned by onMount (also in reverse registration order) and finally removes the feature. The SDK ships no built-in plugins.
Properties
§name:stringUnique plugin name, surfaced in debug logs.
§interface

ExperiencePluginContext

Context handed to an ExperiencePlugin around a feature's mount lifecycle.
Properties
§displayMode:DisplayModeThe display mode the feature was surfaced in.
§element:HTMLElementThe in-document root the display mode mounted: the iframe for embedded, the dialog container for dialog, and null for popup and standalone, which open a separate window with no in-document element.
§interface

FeatureContract

The set of actions a feature emits to, and accepts from, its counterpart.
This is the same shape the on-disk *.contract.json files and the shell generator consume.
Properties
§accepted:ActionDescription[]Actions this side handles from the other side.
§emitted:ActionDescription[]Actions this side sends to the other side.
§version?:stringOptional semver version announcing the contract cut this side holds. Builds canonicalize and bake it into the generated shell; the two sides compare their announcements during the connection handshake and incompatible cuts are denied before the channel opens. Absent on either side, the check passes, so unversioned peers keep connecting.
§interface

PresentPayload

Payload of the reserved present control message the host sends once per mount: the display mode this mount uses, the frame's initial dimensions, and the agreed inner dialog box geometry when the mode is dialog.
Properties
§dialog?:DialogBoxSizeAgreed inner dialog box geometry (dialog mode only).
§mode:DisplayModeThe display mode the host mounted the feature in.
§viewport?:ViewportPayloadThe frame's usable space at mount time, in exact pixels (iframe modes only), so the feature can lay itself out without waiting for the first viewport report; later changes arrive as viewport reports.
§interface

ReopenOptions

Tuning for the reopen UnresponsivePolicy: how patiently, how often, and how many times the shell brings back a feature whose frame went silent.
One episode starts at the first verdict and keeps counting while the feature keeps dying; a reopened session that stays open for stableMs ends the episode and restores the full budget.
Properties
§attempts?:numberReopens one episode may spend; a positive integer, defaults to 3. A feature still silent after the last one is torn down and an error with reason: 'reopen-exhausted' is emitted.
§backoff?:numberFactor each further grace in the same episode grows by; at least 1, defaults to 3.
§graceMs?:numberMilliseconds a verdict must stand before the first reopen; defaults to
  1. A frame that beats again within the grace was stalled, not dead, and
keeps its session. Each further reopen in the same episode waits backoff times longer than the one before.
§stableMs?:numberMilliseconds a reopened session must stay open before the episode ends and the budget is restored; defaults to 60000.
§interface

ReopenPolicy

The tuned form of the reopen UnresponsivePolicy.
Properties
§reopen:ReopenOptionsThe revival tuning; every omitted field takes its default.
§interface

RequestOptions

Per-request settings accepted by request.
Properties
§timeoutMs?:numberMilliseconds to wait for the response before rejecting; defaults to 30000.
§interface

SandboxOptions

Containment opt-ins for a sandboxed feature frame.
Enabling ShellOptions.sandbox starts the frame from the browser's deny-all sandbox and returns capabilities selectively. Two tokens are managed by the SDK and are not configurable: allow-scripts is always present (the feature runtime is JavaScript, so a script-less frame can never connect), and allow-same-origin is granted only when the feature URL resolves to a different origin than the host page: a same-origin frame holding both tokens could remove its own sandbox, so that pairing cannot be expressed. A sandboxed same-origin feature therefore runs with an opaque origin (no cookies or storage); the messaging protocol still connects. Every opt-in below defaults to false (denied).
Properties
§downloads?:booleanAllow the feature to trigger file downloads; defaults to false.
§forms?:booleanAllow the feature to submit forms; defaults to false.
§modals?:booleanAllow the feature to open modal dialogs (alert, confirm, print); defaults to false.
§popups?:booleanAllow the feature to open popup windows; defaults to false.
§topNavigationByUserActivation?:booleanAllow the feature to navigate the top-level page in response to a user activation (e.g. a clicked link targeting _top); defaults to false. Unrestricted top-level navigation is deliberately not offered: it enables an entire class of takeover incidents that user-activation gating avoids.
§interface

ShellOptions

Options accepted by the host-side FeatureContract consumer when creating or opening a shell.
Properties
§closeOnEscape?:booleanWhether Escape closes the dialog; defaults to true. Enforced on both sides of the boundary: the host listens in its own document, and the feature reports an Escape pressed inside its frame as a dismiss signal the host acts on (dialog mode only).
§concealUnresponsive?:booleanWhether the shell hides the feature's frame on the unresponsive verdict; defaults to false. The frame stays mounted with its session open, and its next beat or the next session to open shows it again. This keeps the browser's crash placeholder for a dead frame off your page, but a frame that merely stalls past the miss budget also disappears until it beats again. Applies to the iframe modes (embedded, dialog) with any UnresponsivePolicy.
§container?:string | HTMLElementAnchor element (or CSS selector) the embedded feature mounts into; required by (and only meaningful for) embedded mode.
§contract?:FeatureContractThe feature's contract exactly as the feature authored it (emitted = what the feature sends, accepted = what the feature handles). The shell derives the host-side orientation itself: hand it the feature's contract, never a pre-swapped copy. Replaces the generic default when provided.
§dialogBackdrop?:BackdropBehaviorHow the host reacts to a pointer interaction on the dialog backdrop, the transparent area around the feature's dialog box; defaults to close. See BackdropBehavior.
§dialogHeight?:numberHeight in pixels of the feature's inner dialog box; see ShellOptions.dialogWidth.
§dialogPosition?:BoxPositionWhere the inner dialog box sits inside the pane (dialog mode only); defaults to center.
§dialogWidth?:numberWidth in pixels of the feature's inner dialog box (dialog mode only). Crosses the boundary at open and is applied by the hostee SDK inside the full-viewport dialog pane; when absent, the hostee derives a size from the viewport and its aspect ratio.
§displayMode?:DisplayModeHow the feature should be surfaced; defaults to DisplayMode.Embedded.
§embedHeight?:numberFixed embedded height in pixels; see ShellOptions.embedWidth.
§embedWidth?:numberFixed embedded width in pixels. When both embedWidth and embedHeight are set, the embedded iframe receives exactly those dimensions instead of filling its container, and the host application is responsible for placing the container somewhere the feature fits: the SDK never distorts or reinterprets fixed dimensions. Setting only one of the pair throws.
§name?:stringStable identifier for the feature; seeds the broker name surfaced in debug logs.
§onUnresponsive?:UnresponsivePolicyHow the host reacts when the feature stops responding; defaults to emit.
§openTimeoutMs?:numberMilliseconds the shell waits for the feature to complete the connection handshake before emitting an error with reason: 'open-timeout' and tearing the mount down; defaults to 10000.
Opening is asynchronous: isOpen stays false and the open event fires only once the wire handshake completes. send/request calls issued in between queue on the channel and flush on open.
§permissions?:unknownPermissions-Policy features delegated to the feature frame, applied as the iframe allow attribute scoped to the frame's own origin. Shell builds bake the feature's declared needs here; a host-supplied list replaces the baked one entirely. Only the iframe modes (embedded, dialog) apply it: popup and standalone open top-level windows, which request these permissions from the user directly.
§plugins?:unknownExperience plugins wrapped around each mount/unmount; onMount runs in registration order, onUnmount in reverse.
§popupHeight?:numberPopup window height in pixels (popup mode only); when absent, derived from the viewport.
§popupPosition?:BoxPositionWhere the popup window sits on the screen (popup mode only); defaults to center.
§popupWidth?:numberPopup window width in pixels (popup mode only); when absent, derived from the viewport.
§protocol?:SecurityProtocolSecurity envelope to negotiate; defaults to none.
§sandbox?:boolean | SandboxOptionsContainment posture for the feature frame. true (or an opt-in object) starts the frame from the browser's deny-all sandbox; the SDK always returns allow-scripts, grants allow-same-origin only to cross-origin feature URLs, and denies everything else unless opted in; see SandboxOptions for the managed tokens and why. Host-decreed and never baked by a shell build. Only meaningful for the iframe modes: opening popup or standalone with a sandbox set throws, because no containment can apply to a top-level window.
§sharedKey?:stringPre-shared key the v4 protocol binds the session to; at least 16 characters, never given with another protocol.
§url?:stringURL of the feature app to load.
§interface

UnresponsiveInfo

Context passed to an UnresponsivePolicy callback when a feature stops beating.
Properties
§displayMode:DisplayModeThe display mode the unresponsive feature was using.
§frame:UnresponsiveFrameWhether the feature's frame still exists; see UnresponsiveFrame.
§lastBeatAt:numberTimestamp (ms) of the last beat received, or null if none ever arrived.
§missedBeats:numberConsecutive missed beats that tripped the watchdog.
§interface

ViewportPayload

Payload of the reserved viewport control message: the exact pixel dimensions of the space the feature's frame occupies, reported by the host whenever the measured space changes (iframe modes only).
Properties
§height:numberUsable height in pixels.
§width:numberUsable width in pixels.

◆ Types

§type

DisplayModeMap

The display modes a shell is created from: mode name to mount function.
Only the mount functions handed in are reachable, so a shell that passes the modes its feature contract declares (as generated shells do) ships no code for the others. Pass builtInDisplayModes to support every mode.
type DisplayModeMap = Partial<Record<DisplayMode, DisplayModeMount>>
§type

DisplayModeMount

Mounts a feature for a single display mode and returns its MountResult.
type DisplayModeMount = (context: MountContext) => MountResult
§type

HeartbeatState

Liveness state of the connected feature, as judged by the watchdog.
  • healthy: beats are arriving within the expected budget.
  • unobservable: silence carries no information yet. Either a page is
hidden, so browser timer throttling makes the quiet meaningless and the watchdog pauses, or watching has just resumed and no beat has arrived to earn healthy back.
  • suspect: the pages are visible and the miss budget is exhausted; the
feature is probably unhealthy.
  • gone: the session is closed or destroyed (or not yet open).
type HeartbeatState = "healthy" | "unobservable" | "suspect" | "gone"
§type

BackdropBehavior

How the host reacts when the feature reports a pointer interaction on the dialog backdrop (the transparent area outside the feature's dialog box).
close (the default) treats the interaction as a close request and starts the polite teardown; event surfaces it as a dismiss event for the host consumer to handle; none ignores it.
type BackdropBehavior = "close" | "event" | "none"
§type

BoxPosition

Where a positioned box sits inside its available area: the dialog inner box within the full-viewport pane, or the popup window on the screen.
center (the default) centers on both axes; the compound values anchor to an edge or corner of the area.
type BoxPosition = "center" | "top-left" | "top-center" | "top-right" | "center-left" | "center-right" | "bottom-left" | "bottom-center" | "bottom-right"
§type

DismissSource

Where a hostee dismiss signal originated: a pointer interaction on the dialog backdrop, or the Escape key pressed inside the feature's document.
type DismissSource = "backdrop" | "escape"
§type

EventHandler

Handler invoked when a subscribed event fires.
type EventHandler = (data: unknown) => void
§type

FeaturePermission

A Permissions-Policy feature name the host can delegate to the feature frame.
Browsers deny powerful features (camera, fullscreen, clipboard, …) to cross-origin frames by default, so a feature that needs one only works when the host delegates it. The union lists the common names for editor completion; any string the browser understands is accepted.
type FeaturePermission = "accelerometer" | "autoplay" | "camera" | "clipboard-read" | "clipboard-write" | "display-capture" | "encrypted-media" | "fullscreen" | "gamepad" | "geolocation" | "gyroscope" | "magnetometer" | "microphone" | "midi" | "payment" | "picture-in-picture" | "publickey-credentials-get" | "screen-wake-lock" | "usb" | "web-share" | "xr-spatial-tracking" | string & { }
§type

RequestHandler

Answers one request type; may return the response value directly or a promise of it.
type RequestHandler = (data: unknown) => unknown
§type

SecurityProtocol

Union of the supported security envelope selectors.
none is the local default (opt-in security); production builds must pick v3 (ephemeral session keys) or v4 (session keys bound to a pre-shared key).
type SecurityProtocol = "none" | "v3" | "v4"
§type

UnresponsiveFrame

What the host can see of a silent feature's frame when the watchdog gives up on it.
  • gone: the frame provably no longer exists: its iframe was taken out of
the document, or its window was closed. The silence is final, and nothing the shell did caused it.
  • present: the frame is still there, so the silence is either a stall that
may end or a death the browser does not disclose. A frame whose renderer process the browser or the operating system killed reads present, because no web API reports that to the embedding page; only time tells the two apart.
type UnresponsiveFrame = "present" | "gone"
§type

UnresponsivePolicy

What the host does when a feature misses too many heartbeats while visible.
emit (the default) emits an error carrying { reason: 'unresponsive', missedBeats, lastBeatAt, displayMode, frame }; unmount also tears the feature down after emitting the same error; a callback takes over handling entirely with the UnresponsiveInfo. The policy runs once per suspect episode: a recovering beat returns the feature to healthy and re-arms it. Hidden pages and freshly resumed watching both read unobservable: silence is weak evidence until a beat earns healthy.
reopen (or a ReopenPolicy to tune it) emits the same error, then brings the feature back if its silence outlasts a grace period: the mount, crash placeholder included, is replaced by a fresh one opened with the same options, and a reopen event carrying { attempt, attempts, displayMode } announces each attempt. Nothing is reopened into a hidden page; the attempt waits for the page to be watched again, then allows a fresh grace. A reopened session that never completes its handshake counts as another death in the same episode. The policy stands down when the frame is gone (the feature is torn down instead, since the page or the visitor removed it), when the host calls close, destroy, or open itself, when the feature closes the session, and when a reopen fails in a way another attempt cannot mend (a refused window, a denied handshake, or a mount that throws).
type UnresponsivePolicy = "emit" | "unmount" | "reopen" | ReopenPolicy | (info: UnresponsiveInfo) => void

● Variables

§type

builtInDisplayModes

Every built-in display mode, mode name to mount function.
createShell composes a shell from this full map; generated shells import the individual mounts and compose only the modes their feature declared, so this map (and the modes it would drag in) stays out of their bundles.
§type

mountDialog

Mounts a feature as a full-viewport dialog pane layered above the host UI.
The pane is a single transparent iframe spanning the host viewport; the feature renders its dialog box inside it and the transparent remainder is the backdrop. It mounts hidden (inert to the user and the page) and is revealed once the session opens. Backdrop and in-frame Escape interactions are detected by the feature side and cross as dismiss signals the shell acts on per dialogBackdrop/closeOnEscape; an Escape pressed while the host document holds focus is handled here directly.
§type

mountEmbedded

Mounts a feature inline inside the host-provided container element.
By default the iframe fills the container's content box — measured before the iframe is inserted, so the announcement carries the container's own dimensions — and a reporter forwards every later change (with viewport-derived fallback dimensions while the container has none). When the merged options agree a fixed embedWidth/embedHeight, the iframe receives exactly those dimensions and the host application places the container so the feature fits. The frame mounts hidden and is revealed once the session opens.
§type

mountPopup

Mounts a feature in a separate, sized browser popup window.
The window opens at the agreed popupWidth/popupHeight (falling back to a viewport-derived size), placed on the screen per popupPosition (centered by default). Once open, the window is the browser's: the user may move and resize it freely, the feature's own window is its viewport (no viewport reports cross the boundary), and no sandbox or permissions delegation can apply to a top-level window. The window's title and chrome belong to the loaded document and the browser: the feature sets document.title; the host cannot.
§type

mountStandalone

Mounts a feature in a full standalone browser tab/window.
The simplest mode: the browser's normal new-tab behavior is sufficient, so no sizing or presentation coordination applies, only the ordinary session lifecycle over the opener relationship.
§type

DisplayMode

Supported ways a host can surface an embedded feature.
The host selects the mode; a feature declares which modes it supports in its feature.config.* display.modes, and the generated shell composes exactly those.