import { ReactElement } from 'react'; import { C as ComponentRegistry } from '../registry-DpCx_LxF.js'; export { P as PrimitiveProps } from '../registry-DpCx_LxF.js'; import { SduiNode } from '@ethisyscore/protocol'; export { PrimitiveName, SduiNode } from '@ethisyscore/protocol'; import { M as McpHttpClient, f as VersionSkew } from '../transport-BuX8gzq1.js'; export { C as CAPABILITY_REJECTION_REASON_HEADER, D as DEFAULT_MAX_CONCURRENT_MCP_REQUESTS, E as EXTENSION_ACTIVE_VERSION_HEADER, a as EXTENSION_VERSION_HEADER, I as InputEventPayload, b as McpHttpRequest, c as McpHttpResponse, d as McpResponseMetadata, e as McpUploadRequest, V as VERSION_MISMATCH_REASON, g as VersionSkewSource, W as WORKER_TRANSPORT_PROTOCOL, h as WorkerCtor, i as WorkerHandshakePayload, j as WorkerLike, k as WorkerRemoteDomTransport, l as WorkerRemoteDomTransportOptions, m as detectVersionSkew } from '../transport-BuX8gzq1.js'; export { RemoteReceiver, RemoteReceiverComment, RemoteReceiverElement, RemoteReceiverNode, RemoteReceiverParent, RemoteReceiverRoot, RemoteReceiverText } from '@remote-dom/core/receivers'; export { RemoteComponentRendererMap, RemoteComponentRendererProps, RemoteRootRenderer, RemoteRootRendererProps, createRemoteComponentRenderer } from '@remote-dom/react/host'; export { M as MCP_ERROR_CODES, a as McpErrorCode, b as McpToolError, c as classifyHostError, d as classifyHostResponse, i as isMcpErrorCode, e as isMcpToolError, f as isRetryableMcpErrorCode, m as mcpErrorCodeFromHttpStatus } from '../mcp-error-CCZQd8Xl.js'; import '../bridge-envelopes-DA6vxbyb.js'; /** * Resolution context for `var` references. Keys are looked up via dot-path * traversal; missing paths resolve to `undefined`. */ type EvaluatorContext = Record; /** * Evaluate a JsonLogic expression in the closed operator subset against the * supplied context. * * @param expr The expression tree. Scalars (`string | number | boolean | null`) * evaluate to themselves; arrays map element-wise; operator * objects must contain exactly one key from * {@link KNOWN_OPERATORS}. * @param context The context bag for `var` resolution. Dot-paths traverse * nested objects. * * @throws Error if `expr` contains an unknown operator, a function-typed value, * an operator object with a number of keys other than one, or any * other unsupported shape (e.g. `undefined`, `Symbol`, `bigint`). */ declare function evaluate(expr: unknown, context: EvaluatorContext): unknown; /** * Walk a parsed SDUI tree and render it as a React element by looking up each * node's `type` in the supplied {@link ComponentRegistry}. * * The interpreter is purely structural: * * - It owns no UI styling, layout, or data fetching. * - It never reads `node.props` — props are forwarded opaquely to the host * component, which owns interpretation per primitive. * - It does not evaluate `node.bindings` — reactive rules are handled in a * separate task (E2.S2). For v1 the interpreter passes through the static * tree only. * * Each child is given a stable React `key` derived from its position so that * React's reconciler can identify list items across renders. The key is a * sibling-local index; the registry consumer is responsible for opting into a * stable identity if it has a domain-meaningful `props.key`. * * @throws Error when `node.type` is not present in the registry. This is the * fail-loud behaviour required by the closed v1 vocabulary — unknown * primitives must not silently degrade. */ declare function interpret(node: SduiNode, registry: ComponentRegistry): ReactElement; /** * Semantic component registry for Contract B (worker remote-runtime) * extensions. * * Worker-side plugin code references UI primitives by name — e.g. * ``, ``, ``. The host owns rendering: * a {@link SemanticComponentRegistry} maps each name to a concrete component * type, and the Remote DOM host receiver consults the registry on every * worker-emitted mutation. The closed primitive vocabulary {@link CONTRACT_B_PRIMITIVES} * is intentionally narrow — adding a primitive is a coordinated host change so * plugins cannot extend the contract ad hoc. * * **Alignment with SDUI:** Where the Contract B name overlaps with the * `@ethisyscore/protocol` SDUI primitive vocabulary (`DataTable`, `Form`), the * names are identical to avoid synonym drift. SDUI's `Action` is intentionally * NOT reused — Contract B's interactive primitive is named `Button` so the * worker authoring API stays close to React DOM idioms. The two vocabularies * may converge as they evolve, but the host registries (Contract A SDUI vs * Contract B Semantic) remain distinct surfaces today. * * The registry is renderer-agnostic — the generic `TComponent` parameter is * whatever component type the host supplies (React `ComponentType` in * production; plain objects in tests). This keeps the registry free of a * React peer-dep requirement and lets tests assert behaviour without rendering. */ /** * The mandatory Contract B (worker remote-runtime) semantic primitive vocabulary. * * Adding a primitive requires a coordinated host change + protocol bump — * plugins cannot extend this set. Each name has a host-supplied concrete * component (gogo-ui in production); the registry enforces 1:1 substitution. */ declare const CONTRACT_B_PRIMITIVES: readonly ["Button", "DataTable", "Form", "EntityPicker", "CommandBar", "Drawer", "Modal", "CanvasSurface", "WebGLSurface", "Card", "Tabs", "Select", "Alert"]; /** * Union type of the Contract B semantic primitive names — exported so callers * (tests, hosts) get full IntelliSense and compile-time exhaustiveness checks. */ type SemanticPrimitiveName = typeof CONTRACT_B_PRIMITIVES[number]; /** * Map of semantic primitive name → concrete component for Contract B * extensions. * * Construction is open-ended (callers register primitives one at a time) or * eager via {@link SemanticComponentRegistry.fromMap} which forces a complete * map up front and aborts if any primitive is missing. * * @typeParam TComponent - The host's concrete component shape. React hosts pass * `ComponentType

`; tests pass any tagged object. */ declare class SemanticComponentRegistry { private readonly entries; /** * Register a primitive → component binding. * * Throws if `name` is not in {@link CONTRACT_B_PRIMITIVES} (drift prevention) * or if `name` is already registered (silent-override prevention). */ register(name: SemanticPrimitiveName, component: TComponent): void; /** * Resolve a primitive name to its concrete component. Throws if the name is * outside the Contract B vocabulary OR if the registry has no binding — * silent fallthrough would let typos render blank components, which is * worse than a fast failure for plugin authors. */ resolve(name: SemanticPrimitiveName): TComponent; /** * Cheap presence probe — never throws. Use this when callers want to * fall back to a default component instead of erroring. */ has(name: SemanticPrimitiveName): boolean; /** * Sorted list of registered primitive names. Stable output makes registry * diagnostics and snapshot tests deterministic. */ listRegistered(): SemanticPrimitiveName[]; /** * Build a registry from a complete `Record` map. * * The `Record` type forces TypeScript to refuse incomplete literals at * compile time, AND we re-check at runtime so callers who type-erase the * record (e.g., via `as any`) still get a fast failure. */ static fromMap(map: Record): SemanticComponentRegistry; } /** * OffscreenCanvas transfer helpers + pointer/keyboard coalescing for * Contract B (worker remote-runtime) extensions. * * **Architecture.** Hosts that expose a {@link import("./component-registry").SemanticComponentRegistry}'s * `CanvasSurface` / `WebGLSurface` primitive create a host-side ``, * transfer control to the worker via {@link createOffscreenCanvasTransfer}, and * the plugin (running inside the worker realm) calls `offscreen.getContext()` * to render. The `` is owned by the host DOM tree (so layout, theming, * and accessibility properties stay on the host side) but every pixel is * produced inside the worker — no per-frame `postMessage` cost, no main-thread * blocking. * * **Pointer / keyboard delivery.** Input events still originate on the host * ``. To avoid spraying the worker port with per-event traffic the * host should wrap pointer-move / wheel / scroll callbacks in * {@link createInputEventCoalescer}, matching the trailing-edge coalescer * already documented in {@link import("./transport").WorkerRemoteDomTransport} * (default 16ms ≈ one rAF). Keyboard events MUST use `discrete: true` — * coalescing them would drop characters. * * **Security.** No capability token, no MCP traffic, and no host-only globals * cross via the offscreen transfer. The transfer envelope is plain JSON with a * single `OffscreenCanvas` handle in the transfer list — the worker side * receives an opaque drawing surface and nothing else. */ /** * Default trailing-edge coalesce window for pointer / wheel / scroll events, * in milliseconds. 16ms ≈ one animation frame at 60Hz. Matches the default * documented in {@link import("./transport").WorkerRemoteDomTransport}. */ declare const DEFAULT_OFFSCREEN_COALESCE_MS = 16; /** * Host → worker envelope shape for an `OffscreenCanvas` transfer. Stable wire * contract — the worker side decodes by `type` and uses `surfaceId` to bind * the offscreen to the right host slot when a plugin renders multiple * canvases concurrently. */ interface OffscreenTransferMessage { type: "ethisys:offscreen:transfer"; surfaceId: string; width: number; height: number; offscreen: OffscreenCanvas; } /** * Construction options for {@link createOffscreenCanvasTransfer}. */ interface OffscreenCanvasTransferOptions { /** * The host-owned `` element. Its `width` / `height` are captured * before transfer so the worker sees the intended pixel dimensions even if * the host element resizes later (host should send a separate resize * message in that case). */ canvas: HTMLCanvasElement; /** * Stable identifier the worker uses to route the offscreen to the matching * surface slot. Typically the SDUI node id of the `CanvasSurface` / * `WebGLSurface` primitive that the worker will draw into. */ surfaceId: string; /** * Host-side postMessage function — typically the * {@link import("./transport").WorkerRemoteDomTransport}'s port-side * `postMessage`. Tests inject a spy. */ postMessage(message: OffscreenTransferMessage, transfer: Transferable[]): void; } /** * Successful transfer result. The caller retains a reference to the * `OffscreenCanvas` only for observability (e.g., to wire to the worker's * reply handshake) — direct rendering from the host side is forbidden because * control has already been transferred and any host-side draw call would * throw. */ interface OffscreenCanvasTransferResult { offscreen: OffscreenCanvas; } /** * Transfer control of a host-owned `` to the worker. * * Throws if: * - the environment lacks `HTMLCanvasElement.prototype.transferControlToOffscreen` * (the helper does NOT silently fall back — Contract B clients must require * the API and surface a clear error in unsupported browsers); * - the same canvas has already had control transferred (idempotency is the * caller's responsibility — re-transferring would throw in the browser * anyway, but we raise a clearer, diagnosable message ahead of that). */ declare function createOffscreenCanvasTransfer(options: OffscreenCanvasTransferOptions): OffscreenCanvasTransferResult; /** * Construction options for {@link createInputEventCoalescer}. */ interface InputEventCoalescerOptions { /** * Coalesce window in milliseconds. Ignored when {@link discrete} is true. * Defaults to {@link DEFAULT_OFFSCREEN_COALESCE_MS} (16ms — one rAF). */ coalesceMs?: number; /** * When `true`, forward every payload synchronously without coalescing. * Use this for keyboard events — coalescing would drop characters by * collapsing multiple key strokes into one trailing delivery. */ discrete?: boolean; } /** * Build a trailing-edge input-event coalescer. The pattern matches * {@link import("./transport").WorkerRemoteDomTransport.createCoalescer} — * factored out here so OffscreenCanvas callers don't need to construct a full * transport just to coalesce pointer-move events. * * Trailing-edge semantics: every burst within a coalesce window collapses to * a single delivery carrying the **last** payload seen in the window. A new * payload arriving after a window has flushed starts a fresh window. */ declare function createInputEventCoalescer(sink: (payload: T) => void, options?: InputEventCoalescerOptions): (payload: T) => void; declare const IFRAME_BRIDGE_PROTOCOL = "ethisys.iframe.bridge.v1"; /** Construction options for {@link IframeBridgeTransport}. */ interface IframeBridgeTransportOptions { /** The cross-origin iframe's `contentWindow` (host never touches its document). */ frameWindow: Window; /** EXACT plugin origin; every postMessage uses this as `targetOrigin`, never `"*"`. */ targetOrigin: string; /** * Mints (or returns a cached) capability token. Resolved per outbound MCP HTTP * call on the HOST side only — the token NEVER crosses to the plugin frame. */ capabilityToken: () => Promise; /** Host-side MCP HTTP client; carries the capability token on the outbound call. */ mcpClient: McpHttpClient; /** * The extension version this surface mounted at. Enables proactive skew detection — a successful * response stamped with a different version means the install moved while the surface is still * working, so it can remount before anything fails. */ mountedExtensionVersion?: string; /** * Called at most once, when this surface is found to be running against a version that is no * longer installed. The host remounts the surface; the transport does not retry, because the * pending call belongs to a page about to be replaced. */ onVersionSkew?: (skew: VersionSkew) => void; /** Bounded in-flight MCP requests before back-pressure. Default 8. */ maxConcurrentMcpRequests?: number; /** Max inbound port messages per rolling second before drop. Default 64. */ maxMessagesPerSecond?: number; /** Optional sink for dropped-message security telemetry (bad schema / nonce / rate). */ onSecurityEvent?: (event: IframeBridgeSecurityEvent) => void; } /** Reason an inbound message was dropped. */ type IframeBridgeSecurityEvent = { reason: "schema-invalid"; } | { reason: "nonce-mismatch"; } | { reason: "rate-limit-exceeded"; type: string; }; /** Handshake payload posted to the frame (carries the nonce + transferred port). */ interface IframeHandshakePayload { readonly type: "ethisys:iframe:handshake"; readonly protocol: string; readonly nonce: string; } /** * Host-side cross-origin iframe bridge. Construct, then call {@link connect} to * post the handshake (transferring one end of a fresh `MessageChannel`). The * plugin frame must echo the handshake `nonce` on every port message. */ declare class IframeBridgeTransport { private readonly frameWindowRef; private readonly targetOrigin; private readonly capabilityTokenProvider; private readonly mcpClient; private readonly maxConcurrentMcpRequests; private readonly maxMessagesPerSecond; private readonly onSecurityEvent; private readonly mountedExtensionVersion; private readonly onVersionSkew; private versionSkewReported; private readonly abortController; private hostPort; private framePort; private nonce; private eventConsumer; private inFlightMcpRequests; private windowTimestamps; private connected; private disposed; constructor(options: IframeBridgeTransportOptions); /** * Post the handshake to the frame. Idempotent — only the FIRST call mints the * nonce, creates the channel, wires the host port, and transfers the frame * port. The handshake uses the exact `targetOrigin` (never `"*"`). */ connect(): void; /** * The per-mount handshake nonce (set after {@link connect}, `undefined` before * connect / after dispose). The host mount reads this to validate inbound * cross-origin `window`-level messages (which carry no MessagePort identity). */ get connectedNonce(): string | undefined; /** Register a consumer for inbound plugin→host `ethisys:event` envelopes. */ onEvent(consumer: (name: string, payload: unknown) => void): void; /** * Push a host→plugin envelope (theme/nav/chrome). Always uses the exact * `targetOrigin` (never `"*"`) and stamps the handshake nonce. */ postOutbound(envelope: Record): void; /** Tear down. Idempotent. Does NOT remove the iframe (the host mount owns it). */ dispose(): void; private handlePortMessage; private isRateLimited; private dispatchMcp; private handleInvokeTool; /** * Raises the skew callback the first time this surface is seen to be running against a version * that is no longer installed. Latched: a page usually has several MCP calls in flight and after * an upgrade every one of them reports the same skew, so firing per response would ask the host * to remount the same surface repeatedly and each remount would tear down the last. * * The callback is host code and may throw. That must neither escape into the MCP handler's catch * (replying `ok: false` for a call that succeeded) nor consume the latch (leaving the surface with * no response and no further chance of being told to remount). */ private reportVersionSkew; private handleGetResource; private replyError; /** Best-effort reply over the host port; tolerates post-dispose races. */ private safePostMessage; } export { CONTRACT_B_PRIMITIVES, ComponentRegistry, DEFAULT_OFFSCREEN_COALESCE_MS, type EvaluatorContext, IFRAME_BRIDGE_PROTOCOL, type IframeBridgeSecurityEvent, IframeBridgeTransport, type IframeBridgeTransportOptions, type IframeHandshakePayload, type InputEventCoalescerOptions, McpHttpClient, type OffscreenCanvasTransferOptions, type OffscreenCanvasTransferResult, type OffscreenTransferMessage, SemanticComponentRegistry, type SemanticPrimitiveName, VersionSkew, createInputEventCoalescer, createOffscreenCanvasTransfer, evaluate, interpret };