{"version":3,"sources":["../../src/host/declarative/interpreter.ts","../../src/plugin/clientPush.ts","../../src/plugin/hostIdentity.ts","../../src/plugin/PluginRealtimeContext.ts","../../src/plugin/ExtensionRuntimeProvider.tsx","../../src/mock-host/InMemoryMcpTransport.ts","../../src/mock-host/DeclarativeMockHost.tsx","../../src/mock-host/InMemoryBridgeTransport.ts","../../src/plugin/BridgeClientContext.ts","../../src/mock-host/WorkerMockHost.tsx"],"names":["createElement","createContext","useMemo","jsx","jsxs"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createElement, type ReactElement, type ReactNode } from \"react\";\nimport type { ComponentRegistry } from \"./registry\";\nimport type { SduiNode } from \"./types\";\n\n/**\n * Walk a parsed SDUI tree and render it as a React element by looking up each\n * node's `type` in the supplied {@link ComponentRegistry}.\n *\n * The interpreter is purely structural:\n *\n * - It owns no UI styling, layout, or data fetching.\n * - It never reads `node.props` — props are forwarded opaquely to the host\n *   component, which owns interpretation per primitive.\n * - It does not evaluate `node.bindings` — reactive rules are handled in a\n *   separate task (E2.S2). For v1 the interpreter passes through the static\n *   tree only.\n *\n * Each child is given a stable React `key` derived from its position so that\n * React's reconciler can identify list items across renders. The key is a\n * sibling-local index; the registry consumer is responsible for opting into a\n * stable identity if it has a domain-meaningful `props.key`.\n *\n * @throws Error when `node.type` is not present in the registry. This is the\n * fail-loud behaviour required by the closed v1 vocabulary — unknown\n * primitives must not silently degrade.\n */\nexport function interpret(node: SduiNode, registry: ComponentRegistry): ReactElement\n{\n    const Component = registry[node.type];\n    if (!Component)\n    {\n        throw new Error(`Unknown SDUI primitive: ${String(node.type)}`);\n    }\n\n    const children: ReactNode[] | undefined = node.children?.map(\n        (child, index) => interpretChild(child, registry, index),\n    );\n\n    return createElement(Component, { props: node.props }, children);\n}\n\nfunction interpretChild(node: SduiNode, registry: ComponentRegistry, index: number): ReactElement\n{\n    const Component = registry[node.type];\n    if (!Component)\n    {\n        throw new Error(`Unknown SDUI primitive: ${String(node.type)}`);\n    }\n\n    const children: ReactNode[] | undefined = node.children?.map(\n        (child, childIndex) => interpretChild(child, registry, childIndex),\n    );\n\n    return createElement(Component, { props: node.props, key: index }, children);\n}\n","import { createContext, useContext, useEffect, useRef } from \"react\";\n\n/**\n * A single client-push event delivered from the host to a plugin surface. The host\n * relays the plugin backend's `IClientPushPublisher` events over its realtime\n * channel (SignalR); the transport envelope's extension identity is bound by the\n * host at mount time, so the plugin sees only the event body.\n *\n * SCOPE + ORDERING: which channel/user/group an event concerns is carried INSIDE\n * `payloadJson` by the emitting plugin — the transport envelope intentionally has no\n * group field. Consumers therefore demultiplex + order by their own payload fields\n * (e.g. a per-channel sequence in the payload), NOT by {@link eventSequence}, which\n * is per-group at the host and would produce false gaps when multiple groups\n * multiplex over one connection.\n */\nexport interface ClientPushEvent\n{\n    /** Plugin-defined discriminator, e.g. `\"chatMessageReceived\"`. */\n    eventType: string;\n    /** Raw JSON payload authored by the plugin backend. */\n    payloadJson: string;\n    /**\n     * Host per-group monotonic sequence. Advisory only — do NOT use for\n     * cross-group gap detection (see the scope note above).\n     */\n    eventSequence: number;\n}\n\nexport interface ClientPushSubscribeOptions\n{\n    /**\n     * Opaque group names to enrol in (e.g. `\"chat:channel:{id}\"`). The host\n     * authorises each subscription via the plugin's `authorize-subscription` tool\n     * and enforces org/extension isolation — a plugin cannot subscribe outside its\n     * own extension + organisation.\n     */\n    groups: string[];\n    /** Called for each delivered (non-resync) event for the subscribed groups. */\n    onEvent: (event: ClientPushEvent) => void;\n    /**\n     * Called when the host signals a gap/resync for the subscribed groups (a\n     * dropped-event backpressure signal, or a reconnect). The consumer should\n     * re-fetch authoritative state (e.g. a delta/cold-load) rather than trusting\n     * incremental events.\n     */\n    onResync?: () => void;\n}\n\n/**\n * Host-provided channel for realtime server-push. The channel is already scoped to\n * the mounted surface's extension + organisation (bound by the host from the trusted\n * mount descriptor — a plugin CANNOT widen it), so {@link subscribe} takes only\n * opaque group names and returns an unsubscribe function.\n */\nexport interface ClientPushChannel\n{\n    subscribe(options: ClientPushSubscribeOptions): () => void;\n}\n\n/**\n * `null` = no host channel (standalone/mock, or a host that predates client-push) →\n * {@link useClientPushSubscription} is inert. Provided by\n * {@link ExtensionRuntimeProvider}'s optional `clientPush` prop.\n */\nexport const ClientPushContext = createContext<ClientPushChannel | null>(null);\n\nexport interface UseClientPushSubscriptionOptions\n{\n    /** Opaque groups to subscribe. Changing the SET re-subscribes; identity/order changes alone do not. */\n    groups: string[];\n    onEvent: (event: ClientPushEvent) => void;\n    onResync?: () => void;\n    /** Gate the subscription (e.g. until an id is known). Default `true`. */\n    enabled?: boolean;\n}\n\n/**\n * Subscribe a plugin surface to host client-push events for `groups`.\n *\n * Inert (no-op) when no host channel is present (standalone/mock), when `enabled` is\n * false, or when `groups` is empty. Re-subscribes when the group set changes and\n * unsubscribes on unmount. Callback identities are held in refs, so passing new\n * inline `onEvent`/`onResync` closures every render does NOT churn the subscription.\n */\nexport function useClientPushSubscription(options: UseClientPushSubscriptionOptions): void\n{\n    const { groups, onEvent, onResync, enabled = true } = options;\n    const channel = useContext(ClientPushContext);\n\n    const onEventRef = useRef(onEvent);\n    onEventRef.current = onEvent;\n    const onResyncRef = useRef(onResync);\n    onResyncRef.current = onResync;\n\n    // Normalise (dedupe + sort) so the effect re-runs only when the group SET actually\n    // changes — reordering the same groups, or passing a fresh array literal of the\n    // same set each render, must NOT churn the subscription.\n    const normalizedGroups = [...new Set(groups)].sort();\n    // JSON-encode (not space-join) so group names containing a delimiter can't alias\n    // distinct sets to the same key (e.g. [\"a b\"] vs [\"a\",\"b\"]).\n    const groupsKey = JSON.stringify(normalizedGroups);\n\n    useEffect(() =>\n    {\n        if (!channel || !enabled || normalizedGroups.length === 0)\n        {\n            return;\n        }\n        const unsubscribe = channel.subscribe({\n            groups: normalizedGroups,\n            onEvent: (event) => onEventRef.current(event),\n            onResync: () => onResyncRef.current?.(),\n        });\n        return unsubscribe;\n        // normalizedGroups is captured via groupsKey; callbacks via refs — intentionally excluded.\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [channel, enabled, groupsKey]);\n}\n","/**\n * Host identity/permission seam for platform-react plugin surfaces (WI 5108, F-AUTH-SEAM).\n *\n * Mirrors the clientPush seam: the host binds a HostIdentity to the mounted surface and\n * provides it via ExtensionRuntimeProvider's `identity` prop; the plugin reads it with\n * `useHostIdentity`. Inert (returns null) when no host provides the context — standalone /\n * mock, or a host that predates this feature.\n *\n * SECURITY: this is presentation/UX data. The plugin BACKEND re-authorises every MCP call\n * from the server session; `permission` is NOT an authorization boundary. The host forwards\n * ONLY the caller's grant for the surface's own extension (least-privilege), never the full set.\n */\nimport { createContext, useContext } from \"react\";\n\n/** Current-user identity for a mounted plugin surface, bound by the host. */\nexport interface HostIdentityUser\n{\n    id: string;\n    firstName: string;\n    lastName: string;\n    fullName: string;\n    isExternal: boolean;\n}\n\n/** One resolved permission grant (bitMask over the plugin's PermissionMask bits). */\nexport interface HostPermission\n{\n    groupCode: string;\n    bitMask: number;\n}\n\n/** Host-provided identity context for the mounted surface. */\nexport interface HostIdentity\n{\n    /** null while host auth is still loading (see isLoading) OR when unauthenticated. */\n    user: HostIdentityUser | null;\n    /** true while the host's /auth/user resolution is in flight — disambiguates loading from unauthenticated. */\n    isLoading: boolean;\n    /** The mounted surface's OWN grant only, or null when the user has no grant for this extension. */\n    permission: HostPermission | null;\n    /** The mounted surface's own extension groupCode, host-bound from the trusted manifest. */\n    extensionGroupCode: string;\n    /**\n     * The active organisation id for the mounted surface, or null while host auth is loading /\n     * unauthenticated. Host-bound from the SPA's active-organisation context. Surfaces that scope\n     * realtime subscriptions or org-keyed queries read this (e.g. the chat client-push gate); it is\n     * NOT a security token — the plugin backend derives org from the server session independently.\n     */\n    organisationId: string | null;\n}\n\n/**\n * `null` = no host channel (standalone/mock, or a host that predates the identity seam) →\n * {@link useHostIdentity} returns null and the plugin falls back to its deny-by-default path.\n * Provided by {@link ExtensionRuntimeProvider}'s optional `identity` prop.\n */\nexport const HostIdentityContext = createContext<HostIdentity | null>(null);\n\n/** Returns the host identity for the mounted surface, or null when no host context is present. */\nexport function useHostIdentity(): HostIdentity | null\n{\n    return useContext(HostIdentityContext);\n}\n","import { createContext, useContext } from \"react\";\n\n/**\n * A host-supplied realtime subscription source for a plugin.\n *\n * The SDK keeps this intentionally dumb — it only calls `source.subscribe`.\n * All SignalR wiring, extensionId filtering, and connection lifecycle management\n * live host-side (Task 6 in coreconnect-web). This lets the SDK be tested with\n * a simple fake source.\n */\nexport interface PluginRealtimeSource\n{\n    /**\n     * Subscribe to notifications whose `typeCode` matches the given value.\n     *\n     * @param typeCode  The application-level event type code to filter on\n     *                  (e.g. `\"HelpdeskTicketCreated\"`). Filtering by\n     *                  extensionId is the host's responsibility.\n     * @param handler   Called with the raw notification payload whenever a\n     *                  matching notification arrives.\n     * @returns         An unsubscribe function. Calling it removes this handler.\n     */\n    subscribe(typeCode: string, handler: (payload: unknown) => void): () => void;\n}\n\n/**\n * React context carrying the plugin's active {@link PluginRealtimeSource}.\n *\n * `null` is the explicit \"not provided\" sentinel — hooks must treat null as a\n * clean no-op (dev/mock/no-connection) rather than an error.\n *\n * Provided by {@link ExtensionRuntimeProvider} when the host passes a\n * `realtime` prop; consumed by `usePluginRealtimeSource()`.\n */\nexport const PluginRealtimeContext = createContext<PluginRealtimeSource | null>(null);\n\n/**\n * Returns the {@link PluginRealtimeSource} from context, or `null` when none\n * is wired (dev/mock environments, unit tests that only care about MCP).\n *\n * Hooks built on top of this (e.g. `usePluginRealtime` in `plugin-ui`) should\n * skip their subscription entirely when this returns `null`.\n */\nexport function usePluginRealtimeSource(): PluginRealtimeSource | null\n{\n    return useContext(PluginRealtimeContext);\n}\n","import { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport type { McpTransport } from \"./transport\";\nimport { ClientPushContext, type ClientPushChannel } from \"./clientPush\";\nimport { HostIdentityContext, type HostIdentity } from \"./hostIdentity\";\nimport { PluginRealtimeContext, type PluginRealtimeSource } from \"./PluginRealtimeContext\";\n\n/**\n * React context carrying the {@link McpTransport} the plugin should use to\n * reach the host. `null` is the explicit \"not provided\" sentinel so the hooks\n * can disambiguate from a transport that was provided but is incidentally\n * falsy in some other dimension.\n */\nconst ExtensionRuntimeContext = createContext<McpTransport | null>(null);\n\nexport interface ExtensionRuntimeProviderProps\n{\n    transport: McpTransport;\n    /**\n     * Optional host realtime channel consumed by {@link useClientPushSubscription}.\n     * Absent (or `null`) in standalone/mock hosts and hosts that predate client-push,\n     * in which case the hook is inert. The host binds this channel to the mounted\n     * surface's trusted extension + organisation identity.\n     */\n    clientPush?: ClientPushChannel | null;\n    /**\n     * Optional host identity/permission context consumed by {@link useHostIdentity}.\n     * Absent (or `null`) in standalone/mock hosts and hosts that predate this seam,\n     * in which case the hook is inert. The host binds this to the mounted surface's\n     * trusted extension identity and forwards only that extension's own grant.\n     */\n    identity?: HostIdentity | null;\n    /**\n     * Optional realtime subscription source supplied by the host.\n     *\n     * When provided, descendant components can call `usePluginRealtimeSource()`\n     * to obtain it and subscribe to push notifications. When omitted (dev/mock\n     * environments or plugins that don't need realtime), the context defaults\n     * to `null` and consumers no-op cleanly.\n     */\n    realtime?: PluginRealtimeSource;\n    children?: ReactNode;\n}\n\n/**\n * Wrap a plugin's React tree so descendant {@link useMcpResource} and\n * {@link useMcpTool} calls resolve a default transport without having to\n * thread it through every component.\n *\n * Hooks still accept a per-call `transport` override, which takes precedence\n * over the context value — useful for tests and for plugins that want to\n * shard work across multiple hosts.\n */\nexport function ExtensionRuntimeProvider({ transport, clientPush = null, identity = null, realtime, children }: ExtensionRuntimeProviderProps): ReactNode\n{\n    // Memoise so swapping `children` doesn't churn the context identity.\n    const value = useMemo(() => transport, [transport]);\n    const push = useMemo(() => clientPush, [clientPush]);\n    const id = useMemo(() => identity, [identity]);\n    const realtimeValue = useMemo(() => realtime ?? null, [realtime]);\n    return (\n        <ExtensionRuntimeContext.Provider value={value}>\n            <ClientPushContext.Provider value={push}>\n                <HostIdentityContext.Provider value={id}>\n                    <PluginRealtimeContext.Provider value={realtimeValue}>\n                        {children}\n                    </PluginRealtimeContext.Provider>\n                </HostIdentityContext.Provider>\n            </ClientPushContext.Provider>\n        </ExtensionRuntimeContext.Provider>\n    );\n}\n\n/**\n * Internal helper used by the hooks. Returns the explicit override when\n * supplied, otherwise falls back to the context. Throws a deterministic\n * error if neither is available so misconfiguration fails loudly at the\n * first render rather than producing silent no-ops.\n */\nexport function useExtensionRuntimeTransport(override?: McpTransport): McpTransport\n{\n    const fromContext = useContext(ExtensionRuntimeContext);\n    const resolved = override ?? fromContext;\n    if (!resolved)\n    {\n        throw new Error(\n            \"No McpTransport available. Wrap your plugin in <ExtensionRuntimeProvider transport={...}> \"\n            + \"or pass `transport` directly to the hook.\",\n        );\n    }\n    return resolved;\n}\n\n/**\n * Non-throwing variant of {@link useExtensionRuntimeTransport}: returns the\n * resolved transport, or `null` when neither an override nor a provider is\n * present. For optional, fire-and-forget consumers (e.g. an auto-injected\n * org-format sync) that must degrade to a no-op rather than crash a surface that\n * renders without a host transport — a standalone/mock-mode run or an isolated\n * unit test.\n */\nexport function useOptionalExtensionRuntimeTransport(override?: McpTransport): McpTransport | null\n{\n    const fromContext = useContext(ExtensionRuntimeContext);\n    return override ?? fromContext ?? null;\n}\n","import type { McpTransport, UploadDocumentMeta, UploadDocumentResult } from \"../plugin/transport\";\nimport type { SduiNode } from \"../host/declarative/types\";\n\n/**\n * Handler for a mocked tool invocation. The handler receives the request\n * payload supplied by the caller and may return synchronously or\n * asynchronously. The result is forwarded verbatim through\n * {@link InMemoryMcpTransport.invokeTool}.\n */\nexport type MockToolHandler = (args: unknown) => Promise<unknown> | unknown;\n\n/**\n * Handler for a mocked document upload. Receives the {@link UploadDocumentMeta}\n * and the transferred bytes; returns the {@link UploadDocumentResult} the FE\n * hook resolves. Optional — when omitted, the transport returns a synthetic\n * result echoing the metadata so a standalone plugin can exercise the flow.\n */\nexport type MockUploadHandler = (\n    meta: UploadDocumentMeta,\n    buffer: ArrayBuffer,\n    signal?: AbortSignal,\n) => Promise<UploadDocumentResult> | UploadDocumentResult;\n\n/**\n * An in-memory {@link McpTransport} backed by a `{ resources, tools }` map.\n *\n * Used by {@link DeclarativeMockHost} so plugin authors can run their app\n * standalone for local development without a real host. The transport mirrors\n * the runtime contract exactly:\n *\n * - `getResource(uri)` resolves a `SduiNode` keyed by URI, or rejects with a\n *   descriptive error if the URI is not registered.\n * - `invokeTool(name, args)` dispatches to a synchronous or async handler,\n *   or rejects if the tool name is unknown.\n *\n * `getResource` / `invokeTool` intentionally do NOT honour the supplied\n * `AbortSignal` — mock handlers are synchronous from the caller's perspective\n * and there is no in-flight network call to abort. Hooks still work correctly\n * because they treat the `AbortSignal` as a one-way notification, not a\n * contract. `uploadDocument` DOES observe the signal (rejecting with an\n * `AbortError`): its contract mandates it, a mock upload handler may be\n * genuinely async, and dev-host flows need to simulate upload cancellation.\n */\nexport class InMemoryMcpTransport implements McpTransport\n{\n    private readonly resources: Record<string, SduiNode>;\n    private readonly tools: Record<string, MockToolHandler>;\n    private readonly uploadHandler?: MockUploadHandler;\n\n    public constructor(\n        resources: Record<string, SduiNode>,\n        tools: Record<string, MockToolHandler> = {},\n        uploadHandler?: MockUploadHandler,\n    )\n    {\n        this.resources = resources;\n        this.tools = tools;\n        this.uploadHandler = uploadHandler;\n    }\n\n    public async getResource<T>(uri: string): Promise<{ uri: string; data: T }>\n    {\n        const data = this.resources[uri];\n        if (data === undefined)\n        {\n            throw new Error(`Mock resource not found: ${uri}`);\n        }\n        return { uri, data: data as unknown as T };\n    }\n\n    public async invokeTool<TReq, TRes>(name: string, args: TReq): Promise<TRes>\n    {\n        const tool = this.tools[name];\n        if (!tool)\n        {\n            throw new Error(`Mock tool not found: ${name}`);\n        }\n        const result = await tool(args);\n        return result as TRes;\n    }\n\n    public async uploadDocument(\n        meta: UploadDocumentMeta,\n        buffer: ArrayBuffer,\n        signal?: AbortSignal,\n    ): Promise<UploadDocumentResult>\n    {\n        if (signal?.aborted === true)\n        {\n            throw abortError();\n        }\n        const produce = (): Promise<UploadDocumentResult> | UploadDocumentResult =>\n        {\n            if (this.uploadHandler)\n            {\n                return this.uploadHandler(meta, buffer, signal);\n            }\n            // Synthetic default: echo the metadata with a generated id so a\n            // standalone plugin can drive the upload flow without a real host.\n            return {\n                storedDocumentId: `mock-${meta.entityName}-${meta.fileName}`,\n                fileName: meta.fileName,\n                contentType: meta.contentType,\n                sizeBytes: buffer.byteLength,\n            };\n        };\n        if (signal === undefined)\n        {\n            return produce();\n        }\n        // Honour cancellation for an async upload handler: reject as soon as the\n        // signal fires rather than waiting for the handler to settle.\n        return new Promise<UploadDocumentResult>((resolve, reject) =>\n        {\n            const onAbort = (): void => reject(abortError());\n            signal.addEventListener(\"abort\", onAbort, { once: true });\n            Promise.resolve(produce()).then(\n                (result) =>\n                {\n                    signal.removeEventListener(\"abort\", onAbort);\n                    resolve(result);\n                },\n                (err: unknown) =>\n                {\n                    signal.removeEventListener(\"abort\", onAbort);\n                    reject(err);\n                },\n            );\n        });\n    }\n}\n\n/** An `AbortError`-shaped `Error`, matching native fetch cancellation. */\nfunction abortError(): Error\n{\n    const e = new Error(\"Aborted\");\n    e.name = \"AbortError\";\n    return e;\n}\n","import { useMemo, type ReactElement, type ReactNode } from \"react\";\nimport { interpret } from \"../host/declarative/interpreter\";\nimport type { ComponentRegistry } from \"../host/declarative/registry\";\nimport type { SduiNode } from \"../host/declarative/types\";\nimport { ExtensionRuntimeProvider } from \"../plugin/ExtensionRuntimeProvider\";\nimport { InMemoryMcpTransport, type MockToolHandler } from \"./InMemoryMcpTransport\";\n\n/**\n * Props for {@link DeclarativeMockHost}.\n *\n * Plugin authors `npm link` the runtime and render `<DeclarativeMockHost>` in\n * their local dev app to exercise the same declarative pipeline the real host\n * uses, but backed by in-memory fakes instead of the platform.\n */\nexport interface DeclarativeMockHostProps\n{\n    /**\n     * In-memory resource map. Keys are MCP resource URIs; values are SDUI trees\n     * that {@link interpret} will render against the supplied registry.\n     */\n    resources: Record<string, SduiNode>;\n\n    /**\n     * In-memory tool map. Keys are MCP tool names; values are handlers invoked\n     * when a child component calls `useMcpTool(name).invoke(args)`.\n     *\n     * Handlers may be sync or async — the transport awaits the result before\n     * forwarding it to the caller.\n     */\n    tools?: Record<string, MockToolHandler>;\n\n    /**\n     * URI of the resource rendered as the host's default tree. If the URI is\n     * not present in `resources` the host renders the supplied `children`\n     * instead — useful for stubs that exercise only tool invocations.\n     */\n    defaultResourceUri: string;\n\n    /**\n     * The same primitive → component registry the real host uses. Passed\n     * verbatim to {@link interpret}; the mock host owns no UI of its own.\n     */\n    registry: ComponentRegistry;\n\n    /**\n     * Optional fallback content rendered when `defaultResourceUri` does not\n     * resolve to a registered resource. Children also have access to the wired\n     * transport via {@link ExtensionRuntimeProvider}, so they can invoke\n     * mocked tools and resources directly through the React hooks.\n     */\n    children?: ReactNode;\n}\n\n/**\n * In-memory host for declarative (Contract A) plugin local-dev.\n *\n * Renders a plugin's SDUI resource against the supplied registry and wires an\n * {@link InMemoryMcpTransport} into context so descendant components that use\n * `useMcpResource` / `useMcpTool` resolve against the same fakes.\n *\n * The host is intentionally minimal: it does not simulate permissions, theme\n * propagation, or capability tokens. Its purpose is to exercise the\n * declarative pipeline end-to-end against deterministic in-memory data so\n * plugin authors can iterate without standing up the real platform.\n */\nexport function DeclarativeMockHost(props: DeclarativeMockHostProps): ReactElement\n{\n    const { resources, tools, defaultResourceUri, registry, children } = props;\n\n    // Memoise the transport so React doesn't churn the context identity every\n    // render — re-rendering this host with stable inputs must not abort\n    // in-flight hook calls.\n    const transport = useMemo(\n        () => new InMemoryMcpTransport(resources, tools ?? {}),\n        [resources, tools],\n    );\n\n    const tree = resources[defaultResourceUri];\n    const renderedTree: ReactNode = tree ? interpret(tree, registry) : children;\n\n    return (\n        <ExtensionRuntimeProvider transport={transport}>\n            {renderedTree}\n            {tree ? children : null}\n        </ExtensionRuntimeProvider>\n    );\n}\n","/**\n * In-realm bridge transport for plugin local-dev and contract testing.\n *\n * Calling `pushTheme(...)`, `pushLocale(...)`, etc. invokes the registered\n * subscriber callbacks **synchronously** — no serialisation, no port. This\n * lets Vitest + React Testing Library drive bridge state changes with `act()`\n * without a real MessageChannel.\n *\n * In production, the bridge client is `createPortBridgeClient` backed by a\n * real MessagePort. `InMemoryBridgeTransport` is the dev/test equivalent:\n * both expose the same `PortBridgeClient`-compatible subscriber API on the\n * consumer side, but `InMemoryBridgeTransport` also exposes the push-side\n * and the `onChromeRequest` handler for test assertions.\n */\nimport type { PortBridgeClient, ThemePayload, LocalePayload, DensityPayload, A11yPayload, NavPayload, SessionTokenPayload } from \"../plugin/bridge-client\";\n\ntype ChromeRequestHandler = (\n  action: string,\n  payload: Record<string, unknown>,\n) => Promise<unknown> | unknown;\n\nexport class InMemoryBridgeTransport implements PortBridgeClient {\n  private _themeCb:   ((p: ThemePayload)   => void) | undefined;\n  private _localeCb:  ((p: LocalePayload)  => void) | undefined;\n  private _densityCb: ((p: DensityPayload) => void) | undefined;\n  private _a11yCb:    ((p: A11yPayload)    => void) | undefined;\n  private _navCb:     ((p: NavPayload)     => void) | undefined;\n  private _tokenCb:   ((p: SessionTokenPayload) => void) | undefined;\n  private _chromeHandler: ChromeRequestHandler | undefined;\n\n  // ── PortBridgeClient subscriber interface ──────────────────────────────────\n\n  onTheme(cb: (p: ThemePayload) => void):   void { this._themeCb   = cb; }\n  onLocale(cb: (p: LocalePayload) => void): void { this._localeCb  = cb; }\n  onDensity(cb: (p: DensityPayload) => void): void { this._densityCb = cb; }\n  onA11y(cb: (p: A11yPayload) => void):     void { this._a11yCb    = cb; }\n  onNav(cb: (p: NavPayload) => void):        void { this._navCb     = cb; }\n  onSessionToken(cb: (p: SessionTokenPayload) => void): void { this._tokenCb = cb; }\n\n  requestChrome(action: string, payload: Record<string, unknown>): Promise<unknown> {\n    return this.simulateChromeRequest(action, payload);\n  }\n\n  announceA11y(_message: string, _politeness: \"polite\" | \"assertive\"): void {\n    // No-op in the mock — tests assert via `onChromeRequest` or inspect DOM.\n  }\n\n  // ── Test / dev control surface ─────────────────────────────────────────────\n\n  /** Push a theme update to the registered subscriber (synchronous). */\n  pushTheme(payload: ThemePayload):     void { this._themeCb?.(payload);   }\n  /** Push a locale update to the registered subscriber. */\n  pushLocale(payload: LocalePayload):   void { this._localeCb?.(payload);  }\n  /** Push a density update. */\n  pushDensity(payload: DensityPayload): void { this._densityCb?.(payload); }\n  /** Push a11y preference changes. */\n  pushA11y(payload: A11yPayload):       void { this._a11yCb?.(payload);    }\n  /** Push a nav state update. */\n  pushNav(payload: NavPayload):          void { this._navCb?.(payload);     }\n  /** Push a frontend-session token. */\n  pushSessionToken(payload: SessionTokenPayload): void { this._tokenCb?.(payload); }\n\n  /**\n   * Register a handler for plugin→host chrome requests (toast, confirm, etc).\n   * Called by `requestChrome` and by `simulateChromeRequest`.\n   */\n  onChromeRequest(handler: ChromeRequestHandler): void {\n    this._chromeHandler = handler;\n  }\n\n  /**\n   * Programmatically send a chrome request as if a plugin component called\n   * `PortBridgeClient.requestChrome(...)`. Useful for test assertions.\n   */\n  async simulateChromeRequest(action: string, payload: Record<string, unknown>): Promise<unknown> {\n    if (this._chromeHandler === undefined) {\n      return null;\n    }\n    return this._chromeHandler(action, payload);\n  }\n}\n","import { createContext, useContext } from \"react\";\nimport type { PortBridgeClient } from \"./bridge-client\";\n\n/**\n * React context carrying the plugin's active {@link PortBridgeClient}.\n * Provided by the host mount (WorkerMockHost in dev, real bridge in production)\n * and consumed by `useBridgeTheme`, `useBridgeLocale`, and the `plugin-ui` hooks.\n */\nexport const BridgeClientContext = createContext<PortBridgeClient | null>(null);\n\n/**\n * Returns the bridge client from context, throwing a clear error when missing.\n * Used by the `useBridge*` hooks to fail loudly on misconfiguration.\n */\nexport function useBridgeClient(): PortBridgeClient {\n  const client = useContext(BridgeClientContext);\n  if (client === null) {\n    throw new Error(\n      \"No PortBridgeClient available. Wrap your plugin in <BridgeClientProvider> \"\n      + \"or ensure the host mount wires a BridgeClientContext.Provider.\",\n    );\n  }\n  return client;\n}\n","/**\n * Mock host for plugins that use bridge hooks (`useBridgeTheme`,\n * `useBridgeLocale`, `useBridgeA11y`, etc.) during local-dev or contract\n * testing.\n *\n * Wraps {@link DeclarativeMockHost} and wires a {@link BridgeClientContext}\n * provider so any descendant bridge hook resolves against the supplied\n * `bridgeTransport` instead of throwing \"no bridge client available\".\n *\n * For tests where bridge state must be driven externally (push a new theme,\n * assert that a component re-renders), pass an {@link InMemoryBridgeTransport}\n * instance and call `transport.pushTheme(...)` wrapped in `act()`.\n */\nimport { type ReactElement } from \"react\";\nimport { DeclarativeMockHost, type DeclarativeMockHostProps } from \"./DeclarativeMockHost\";\nimport { BridgeClientContext } from \"../plugin/BridgeClientContext\";\nimport type { PortBridgeClient } from \"../plugin/bridge-client\";\n\nexport interface WorkerMockHostProps extends DeclarativeMockHostProps {\n  /**\n   * The bridge transport to wire into context. Pass an\n   * {@link InMemoryBridgeTransport} for tests; pass a\n   * `createPortBridgeClient(port)` instance for postMessage integration tests.\n   */\n  bridgeTransport: PortBridgeClient;\n}\n\n/**\n * Mock host that combines the SDUI declarative pipeline with bridge context.\n *\n * Rendering contract (same as DeclarativeMockHost):\n * - `defaultResourceUri` present in `resources` → renders the SDUI tree.\n * - URI absent → renders `children` instead (tool-invocation stubs, etc).\n *\n * Bridge contract:\n * - All `useBridgeTheme`, `useBridgeLocale`, etc. hooks in the subtree resolve\n *   against `bridgeTransport`.\n */\nexport function WorkerMockHost(props: WorkerMockHostProps): ReactElement {\n  const { bridgeTransport, ...declarativeProps } = props;\n\n  return (\n    <BridgeClientContext.Provider value={bridgeTransport}>\n      <DeclarativeMockHost {...declarativeProps} />\n    </BridgeClientContext.Provider>\n  );\n}\n"]}