@ethisyscore/extension-runtime 1.70.0 → 1.71.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/host/index.cjs +129 -7
- package/dist/host/index.cjs.map +1 -1
- package/dist/host/index.d.cts +3 -2
- package/dist/host/index.d.ts +3 -2
- package/dist/host/index.js +122 -8
- package/dist/host/index.js.map +1 -1
- package/dist/index.cjs +104 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +97 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-error-CCZQd8Xl.d.cts +118 -0
- package/dist/mcp-error-CCZQd8Xl.d.ts +118 -0
- package/dist/mock-host/cli.cjs +84 -4
- package/dist/mock-host/cli.cjs.map +1 -1
- package/dist/mock-host/cli.d.cts +1 -1
- package/dist/mock-host/cli.d.ts +1 -1
- package/dist/mock-host/cli.js +84 -4
- package/dist/mock-host/cli.js.map +1 -1
- package/dist/plugin/index.cjs +106 -5
- package/dist/plugin/index.cjs.map +1 -1
- package/dist/plugin/index.d.cts +1 -0
- package/dist/plugin/index.d.ts +1 -0
- package/dist/plugin/index.js +99 -6
- package/dist/plugin/index.js.map +1 -1
- package/dist/{transport-BPwasdNz.d.ts → transport-BuX8gzq1.d.ts} +7 -0
- package/dist/{transport-BbSPAS_N.d.cts → transport-C4xoMWBE.d.cts} +7 -0
- package/package.json +2 -2
package/dist/plugin/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugin/clientPush.ts","../../src/plugin/hostIdentity.ts","../../src/plugin/PluginRealtimeContext.ts","../../src/plugin/ExtensionRuntimeProvider.tsx","../../src/plugin/useMcpResource.ts","../../src/plugin/useMcpTool.ts","../../src/plugin/useMcpQuery.ts","../../src/plugin/define.ts","../../src/plugin/reconciler.ts","../../src/plugin/createRemoteRoot.ts","../../src/plugin/createPortMcpTransport.ts","../../src/host/worker/bridge-envelopes.ts","../../src/plugin/bridge-client.ts","../../src/plugin/BridgeClientContext.ts","../../src/plugin/useBridgeTheme.ts","../../src/plugin/useBridgeLocale.ts","../../src/plugin/useFrontendSessionToken.ts","../../src/plugin/decodeJwtPayload.ts","../../src/plugin/useAuth.ts"],"names":["createContext","useContext","useRef","useEffect","useState","useCallback","nextId","useMemo"],"mappings":"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","file":"index.js","sourcesContent":["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","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useExtensionRuntimeTransport } from \"./ExtensionRuntimeProvider\";\nimport type { McpTransport } from \"./transport\";\n\nexport interface UseMcpResourceOptions\n{\n /**\n * Override the transport resolved from {@link ExtensionRuntimeProvider}.\n * Primarily intended for tests and advanced multi-host scenarios.\n */\n transport?: McpTransport;\n}\n\nexport interface UseMcpResourceResult<T>\n{\n data: T | undefined;\n error: Error | undefined;\n loading: boolean;\n /** Re-run the fetch against the current URI. Stable across renders. */\n refetch: () => void;\n}\n\n/**\n * Subscribe to an MCP resource by URI.\n *\n * The hook fetches via the resolved {@link McpTransport} on mount and whenever\n * the URI changes. Each fetch is cancellable: when the component unmounts or\n * the URI changes, the in-flight call is aborted via an {@link AbortController}\n * so stale responses cannot overwrite later state.\n *\n * `refetch` re-runs the fetch against the latest URI. The returned callback is\n * stable across renders so it is safe to include in `useEffect` dependency\n * arrays.\n */\nexport function useMcpResource<T>(\n uri: string,\n opts?: UseMcpResourceOptions,\n): UseMcpResourceResult<T>\n{\n const transport = useExtensionRuntimeTransport(opts?.transport);\n\n const [data, setData] = useState<T | undefined>(undefined);\n const [error, setError] = useState<Error | undefined>(undefined);\n const [loading, setLoading] = useState<boolean>(true);\n\n // Bump this counter to force a re-fetch from `refetch()`.\n const [refetchTick, setRefetchTick] = useState(0);\n\n // Track the active controller so `refetch` and unmount can abort it.\n const controllerRef = useRef<AbortController | null>(null);\n\n useEffect(() =>\n {\n const controller = new AbortController();\n controllerRef.current?.abort();\n controllerRef.current = controller;\n\n let cancelled = false;\n setLoading(true);\n setError(undefined);\n\n transport\n .getResource<T>(uri, controller.signal)\n .then((result) =>\n {\n if (cancelled || controller.signal.aborted)\n {\n return;\n }\n setData(result.data);\n setLoading(false);\n })\n .catch((err: unknown) =>\n {\n if (cancelled || controller.signal.aborted)\n {\n return;\n }\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n\n return () =>\n {\n cancelled = true;\n controller.abort();\n };\n }, [uri, transport, refetchTick]);\n\n const refetch = useCallback(() =>\n {\n setRefetchTick((tick) => tick + 1);\n }, []);\n\n return { data, error, loading, refetch };\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useExtensionRuntimeTransport } from \"./ExtensionRuntimeProvider\";\nimport type { McpTransport } from \"./transport\";\n\nexport interface UseMcpToolOptions\n{\n /**\n * Override the transport resolved from {@link ExtensionRuntimeProvider}.\n * Primarily intended for tests and advanced multi-host scenarios.\n */\n transport?: McpTransport;\n}\n\nexport interface UseMcpToolResult<TReq, TRes>\n{\n /**\n * Invoke the tool. Each call gets a fresh {@link AbortController} that is\n * aborted on unmount so unresolved promises cannot keep state alive after\n * the component is gone.\n */\n invoke: (req: TReq) => Promise<TRes>;\n loading: boolean;\n error: Error | undefined;\n}\n\n/**\n * Hook returning a callback that invokes an MCP tool by name through the\n * resolved {@link McpTransport}.\n *\n * `loading` and `error` track the most recent in-flight invocation. Callers\n * may also `await` the returned promise directly — failures are both rejected\n * to the caller AND surfaced via `error` so component-level UI can react.\n *\n * `invoke` is a stable reference across renders for the same `toolName` and\n * transport, making it safe to use inside `useEffect`/`useCallback` deps.\n */\nexport function useMcpTool<TReq, TRes>(\n toolName: string,\n opts?: UseMcpToolOptions,\n): UseMcpToolResult<TReq, TRes>\n{\n const transport = useExtensionRuntimeTransport(opts?.transport);\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n\n // Track the latest controller so unmount can abort an in-flight call.\n const controllerRef = useRef<AbortController | null>(null);\n const mountedRef = useRef(true);\n\n useEffect(() =>\n {\n mountedRef.current = true;\n return () =>\n {\n mountedRef.current = false;\n controllerRef.current?.abort();\n };\n }, []);\n\n const invoke = useCallback(\n async (req: TReq): Promise<TRes> =>\n {\n const controller = new AbortController();\n controllerRef.current?.abort();\n controllerRef.current = controller;\n\n setLoading(true);\n setError(undefined);\n\n try\n {\n const result = await transport.invokeTool<TReq, TRes>(\n toolName,\n req,\n controller.signal,\n );\n if (mountedRef.current && !controller.signal.aborted)\n {\n setLoading(false);\n }\n return result;\n }\n catch (err)\n {\n const wrapped = err instanceof Error ? err : new Error(String(err));\n if (mountedRef.current && !controller.signal.aborted)\n {\n setError(wrapped);\n setLoading(false);\n }\n throw wrapped;\n }\n },\n [transport, toolName],\n );\n\n return { invoke, loading, error };\n}\n","import { useEffect, useState } from \"react\";\nimport { useMcpTool } from \"./useMcpTool\";\n\n/**\n * Result shape returned by {@link useMcpQuery}. Mirrors the host-app\n * `react-query`-style envelope so consumers can render loading / error /\n * data states declaratively. `refetch` is stable across renders.\n */\nexport interface UseMcpQueryResult<TRes>\n{\n data: TRes | undefined;\n loading: boolean;\n error: Error | undefined;\n /** Re-run the fetch against the current args. Stable across renders. */\n refetch: () => void;\n}\n\n/**\n * Options for {@link useMcpQuery}.\n */\nexport interface UseMcpQueryOptions\n{\n /**\n * Gate the fetch on a guard. When false, the hook returns idle state and\n * never invokes — important because hooks must be called unconditionally\n * at the top of the component, so an `enabled` flag is the standard way\n * to express \"fetch only when a selection is present\" without violating\n * the rules of hooks. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n/**\n * Auto-fetch wrapper over the imperative {@link useMcpTool}. Fires the tool\n * call on mount AND whenever the serialised `args` change. Use for\n * read-shaped MCP tool calls that back a component's render data; for\n * mutations, call {@link useMcpTool} directly and trigger\n * `invoke(...)` from event handlers.\n *\n * Why this lives alongside {@link useMcpTool}: many plugin MCP tools are\n * effectively queries (`list-tasks`, `get-pending-approvals`, …) — paginated\n * lookups that change with filter inputs. The base hook's imperative\n * `invoke` signature is correct for mutations but ergonomically wrong for\n * reads, where every consumer ends up writing the same `useEffect` +\n * AbortController + cancellation-on-unmount boilerplate. This hook absorbs\n * that boilerplate once.\n *\n * @template TArgs The request shape sent to the tool.\n * @template TRes The response shape the tool returns.\n */\nexport function useMcpQuery<TArgs, TRes>(\n toolName: string,\n args: TArgs,\n options?: UseMcpQueryOptions,\n): UseMcpQueryResult<TRes>\n{\n const enabled = options?.enabled ?? true;\n const { invoke } = useMcpTool<TArgs, TRes>(toolName);\n const [data, setData] = useState<TRes | undefined>(undefined);\n const [loading, setLoading] = useState<boolean>(enabled);\n const [error, setError] = useState<Error | undefined>(undefined);\n const [tick, setTick] = useState(0);\n\n // Stringify args for the effect's dependency key so an args object with\n // new identity but unchanged contents does not force a refetch. The\n // shape JSON-serialises trivially because tool args are wire-bound (JSON\n // round-trips on the transport anyway).\n const argsKey = JSON.stringify(args);\n\n useEffect(() =>\n {\n if (!enabled)\n {\n setLoading(false);\n return;\n }\n let cancelled = false;\n setLoading(true);\n invoke(args)\n .then((result) =>\n {\n if (cancelled)\n {\n return;\n }\n setData(result);\n setError(undefined);\n setLoading(false);\n })\n .catch((err: unknown) =>\n {\n if (cancelled)\n {\n return;\n }\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n return () =>\n {\n cancelled = true;\n };\n // `args` is intentionally tracked via `argsKey` (serialised contents)\n // so reference-only changes don't refetch. eslint sees this as a\n // missing dep but the effect is correct as-is.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [argsKey, tick, invoke, enabled]);\n\n return {\n data,\n loading,\n error,\n refetch: () => setTick((t) => t + 1),\n };\n}\n\n// ── Response-envelope helpers ────────────────────────────────────\n//\n// MCP tool responses across the platform's BE conventions arrive in either\n// `{ items: T[] }` (raw lists) or `{ rows: T[] }` (paginated envelopes). The\n// envelope shape is a BE-side detail plugin authors don't want to encode at\n// every read site. {@link ItemsResponse} types the union and\n// {@link unwrapItems} normalises to a plain array.\n\n/**\n * Envelope shape that covers both `{ items: T[] }` and `{ rows: T[] }`\n * conventions emitted by platform / plugin MCP read tools. Optional both\n * sides so empty responses still type-check.\n */\nexport interface ItemsResponse<T>\n{\n items?: T[];\n rows?: T[];\n}\n\n/**\n * Normalise an {@link ItemsResponse} envelope to a plain array.\n * Returns `[]` when the response is `undefined` (typical pre-first-fetch\n * state) or when neither field is populated.\n */\nexport function unwrapItems<T>(response: ItemsResponse<T> | undefined): T[]\n{\n return response?.items ?? response?.rows ?? [];\n}\n","import type { RenderMode, SduiNode } from \"@ethisyscore/protocol\";\n\n/**\n * Configuration accepted by {@link defineDeclarativePlugin}.\n *\n * A Contract A (host-rendered) plugin contributes a map of resource URIs to\n * declarative SDUI trees. The host fetches a resource by URI and renders it\n * against the v1 vocabulary.\n */\nexport interface DeclarativePluginConfig\n{\n resources: Record<string, SduiNode>;\n}\n\n/**\n * Configuration accepted by {@link defineEthisysPlugin}.\n *\n * `renderMode` is constrained to the protocol's {@link RenderMode} enum so\n * authoring mistakes (`\"iframe\"`, `\"webview\"`, …) are caught at compile time.\n * `mount` is generic so Contract B remote-runtime authors can attach their\n * own mount surface without losing type information at the call site.\n */\nexport interface EthisysPluginConfig<TMount>\n{\n renderMode: RenderMode;\n mount?: TMount;\n}\n\n/**\n * Author-facing identity helper for a Contract A (host-rendered) declarative\n * plugin definition.\n *\n * The helper performs no runtime work — it exists purely so authoring sites\n * receive precise type inference and editor tooling against the protocol's\n * {@link SduiNode} contract. The returned value is the exact same reference\n * the caller passed in.\n */\nexport const defineDeclarativePlugin = (\n cfg: DeclarativePluginConfig,\n): DeclarativePluginConfig => cfg;\n\n/**\n * Author-facing identity helper for a generic EthisysCore plugin definition.\n *\n * The helper performs no runtime work. It constrains `renderMode` to the\n * protocol's {@link RenderMode} enum and preserves the inferred type of an\n * optional `mount` surface, so Contract B authors can pass through their own\n * mount object without widening it to `unknown`.\n */\nexport const defineEthisysPlugin = <TMount>(\n cfg: EthisysPluginConfig<TMount>,\n): EthisysPluginConfig<TMount> => cfg;\n","import ReactReconciler from \"react-reconciler\";\nimport {\n ConcurrentRoot,\n LegacyRoot,\n DefaultEventPriority,\n} from \"react-reconciler/constants.js\";\nimport {\n MUTATION_TYPE_INSERT_CHILD,\n MUTATION_TYPE_REMOVE_CHILD,\n MUTATION_TYPE_UPDATE_TEXT,\n MUTATION_TYPE_UPDATE_PROPERTY,\n NODE_TYPE_ELEMENT,\n NODE_TYPE_TEXT,\n ROOT_ID,\n type RemoteConnection,\n type RemoteNodeSerialization,\n} from \"@remote-dom/core\";\n\n// ── Internal node representation ───────────────────────────────────\n//\n// Lightweight JS-object stand-ins for the Remote DOM tree. Each node carries\n// a stable string id that the host uses to address mutations. We do NOT use\n// `@remote-dom/core`'s `RemoteElement` Custom Element classes because:\n// 1. They require `customElements` registration which the worker realm\n// does not have by default (would force every plugin to bundle a DOM\n// polyfill).\n// 2. The plain-object form serialises identically over the connection's\n// `RemoteNodeSerialization` contract — the host's `RemoteReceiver`\n// reconstructs proper elements on its side.\n\ninterface RemoteElementInstance {\n readonly id: string;\n readonly kind: \"element\";\n readonly type: string;\n properties: Record<string, unknown>;\n eventListeners: Record<string, (...args: unknown[]) => unknown>;\n children: RemoteNode[];\n parent: RemoteParent | null;\n}\n\ninterface RemoteTextInstance {\n readonly id: string;\n readonly kind: \"text\";\n text: string;\n parent: RemoteParent | null;\n}\n\ninterface RemoteRootContainer {\n readonly id: typeof ROOT_ID;\n readonly kind: \"root\";\n children: RemoteNode[];\n readonly connection: RemoteConnection;\n}\n\ntype RemoteNode = RemoteElementInstance | RemoteTextInstance;\ntype RemoteParent = RemoteElementInstance | RemoteRootContainer;\n\nlet nextId = 0;\nfunction newId(): string {\n nextId += 1;\n return `r-${nextId.toString(36)}`;\n}\n\nfunction serialize(node: RemoteNode): RemoteNodeSerialization {\n if (node.kind === \"text\") {\n return { id: node.id, type: NODE_TYPE_TEXT, data: node.text };\n }\n // Event-listener functions are intentionally NOT included in the\n // serialised payload — they can't be structured-cloned across a\n // MessagePort and would throw `DataCloneError` on the first commit.\n // Listeners are retained locally on the RemoteElementInstance for the\n // future `ethisys:remotedom:call` round-trip (see createRemoteRoot.ts's\n // top-of-file \"What's NOT plumbed yet\" note); until that channel lands,\n // worker-side `onClick`-style handlers are no-ops on the host. The host\n // RemoteReceiver tolerates the missing field — `eventListeners` is\n // optional on `RemoteNodeSerialization`.\n return {\n id: node.id,\n type: NODE_TYPE_ELEMENT,\n element: node.type,\n properties: node.properties,\n children: node.children.map(serialize),\n };\n}\n\nfunction indexInParent(parent: RemoteParent, child: RemoteNode): number {\n return parent.children.indexOf(child);\n}\n\n// ── Prop classification ────────────────────────────────────────────\n//\n// Children (the JSX content) are handled by the reconciler tree walk;\n// `ref` and `key` are React-internal. Everything else is forwarded as a\n// remote property OR an event listener. Convention: any prop starting with\n// `on` followed by an uppercase letter is treated as an event listener.\n\nfunction isEventListenerProp(key: string): boolean {\n return key.length > 2 && key.startsWith(\"on\") && key[2] >= \"A\" && key[2] <= \"Z\";\n}\n\nfunction eventNameFromProp(propName: string): string {\n // onClick → click. The host's RemoteReceiver expects lowercase event names.\n return propName.slice(2).toLowerCase();\n}\n\nfunction isReservedProp(key: string): boolean {\n return key === \"children\" || key === \"key\" || key === \"ref\";\n}\n\nfunction classifyProps(props: Record<string, unknown>): {\n properties: Record<string, unknown>;\n eventListeners: Record<string, (...args: unknown[]) => unknown>;\n} {\n const properties: Record<string, unknown> = {};\n const eventListeners: Record<string, (...args: unknown[]) => unknown> = {};\n for (const key of Object.keys(props)) {\n if (isReservedProp(key)) {\n continue;\n }\n const value = props[key];\n if (isEventListenerProp(key) && typeof value === \"function\") {\n eventListeners[eventNameFromProp(key)] = value as (...args: unknown[]) => unknown;\n } else if (value !== undefined) {\n properties[key] = value;\n }\n }\n return { properties, eventListeners };\n}\n\n// ── HostConfig factory ─────────────────────────────────────────────\n\n/**\n * Build the react-reconciler instance bound to the supplied `RemoteConnection`.\n * Every mutation the reconciler applies translates into a single\n * `connection.mutate([record])` call; the connection forwards the record over\n * the `MessagePort` set up by `createRemoteRoot`.\n */\nexport function createReactReconciler(connection: RemoteConnection): {\n reconciler: ReactReconciler.Reconciler<\n RemoteParent,\n RemoteElementInstance,\n RemoteTextInstance,\n never,\n RemoteElementInstance\n >;\n createContainer: () => RemoteRootContainer;\n} {\n const reconciler = ReactReconciler<\n string, // type\n Record<string, unknown>, // props\n RemoteParent, // container\n RemoteElementInstance, // instance\n RemoteTextInstance, // text instance\n never, // suspense instance\n never, // hydratable instance\n RemoteElementInstance, // public instance (returned from ref)\n object, // host context\n string[], // update payload (list of changed prop names)\n never, // child set (persistent mode only)\n ReturnType<typeof setTimeout>, // timeout handle\n -1 // no timeout sentinel\n >({\n supportsMutation: true,\n supportsPersistence: false,\n supportsHydration: false,\n isPrimaryRenderer: true,\n noTimeout: -1,\n scheduleTimeout: setTimeout,\n cancelTimeout: clearTimeout,\n getCurrentEventPriority: () => DefaultEventPriority,\n getRootHostContext: () => ({}),\n getChildHostContext: () => ({}),\n prepareForCommit: () => null,\n resetAfterCommit: () => {},\n preparePortalMount: () => {},\n shouldSetTextContent: (_type, props) =>\n typeof props.children === \"string\" || typeof props.children === \"number\",\n getPublicInstance: (instance) => instance as RemoteElementInstance,\n clearContainer: (container) => {\n // Remove every direct child from the root. The host receiver mirrors\n // each removal on its side.\n while (container.children.length > 0) {\n connection.mutate([[MUTATION_TYPE_REMOVE_CHILD, container.id, 0]]);\n container.children.shift();\n }\n },\n\n // ── createInstance / createTextInstance ────────────────────────\n createInstance: (type, props): RemoteElementInstance => {\n const { properties, eventListeners } = classifyProps(props);\n return {\n id: newId(),\n kind: \"element\",\n type,\n properties,\n eventListeners,\n children: [],\n parent: null,\n };\n },\n createTextInstance: (text): RemoteTextInstance => ({\n id: newId(),\n kind: \"text\",\n text,\n parent: null,\n }),\n\n // ── Initial mount: build the subtree before attaching to root ──\n appendInitialChild: (parent, child) => {\n parent.children.push(child);\n child.parent = parent;\n },\n finalizeInitialChildren: () => false,\n\n // ── Container-level mutations (root attach/detach) ─────────────\n appendChildToContainer: (container, child) => {\n const index = container.children.length;\n container.children.push(child);\n child.parent = container;\n connection.mutate([\n [MUTATION_TYPE_INSERT_CHILD, container.id, serialize(child), index],\n ]);\n },\n removeChildFromContainer: (container, child) => {\n const index = indexInParent(container, child);\n if (index < 0) {\n return;\n }\n container.children.splice(index, 1);\n child.parent = null;\n connection.mutate([[MUTATION_TYPE_REMOVE_CHILD, container.id, index]]);\n },\n insertInContainerBefore: (container, child, beforeChild) => {\n const beforeIndex = indexInParent(container, beforeChild);\n const insertAt = beforeIndex < 0 ? container.children.length : beforeIndex;\n container.children.splice(insertAt, 0, child);\n child.parent = container;\n connection.mutate([\n [MUTATION_TYPE_INSERT_CHILD, container.id, serialize(child), insertAt],\n ]);\n },\n\n // ── Sibling-level mutations (incremental tree updates) ─────────\n appendChild: (parent, child) => {\n const index = parent.children.length;\n parent.children.push(child);\n child.parent = parent;\n connection.mutate([\n [MUTATION_TYPE_INSERT_CHILD, parent.id, serialize(child), index],\n ]);\n },\n removeChild: (parent, child) => {\n const index = indexInParent(parent, child);\n if (index < 0) {\n return;\n }\n parent.children.splice(index, 1);\n child.parent = null;\n connection.mutate([[MUTATION_TYPE_REMOVE_CHILD, parent.id, index]]);\n },\n insertBefore: (parent, child, beforeChild) => {\n const beforeIndex = indexInParent(parent, beforeChild);\n const insertAt = beforeIndex < 0 ? parent.children.length : beforeIndex;\n parent.children.splice(insertAt, 0, child);\n child.parent = parent;\n connection.mutate([\n [MUTATION_TYPE_INSERT_CHILD, parent.id, serialize(child), insertAt],\n ]);\n },\n\n // ── Update path ────────────────────────────────────────────────\n prepareUpdate: (_instance, _type, oldProps, newProps): string[] | null => {\n const changed: string[] = [];\n const allKeys = new Set<string>([\n ...Object.keys(oldProps),\n ...Object.keys(newProps),\n ]);\n for (const key of allKeys) {\n if (isReservedProp(key)) {\n continue;\n }\n if (oldProps[key] !== newProps[key]) {\n changed.push(key);\n }\n }\n return changed.length > 0 ? changed : null;\n },\n commitUpdate: (instance, updatePayload, _type, _oldProps, newProps) => {\n if (updatePayload === null) {\n return;\n }\n for (const key of updatePayload) {\n const value = newProps[key];\n if (isEventListenerProp(key) && (typeof value === \"function\" || value === undefined)) {\n const eventName = eventNameFromProp(key);\n if (typeof value === \"function\") {\n instance.eventListeners[eventName] = value as (...args: unknown[]) => unknown;\n } else {\n delete instance.eventListeners[eventName];\n }\n // Functions can't cross a MessagePort via structured clone, so the\n // wire payload only signals whether the listener is now present or\n // cleared — `true` for \"listener exists, wire it on the host once\n // the call channel lands\", `null` for \"listener removed\". See the\n // serialize() comment for the broader Phase 1 limitation.\n connection.mutate([\n [\n MUTATION_TYPE_UPDATE_PROPERTY,\n instance.id,\n eventName,\n typeof value === \"function\" ? true : null,\n 3 /* event listener */,\n ],\n ]);\n } else {\n if (value === undefined) {\n delete instance.properties[key];\n } else {\n instance.properties[key] = value;\n }\n connection.mutate([\n [MUTATION_TYPE_UPDATE_PROPERTY, instance.id, key, value],\n ]);\n }\n }\n },\n commitTextUpdate: (textInstance, _oldText, newText) => {\n textInstance.text = newText;\n connection.mutate([[MUTATION_TYPE_UPDATE_TEXT, textInstance.id, newText]]);\n },\n\n // ── No-op methods required by the HostConfig contract ──────────\n detachDeletedInstance: () => {},\n beforeActiveInstanceBlur: () => {},\n afterActiveInstanceBlur: () => {},\n prepareScopeUpdate: () => {},\n getInstanceFromScope: () => null,\n getInstanceFromNode: () => null,\n\n // Microtask scheduling — use the platform's default queueing.\n supportsMicrotasks: true,\n scheduleMicrotask:\n typeof queueMicrotask === \"function\"\n ? queueMicrotask\n : (callback) => Promise.resolve().then(callback),\n });\n\n function createContainer(): RemoteRootContainer {\n return { id: ROOT_ID, kind: \"root\", children: [], connection };\n }\n\n return { reconciler, createContainer };\n}\n\nexport { ConcurrentRoot, LegacyRoot };\nexport type { RemoteRootContainer, RemoteElementInstance, RemoteTextInstance };\n","/**\n * Contract B (worker remote-runtime) plugin-side React root.\n *\n * # The problem this solves\n *\n * A Contract B plugin runs in a sandboxed Web Worker — no `document`, no\n * `window`, no DOM. The host owns rendering: the worker constructs a\n * `RemoteElement` tree via `@remote-dom/core`, mutations forward over a\n * `MessagePort`, the host receives them and commits them to a real React tree\n * (see `coreconnect-web/src/extensions/runtime/WorkerSurfaceMount.tsx` for the\n * receiver side).\n *\n * Until now plugin authors had to hand-author the `RemoteElement` construction\n * + mutation forwarding manually. `@remote-dom/react` ships only the host-side\n * primitives (`createRemoteComponent`, `RemoteRootRenderer`, etc.) — there is\n * no worker-side React reconciler in the package. This helper provides one.\n *\n * # What this is\n *\n * The public surface (`createRemoteRoot(port, options)`) plus a working\n * `react-reconciler` HostConfig that commits to a Remote DOM tree and\n * forwards mutation records over the `MessagePort` to the host receiver.\n * Authors call `root.render(<App />)` and the host's `WorkerSurfaceMount`\n * sees the result.\n *\n * # What's NOT plumbed yet (Phase 1 limitation)\n *\n * Event-listener round-trip. The host transport currently has no\n * `ethisys:remotedom:call` channel — `WorkerRemoteDomTransport` only\n * forwards `ethisys:remotedom` payloads from worker → host, never the\n * other direction. So a worker-side `onClick={() => ...}` is **registered\n * locally** but the host can't call it. The reconciler retains every\n * listener on its in-memory `RemoteElementInstance` so the wiring is ready\n * the moment the call channel lands; until then, plugin authors should\n * keep interactive surfaces on Contract A.\n *\n * # The API\n *\n * ```ts\n * import { createRemoteRoot } from \"@ethisyscore/extension-runtime/plugin\";\n *\n * export async function activate(port: MessagePort): Promise<void> {\n * const root = createRemoteRoot(port);\n * root.render(<App />);\n * }\n * ```\n *\n * `<App />` is plain React. Any component shipped by the host's frozen\n * import-map allowlist (the closed Contract B primitive vocabulary —\n * `Button`, `DataTable`, `Form`, `Card`, `Tabs`, `Select`, `Alert`, etc.)\n * renders. The reconciler walks the React tree and commits to a\n * `RemoteRootElement`; mutation records forward over the port; the host's\n * `RemoteReceiver` commits the result into the real React tree.\n *\n * # Wire shape\n *\n * Every reconciler mutation is posted as\n * `{ type: \"ethisys:remotedom\", payload: RemoteMutationRecord[] }` to match\n * `WorkerRemoteDomTransport`'s `RemoteDomMessage` contract — the host\n * extracts `payload` and feeds it straight into `receiver.mutate(records)`.\n * `BatchingRemoteConnection` (controlled via `options.batchMutations`)\n * collapses contiguous mutations into a single port post per commit.\n */\nimport type { RemoteConnection, RemoteMutationRecord } from \"@remote-dom/core\";\nimport { BatchingRemoteConnection } from \"@remote-dom/core/elements\";\nimport type { ReactNode } from \"react\";\nimport {\n createReactReconciler,\n LegacyRoot,\n type RemoteRootContainer,\n} from \"./reconciler\";\n\n/**\n * Options for {@link createRemoteRoot}. Reserved for forward compatibility —\n * Phase 1 ships with zero required options. Adding fields here is additive;\n * removing them is a breaking change.\n */\nexport interface CreateRemoteRootOptions\n{\n /**\n * When supplied, the reconciler batches contiguous mutations into a single\n * `port.postMessage` rather than firing one per mutation. Default `true`\n * — measurably reduces host-side commit cost on the first render of a\n * non-trivial tree.\n */\n batchMutations?: boolean;\n\n /**\n * Override the connection factory. Reserved for tests; production callers\n * never pass this. The factory's contract is \"build a RemoteConnection\n * that forwards mutations over the supplied port\"; the default uses\n * `createRemoteConnection` from `@remote-dom/core`.\n */\n connectionFactory?: (port: MessagePort) => RemoteConnection;\n}\n\n/**\n * Plugin-side React root. Mirrors the shape of `ReactDOMClient.Root` so\n * authors familiar with `createRoot().render(...)` find the same API on the\n * worker side.\n */\nexport interface RemoteRoot\n{\n /**\n * Render a React element tree against the worker's `RemoteRootElement`.\n * The reconciler commits to the root, the mutation observer forwards\n * mutations over the port, the host re-renders. Idempotent — calling\n * `render` twice with the same element is fine; the reconciler dedupes.\n */\n render(element: ReactNode): void;\n\n /**\n * Tear down the reconciled tree and stop forwarding mutations. Authors\n * should call this from a `Symbol.dispose` or equivalent when the worker\n * is shutting down — leaking the reconciler holds the `MessagePort` open\n * and prevents the worker from being collected.\n */\n unmount(): void;\n}\n\n/**\n * Construct a plugin-side React root that commits to a `RemoteRootElement`\n * and forwards mutations over the supplied `MessagePort`.\n *\n * **API stability:** the function signature is stable for Phase 1. The\n * options bag is forward-compatible (additive only).\n *\n * **Implementation status:** the connection + root construction lands in this\n * commit. The `react-reconciler` `HostConfig` is a scaffold — `render()`\n * throws a structured error directing authors to the W1A tracking issue.\n * Authors should treat this commit as \"the API is locked, the reconciler is\n * being authored.\" See follow-on commits on the `feature/contract-b-create-remote-root-w1a`\n * branch.\n */\nexport function createRemoteRoot(\n port: MessagePort,\n options: CreateRemoteRootOptions = {},\n): RemoteRoot\n{\n if (port === undefined || port === null)\n {\n throw new TypeError(\n \"[createRemoteRoot] `port` is required. Pass the MessagePort the host transferred via the worker's `activate(port)` entry point.\",\n );\n }\n\n // Build the connection that forwards Remote DOM mutations over the port.\n // The default factory uses `@remote-dom/core`'s `createRemoteConnection`;\n // tests inject their own factory via `options.connectionFactory`.\n const baseConnection = (options.connectionFactory ?? defaultConnectionFactory)(port);\n\n const connection: RemoteConnection = options.batchMutations === false\n ? baseConnection\n : new BatchingRemoteConnection(baseConnection);\n\n // W1A — lazy reconciler + container construction. The reconciler is built\n // once on first render(); subsequent calls reuse the same container so React\n // commits behave like ReactDOM.createRoot(...).render(...) — call render\n // multiple times to update; call unmount to tear down.\n const { reconciler, createContainer } = createReactReconciler(connection);\n let container: RemoteRootContainer | null = null;\n let fiberRoot: ReturnType<typeof reconciler.createContainer> | null = null;\n\n return {\n render(element: ReactNode): void\n {\n if (container === null)\n {\n container = createContainer();\n fiberRoot = reconciler.createContainer(\n container,\n LegacyRoot,\n null, // hydration callbacks\n false, // isStrictMode\n null, // concurrentUpdatesByDefaultOverride\n \"\", // identifierPrefix\n (error: Error) =>\n {\n // The host's SurfaceErrorBoundary catches render-time\n // failures via the connection's call() path; mirroring\n // them locally as a console.error is the best we can do\n // from inside the worker.\n // eslint-disable-next-line no-console -- worker console is pumped to the host logger.\n console.error(\"[createRemoteRoot] render error\", error);\n },\n null, // transitionCallbacks\n );\n }\n reconciler.updateContainer(element, fiberRoot!, null, null);\n },\n unmount(): void\n {\n if (fiberRoot !== null)\n {\n reconciler.updateContainer(null, fiberRoot, null, null);\n fiberRoot = null;\n container = null;\n }\n // The connection itself does NOT close the port — plugins may have\n // side-channel consumers. Authors that want a hard tear-down call\n // `port.close()` themselves after unmount().\n },\n };\n}\n\n// ── Internals ──────────────────────────────────────────────────────────\n\n/**\n * Default factory: build a `RemoteConnection` that posts mutation records\n * as the `payload` of a single `ethisys:remotedom` port message — the\n * wire shape `WorkerRemoteDomTransport`'s `RemoteDomMessage` declares\n * (see `host/worker/transport.ts`'s `handlePortMessage`: it forwards\n * `(message as RemoteDomMessage).payload` straight into the host's\n * `RemoteReceiver.mutate(records)` call).\n *\n * Going through `createRemoteConnection` (which fires per-op callbacks)\n * was the wrong abstraction for this wire — the host expects the raw\n * record list under `payload`, not bespoke `op`-suffixed envelopes. By\n * implementing the connection directly we hand the reconciler's\n * mutation records straight to the host receiver without translation.\n *\n * Wrapping in `BatchingRemoteConnection` (controlled via\n * `options.batchMutations`) collapses contiguous mutation calls into a\n * single `port.postMessage` payload, which is the difference between \"one\n * postMessage per appended child\" and \"one postMessage per React commit\"\n * — measurably cheaper for the first paint of a non-trivial tree.\n */\nfunction defaultConnectionFactory(port: MessagePort): RemoteConnection\n{\n return {\n mutate(records: readonly RemoteMutationRecord[]): void\n {\n // Snapshot to a plain array so the host receiver isn't handed a\n // reference into the reconciler's internal buffer (the BatchingRemoteConnection\n // wrapper recycles its own array between flushes).\n port.postMessage({ type: \"ethisys:remotedom\", payload: records.slice() });\n },\n call(_id, _method, ..._args): unknown\n {\n // Worker → host method invocation. Reserved for the future\n // `ethisys:remotedom:call` channel — `WorkerRemoteDomTransport`\n // doesn't yet handle a `call`-shaped reply path, so event-\n // listener round-trip is still on the Phase 1 follow-on list\n // (see this file's top-of-file \"What's NOT plumbed yet\" note).\n return undefined;\n },\n };\n}\n\n","/**\n * W1B — MessagePort-backed McpTransport for Contract B (worker remote-runtime)\n * plugins.\n *\n * The plugin-side React hooks (`useMcpResource`, `useMcpTool`) consume an\n * {@link McpTransport} — an abstraction over the host call. Contract A\n * (host-rendered) plugins pick up the host-injected transport via\n * `ExtensionRuntimeProvider`. Contract B plugins run in a Web Worker with\n * no shared object surface — the only channel is the `MessagePort` the\n * host transferred via `activate(port)`. This helper bridges the two\n * worlds: it exposes the {@link McpTransport} contract on the worker side\n * and serialises every call into a request/response envelope over the\n * port.\n *\n * # The protocol on the wire\n *\n * The wire shape mirrors `WorkerRemoteDomTransport` (in `host/worker/transport.ts`)\n * one-for-one — that transport is the host receiver and decides what a \"well-\n * formed\" message looks like. Two request shapes, two `:result`-suffixed reply\n * shapes, one abort envelope. Each call mints a fresh request id (`req-{n}`).\n *\n * ```jsonc\n * // worker → host\n * { type: \"ethisys:mcp:getResource\", id: \"req-3\", uri: \"tickets://home\" }\n * { type: \"ethisys:mcp:invokeTool\", id: \"req-4\", name: \"tickets:open\", args: { ... } }\n *\n * // host → worker (matching id, suffixed type)\n * { type: \"ethisys:mcp:getResource:result\", id: \"req-3\", ok: true, data: { uri, data } }\n * { type: \"ethisys:mcp:invokeTool:result\", id: \"req-4\", ok: false, error: \"...\" }\n * ```\n *\n * Requests honour the optional `AbortSignal`. On abort, the transport posts\n * a `{ type: \"ethisys:mcp:abort\", id }` envelope so the host can cancel\n * in-flight work, then rejects the pending promise with an `AbortError`-shaped\n * `Error` so the consuming hooks see the same shape as native fetch cancellation.\n *\n * # Authoring shape\n *\n * ```ts\n * export async function activate(port: MessagePort): Promise<void> {\n * const transport = createPortMcpTransport(port);\n * const root = createRemoteRoot(port);\n * root.render(\n * <ExtensionRuntimeProvider transport={transport}>\n * <App />\n * </ExtensionRuntimeProvider>,\n * );\n * }\n * ```\n *\n * `<App />` uses `useMcpResource` / `useMcpTool` as it would on the host\n * side; the transport translates each call into the port envelope.\n */\nimport type { McpTransport, UploadDocumentMeta, UploadDocumentResult } from \"./transport\";\n\n/**\n * Options for {@link createPortMcpTransport}. Reserved for forward\n * compatibility — the public surface is empty in Phase 1.\n */\nexport interface CreatePortMcpTransportOptions\n{\n /**\n * Override the request-id generator. Reserved for tests so they can\n * make request ids deterministic. Production callers never pass this.\n */\n requestIdFactory?: () => string;\n\n /**\n * Override the port's `addEventListener` / `removeEventListener` /\n * `postMessage` triple. Reserved for tests so the transport can be\n * exercised without instantiating a real MessageChannel.\n */\n portShimForTests?: PortShim;\n}\n\n/**\n * Minimal subset of the MessagePort surface the transport actually uses.\n * Exposed so tests can construct a polyfill without faking the full\n * MessagePort.\n */\nexport interface PortShim\n{\n addEventListener(\n type: \"message\",\n listener: (event: { data: unknown }) => void,\n ): void;\n removeEventListener(\n type: \"message\",\n listener: (event: { data: unknown }) => void,\n ): void;\n postMessage(value: unknown, transfer?: Transferable[]): void;\n}\n\n// ── Wire-shape constants ────────────────────────────────────────────\n//\n// These four strings are the contract with `WorkerRemoteDomTransport`. Any\n// rename here breaks the host receiver silently — the unknown message hits\n// the host's default branch and is dropped. Keep these in sync with\n// `host/worker/transport.ts`'s `handlePortMessage` switch / result-type\n// strings.\nconst WIRE_INVOKE_TOOL_REQUEST = \"ethisys:mcp:invokeTool\";\nconst WIRE_INVOKE_TOOL_RESULT = \"ethisys:mcp:invokeTool:result\";\nconst WIRE_GET_RESOURCE_REQUEST = \"ethisys:mcp:getResource\";\nconst WIRE_GET_RESOURCE_RESULT = \"ethisys:mcp:getResource:result\";\nconst WIRE_UPLOAD_DOCUMENT_REQUEST = \"ethisys:mcp:uploadDocument\";\nconst WIRE_UPLOAD_DOCUMENT_RESULT = \"ethisys:mcp:uploadDocument:result\";\nconst WIRE_ABORT = \"ethisys:mcp:abort\";\n\ntype McpResultType =\n | typeof WIRE_INVOKE_TOOL_RESULT\n | typeof WIRE_GET_RESOURCE_RESULT\n | typeof WIRE_UPLOAD_DOCUMENT_RESULT;\n\ninterface McpResultEnvelope\n{\n type: McpResultType;\n id: string;\n ok?: boolean;\n error?: string;\n data?: unknown;\n}\n\nfunction isMcpResultEnvelope(value: unknown): value is McpResultEnvelope\n{\n if (value === null || typeof value !== \"object\")\n {\n return false;\n }\n const v = value as { type?: unknown; id?: unknown };\n return (v.type === WIRE_INVOKE_TOOL_RESULT\n || v.type === WIRE_GET_RESOURCE_RESULT\n || v.type === WIRE_UPLOAD_DOCUMENT_RESULT)\n && typeof v.id === \"string\";\n}\n\n/**\n * Construct an {@link McpTransport} backed by a MessagePort.\n *\n * @param port The host-transferred MessagePort for the worker surface.\n * @param options Reserved for forward compatibility / test injection.\n *\n * @returns A transport implementation honouring the {@link McpTransport}\n * contract — fetch a resource, invoke a tool, observe abort\n * signals, settle the promise on the host's reply envelope.\n */\nexport function createPortMcpTransport(\n port: MessagePort | PortShim,\n options: CreatePortMcpTransportOptions = {},\n): McpTransport\n{\n if (port === undefined || port === null)\n {\n throw new TypeError(\n \"[createPortMcpTransport] `port` is required. Pass the MessagePort the host transferred via the worker's `activate(port)` entry point.\",\n );\n }\n\n const shim: PortShim = options.portShimForTests ?? port as PortShim;\n const factory = options.requestIdFactory ?? defaultRequestIdFactory();\n\n type Resolver = {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n };\n const pending = new Map<string, Resolver>();\n\n function onMessage(event: { data: unknown }): void\n {\n if (!isMcpResultEnvelope(event.data))\n {\n return;\n }\n const reply = event.data;\n const resolver = pending.get(reply.id);\n if (resolver === undefined)\n {\n return;\n }\n pending.delete(reply.id);\n if (reply.ok === true)\n {\n // Host posts the payload under `data` (matching\n // `WorkerRemoteDomTransport.safePostMessage`). For getResource\n // calls the data is the resource body; for invokeTool calls it\n // is the tool's return value.\n resolver.resolve(reply.data);\n }\n else\n {\n const error = new Error(\n typeof reply.error === \"string\" ? reply.error : \"Unknown host error\",\n );\n error.name = \"McpHostError\";\n resolver.reject(error);\n }\n }\n\n shim.addEventListener(\"message\", onMessage);\n // MessagePort.addEventListener doesn't auto-start the port — without an\n // explicit `start()`, the message queue stays paused and the host's\n // replies never reach `onMessage`. This is a no-op on plain\n // EventTarget-shaped shims used in tests (the type guard guarantees\n // safety) but is required on real MessagePort instances.\n const realPort = port as Partial<MessagePort>;\n if (!options.portShimForTests && typeof realPort.start === \"function\")\n {\n realPort.start();\n }\n\n function send(\n requestType:\n | typeof WIRE_INVOKE_TOOL_REQUEST\n | typeof WIRE_GET_RESOURCE_REQUEST\n | typeof WIRE_UPLOAD_DOCUMENT_REQUEST,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n transfer?: Transferable[],\n ): Promise<unknown>\n {\n const id = factory();\n return new Promise<unknown>((resolve, reject) =>\n {\n if (signal?.aborted === true)\n {\n reject(abortError());\n return;\n }\n pending.set(id, { resolve, reject });\n\n const onAbort = (): void =>\n {\n if (!pending.has(id))\n {\n return;\n }\n pending.delete(id);\n shim.postMessage({ type: WIRE_ABORT, id });\n reject(abortError());\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n // `transfer` moves the ArrayBuffer zero-copy for uploadDocument; the\n // JSON request shapes pass no transfer list and post by structured clone.\n shim.postMessage({ type: requestType, id, ...body }, transfer);\n });\n }\n\n return {\n async getResource<T>(uri: string, signal?: AbortSignal): Promise<{ uri: string; data: T }>\n {\n // Host sends only the resource body under `data`. Reconstruct the\n // `{ uri, data }` envelope the McpTransport contract returns —\n // `uri` is round-tripped from the request because the resolver\n // only sees the reply.\n const data = await send(WIRE_GET_RESOURCE_REQUEST, { uri }, signal);\n return { uri, data: data as T };\n },\n async invokeTool<TReq, TRes>(name: string, args: TReq, signal?: AbortSignal): Promise<TRes>\n {\n const data = await send(WIRE_INVOKE_TOOL_REQUEST, { name, args: args as unknown }, signal);\n return data as TRes;\n },\n async uploadDocument(\n meta: UploadDocumentMeta,\n buffer: ArrayBuffer,\n signal?: AbortSignal,\n ): Promise<UploadDocumentResult>\n {\n // The ArrayBuffer rides as a transferable — after posting, `buffer`\n // is detached on the worker side (zero-copy hand-off to the host).\n const data = await send(\n WIRE_UPLOAD_DOCUMENT_REQUEST,\n { meta, buffer },\n signal,\n [buffer],\n );\n return data as UploadDocumentResult;\n },\n };\n}\n\nfunction defaultRequestIdFactory(): () => string\n{\n let counter = 0;\n return (): string =>\n {\n counter += 1;\n return `req-${counter}`;\n };\n}\n\nfunction abortError(): Error\n{\n const e = new Error(\"Aborted\");\n e.name = \"AbortError\";\n return e;\n}\n","/**\n * Wire-shape constants and type guards for host→plugin bridge push messages\n * (WI 4858 sub-plan 2). These types ride the existing MessagePort channel\n * alongside the `ethisys:mcp:*` and `ethisys:remotedom` envelopes — the\n * `type` discriminant keeps them fully separate at the dispatch site.\n *\n * The host posts bridge pushes through `WorkerRemoteDomTransport.hostPort`;\n * the plugin side reads them in `createPortBridgeClient` (plugin-ui package).\n * In-realm (Tier T) plugins use `InMemoryBridgeTransport` which calls the\n * subscriber callbacks directly — no serialisation needed.\n */\n\n// ── Type constants ───────────────────────────────────────────────────────────\n\nexport const BRIDGE_PUSH_THEME = \"ethisys:bridge:theme\" as const;\nexport const BRIDGE_PUSH_LOCALE = \"ethisys:bridge:locale\" as const;\nexport const BRIDGE_PUSH_DENSITY = \"ethisys:bridge:density\" as const;\nexport const BRIDGE_PUSH_A11Y = \"ethisys:bridge:a11y\" as const;\nexport const BRIDGE_NAV_PUSH = \"ethisys:bridge:nav\" as const;\nexport const BRIDGE_CHROME_REQUEST = \"ethisys:bridge:chrome:request\" as const;\nexport const BRIDGE_CHROME_RESPONSE = \"ethisys:bridge:chrome:response\" as const;\nexport const BRIDGE_SESSION_TOKEN_PUSH = \"ethisys:bridge:token\" as const;\nexport const BRIDGE_LIFECYCLE_EVENT = \"ethisys:bridge:lifecycle\" as const;\nexport const BRIDGE_A11Y_ANNOUNCE = \"ethisys:bridge:a11y:announce\" as const;\n\n// ── Envelope shapes ──────────────────────────────────────────────────────────\n\n/** Theme push: host → plugin whenever the host theme changes. */\nexport interface BridgePushThemeEnvelope {\n readonly type: typeof BRIDGE_PUSH_THEME;\n /** \"light\" | \"dark\" | \"high-contrast\" */\n readonly mode: string;\n /** Flat design-token map (CSS-variable-name → value). May be empty. */\n readonly tokens: Readonly<Record<string, string>>;\n}\n\n/** Locale push: host → plugin whenever the active locale changes. */\nexport interface BridgePushLocaleEnvelope {\n readonly type: typeof BRIDGE_PUSH_LOCALE;\n /** BCP 47 tag, e.g. \"en-GB\". */\n readonly locale: string;\n /** \"ltr\" | \"rtl\" */\n readonly dir: \"ltr\" | \"rtl\";\n}\n\n/** Density push: host → plugin when the UI density preference changes. */\nexport interface BridgePushDensityEnvelope {\n readonly type: typeof BRIDGE_PUSH_DENSITY;\n /** \"comfortable\" | \"compact\" */\n readonly density: string;\n}\n\n/** A11y push: host → plugin when accessibility prefs change. */\nexport interface BridgePushA11yEnvelope {\n readonly type: typeof BRIDGE_PUSH_A11Y;\n readonly reducedMotion: boolean;\n readonly highContrast: boolean;\n}\n\n/** Nav push: host → plugin when the SPA location changes. */\nexport interface BridgeNavPushEnvelope {\n readonly type: typeof BRIDGE_NAV_PUSH;\n /** Current SPA path (pathname + search). */\n readonly path: string;\n /** `window.history.length` at the time of push. */\n readonly historyLength: number;\n}\n\n/** Plugin → host: request a chrome action (e.g. toast, modal). */\nexport interface BridgeChromeRequestEnvelope {\n readonly type: typeof BRIDGE_CHROME_REQUEST;\n /** Correlation id — matched against the response. */\n readonly id: string;\n /** \"toast\" | \"confirm\" | \"openUrl\" */\n readonly action: string;\n readonly payload: Readonly<Record<string, unknown>>;\n}\n\n/** Host → plugin: response to a chrome request. */\nexport interface BridgeChromeResponseEnvelope {\n readonly type: typeof BRIDGE_CHROME_RESPONSE;\n readonly id: string;\n readonly ok: boolean;\n readonly result?: unknown;\n readonly error?: string;\n}\n\n/** Host → plugin: push a short-lived frontend-session token. */\nexport interface BridgeSessionTokenPushEnvelope {\n readonly type: typeof BRIDGE_SESSION_TOKEN_PUSH;\n /** Opaque JWT string — audience-restricted to the plugin's own backend. */\n readonly token: string;\n /** Absolute epoch-ms at which the token expires. Refresh fires 30 s before. */\n readonly expiresAtMs: number;\n}\n\n/** Host ↔ plugin lifecycle event. */\nexport type BridgeLifecyclePhase = \"load\" | \"ready\" | \"error\" | \"unload\";\n\nexport interface BridgeLifecycleEventEnvelope {\n readonly type: typeof BRIDGE_LIFECYCLE_EVENT;\n readonly phase: BridgeLifecyclePhase;\n /** Present when phase === \"error\". */\n readonly message?: string;\n}\n\n/** Plugin → host: a11y live-region announcement request. */\nexport interface BridgeA11yAnnounceEnvelope {\n readonly type: typeof BRIDGE_A11Y_ANNOUNCE;\n readonly message: string;\n /** \"polite\" | \"assertive\" */\n readonly politeness: \"polite\" | \"assertive\";\n}\n\n// ── Type guards ──────────────────────────────────────────────────────────────\n\nfunction isObj(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === \"object\";\n}\n\nexport function isBridgePushTheme(v: unknown): v is BridgePushThemeEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_THEME && typeof v[\"mode\"] === \"string\";\n}\n\nexport function isBridgePushLocale(v: unknown): v is BridgePushLocaleEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_LOCALE && typeof v[\"locale\"] === \"string\";\n}\n\nexport function isBridgePushDensity(v: unknown): v is BridgePushDensityEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_DENSITY && typeof v[\"density\"] === \"string\";\n}\n\nexport function isBridgePushA11y(v: unknown): v is BridgePushA11yEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_A11Y\n && typeof v[\"reducedMotion\"] === \"boolean\"\n && typeof v[\"highContrast\"] === \"boolean\";\n}\n\nexport function isBridgeNavPush(v: unknown): v is BridgeNavPushEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_NAV_PUSH && typeof v[\"path\"] === \"string\";\n}\n\nexport function isBridgeChromeRequest(v: unknown): v is BridgeChromeRequestEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_CHROME_REQUEST && typeof v[\"id\"] === \"string\";\n}\n\nexport function isBridgeChromeResponse(v: unknown): v is BridgeChromeResponseEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_CHROME_RESPONSE && typeof v[\"id\"] === \"string\";\n}\n\nexport function isBridgeSessionTokenPush(v: unknown): v is BridgeSessionTokenPushEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_SESSION_TOKEN_PUSH\n && typeof v[\"token\"] === \"string\"\n && typeof v[\"expiresAtMs\"] === \"number\";\n}\n\nexport function isBridgeLifecycleEvent(v: unknown): v is BridgeLifecycleEventEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_LIFECYCLE_EVENT && typeof v[\"phase\"] === \"string\";\n}\n\nexport function isBridgeA11yAnnounce(v: unknown): v is BridgeA11yAnnounceEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_A11Y_ANNOUNCE && typeof v[\"message\"] === \"string\";\n}\n","/**\n * Plugin-side bridge client for the host ↔ plugin platform contract\n * (WI 4858 sub-plan 2).\n *\n * `createPortBridgeClient` wraps a MessagePort (or an in-realm shim) and\n * exposes typed subscription callbacks for each host push (theme, locale,\n * density, a11y, nav, session token) and typed fire-and-wait calls for\n * chrome requests and a11y live-region announcements.\n *\n * For in-realm (Tier T) plugins, use `InMemoryBridgeTransport` from the\n * mock-host package instead — it calls callbacks synchronously without\n * any serialisation.\n */\nimport {\n BRIDGE_CHROME_REQUEST,\n BRIDGE_A11Y_ANNOUNCE,\n isBridgePushTheme,\n isBridgePushLocale,\n isBridgePushDensity,\n isBridgePushA11y,\n isBridgeNavPush,\n isBridgeChromeResponse,\n isBridgeSessionTokenPush,\n type BridgePushThemeEnvelope,\n type BridgePushLocaleEnvelope,\n type BridgePushDensityEnvelope,\n type BridgePushA11yEnvelope,\n type BridgeNavPushEnvelope,\n type BridgeSessionTokenPushEnvelope,\n} from \"../host/worker/bridge-envelopes\";\n\n// ── Subscriber payload types (strips the `type` discriminant) ────────────────\n\nexport type ThemePayload = Omit<BridgePushThemeEnvelope, \"type\">;\nexport type LocalePayload = Omit<BridgePushLocaleEnvelope, \"type\">;\nexport type DensityPayload = Omit<BridgePushDensityEnvelope, \"type\">;\nexport type A11yPayload = Omit<BridgePushA11yEnvelope, \"type\">;\nexport type NavPayload = Omit<BridgeNavPushEnvelope, \"type\">;\nexport type SessionTokenPayload = Omit<BridgeSessionTokenPushEnvelope, \"type\">;\n\n// ── Port shim (test injection) ───────────────────────────────────────────────\n\n/**\n * Minimal port surface `createPortBridgeClient` actually uses. Matches\n * `PortShim` in `createPortMcpTransport` — same test-injection pattern.\n */\nexport interface BridgePortShim {\n onmessage: ((ev: { data: unknown }) => void) | null;\n postMessage(msg: unknown): void;\n}\n\n// ── Public client interface ──────────────────────────────────────────────────\n\nexport interface PortBridgeClient {\n /** Replace the theme subscriber. Only the latest subscriber is called. */\n onTheme(cb: (payload: ThemePayload) => void): void;\n /** Replace the locale subscriber. */\n onLocale(cb: (payload: LocalePayload) => void): void;\n /** Replace the density subscriber. */\n onDensity(cb: (payload: DensityPayload) => void): void;\n /** Replace the a11y-preferences subscriber. */\n onA11y(cb: (payload: A11yPayload) => void): void;\n /** Replace the nav-state subscriber. */\n onNav(cb: (payload: NavPayload) => void): void;\n /** Replace the session-token subscriber. */\n onSessionToken(cb: (payload: SessionTokenPayload) => void): void;\n /**\n * Request a host chrome action. Returns the host's result or rejects with\n * the host's error string. Callers should catch and surface gracefully.\n *\n * @param action \"toast\" | \"confirm\" | \"openUrl\"\n * @param payload Action-specific payload.\n */\n requestChrome(action: string, payload: Record<string, unknown>): Promise<unknown>;\n /**\n * Ask the host to make a live-region announcement on behalf of the plugin.\n * Prefers this over writing directly to the DOM so the host can manage\n * ARIA live regions centrally.\n */\n announceA11y(message: string, politeness: \"polite\" | \"assertive\"): void;\n}\n\n// ── Implementation ───────────────────────────────────────────────────────────\n\nlet _requestCounter = 0;\nfunction nextId(): string {\n _requestCounter += 1;\n return `br-${_requestCounter}`;\n}\n\n/**\n * Construct a bridge client backed by a `MessagePort` (or shim).\n *\n * Registers a single `onmessage` handler on the port that dispatches each\n * bridge push to the appropriate subscriber. Unknown message types are\n * silently ignored — the port carries MCP traffic on the same channel and\n * those messages must not trigger an error here.\n */\nexport function createPortBridgeClient(port: BridgePortShim): PortBridgeClient {\n let themeCb: ((p: ThemePayload) => void) | undefined;\n let localeCb: ((p: LocalePayload) => void) | undefined;\n let densityCb: ((p: DensityPayload) => void) | undefined;\n let a11yCb: ((p: A11yPayload) => void) | undefined;\n let navCb: ((p: NavPayload) => void) | undefined;\n let tokenCb: ((p: SessionTokenPayload) => void) | undefined;\n\n type PendingChrome = { resolve: (v: unknown) => void; reject: (e: Error) => void };\n const pending = new Map<string, PendingChrome>();\n\n port.onmessage = (ev: { data: unknown }): void => {\n const msg = ev.data;\n if (isBridgePushTheme(msg)) { themeCb?.({ mode: msg.mode, tokens: msg.tokens }); return; }\n if (isBridgePushLocale(msg)) { localeCb?.({ locale: msg.locale, dir: msg.dir }); return; }\n if (isBridgePushDensity(msg)) { densityCb?.({ density: msg.density }); return; }\n if (isBridgePushA11y(msg)) { a11yCb?.({ reducedMotion: msg.reducedMotion, highContrast: msg.highContrast }); return; }\n if (isBridgeNavPush(msg)) { navCb?.({ path: msg.path, historyLength: msg.historyLength }); return; }\n if (isBridgeSessionTokenPush(msg)) { tokenCb?.({ token: msg.token, expiresAtMs: msg.expiresAtMs }); return; }\n if (isBridgeChromeResponse(msg)) {\n const p = pending.get(msg.id);\n if (p === undefined) { return; }\n pending.delete(msg.id);\n if (msg.ok) { p.resolve(msg.result ?? null); }\n else { p.reject(new Error(typeof msg.error === \"string\" ? msg.error : \"Chrome request failed\")); }\n }\n };\n\n return {\n onTheme(cb) { themeCb = cb; },\n onLocale(cb) { localeCb = cb; },\n onDensity(cb) { densityCb = cb; },\n onA11y(cb) { a11yCb = cb; },\n onNav(cb) { navCb = cb; },\n onSessionToken(cb) { tokenCb = cb; },\n\n requestChrome(action, payload): Promise<unknown> {\n const id = nextId();\n return new Promise<unknown>((resolve, reject) => {\n pending.set(id, { resolve, reject });\n port.postMessage({ type: BRIDGE_CHROME_REQUEST, id, action, payload });\n });\n },\n\n announceA11y(message, politeness): void {\n port.postMessage({ type: BRIDGE_A11Y_ANNOUNCE, message, politeness });\n },\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","import { useEffect, useState } from \"react\";\nimport { useBridgeClient } from \"./BridgeClientContext\";\nimport type { ThemePayload } from \"./bridge-client\";\n\n/**\n * Subscribe to host theme pushes. Returns the most recently pushed theme,\n * or `undefined` before the first push (e.g. during initial render before\n * the host has sent its first `BRIDGE_PUSH_THEME` message).\n *\n * The hook re-registers with the bridge client if the client identity changes\n * (e.g. on hot-reload of the mock host in dev), keeping state consistent.\n */\nexport function useBridgeTheme(): ThemePayload | undefined {\n const client = useBridgeClient();\n const [theme, setTheme] = useState<ThemePayload | undefined>(undefined);\n\n useEffect(() => {\n client.onTheme((payload) => setTheme(payload));\n // No cleanup needed — `onTheme` replaces the subscriber; unmounting\n // drops the hook's closure reference, preventing stale-state updates.\n }, [client]);\n\n return theme;\n}\n","import { useEffect, useState } from \"react\";\nimport { useBridgeClient } from \"./BridgeClientContext\";\nimport type { LocalePayload } from \"./bridge-client\";\n\n/**\n * Subscribe to host locale pushes. Returns the most recently pushed locale\n * and text direction, or `undefined` before the first push.\n */\nexport function useBridgeLocale(): LocalePayload | undefined {\n const client = useBridgeClient();\n const [locale, setLocale] = useState<LocalePayload | undefined>(undefined);\n\n useEffect(() => {\n client.onLocale((payload) => setLocale(payload));\n }, [client]);\n\n return locale;\n}\n","/**\n * W4A — Bridge-mediated frontend-session token hook (WI 4858 sub-plan 4).\n *\n * # What this does\n * Fetches the audience-restricted FE-session token from the host bridge\n * (resource URI: `\"ethisys:session-token\"`) and manages silent refresh.\n * The token is delivered to this hook; it is NEVER the capability JWT\n * (that stays brokered host-side and never reaches plugin JS).\n *\n * # Refresh policy\n * Refresh fires 30 s before `expiresAtUtc`. The timer is cleared on\n * unmount; no stale async operations survive component teardown.\n *\n * # Transport\n * The hook is transport-agnostic — it accepts any {@link McpTransport}.\n * Pass the context transport in production; a mock in tests.\n *\n * This is the canonical FE-session-token hook; `@ethisyscore/plugin-ui/token`\n * re-exports it so plugin authors take a single dependency on plugin-ui.\n */\nimport { useState, useEffect, useRef, useCallback } from \"react\";\nimport type { McpTransport } from \"./transport\";\n\nconst SESSION_TOKEN_URI = \"ethisys:session-token\";\nconst REFRESH_SKEW_MS = 30_000; // refresh 30 s before expiry\n\ninterface FrontendSessionTokenPayload\n{\n accessToken: string;\n expiresAtUtc: string; // ISO-8601\n jti: string;\n}\n\nexport interface UseFrontendSessionTokenResult\n{\n /** The compact-serialised JWT, or null while loading / on error. */\n token: string | null;\n /** True while the first fetch (or a refresh) is in-flight. */\n isLoading: boolean;\n /** Set when the bridge rejects; null otherwise. */\n error: Error | null;\n}\n\n/**\n * Returns the current FE-session token fetched from the host bridge.\n * Schedules silent refresh 30 s before the token expires.\n *\n * @param transport The {@link McpTransport} to use. Typically the context\n * transport from the extension runtime provider.\n */\nexport function useFrontendSessionToken(\n transport: McpTransport,\n): UseFrontendSessionTokenResult\n{\n const [token, setToken] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const refreshTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async (): Promise<void> =>\n {\n if (refreshTimerRef.current !== null)\n {\n clearTimeout(refreshTimerRef.current);\n refreshTimerRef.current = null;\n }\n try\n {\n const { data } = await transport.getResource<FrontendSessionTokenPayload>(\n SESSION_TOKEN_URI,\n );\n\n if (!mountedRef.current)\n {\n return;\n }\n\n setToken(data.accessToken);\n setError(null);\n setIsLoading(false);\n\n // Schedule refresh 30 s before expiry.\n const expiresAtMs = Date.parse(data.expiresAtUtc);\n const refreshIn = expiresAtMs - Date.now() - REFRESH_SKEW_MS;\n if (refreshIn > 0)\n {\n refreshTimerRef.current = setTimeout(() =>\n {\n void fetch();\n }, refreshIn);\n }\n }\n catch (err)\n {\n if (!mountedRef.current)\n {\n return;\n }\n setError(err instanceof Error ? err : new Error(String(err)));\n setToken(null);\n setIsLoading(false);\n }\n }, [transport]);\n\n useEffect(() =>\n {\n mountedRef.current = true;\n setIsLoading(true);\n void fetch();\n\n return (): void =>\n {\n mountedRef.current = false;\n if (refreshTimerRef.current !== null)\n {\n clearTimeout(refreshTimerRef.current);\n }\n };\n }, [fetch]);\n\n return { token, isLoading, error };\n}\n","/**\n * Pure JWT-payload decode helper (WI 5160 — F-AUTH-SEAM FE face).\n *\n * Extracts and JSON-parses the middle (payload) segment of a compact JWT.\n * URL-safe base64url characters (`-` → `+`, `_` → `/`) are normalised and\n * missing `=` padding is restored before passing to `atob`.\n *\n * This helper is intentionally side-effect-free and React-free so it can be\n * unit-tested without a DOM environment.\n *\n * **Security note:** this function does NOT validate the JWT signature —\n * the host already validated the token at mint time. The FE only reads claims.\n *\n * @param token A compact-serialised JWT (`header.payload.signature`) or null.\n * @returns The parsed payload object, or `{}` if the token is absent, malformed,\n * has fewer than 3 segments, or the payload is not valid JSON.\n */\nexport function decodeJwtPayload(token: string | null): Record<string, unknown>\n{\n if (token === null || token === \"\")\n {\n return {};\n }\n\n const segments = token.split(\".\");\n if (segments.length < 3)\n {\n return {};\n }\n\n const payloadSegment = segments[1];\n\n try\n {\n // Convert base64url → standard base64 and restore padding.\n const base64 = payloadSegment\n .replace(/-/g, \"+\")\n .replace(/_/g, \"/\");\n\n // Restore `=` padding so atob does not throw.\n const padded = base64 + \"===\".slice(0, (4 - (base64.length % 4)) % 4);\n\n const json = atob(padded);\n const parsed: unknown = JSON.parse(json);\n\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n {\n return {};\n }\n\n return parsed as Record<string, unknown>;\n }\n catch\n {\n return {};\n }\n}\n","/**\n * W5160 — `useAuth` FE gate (F-AUTH-SEAM FE face).\n *\n * # What this does\n * Wraps {@link useFrontendSessionToken} and decodes the returned compact JWT\n * to expose the caller's identity (`currentUserId`) and a short-code permission\n * gate (`hasPermission`).\n *\n * # Claim layout (host-minted, per FrontendSessionTokenIssuer)\n * - `user_id` (or `sub` as fallback) — the caller's user ID.\n * - `permissions` — a space-delimited string of short codes,\n * e.g. `\"timeslip.read timeslip.write\"`.\n *\n * # Fail-closed behaviour\n * While the token is loading, absent, or the bridge errors, `currentUserId`\n * is `null` and `hasPermission` always returns `false`.\n *\n * # Security note\n * The JWT signature is NOT re-validated here — the host validated it at mint.\n * The FE gate is advisory (UI gating); privileged execution is always\n * RBAC-gated host-side (§4.4 of the auth-seam spec).\n *\n * This is the canonical `useAuth` hook; `@ethisyscore/plugin-ui/token`\n * re-exports it so plugin authors take a single dependency on plugin-ui.\n */\nimport { useMemo } from \"react\";\nimport { useFrontendSessionToken } from \"./useFrontendSessionToken\";\nimport { decodeJwtPayload } from \"./decodeJwtPayload\";\nimport type { McpTransport } from \"./transport\";\n\nexport interface UseAuthResult\n{\n /** The caller's user ID from the FE-session JWT, or null while loading / on error. */\n currentUserId: string | null;\n /**\n * Returns true if the caller has the given short-code permission.\n * Always returns false while the token is loading or absent (fail-closed).\n *\n * @param shortCode The exact permission short code, e.g. `\"timeslip.read\"`.\n */\n hasPermission(shortCode: string): boolean;\n /** True while the first fetch (or a silent refresh) is in-flight. */\n isLoading: boolean;\n /** Set when the bridge rejects; null otherwise. */\n error: Error | null;\n}\n\n/**\n * Returns the caller's identity and permission gate derived from the\n * FE-session token.\n *\n * @param transport The {@link McpTransport} to use. Typically the context\n * transport from the extension runtime provider.\n */\nexport function useAuth(transport: McpTransport): UseAuthResult\n{\n const { token, isLoading, error } = useFrontendSessionToken(transport);\n\n const { currentUserId, permissionSet } = useMemo(() =>\n {\n const claims = decodeJwtPayload(token);\n\n const userId =\n (typeof claims[\"user_id\"] === \"string\" && claims[\"user_id\"] !== \"\" ? claims[\"user_id\"] : null) ??\n (typeof claims[\"sub\"] === \"string\" && claims[\"sub\"] !== \"\" ? claims[\"sub\"] : null);\n\n const permissionsRaw = typeof claims[\"permissions\"] === \"string\" ? claims[\"permissions\"] : \"\";\n const set = permissionsRaw.length > 0\n ? new Set(permissionsRaw.split(\" \").filter((s) => s.length > 0))\n : new Set<string>();\n\n return { currentUserId: userId, permissionSet: set };\n }, [token]);\n\n return {\n currentUserId,\n hasPermission: (shortCode: string): boolean => permissionSet.has(shortCode),\n isLoading,\n error,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/plugin/clientPush.ts","../../src/plugin/hostIdentity.ts","../../src/plugin/PluginRealtimeContext.ts","../../src/plugin/ExtensionRuntimeProvider.tsx","../../src/plugin/useMcpResource.ts","../../src/plugin/useMcpTool.ts","../../src/plugin/useMcpQuery.ts","../../src/plugin/define.ts","../../src/plugin/reconciler.ts","../../src/plugin/createRemoteRoot.ts","../../src/bridge/mcp-error.ts","../../src/plugin/createPortMcpTransport.ts","../../src/host/worker/bridge-envelopes.ts","../../src/plugin/bridge-client.ts","../../src/plugin/BridgeClientContext.ts","../../src/plugin/useBridgeTheme.ts","../../src/plugin/useBridgeLocale.ts","../../src/plugin/useFrontendSessionToken.ts","../../src/plugin/decodeJwtPayload.ts","../../src/plugin/useAuth.ts"],"names":["createContext","useContext","useRef","useEffect","useState","useCallback","nextId","useMemo"],"mappings":"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","file":"index.js","sourcesContent":["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","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useExtensionRuntimeTransport } from \"./ExtensionRuntimeProvider\";\nimport type { McpTransport } from \"./transport\";\n\nexport interface UseMcpResourceOptions\n{\n /**\n * Override the transport resolved from {@link ExtensionRuntimeProvider}.\n * Primarily intended for tests and advanced multi-host scenarios.\n */\n transport?: McpTransport;\n}\n\nexport interface UseMcpResourceResult<T>\n{\n data: T | undefined;\n error: Error | undefined;\n loading: boolean;\n /** Re-run the fetch against the current URI. Stable across renders. */\n refetch: () => void;\n}\n\n/**\n * Subscribe to an MCP resource by URI.\n *\n * The hook fetches via the resolved {@link McpTransport} on mount and whenever\n * the URI changes. Each fetch is cancellable: when the component unmounts or\n * the URI changes, the in-flight call is aborted via an {@link AbortController}\n * so stale responses cannot overwrite later state.\n *\n * `refetch` re-runs the fetch against the latest URI. The returned callback is\n * stable across renders so it is safe to include in `useEffect` dependency\n * arrays.\n */\nexport function useMcpResource<T>(\n uri: string,\n opts?: UseMcpResourceOptions,\n): UseMcpResourceResult<T>\n{\n const transport = useExtensionRuntimeTransport(opts?.transport);\n\n const [data, setData] = useState<T | undefined>(undefined);\n const [error, setError] = useState<Error | undefined>(undefined);\n const [loading, setLoading] = useState<boolean>(true);\n\n // Bump this counter to force a re-fetch from `refetch()`.\n const [refetchTick, setRefetchTick] = useState(0);\n\n // Track the active controller so `refetch` and unmount can abort it.\n const controllerRef = useRef<AbortController | null>(null);\n\n useEffect(() =>\n {\n const controller = new AbortController();\n controllerRef.current?.abort();\n controllerRef.current = controller;\n\n let cancelled = false;\n setLoading(true);\n setError(undefined);\n\n transport\n .getResource<T>(uri, controller.signal)\n .then((result) =>\n {\n if (cancelled || controller.signal.aborted)\n {\n return;\n }\n setData(result.data);\n setLoading(false);\n })\n .catch((err: unknown) =>\n {\n if (cancelled || controller.signal.aborted)\n {\n return;\n }\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n\n return () =>\n {\n cancelled = true;\n controller.abort();\n };\n }, [uri, transport, refetchTick]);\n\n const refetch = useCallback(() =>\n {\n setRefetchTick((tick) => tick + 1);\n }, []);\n\n return { data, error, loading, refetch };\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useExtensionRuntimeTransport } from \"./ExtensionRuntimeProvider\";\nimport type { McpTransport } from \"./transport\";\n\nexport interface UseMcpToolOptions\n{\n /**\n * Override the transport resolved from {@link ExtensionRuntimeProvider}.\n * Primarily intended for tests and advanced multi-host scenarios.\n */\n transport?: McpTransport;\n}\n\nexport interface UseMcpToolResult<TReq, TRes>\n{\n /**\n * Invoke the tool. Each call gets a fresh {@link AbortController} that is\n * aborted on unmount so unresolved promises cannot keep state alive after\n * the component is gone.\n */\n invoke: (req: TReq) => Promise<TRes>;\n loading: boolean;\n error: Error | undefined;\n}\n\n/**\n * Hook returning a callback that invokes an MCP tool by name through the\n * resolved {@link McpTransport}.\n *\n * `loading` and `error` track the most recent in-flight invocation. Callers\n * may also `await` the returned promise directly — failures are both rejected\n * to the caller AND surfaced via `error` so component-level UI can react.\n *\n * `invoke` is a stable reference across renders for the same `toolName` and\n * transport, making it safe to use inside `useEffect`/`useCallback` deps.\n */\nexport function useMcpTool<TReq, TRes>(\n toolName: string,\n opts?: UseMcpToolOptions,\n): UseMcpToolResult<TReq, TRes>\n{\n const transport = useExtensionRuntimeTransport(opts?.transport);\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n\n // Track the latest controller so unmount can abort an in-flight call.\n const controllerRef = useRef<AbortController | null>(null);\n const mountedRef = useRef(true);\n\n useEffect(() =>\n {\n mountedRef.current = true;\n return () =>\n {\n mountedRef.current = false;\n controllerRef.current?.abort();\n };\n }, []);\n\n const invoke = useCallback(\n async (req: TReq): Promise<TRes> =>\n {\n const controller = new AbortController();\n controllerRef.current?.abort();\n controllerRef.current = controller;\n\n setLoading(true);\n setError(undefined);\n\n try\n {\n const result = await transport.invokeTool<TReq, TRes>(\n toolName,\n req,\n controller.signal,\n );\n if (mountedRef.current && !controller.signal.aborted)\n {\n setLoading(false);\n }\n return result;\n }\n catch (err)\n {\n const wrapped = err instanceof Error ? err : new Error(String(err));\n if (mountedRef.current && !controller.signal.aborted)\n {\n setError(wrapped);\n setLoading(false);\n }\n throw wrapped;\n }\n },\n [transport, toolName],\n );\n\n return { invoke, loading, error };\n}\n","import { useEffect, useState } from \"react\";\nimport { useMcpTool } from \"./useMcpTool\";\n\n/**\n * Result shape returned by {@link useMcpQuery}. Mirrors the host-app\n * `react-query`-style envelope so consumers can render loading / error /\n * data states declaratively. `refetch` is stable across renders.\n */\nexport interface UseMcpQueryResult<TRes>\n{\n data: TRes | undefined;\n loading: boolean;\n error: Error | undefined;\n /** Re-run the fetch against the current args. Stable across renders. */\n refetch: () => void;\n}\n\n/**\n * Options for {@link useMcpQuery}.\n */\nexport interface UseMcpQueryOptions\n{\n /**\n * Gate the fetch on a guard. When false, the hook returns idle state and\n * never invokes — important because hooks must be called unconditionally\n * at the top of the component, so an `enabled` flag is the standard way\n * to express \"fetch only when a selection is present\" without violating\n * the rules of hooks. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n/**\n * Auto-fetch wrapper over the imperative {@link useMcpTool}. Fires the tool\n * call on mount AND whenever the serialised `args` change. Use for\n * read-shaped MCP tool calls that back a component's render data; for\n * mutations, call {@link useMcpTool} directly and trigger\n * `invoke(...)` from event handlers.\n *\n * Why this lives alongside {@link useMcpTool}: many plugin MCP tools are\n * effectively queries (`list-tasks`, `get-pending-approvals`, …) — paginated\n * lookups that change with filter inputs. The base hook's imperative\n * `invoke` signature is correct for mutations but ergonomically wrong for\n * reads, where every consumer ends up writing the same `useEffect` +\n * AbortController + cancellation-on-unmount boilerplate. This hook absorbs\n * that boilerplate once.\n *\n * @template TArgs The request shape sent to the tool.\n * @template TRes The response shape the tool returns.\n */\nexport function useMcpQuery<TArgs, TRes>(\n toolName: string,\n args: TArgs,\n options?: UseMcpQueryOptions,\n): UseMcpQueryResult<TRes>\n{\n const enabled = options?.enabled ?? true;\n const { invoke } = useMcpTool<TArgs, TRes>(toolName);\n const [data, setData] = useState<TRes | undefined>(undefined);\n const [loading, setLoading] = useState<boolean>(enabled);\n const [error, setError] = useState<Error | undefined>(undefined);\n const [tick, setTick] = useState(0);\n\n // Stringify args for the effect's dependency key so an args object with\n // new identity but unchanged contents does not force a refetch. The\n // shape JSON-serialises trivially because tool args are wire-bound (JSON\n // round-trips on the transport anyway).\n const argsKey = JSON.stringify(args);\n\n useEffect(() =>\n {\n if (!enabled)\n {\n setLoading(false);\n return;\n }\n let cancelled = false;\n setLoading(true);\n invoke(args)\n .then((result) =>\n {\n if (cancelled)\n {\n return;\n }\n setData(result);\n setError(undefined);\n setLoading(false);\n })\n .catch((err: unknown) =>\n {\n if (cancelled)\n {\n return;\n }\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n return () =>\n {\n cancelled = true;\n };\n // `args` is intentionally tracked via `argsKey` (serialised contents)\n // so reference-only changes don't refetch. eslint sees this as a\n // missing dep but the effect is correct as-is.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [argsKey, tick, invoke, enabled]);\n\n return {\n data,\n loading,\n error,\n refetch: () => setTick((t) => t + 1),\n };\n}\n\n// ── Response-envelope helpers ────────────────────────────────────\n//\n// MCP tool responses across the platform's BE conventions arrive in either\n// `{ items: T[] }` (raw lists) or `{ rows: T[] }` (paginated envelopes). The\n// envelope shape is a BE-side detail plugin authors don't want to encode at\n// every read site. {@link ItemsResponse} types the union and\n// {@link unwrapItems} normalises to a plain array.\n\n/**\n * Envelope shape that covers both `{ items: T[] }` and `{ rows: T[] }`\n * conventions emitted by platform / plugin MCP read tools. Optional both\n * sides so empty responses still type-check.\n */\nexport interface ItemsResponse<T>\n{\n items?: T[];\n rows?: T[];\n}\n\n/**\n * Normalise an {@link ItemsResponse} envelope to a plain array.\n * Returns `[]` when the response is `undefined` (typical pre-first-fetch\n * state) or when neither field is populated.\n */\nexport function unwrapItems<T>(response: ItemsResponse<T> | undefined): T[]\n{\n return response?.items ?? response?.rows ?? [];\n}\n","import type { RenderMode, SduiNode } from \"@ethisyscore/protocol\";\n\n/**\n * Configuration accepted by {@link defineDeclarativePlugin}.\n *\n * A Contract A (host-rendered) plugin contributes a map of resource URIs to\n * declarative SDUI trees. The host fetches a resource by URI and renders it\n * against the v1 vocabulary.\n */\nexport interface DeclarativePluginConfig\n{\n resources: Record<string, SduiNode>;\n}\n\n/**\n * Configuration accepted by {@link defineEthisysPlugin}.\n *\n * `renderMode` is constrained to the protocol's {@link RenderMode} enum so\n * authoring mistakes (`\"iframe\"`, `\"webview\"`, …) are caught at compile time.\n * `mount` is generic so Contract B remote-runtime authors can attach their\n * own mount surface without losing type information at the call site.\n */\nexport interface EthisysPluginConfig<TMount>\n{\n renderMode: RenderMode;\n mount?: TMount;\n}\n\n/**\n * Author-facing identity helper for a Contract A (host-rendered) declarative\n * plugin definition.\n *\n * The helper performs no runtime work — it exists purely so authoring sites\n * receive precise type inference and editor tooling against the protocol's\n * {@link SduiNode} contract. The returned value is the exact same reference\n * the caller passed in.\n */\nexport const defineDeclarativePlugin = (\n cfg: DeclarativePluginConfig,\n): DeclarativePluginConfig => cfg;\n\n/**\n * Author-facing identity helper for a generic EthisysCore plugin definition.\n *\n * The helper performs no runtime work. It constrains `renderMode` to the\n * protocol's {@link RenderMode} enum and preserves the inferred type of an\n * optional `mount` surface, so Contract B authors can pass through their own\n * mount object without widening it to `unknown`.\n */\nexport const defineEthisysPlugin = <TMount>(\n cfg: EthisysPluginConfig<TMount>,\n): EthisysPluginConfig<TMount> => cfg;\n","import ReactReconciler from \"react-reconciler\";\nimport {\n ConcurrentRoot,\n LegacyRoot,\n DefaultEventPriority,\n} from \"react-reconciler/constants.js\";\nimport {\n MUTATION_TYPE_INSERT_CHILD,\n MUTATION_TYPE_REMOVE_CHILD,\n MUTATION_TYPE_UPDATE_TEXT,\n MUTATION_TYPE_UPDATE_PROPERTY,\n NODE_TYPE_ELEMENT,\n NODE_TYPE_TEXT,\n ROOT_ID,\n type RemoteConnection,\n type RemoteNodeSerialization,\n} from \"@remote-dom/core\";\n\n// ── Internal node representation ───────────────────────────────────\n//\n// Lightweight JS-object stand-ins for the Remote DOM tree. Each node carries\n// a stable string id that the host uses to address mutations. We do NOT use\n// `@remote-dom/core`'s `RemoteElement` Custom Element classes because:\n// 1. They require `customElements` registration which the worker realm\n// does not have by default (would force every plugin to bundle a DOM\n// polyfill).\n// 2. The plain-object form serialises identically over the connection's\n// `RemoteNodeSerialization` contract — the host's `RemoteReceiver`\n// reconstructs proper elements on its side.\n\ninterface RemoteElementInstance {\n readonly id: string;\n readonly kind: \"element\";\n readonly type: string;\n properties: Record<string, unknown>;\n eventListeners: Record<string, (...args: unknown[]) => unknown>;\n children: RemoteNode[];\n parent: RemoteParent | null;\n}\n\ninterface RemoteTextInstance {\n readonly id: string;\n readonly kind: \"text\";\n text: string;\n parent: RemoteParent | null;\n}\n\ninterface RemoteRootContainer {\n readonly id: typeof ROOT_ID;\n readonly kind: \"root\";\n children: RemoteNode[];\n readonly connection: RemoteConnection;\n}\n\ntype RemoteNode = RemoteElementInstance | RemoteTextInstance;\ntype RemoteParent = RemoteElementInstance | RemoteRootContainer;\n\nlet nextId = 0;\nfunction newId(): string {\n nextId += 1;\n return `r-${nextId.toString(36)}`;\n}\n\nfunction serialize(node: RemoteNode): RemoteNodeSerialization {\n if (node.kind === \"text\") {\n return { id: node.id, type: NODE_TYPE_TEXT, data: node.text };\n }\n // Event-listener functions are intentionally NOT included in the\n // serialised payload — they can't be structured-cloned across a\n // MessagePort and would throw `DataCloneError` on the first commit.\n // Listeners are retained locally on the RemoteElementInstance for the\n // future `ethisys:remotedom:call` round-trip (see createRemoteRoot.ts's\n // top-of-file \"What's NOT plumbed yet\" note); until that channel lands,\n // worker-side `onClick`-style handlers are no-ops on the host. The host\n // RemoteReceiver tolerates the missing field — `eventListeners` is\n // optional on `RemoteNodeSerialization`.\n return {\n id: node.id,\n type: NODE_TYPE_ELEMENT,\n element: node.type,\n properties: node.properties,\n children: node.children.map(serialize),\n };\n}\n\nfunction indexInParent(parent: RemoteParent, child: RemoteNode): number {\n return parent.children.indexOf(child);\n}\n\n// ── Prop classification ────────────────────────────────────────────\n//\n// Children (the JSX content) are handled by the reconciler tree walk;\n// `ref` and `key` are React-internal. Everything else is forwarded as a\n// remote property OR an event listener. Convention: any prop starting with\n// `on` followed by an uppercase letter is treated as an event listener.\n\nfunction isEventListenerProp(key: string): boolean {\n return key.length > 2 && key.startsWith(\"on\") && key[2] >= \"A\" && key[2] <= \"Z\";\n}\n\nfunction eventNameFromProp(propName: string): string {\n // onClick → click. The host's RemoteReceiver expects lowercase event names.\n return propName.slice(2).toLowerCase();\n}\n\nfunction isReservedProp(key: string): boolean {\n return key === \"children\" || key === \"key\" || key === \"ref\";\n}\n\nfunction classifyProps(props: Record<string, unknown>): {\n properties: Record<string, unknown>;\n eventListeners: Record<string, (...args: unknown[]) => unknown>;\n} {\n const properties: Record<string, unknown> = {};\n const eventListeners: Record<string, (...args: unknown[]) => unknown> = {};\n for (const key of Object.keys(props)) {\n if (isReservedProp(key)) {\n continue;\n }\n const value = props[key];\n if (isEventListenerProp(key) && typeof value === \"function\") {\n eventListeners[eventNameFromProp(key)] = value as (...args: unknown[]) => unknown;\n } else if (value !== undefined) {\n properties[key] = value;\n }\n }\n return { properties, eventListeners };\n}\n\n// ── HostConfig factory ─────────────────────────────────────────────\n\n/**\n * Build the react-reconciler instance bound to the supplied `RemoteConnection`.\n * Every mutation the reconciler applies translates into a single\n * `connection.mutate([record])` call; the connection forwards the record over\n * the `MessagePort` set up by `createRemoteRoot`.\n */\nexport function createReactReconciler(connection: RemoteConnection): {\n reconciler: ReactReconciler.Reconciler<\n RemoteParent,\n RemoteElementInstance,\n RemoteTextInstance,\n never,\n RemoteElementInstance\n >;\n createContainer: () => RemoteRootContainer;\n} {\n const reconciler = ReactReconciler<\n string, // type\n Record<string, unknown>, // props\n RemoteParent, // container\n RemoteElementInstance, // instance\n RemoteTextInstance, // text instance\n never, // suspense instance\n never, // hydratable instance\n RemoteElementInstance, // public instance (returned from ref)\n object, // host context\n string[], // update payload (list of changed prop names)\n never, // child set (persistent mode only)\n ReturnType<typeof setTimeout>, // timeout handle\n -1 // no timeout sentinel\n >({\n supportsMutation: true,\n supportsPersistence: false,\n supportsHydration: false,\n isPrimaryRenderer: true,\n noTimeout: -1,\n scheduleTimeout: setTimeout,\n cancelTimeout: clearTimeout,\n getCurrentEventPriority: () => DefaultEventPriority,\n getRootHostContext: () => ({}),\n getChildHostContext: () => ({}),\n prepareForCommit: () => null,\n resetAfterCommit: () => {},\n preparePortalMount: () => {},\n shouldSetTextContent: (_type, props) =>\n typeof props.children === \"string\" || typeof props.children === \"number\",\n getPublicInstance: (instance) => instance as RemoteElementInstance,\n clearContainer: (container) => {\n // Remove every direct child from the root. The host receiver mirrors\n // each removal on its side.\n while (container.children.length > 0) {\n connection.mutate([[MUTATION_TYPE_REMOVE_CHILD, container.id, 0]]);\n container.children.shift();\n }\n },\n\n // ── createInstance / createTextInstance ────────────────────────\n createInstance: (type, props): RemoteElementInstance => {\n const { properties, eventListeners } = classifyProps(props);\n return {\n id: newId(),\n kind: \"element\",\n type,\n properties,\n eventListeners,\n children: [],\n parent: null,\n };\n },\n createTextInstance: (text): RemoteTextInstance => ({\n id: newId(),\n kind: \"text\",\n text,\n parent: null,\n }),\n\n // ── Initial mount: build the subtree before attaching to root ──\n appendInitialChild: (parent, child) => {\n parent.children.push(child);\n child.parent = parent;\n },\n finalizeInitialChildren: () => false,\n\n // ── Container-level mutations (root attach/detach) ─────────────\n appendChildToContainer: (container, child) => {\n const index = container.children.length;\n container.children.push(child);\n child.parent = container;\n connection.mutate([\n [MUTATION_TYPE_INSERT_CHILD, container.id, serialize(child), index],\n ]);\n },\n removeChildFromContainer: (container, child) => {\n const index = indexInParent(container, child);\n if (index < 0) {\n return;\n }\n container.children.splice(index, 1);\n child.parent = null;\n connection.mutate([[MUTATION_TYPE_REMOVE_CHILD, container.id, index]]);\n },\n insertInContainerBefore: (container, child, beforeChild) => {\n const beforeIndex = indexInParent(container, beforeChild);\n const insertAt = beforeIndex < 0 ? container.children.length : beforeIndex;\n container.children.splice(insertAt, 0, child);\n child.parent = container;\n connection.mutate([\n [MUTATION_TYPE_INSERT_CHILD, container.id, serialize(child), insertAt],\n ]);\n },\n\n // ── Sibling-level mutations (incremental tree updates) ─────────\n appendChild: (parent, child) => {\n const index = parent.children.length;\n parent.children.push(child);\n child.parent = parent;\n connection.mutate([\n [MUTATION_TYPE_INSERT_CHILD, parent.id, serialize(child), index],\n ]);\n },\n removeChild: (parent, child) => {\n const index = indexInParent(parent, child);\n if (index < 0) {\n return;\n }\n parent.children.splice(index, 1);\n child.parent = null;\n connection.mutate([[MUTATION_TYPE_REMOVE_CHILD, parent.id, index]]);\n },\n insertBefore: (parent, child, beforeChild) => {\n const beforeIndex = indexInParent(parent, beforeChild);\n const insertAt = beforeIndex < 0 ? parent.children.length : beforeIndex;\n parent.children.splice(insertAt, 0, child);\n child.parent = parent;\n connection.mutate([\n [MUTATION_TYPE_INSERT_CHILD, parent.id, serialize(child), insertAt],\n ]);\n },\n\n // ── Update path ────────────────────────────────────────────────\n prepareUpdate: (_instance, _type, oldProps, newProps): string[] | null => {\n const changed: string[] = [];\n const allKeys = new Set<string>([\n ...Object.keys(oldProps),\n ...Object.keys(newProps),\n ]);\n for (const key of allKeys) {\n if (isReservedProp(key)) {\n continue;\n }\n if (oldProps[key] !== newProps[key]) {\n changed.push(key);\n }\n }\n return changed.length > 0 ? changed : null;\n },\n commitUpdate: (instance, updatePayload, _type, _oldProps, newProps) => {\n if (updatePayload === null) {\n return;\n }\n for (const key of updatePayload) {\n const value = newProps[key];\n if (isEventListenerProp(key) && (typeof value === \"function\" || value === undefined)) {\n const eventName = eventNameFromProp(key);\n if (typeof value === \"function\") {\n instance.eventListeners[eventName] = value as (...args: unknown[]) => unknown;\n } else {\n delete instance.eventListeners[eventName];\n }\n // Functions can't cross a MessagePort via structured clone, so the\n // wire payload only signals whether the listener is now present or\n // cleared — `true` for \"listener exists, wire it on the host once\n // the call channel lands\", `null` for \"listener removed\". See the\n // serialize() comment for the broader Phase 1 limitation.\n connection.mutate([\n [\n MUTATION_TYPE_UPDATE_PROPERTY,\n instance.id,\n eventName,\n typeof value === \"function\" ? true : null,\n 3 /* event listener */,\n ],\n ]);\n } else {\n if (value === undefined) {\n delete instance.properties[key];\n } else {\n instance.properties[key] = value;\n }\n connection.mutate([\n [MUTATION_TYPE_UPDATE_PROPERTY, instance.id, key, value],\n ]);\n }\n }\n },\n commitTextUpdate: (textInstance, _oldText, newText) => {\n textInstance.text = newText;\n connection.mutate([[MUTATION_TYPE_UPDATE_TEXT, textInstance.id, newText]]);\n },\n\n // ── No-op methods required by the HostConfig contract ──────────\n detachDeletedInstance: () => {},\n beforeActiveInstanceBlur: () => {},\n afterActiveInstanceBlur: () => {},\n prepareScopeUpdate: () => {},\n getInstanceFromScope: () => null,\n getInstanceFromNode: () => null,\n\n // Microtask scheduling — use the platform's default queueing.\n supportsMicrotasks: true,\n scheduleMicrotask:\n typeof queueMicrotask === \"function\"\n ? queueMicrotask\n : (callback) => Promise.resolve().then(callback),\n });\n\n function createContainer(): RemoteRootContainer {\n return { id: ROOT_ID, kind: \"root\", children: [], connection };\n }\n\n return { reconciler, createContainer };\n}\n\nexport { ConcurrentRoot, LegacyRoot };\nexport type { RemoteRootContainer, RemoteElementInstance, RemoteTextInstance };\n","/**\n * Contract B (worker remote-runtime) plugin-side React root.\n *\n * # The problem this solves\n *\n * A Contract B plugin runs in a sandboxed Web Worker — no `document`, no\n * `window`, no DOM. The host owns rendering: the worker constructs a\n * `RemoteElement` tree via `@remote-dom/core`, mutations forward over a\n * `MessagePort`, the host receives them and commits them to a real React tree\n * (see `coreconnect-web/src/extensions/runtime/WorkerSurfaceMount.tsx` for the\n * receiver side).\n *\n * Until now plugin authors had to hand-author the `RemoteElement` construction\n * + mutation forwarding manually. `@remote-dom/react` ships only the host-side\n * primitives (`createRemoteComponent`, `RemoteRootRenderer`, etc.) — there is\n * no worker-side React reconciler in the package. This helper provides one.\n *\n * # What this is\n *\n * The public surface (`createRemoteRoot(port, options)`) plus a working\n * `react-reconciler` HostConfig that commits to a Remote DOM tree and\n * forwards mutation records over the `MessagePort` to the host receiver.\n * Authors call `root.render(<App />)` and the host's `WorkerSurfaceMount`\n * sees the result.\n *\n * # What's NOT plumbed yet (Phase 1 limitation)\n *\n * Event-listener round-trip. The host transport currently has no\n * `ethisys:remotedom:call` channel — `WorkerRemoteDomTransport` only\n * forwards `ethisys:remotedom` payloads from worker → host, never the\n * other direction. So a worker-side `onClick={() => ...}` is **registered\n * locally** but the host can't call it. The reconciler retains every\n * listener on its in-memory `RemoteElementInstance` so the wiring is ready\n * the moment the call channel lands; until then, plugin authors should\n * keep interactive surfaces on Contract A.\n *\n * # The API\n *\n * ```ts\n * import { createRemoteRoot } from \"@ethisyscore/extension-runtime/plugin\";\n *\n * export async function activate(port: MessagePort): Promise<void> {\n * const root = createRemoteRoot(port);\n * root.render(<App />);\n * }\n * ```\n *\n * `<App />` is plain React. Any component shipped by the host's frozen\n * import-map allowlist (the closed Contract B primitive vocabulary —\n * `Button`, `DataTable`, `Form`, `Card`, `Tabs`, `Select`, `Alert`, etc.)\n * renders. The reconciler walks the React tree and commits to a\n * `RemoteRootElement`; mutation records forward over the port; the host's\n * `RemoteReceiver` commits the result into the real React tree.\n *\n * # Wire shape\n *\n * Every reconciler mutation is posted as\n * `{ type: \"ethisys:remotedom\", payload: RemoteMutationRecord[] }` to match\n * `WorkerRemoteDomTransport`'s `RemoteDomMessage` contract — the host\n * extracts `payload` and feeds it straight into `receiver.mutate(records)`.\n * `BatchingRemoteConnection` (controlled via `options.batchMutations`)\n * collapses contiguous mutations into a single port post per commit.\n */\nimport type { RemoteConnection, RemoteMutationRecord } from \"@remote-dom/core\";\nimport { BatchingRemoteConnection } from \"@remote-dom/core/elements\";\nimport type { ReactNode } from \"react\";\nimport {\n createReactReconciler,\n LegacyRoot,\n type RemoteRootContainer,\n} from \"./reconciler\";\n\n/**\n * Options for {@link createRemoteRoot}. Reserved for forward compatibility —\n * Phase 1 ships with zero required options. Adding fields here is additive;\n * removing them is a breaking change.\n */\nexport interface CreateRemoteRootOptions\n{\n /**\n * When supplied, the reconciler batches contiguous mutations into a single\n * `port.postMessage` rather than firing one per mutation. Default `true`\n * — measurably reduces host-side commit cost on the first render of a\n * non-trivial tree.\n */\n batchMutations?: boolean;\n\n /**\n * Override the connection factory. Reserved for tests; production callers\n * never pass this. The factory's contract is \"build a RemoteConnection\n * that forwards mutations over the supplied port\"; the default uses\n * `createRemoteConnection` from `@remote-dom/core`.\n */\n connectionFactory?: (port: MessagePort) => RemoteConnection;\n}\n\n/**\n * Plugin-side React root. Mirrors the shape of `ReactDOMClient.Root` so\n * authors familiar with `createRoot().render(...)` find the same API on the\n * worker side.\n */\nexport interface RemoteRoot\n{\n /**\n * Render a React element tree against the worker's `RemoteRootElement`.\n * The reconciler commits to the root, the mutation observer forwards\n * mutations over the port, the host re-renders. Idempotent — calling\n * `render` twice with the same element is fine; the reconciler dedupes.\n */\n render(element: ReactNode): void;\n\n /**\n * Tear down the reconciled tree and stop forwarding mutations. Authors\n * should call this from a `Symbol.dispose` or equivalent when the worker\n * is shutting down — leaking the reconciler holds the `MessagePort` open\n * and prevents the worker from being collected.\n */\n unmount(): void;\n}\n\n/**\n * Construct a plugin-side React root that commits to a `RemoteRootElement`\n * and forwards mutations over the supplied `MessagePort`.\n *\n * **API stability:** the function signature is stable for Phase 1. The\n * options bag is forward-compatible (additive only).\n *\n * **Implementation status:** the connection + root construction lands in this\n * commit. The `react-reconciler` `HostConfig` is a scaffold — `render()`\n * throws a structured error directing authors to the W1A tracking issue.\n * Authors should treat this commit as \"the API is locked, the reconciler is\n * being authored.\" See follow-on commits on the `feature/contract-b-create-remote-root-w1a`\n * branch.\n */\nexport function createRemoteRoot(\n port: MessagePort,\n options: CreateRemoteRootOptions = {},\n): RemoteRoot\n{\n if (port === undefined || port === null)\n {\n throw new TypeError(\n \"[createRemoteRoot] `port` is required. Pass the MessagePort the host transferred via the worker's `activate(port)` entry point.\",\n );\n }\n\n // Build the connection that forwards Remote DOM mutations over the port.\n // The default factory uses `@remote-dom/core`'s `createRemoteConnection`;\n // tests inject their own factory via `options.connectionFactory`.\n const baseConnection = (options.connectionFactory ?? defaultConnectionFactory)(port);\n\n const connection: RemoteConnection = options.batchMutations === false\n ? baseConnection\n : new BatchingRemoteConnection(baseConnection);\n\n // W1A — lazy reconciler + container construction. The reconciler is built\n // once on first render(); subsequent calls reuse the same container so React\n // commits behave like ReactDOM.createRoot(...).render(...) — call render\n // multiple times to update; call unmount to tear down.\n const { reconciler, createContainer } = createReactReconciler(connection);\n let container: RemoteRootContainer | null = null;\n let fiberRoot: ReturnType<typeof reconciler.createContainer> | null = null;\n\n return {\n render(element: ReactNode): void\n {\n if (container === null)\n {\n container = createContainer();\n fiberRoot = reconciler.createContainer(\n container,\n LegacyRoot,\n null, // hydration callbacks\n false, // isStrictMode\n null, // concurrentUpdatesByDefaultOverride\n \"\", // identifierPrefix\n (error: Error) =>\n {\n // The host's SurfaceErrorBoundary catches render-time\n // failures via the connection's call() path; mirroring\n // them locally as a console.error is the best we can do\n // from inside the worker.\n // eslint-disable-next-line no-console -- worker console is pumped to the host logger.\n console.error(\"[createRemoteRoot] render error\", error);\n },\n null, // transitionCallbacks\n );\n }\n reconciler.updateContainer(element, fiberRoot!, null, null);\n },\n unmount(): void\n {\n if (fiberRoot !== null)\n {\n reconciler.updateContainer(null, fiberRoot, null, null);\n fiberRoot = null;\n container = null;\n }\n // The connection itself does NOT close the port — plugins may have\n // side-channel consumers. Authors that want a hard tear-down call\n // `port.close()` themselves after unmount().\n },\n };\n}\n\n// ── Internals ──────────────────────────────────────────────────────────\n\n/**\n * Default factory: build a `RemoteConnection` that posts mutation records\n * as the `payload` of a single `ethisys:remotedom` port message — the\n * wire shape `WorkerRemoteDomTransport`'s `RemoteDomMessage` declares\n * (see `host/worker/transport.ts`'s `handlePortMessage`: it forwards\n * `(message as RemoteDomMessage).payload` straight into the host's\n * `RemoteReceiver.mutate(records)` call).\n *\n * Going through `createRemoteConnection` (which fires per-op callbacks)\n * was the wrong abstraction for this wire — the host expects the raw\n * record list under `payload`, not bespoke `op`-suffixed envelopes. By\n * implementing the connection directly we hand the reconciler's\n * mutation records straight to the host receiver without translation.\n *\n * Wrapping in `BatchingRemoteConnection` (controlled via\n * `options.batchMutations`) collapses contiguous mutation calls into a\n * single `port.postMessage` payload, which is the difference between \"one\n * postMessage per appended child\" and \"one postMessage per React commit\"\n * — measurably cheaper for the first paint of a non-trivial tree.\n */\nfunction defaultConnectionFactory(port: MessagePort): RemoteConnection\n{\n return {\n mutate(records: readonly RemoteMutationRecord[]): void\n {\n // Snapshot to a plain array so the host receiver isn't handed a\n // reference into the reconciler's internal buffer (the BatchingRemoteConnection\n // wrapper recycles its own array between flushes).\n port.postMessage({ type: \"ethisys:remotedom\", payload: records.slice() });\n },\n call(_id, _method, ..._args): unknown\n {\n // Worker → host method invocation. Reserved for the future\n // `ethisys:remotedom:call` channel — `WorkerRemoteDomTransport`\n // doesn't yet handle a `call`-shaped reply path, so event-\n // listener round-trip is still on the Phase 1 follow-on list\n // (see this file's top-of-file \"What's NOT plumbed yet\" note).\n return undefined;\n },\n };\n}\n\n","/**\n * A machine-readable classification for an MCP call that failed, carried across the host/plugin\n * bridge alongside the human-readable message.\n *\n * ## Why this exists\n *\n * The bridge used to reduce every failure to `err.message`, a bare string. A plugin therefore could\n * not tell \"you lack permission for this tool\" from \"the host is unreachable\" without matching on\n * host error prose, and the practical consequence was that plugins swallowed *all* failures into a\n * successful empty result to keep a permission denial from looking like a crash. That turns a\n * transient outage into \"you have no connectors\" - a false statement about the user's data, and one\n * that also suppresses the query layer's retry.\n *\n * A closed vocabulary lets the plugin branch on the one case it wants to tolerate and rethrow the\n * rest.\n *\n * ## Deliberately small, and deliberately not HTTP\n *\n * These are the distinctions a *caller* acts on differently, not a mirror of any status enum. Codes\n * that would prompt the same handling are folded together, because a vocabulary nobody can apply is\n * just a wider surface to get wrong. `unavailable` and `timeout` stay separate only because retry\n * policy differs between them.\n */\nexport type McpErrorCode =\n /** The caller is not authenticated, or its token expired. Re-authentication may fix it. */\n | \"unauthorized\"\n /** Authenticated, but not permitted this tool or resource. Retrying will not help. */\n | \"forbidden\"\n /** The tool, resource, or addressed entity does not exist. */\n | \"not_found\"\n /** The arguments were rejected. A caller bug, or stale client-side validation. */\n | \"invalid_request\"\n /** A concurrency or state conflict - a row version, or a duplicate. */\n | \"conflict\"\n /** Throttled. Retry later, with backoff. */\n | \"rate_limited\"\n /** The call did not complete in time. Safe to retry only if the operation is idempotent. */\n | \"timeout\"\n /** The host or an upstream dependency is down. Retryable. */\n | \"unavailable\"\n /** Anything else, including an unclassifiable failure. The default - never a claim. */\n | \"internal\";\n\n/**\n * Every valid {@link McpErrorCode}. Used to validate a code arriving over the wire: an unknown\n * string is downgraded rather than trusted, so a newer host cannot make an older plugin branch on a\n * code it has never heard of.\n */\nexport const MCP_ERROR_CODES: readonly McpErrorCode[] = [\n \"unauthorized\",\n \"forbidden\",\n \"not_found\",\n \"invalid_request\",\n \"conflict\",\n \"rate_limited\",\n \"timeout\",\n \"unavailable\",\n \"internal\",\n];\n\n/** True when `value` is a code this build understands. */\nexport function isMcpErrorCode(value: unknown): value is McpErrorCode\n{\n return typeof value === \"string\"\n && (MCP_ERROR_CODES as readonly string[]).includes(value);\n}\n\n/**\n * Codes a caller can retry without changing anything about the request. Exposed so retry policy is\n * decided once here rather than re-derived, subtly differently, at each call site.\n *\n * `unauthorized` is absent on purpose: a retry only helps once something else has refreshed the\n * token, which is a different action from retrying.\n */\nconst RETRYABLE: readonly McpErrorCode[] = [\"rate_limited\", \"timeout\", \"unavailable\"];\n\n/** True when the failure is worth retrying as-is. */\nexport function isRetryableMcpErrorCode(code: McpErrorCode): boolean\n{\n return RETRYABLE.includes(code);\n}\n\n/**\n * An MCP call rejected by the host, carrying its {@link McpErrorCode}.\n *\n * `name` stays `\"McpHostError\"` - the string the bridge has always set - so code that matches on the\n * name keeps working. Prefer {@link isMcpToolError}, which survives a name change and works across\n * realm boundaries where `instanceof` does not.\n */\nexport class McpToolError extends Error\n{\n /** Machine-readable classification. `internal` when the host sent none. */\n public readonly code: McpErrorCode;\n\n /**\n * Marks the instance for {@link isMcpToolError}. A structural marker rather than a prototype\n * check because the error can be constructed in one bundle and inspected in another, where\n * `instanceof` compares two different class objects and answers false.\n */\n public readonly isMcpToolError = true as const;\n\n public constructor(message: string, code: McpErrorCode = \"internal\")\n {\n super(message);\n this.name = \"McpHostError\";\n this.code = code;\n }\n\n /** True when this failure is worth retrying unchanged. */\n public get retryable(): boolean\n {\n return isRetryableMcpErrorCode(this.code);\n }\n}\n\n/** True when `value` is an {@link McpToolError}, including one from another bundle. */\nexport function isMcpToolError(value: unknown): value is McpToolError\n{\n return value instanceof McpToolError\n || (value !== null\n && typeof value === \"object\"\n && (value as { isMcpToolError?: unknown }).isMcpToolError === true\n && isMcpErrorCode((value as { code?: unknown }).code));\n}\n\n/**\n * Maps an HTTP status onto a code. Split out because host MCP clients are commonly HTTP clients, so\n * a status is the classification most of them already have.\n *\n * Unrecognised statuses (including every 2xx and 3xx, which should not be reaching an error path)\n * yield `internal` rather than a guess.\n */\nexport function mcpErrorCodeFromHttpStatus(status: number): McpErrorCode\n{\n switch (status)\n {\n case 401:\n return \"unauthorized\";\n case 403:\n return \"forbidden\";\n case 404:\n return \"not_found\";\n case 400:\n case 422:\n return \"invalid_request\";\n case 409:\n case 412:\n return \"conflict\";\n case 429:\n return \"rate_limited\";\n case 408:\n case 504:\n return \"timeout\";\n case 502:\n case 503:\n return \"unavailable\";\n default:\n // Everything unmapped, 500 included. `internal` is the honest answer for a status this\n // vocabulary has no distinct handling for - inventing a closer-looking code would tell\n // the caller something the status does not actually say.\n return \"internal\";\n }\n}\n\n/**\n * Derives a code from a host MCP client's RETURNED failure, as opposed to a thrown one.\n *\n * Both paths exist and both have to be covered. A client that throws is handled by\n * {@link classifyHostError}; a client that reports failure as `{ ok: false, error, status }` - the\n * common shape for anything wrapping HTTP - comes through here. Covering only the throw path leaves\n * the majority of real failures arriving as `internal`, which is the same blindness the code was\n * added to remove.\n *\n * Returns `undefined` for a successful response, so a caller can spread it without putting a\n * meaningless code on the happy path.\n */\nexport function classifyHostResponse(response: {\n readonly ok: boolean;\n readonly status?: number;\n readonly mcpErrorCode?: unknown;\n}): McpErrorCode | undefined\n{\n if (response.ok)\n {\n return undefined;\n }\n\n // Same precedence as the thrown path: an explicit code from the client beats the transport's\n // status, and neither is ever inferred from the message.\n if (isMcpErrorCode(response.mcpErrorCode))\n {\n return response.mcpErrorCode;\n }\n\n return typeof response.status === \"number\"\n ? mcpErrorCodeFromHttpStatus(response.status)\n : \"internal\";\n}\n\n/**\n * Derives a code from an arbitrary thrown value, for the host side of the bridge.\n *\n * Precedence, most explicit first:\n *\n * 1. An `mcpErrorCode` property holding a known code. The intended contract: a host MCP client that\n * knows why a call failed says so directly.\n * 2. A numeric `status` / `statusCode`, mapped by {@link mcpErrorCodeFromHttpStatus}. Covers the\n * HTTP clients that already carry one without asking every host to adopt the field above.\n * 3. `name === \"AbortError\"` / `\"TimeoutError\"`, which is how the platform's own aborts surface.\n * 4. `internal`.\n *\n * **Never infers from the message.** Matching prose would make the classification depend on wording\n * nobody treats as a contract, and it would silently reclassify itself the day someone improves an\n * error string. An unclassifiable failure is `internal`, which is honest.\n */\nexport function classifyHostError(err: unknown): McpErrorCode\n{\n if (err === null || typeof err !== \"object\")\n {\n return \"internal\";\n }\n\n const candidate = err as {\n mcpErrorCode?: unknown;\n status?: unknown;\n statusCode?: unknown;\n name?: unknown;\n };\n\n if (isMcpErrorCode(candidate.mcpErrorCode))\n {\n return candidate.mcpErrorCode;\n }\n\n const status = typeof candidate.status === \"number\"\n ? candidate.status\n : typeof candidate.statusCode === \"number\" ? candidate.statusCode : undefined;\n\n if (status !== undefined)\n {\n return mcpErrorCodeFromHttpStatus(status);\n }\n\n if (candidate.name === \"AbortError\" || candidate.name === \"TimeoutError\")\n {\n return \"timeout\";\n }\n\n return \"internal\";\n}\n","/**\n * W1B — MessagePort-backed McpTransport for Contract B (worker remote-runtime)\n * plugins.\n *\n * The plugin-side React hooks (`useMcpResource`, `useMcpTool`) consume an\n * {@link McpTransport} — an abstraction over the host call. Contract A\n * (host-rendered) plugins pick up the host-injected transport via\n * `ExtensionRuntimeProvider`. Contract B plugins run in a Web Worker with\n * no shared object surface — the only channel is the `MessagePort` the\n * host transferred via `activate(port)`. This helper bridges the two\n * worlds: it exposes the {@link McpTransport} contract on the worker side\n * and serialises every call into a request/response envelope over the\n * port.\n *\n * # The protocol on the wire\n *\n * The wire shape mirrors `WorkerRemoteDomTransport` (in `host/worker/transport.ts`)\n * one-for-one — that transport is the host receiver and decides what a \"well-\n * formed\" message looks like. Two request shapes, two `:result`-suffixed reply\n * shapes, one abort envelope. Each call mints a fresh request id (`req-{n}`).\n *\n * ```jsonc\n * // worker → host\n * { type: \"ethisys:mcp:getResource\", id: \"req-3\", uri: \"tickets://home\" }\n * { type: \"ethisys:mcp:invokeTool\", id: \"req-4\", name: \"tickets:open\", args: { ... } }\n *\n * // host → worker (matching id, suffixed type)\n * { type: \"ethisys:mcp:getResource:result\", id: \"req-3\", ok: true, data: { uri, data } }\n * { type: \"ethisys:mcp:invokeTool:result\", id: \"req-4\", ok: false, error: \"...\" }\n * ```\n *\n * Requests honour the optional `AbortSignal`. On abort, the transport posts\n * a `{ type: \"ethisys:mcp:abort\", id }` envelope so the host can cancel\n * in-flight work, then rejects the pending promise with an `AbortError`-shaped\n * `Error` so the consuming hooks see the same shape as native fetch cancellation.\n *\n * # Authoring shape\n *\n * ```ts\n * export async function activate(port: MessagePort): Promise<void> {\n * const transport = createPortMcpTransport(port);\n * const root = createRemoteRoot(port);\n * root.render(\n * <ExtensionRuntimeProvider transport={transport}>\n * <App />\n * </ExtensionRuntimeProvider>,\n * );\n * }\n * ```\n *\n * `<App />` uses `useMcpResource` / `useMcpTool` as it would on the host\n * side; the transport translates each call into the port envelope.\n */\nimport type { McpTransport, UploadDocumentMeta, UploadDocumentResult } from \"./transport\";\nimport { McpToolError, isMcpErrorCode } from \"../bridge/mcp-error\";\n\n/**\n * Options for {@link createPortMcpTransport}. Reserved for forward\n * compatibility — the public surface is empty in Phase 1.\n */\nexport interface CreatePortMcpTransportOptions\n{\n /**\n * Override the request-id generator. Reserved for tests so they can\n * make request ids deterministic. Production callers never pass this.\n */\n requestIdFactory?: () => string;\n\n /**\n * Override the port's `addEventListener` / `removeEventListener` /\n * `postMessage` triple. Reserved for tests so the transport can be\n * exercised without instantiating a real MessageChannel.\n */\n portShimForTests?: PortShim;\n}\n\n/**\n * Minimal subset of the MessagePort surface the transport actually uses.\n * Exposed so tests can construct a polyfill without faking the full\n * MessagePort.\n */\nexport interface PortShim\n{\n addEventListener(\n type: \"message\",\n listener: (event: { data: unknown }) => void,\n ): void;\n removeEventListener(\n type: \"message\",\n listener: (event: { data: unknown }) => void,\n ): void;\n postMessage(value: unknown, transfer?: Transferable[]): void;\n}\n\n// ── Wire-shape constants ────────────────────────────────────────────\n//\n// These four strings are the contract with `WorkerRemoteDomTransport`. Any\n// rename here breaks the host receiver silently — the unknown message hits\n// the host's default branch and is dropped. Keep these in sync with\n// `host/worker/transport.ts`'s `handlePortMessage` switch / result-type\n// strings.\nconst WIRE_INVOKE_TOOL_REQUEST = \"ethisys:mcp:invokeTool\";\nconst WIRE_INVOKE_TOOL_RESULT = \"ethisys:mcp:invokeTool:result\";\nconst WIRE_GET_RESOURCE_REQUEST = \"ethisys:mcp:getResource\";\nconst WIRE_GET_RESOURCE_RESULT = \"ethisys:mcp:getResource:result\";\nconst WIRE_UPLOAD_DOCUMENT_REQUEST = \"ethisys:mcp:uploadDocument\";\nconst WIRE_UPLOAD_DOCUMENT_RESULT = \"ethisys:mcp:uploadDocument:result\";\nconst WIRE_ABORT = \"ethisys:mcp:abort\";\n\ntype McpResultType =\n | typeof WIRE_INVOKE_TOOL_RESULT\n | typeof WIRE_GET_RESOURCE_RESULT\n | typeof WIRE_UPLOAD_DOCUMENT_RESULT;\n\ninterface McpResultEnvelope\n{\n type: McpResultType;\n id: string;\n ok?: boolean;\n error?: string;\n /**\n * Machine-readable failure classification. Optional: a host built before the bridge carried one\n * sends none, and those replies must still settle rather than being treated as malformed.\n */\n code?: unknown;\n data?: unknown;\n}\n\nfunction isMcpResultEnvelope(value: unknown): value is McpResultEnvelope\n{\n if (value === null || typeof value !== \"object\")\n {\n return false;\n }\n const v = value as { type?: unknown; id?: unknown };\n return (v.type === WIRE_INVOKE_TOOL_RESULT\n || v.type === WIRE_GET_RESOURCE_RESULT\n || v.type === WIRE_UPLOAD_DOCUMENT_RESULT)\n && typeof v.id === \"string\";\n}\n\n/**\n * Construct an {@link McpTransport} backed by a MessagePort.\n *\n * @param port The host-transferred MessagePort for the worker surface.\n * @param options Reserved for forward compatibility / test injection.\n *\n * @returns A transport implementation honouring the {@link McpTransport}\n * contract — fetch a resource, invoke a tool, observe abort\n * signals, settle the promise on the host's reply envelope.\n */\nexport function createPortMcpTransport(\n port: MessagePort | PortShim,\n options: CreatePortMcpTransportOptions = {},\n): McpTransport\n{\n if (port === undefined || port === null)\n {\n throw new TypeError(\n \"[createPortMcpTransport] `port` is required. Pass the MessagePort the host transferred via the worker's `activate(port)` entry point.\",\n );\n }\n\n const shim: PortShim = options.portShimForTests ?? port as PortShim;\n const factory = options.requestIdFactory ?? defaultRequestIdFactory();\n\n type Resolver = {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n };\n const pending = new Map<string, Resolver>();\n\n function onMessage(event: { data: unknown }): void\n {\n if (!isMcpResultEnvelope(event.data))\n {\n return;\n }\n const reply = event.data;\n const resolver = pending.get(reply.id);\n if (resolver === undefined)\n {\n return;\n }\n pending.delete(reply.id);\n if (reply.ok === true)\n {\n // Host posts the payload under `data` (matching\n // `WorkerRemoteDomTransport.safePostMessage`). For getResource\n // calls the data is the resource body; for invokeTool calls it\n // is the tool's return value.\n resolver.resolve(reply.data);\n }\n else\n {\n // The code is VALIDATED, not trusted: an unknown string from a newer host degrades to\n // `internal` rather than reaching plugin code that cannot branch on it. A host with no\n // `code` at all also lands on `internal`, so an older host keeps working and simply\n // offers no classification.\n resolver.reject(new McpToolError(\n typeof reply.error === \"string\" ? reply.error : \"Unknown host error\",\n isMcpErrorCode(reply.code) ? reply.code : \"internal\",\n ));\n }\n }\n\n shim.addEventListener(\"message\", onMessage);\n // MessagePort.addEventListener doesn't auto-start the port — without an\n // explicit `start()`, the message queue stays paused and the host's\n // replies never reach `onMessage`. This is a no-op on plain\n // EventTarget-shaped shims used in tests (the type guard guarantees\n // safety) but is required on real MessagePort instances.\n const realPort = port as Partial<MessagePort>;\n if (!options.portShimForTests && typeof realPort.start === \"function\")\n {\n realPort.start();\n }\n\n function send(\n requestType:\n | typeof WIRE_INVOKE_TOOL_REQUEST\n | typeof WIRE_GET_RESOURCE_REQUEST\n | typeof WIRE_UPLOAD_DOCUMENT_REQUEST,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n transfer?: Transferable[],\n ): Promise<unknown>\n {\n const id = factory();\n return new Promise<unknown>((resolve, reject) =>\n {\n if (signal?.aborted === true)\n {\n reject(abortError());\n return;\n }\n pending.set(id, { resolve, reject });\n\n const onAbort = (): void =>\n {\n if (!pending.has(id))\n {\n return;\n }\n pending.delete(id);\n shim.postMessage({ type: WIRE_ABORT, id });\n reject(abortError());\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n // `transfer` moves the ArrayBuffer zero-copy for uploadDocument; the\n // JSON request shapes pass no transfer list and post by structured clone.\n shim.postMessage({ type: requestType, id, ...body }, transfer);\n });\n }\n\n return {\n async getResource<T>(uri: string, signal?: AbortSignal): Promise<{ uri: string; data: T }>\n {\n // Host sends only the resource body under `data`. Reconstruct the\n // `{ uri, data }` envelope the McpTransport contract returns —\n // `uri` is round-tripped from the request because the resolver\n // only sees the reply.\n const data = await send(WIRE_GET_RESOURCE_REQUEST, { uri }, signal);\n return { uri, data: data as T };\n },\n async invokeTool<TReq, TRes>(name: string, args: TReq, signal?: AbortSignal): Promise<TRes>\n {\n const data = await send(WIRE_INVOKE_TOOL_REQUEST, { name, args: args as unknown }, signal);\n return data as TRes;\n },\n async uploadDocument(\n meta: UploadDocumentMeta,\n buffer: ArrayBuffer,\n signal?: AbortSignal,\n ): Promise<UploadDocumentResult>\n {\n // The ArrayBuffer rides as a transferable — after posting, `buffer`\n // is detached on the worker side (zero-copy hand-off to the host).\n const data = await send(\n WIRE_UPLOAD_DOCUMENT_REQUEST,\n { meta, buffer },\n signal,\n [buffer],\n );\n return data as UploadDocumentResult;\n },\n };\n}\n\nfunction defaultRequestIdFactory(): () => string\n{\n let counter = 0;\n return (): string =>\n {\n counter += 1;\n return `req-${counter}`;\n };\n}\n\nfunction abortError(): Error\n{\n const e = new Error(\"Aborted\");\n e.name = \"AbortError\";\n return e;\n}\n","/**\n * Wire-shape constants and type guards for host→plugin bridge push messages\n * (WI 4858 sub-plan 2). These types ride the existing MessagePort channel\n * alongside the `ethisys:mcp:*` and `ethisys:remotedom` envelopes — the\n * `type` discriminant keeps them fully separate at the dispatch site.\n *\n * The host posts bridge pushes through `WorkerRemoteDomTransport.hostPort`;\n * the plugin side reads them in `createPortBridgeClient` (plugin-ui package).\n * In-realm (Tier T) plugins use `InMemoryBridgeTransport` which calls the\n * subscriber callbacks directly — no serialisation needed.\n */\n\n// ── Type constants ───────────────────────────────────────────────────────────\n\nexport const BRIDGE_PUSH_THEME = \"ethisys:bridge:theme\" as const;\nexport const BRIDGE_PUSH_LOCALE = \"ethisys:bridge:locale\" as const;\nexport const BRIDGE_PUSH_DENSITY = \"ethisys:bridge:density\" as const;\nexport const BRIDGE_PUSH_A11Y = \"ethisys:bridge:a11y\" as const;\nexport const BRIDGE_NAV_PUSH = \"ethisys:bridge:nav\" as const;\nexport const BRIDGE_CHROME_REQUEST = \"ethisys:bridge:chrome:request\" as const;\nexport const BRIDGE_CHROME_RESPONSE = \"ethisys:bridge:chrome:response\" as const;\nexport const BRIDGE_SESSION_TOKEN_PUSH = \"ethisys:bridge:token\" as const;\nexport const BRIDGE_LIFECYCLE_EVENT = \"ethisys:bridge:lifecycle\" as const;\nexport const BRIDGE_A11Y_ANNOUNCE = \"ethisys:bridge:a11y:announce\" as const;\n\n// ── Envelope shapes ──────────────────────────────────────────────────────────\n\n/** Theme push: host → plugin whenever the host theme changes. */\nexport interface BridgePushThemeEnvelope {\n readonly type: typeof BRIDGE_PUSH_THEME;\n /** \"light\" | \"dark\" | \"high-contrast\" */\n readonly mode: string;\n /** Flat design-token map (CSS-variable-name → value). May be empty. */\n readonly tokens: Readonly<Record<string, string>>;\n}\n\n/** Locale push: host → plugin whenever the active locale changes. */\nexport interface BridgePushLocaleEnvelope {\n readonly type: typeof BRIDGE_PUSH_LOCALE;\n /** BCP 47 tag, e.g. \"en-GB\". */\n readonly locale: string;\n /** \"ltr\" | \"rtl\" */\n readonly dir: \"ltr\" | \"rtl\";\n}\n\n/** Density push: host → plugin when the UI density preference changes. */\nexport interface BridgePushDensityEnvelope {\n readonly type: typeof BRIDGE_PUSH_DENSITY;\n /** \"comfortable\" | \"compact\" */\n readonly density: string;\n}\n\n/** A11y push: host → plugin when accessibility prefs change. */\nexport interface BridgePushA11yEnvelope {\n readonly type: typeof BRIDGE_PUSH_A11Y;\n readonly reducedMotion: boolean;\n readonly highContrast: boolean;\n}\n\n/** Nav push: host → plugin when the SPA location changes. */\nexport interface BridgeNavPushEnvelope {\n readonly type: typeof BRIDGE_NAV_PUSH;\n /** Current SPA path (pathname + search). */\n readonly path: string;\n /** `window.history.length` at the time of push. */\n readonly historyLength: number;\n}\n\n/** Plugin → host: request a chrome action (e.g. toast, modal). */\nexport interface BridgeChromeRequestEnvelope {\n readonly type: typeof BRIDGE_CHROME_REQUEST;\n /** Correlation id — matched against the response. */\n readonly id: string;\n /** \"toast\" | \"confirm\" | \"openUrl\" */\n readonly action: string;\n readonly payload: Readonly<Record<string, unknown>>;\n}\n\n/** Host → plugin: response to a chrome request. */\nexport interface BridgeChromeResponseEnvelope {\n readonly type: typeof BRIDGE_CHROME_RESPONSE;\n readonly id: string;\n readonly ok: boolean;\n readonly result?: unknown;\n readonly error?: string;\n}\n\n/** Host → plugin: push a short-lived frontend-session token. */\nexport interface BridgeSessionTokenPushEnvelope {\n readonly type: typeof BRIDGE_SESSION_TOKEN_PUSH;\n /** Opaque JWT string — audience-restricted to the plugin's own backend. */\n readonly token: string;\n /** Absolute epoch-ms at which the token expires. Refresh fires 30 s before. */\n readonly expiresAtMs: number;\n}\n\n/** Host ↔ plugin lifecycle event. */\nexport type BridgeLifecyclePhase = \"load\" | \"ready\" | \"error\" | \"unload\";\n\nexport interface BridgeLifecycleEventEnvelope {\n readonly type: typeof BRIDGE_LIFECYCLE_EVENT;\n readonly phase: BridgeLifecyclePhase;\n /** Present when phase === \"error\". */\n readonly message?: string;\n}\n\n/** Plugin → host: a11y live-region announcement request. */\nexport interface BridgeA11yAnnounceEnvelope {\n readonly type: typeof BRIDGE_A11Y_ANNOUNCE;\n readonly message: string;\n /** \"polite\" | \"assertive\" */\n readonly politeness: \"polite\" | \"assertive\";\n}\n\n// ── Type guards ──────────────────────────────────────────────────────────────\n\nfunction isObj(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === \"object\";\n}\n\nexport function isBridgePushTheme(v: unknown): v is BridgePushThemeEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_THEME && typeof v[\"mode\"] === \"string\";\n}\n\nexport function isBridgePushLocale(v: unknown): v is BridgePushLocaleEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_LOCALE && typeof v[\"locale\"] === \"string\";\n}\n\nexport function isBridgePushDensity(v: unknown): v is BridgePushDensityEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_DENSITY && typeof v[\"density\"] === \"string\";\n}\n\nexport function isBridgePushA11y(v: unknown): v is BridgePushA11yEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_A11Y\n && typeof v[\"reducedMotion\"] === \"boolean\"\n && typeof v[\"highContrast\"] === \"boolean\";\n}\n\nexport function isBridgeNavPush(v: unknown): v is BridgeNavPushEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_NAV_PUSH && typeof v[\"path\"] === \"string\";\n}\n\nexport function isBridgeChromeRequest(v: unknown): v is BridgeChromeRequestEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_CHROME_REQUEST && typeof v[\"id\"] === \"string\";\n}\n\nexport function isBridgeChromeResponse(v: unknown): v is BridgeChromeResponseEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_CHROME_RESPONSE && typeof v[\"id\"] === \"string\";\n}\n\nexport function isBridgeSessionTokenPush(v: unknown): v is BridgeSessionTokenPushEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_SESSION_TOKEN_PUSH\n && typeof v[\"token\"] === \"string\"\n && typeof v[\"expiresAtMs\"] === \"number\";\n}\n\nexport function isBridgeLifecycleEvent(v: unknown): v is BridgeLifecycleEventEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_LIFECYCLE_EVENT && typeof v[\"phase\"] === \"string\";\n}\n\nexport function isBridgeA11yAnnounce(v: unknown): v is BridgeA11yAnnounceEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_A11Y_ANNOUNCE && typeof v[\"message\"] === \"string\";\n}\n","/**\n * Plugin-side bridge client for the host ↔ plugin platform contract\n * (WI 4858 sub-plan 2).\n *\n * `createPortBridgeClient` wraps a MessagePort (or an in-realm shim) and\n * exposes typed subscription callbacks for each host push (theme, locale,\n * density, a11y, nav, session token) and typed fire-and-wait calls for\n * chrome requests and a11y live-region announcements.\n *\n * For in-realm (Tier T) plugins, use `InMemoryBridgeTransport` from the\n * mock-host package instead — it calls callbacks synchronously without\n * any serialisation.\n */\nimport {\n BRIDGE_CHROME_REQUEST,\n BRIDGE_A11Y_ANNOUNCE,\n isBridgePushTheme,\n isBridgePushLocale,\n isBridgePushDensity,\n isBridgePushA11y,\n isBridgeNavPush,\n isBridgeChromeResponse,\n isBridgeSessionTokenPush,\n type BridgePushThemeEnvelope,\n type BridgePushLocaleEnvelope,\n type BridgePushDensityEnvelope,\n type BridgePushA11yEnvelope,\n type BridgeNavPushEnvelope,\n type BridgeSessionTokenPushEnvelope,\n} from \"../host/worker/bridge-envelopes\";\n\n// ── Subscriber payload types (strips the `type` discriminant) ────────────────\n\nexport type ThemePayload = Omit<BridgePushThemeEnvelope, \"type\">;\nexport type LocalePayload = Omit<BridgePushLocaleEnvelope, \"type\">;\nexport type DensityPayload = Omit<BridgePushDensityEnvelope, \"type\">;\nexport type A11yPayload = Omit<BridgePushA11yEnvelope, \"type\">;\nexport type NavPayload = Omit<BridgeNavPushEnvelope, \"type\">;\nexport type SessionTokenPayload = Omit<BridgeSessionTokenPushEnvelope, \"type\">;\n\n// ── Port shim (test injection) ───────────────────────────────────────────────\n\n/**\n * Minimal port surface `createPortBridgeClient` actually uses. Matches\n * `PortShim` in `createPortMcpTransport` — same test-injection pattern.\n */\nexport interface BridgePortShim {\n onmessage: ((ev: { data: unknown }) => void) | null;\n postMessage(msg: unknown): void;\n}\n\n// ── Public client interface ──────────────────────────────────────────────────\n\nexport interface PortBridgeClient {\n /** Replace the theme subscriber. Only the latest subscriber is called. */\n onTheme(cb: (payload: ThemePayload) => void): void;\n /** Replace the locale subscriber. */\n onLocale(cb: (payload: LocalePayload) => void): void;\n /** Replace the density subscriber. */\n onDensity(cb: (payload: DensityPayload) => void): void;\n /** Replace the a11y-preferences subscriber. */\n onA11y(cb: (payload: A11yPayload) => void): void;\n /** Replace the nav-state subscriber. */\n onNav(cb: (payload: NavPayload) => void): void;\n /** Replace the session-token subscriber. */\n onSessionToken(cb: (payload: SessionTokenPayload) => void): void;\n /**\n * Request a host chrome action. Returns the host's result or rejects with\n * the host's error string. Callers should catch and surface gracefully.\n *\n * @param action \"toast\" | \"confirm\" | \"openUrl\"\n * @param payload Action-specific payload.\n */\n requestChrome(action: string, payload: Record<string, unknown>): Promise<unknown>;\n /**\n * Ask the host to make a live-region announcement on behalf of the plugin.\n * Prefers this over writing directly to the DOM so the host can manage\n * ARIA live regions centrally.\n */\n announceA11y(message: string, politeness: \"polite\" | \"assertive\"): void;\n}\n\n// ── Implementation ───────────────────────────────────────────────────────────\n\nlet _requestCounter = 0;\nfunction nextId(): string {\n _requestCounter += 1;\n return `br-${_requestCounter}`;\n}\n\n/**\n * Construct a bridge client backed by a `MessagePort` (or shim).\n *\n * Registers a single `onmessage` handler on the port that dispatches each\n * bridge push to the appropriate subscriber. Unknown message types are\n * silently ignored — the port carries MCP traffic on the same channel and\n * those messages must not trigger an error here.\n */\nexport function createPortBridgeClient(port: BridgePortShim): PortBridgeClient {\n let themeCb: ((p: ThemePayload) => void) | undefined;\n let localeCb: ((p: LocalePayload) => void) | undefined;\n let densityCb: ((p: DensityPayload) => void) | undefined;\n let a11yCb: ((p: A11yPayload) => void) | undefined;\n let navCb: ((p: NavPayload) => void) | undefined;\n let tokenCb: ((p: SessionTokenPayload) => void) | undefined;\n\n type PendingChrome = { resolve: (v: unknown) => void; reject: (e: Error) => void };\n const pending = new Map<string, PendingChrome>();\n\n port.onmessage = (ev: { data: unknown }): void => {\n const msg = ev.data;\n if (isBridgePushTheme(msg)) { themeCb?.({ mode: msg.mode, tokens: msg.tokens }); return; }\n if (isBridgePushLocale(msg)) { localeCb?.({ locale: msg.locale, dir: msg.dir }); return; }\n if (isBridgePushDensity(msg)) { densityCb?.({ density: msg.density }); return; }\n if (isBridgePushA11y(msg)) { a11yCb?.({ reducedMotion: msg.reducedMotion, highContrast: msg.highContrast }); return; }\n if (isBridgeNavPush(msg)) { navCb?.({ path: msg.path, historyLength: msg.historyLength }); return; }\n if (isBridgeSessionTokenPush(msg)) { tokenCb?.({ token: msg.token, expiresAtMs: msg.expiresAtMs }); return; }\n if (isBridgeChromeResponse(msg)) {\n const p = pending.get(msg.id);\n if (p === undefined) { return; }\n pending.delete(msg.id);\n if (msg.ok) { p.resolve(msg.result ?? null); }\n else { p.reject(new Error(typeof msg.error === \"string\" ? msg.error : \"Chrome request failed\")); }\n }\n };\n\n return {\n onTheme(cb) { themeCb = cb; },\n onLocale(cb) { localeCb = cb; },\n onDensity(cb) { densityCb = cb; },\n onA11y(cb) { a11yCb = cb; },\n onNav(cb) { navCb = cb; },\n onSessionToken(cb) { tokenCb = cb; },\n\n requestChrome(action, payload): Promise<unknown> {\n const id = nextId();\n return new Promise<unknown>((resolve, reject) => {\n pending.set(id, { resolve, reject });\n port.postMessage({ type: BRIDGE_CHROME_REQUEST, id, action, payload });\n });\n },\n\n announceA11y(message, politeness): void {\n port.postMessage({ type: BRIDGE_A11Y_ANNOUNCE, message, politeness });\n },\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","import { useEffect, useState } from \"react\";\nimport { useBridgeClient } from \"./BridgeClientContext\";\nimport type { ThemePayload } from \"./bridge-client\";\n\n/**\n * Subscribe to host theme pushes. Returns the most recently pushed theme,\n * or `undefined` before the first push (e.g. during initial render before\n * the host has sent its first `BRIDGE_PUSH_THEME` message).\n *\n * The hook re-registers with the bridge client if the client identity changes\n * (e.g. on hot-reload of the mock host in dev), keeping state consistent.\n */\nexport function useBridgeTheme(): ThemePayload | undefined {\n const client = useBridgeClient();\n const [theme, setTheme] = useState<ThemePayload | undefined>(undefined);\n\n useEffect(() => {\n client.onTheme((payload) => setTheme(payload));\n // No cleanup needed — `onTheme` replaces the subscriber; unmounting\n // drops the hook's closure reference, preventing stale-state updates.\n }, [client]);\n\n return theme;\n}\n","import { useEffect, useState } from \"react\";\nimport { useBridgeClient } from \"./BridgeClientContext\";\nimport type { LocalePayload } from \"./bridge-client\";\n\n/**\n * Subscribe to host locale pushes. Returns the most recently pushed locale\n * and text direction, or `undefined` before the first push.\n */\nexport function useBridgeLocale(): LocalePayload | undefined {\n const client = useBridgeClient();\n const [locale, setLocale] = useState<LocalePayload | undefined>(undefined);\n\n useEffect(() => {\n client.onLocale((payload) => setLocale(payload));\n }, [client]);\n\n return locale;\n}\n","/**\n * W4A — Bridge-mediated frontend-session token hook (WI 4858 sub-plan 4).\n *\n * # What this does\n * Fetches the audience-restricted FE-session token from the host bridge\n * (resource URI: `\"ethisys:session-token\"`) and manages silent refresh.\n * The token is delivered to this hook; it is NEVER the capability JWT\n * (that stays brokered host-side and never reaches plugin JS).\n *\n * # Refresh policy\n * Refresh fires 30 s before `expiresAtUtc`. The timer is cleared on\n * unmount; no stale async operations survive component teardown.\n *\n * # Transport\n * The hook is transport-agnostic — it accepts any {@link McpTransport}.\n * Pass the context transport in production; a mock in tests.\n *\n * This is the canonical FE-session-token hook; `@ethisyscore/plugin-ui/token`\n * re-exports it so plugin authors take a single dependency on plugin-ui.\n */\nimport { useState, useEffect, useRef, useCallback } from \"react\";\nimport type { McpTransport } from \"./transport\";\n\nconst SESSION_TOKEN_URI = \"ethisys:session-token\";\nconst REFRESH_SKEW_MS = 30_000; // refresh 30 s before expiry\n\ninterface FrontendSessionTokenPayload\n{\n accessToken: string;\n expiresAtUtc: string; // ISO-8601\n jti: string;\n}\n\nexport interface UseFrontendSessionTokenResult\n{\n /** The compact-serialised JWT, or null while loading / on error. */\n token: string | null;\n /** True while the first fetch (or a refresh) is in-flight. */\n isLoading: boolean;\n /** Set when the bridge rejects; null otherwise. */\n error: Error | null;\n}\n\n/**\n * Returns the current FE-session token fetched from the host bridge.\n * Schedules silent refresh 30 s before the token expires.\n *\n * @param transport The {@link McpTransport} to use. Typically the context\n * transport from the extension runtime provider.\n */\nexport function useFrontendSessionToken(\n transport: McpTransport,\n): UseFrontendSessionTokenResult\n{\n const [token, setToken] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const refreshTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async (): Promise<void> =>\n {\n if (refreshTimerRef.current !== null)\n {\n clearTimeout(refreshTimerRef.current);\n refreshTimerRef.current = null;\n }\n try\n {\n const { data } = await transport.getResource<FrontendSessionTokenPayload>(\n SESSION_TOKEN_URI,\n );\n\n if (!mountedRef.current)\n {\n return;\n }\n\n setToken(data.accessToken);\n setError(null);\n setIsLoading(false);\n\n // Schedule refresh 30 s before expiry.\n const expiresAtMs = Date.parse(data.expiresAtUtc);\n const refreshIn = expiresAtMs - Date.now() - REFRESH_SKEW_MS;\n if (refreshIn > 0)\n {\n refreshTimerRef.current = setTimeout(() =>\n {\n void fetch();\n }, refreshIn);\n }\n }\n catch (err)\n {\n if (!mountedRef.current)\n {\n return;\n }\n setError(err instanceof Error ? err : new Error(String(err)));\n setToken(null);\n setIsLoading(false);\n }\n }, [transport]);\n\n useEffect(() =>\n {\n mountedRef.current = true;\n setIsLoading(true);\n void fetch();\n\n return (): void =>\n {\n mountedRef.current = false;\n if (refreshTimerRef.current !== null)\n {\n clearTimeout(refreshTimerRef.current);\n }\n };\n }, [fetch]);\n\n return { token, isLoading, error };\n}\n","/**\n * Pure JWT-payload decode helper (WI 5160 — F-AUTH-SEAM FE face).\n *\n * Extracts and JSON-parses the middle (payload) segment of a compact JWT.\n * URL-safe base64url characters (`-` → `+`, `_` → `/`) are normalised and\n * missing `=` padding is restored before passing to `atob`.\n *\n * This helper is intentionally side-effect-free and React-free so it can be\n * unit-tested without a DOM environment.\n *\n * **Security note:** this function does NOT validate the JWT signature —\n * the host already validated the token at mint time. The FE only reads claims.\n *\n * @param token A compact-serialised JWT (`header.payload.signature`) or null.\n * @returns The parsed payload object, or `{}` if the token is absent, malformed,\n * has fewer than 3 segments, or the payload is not valid JSON.\n */\nexport function decodeJwtPayload(token: string | null): Record<string, unknown>\n{\n if (token === null || token === \"\")\n {\n return {};\n }\n\n const segments = token.split(\".\");\n if (segments.length < 3)\n {\n return {};\n }\n\n const payloadSegment = segments[1];\n\n try\n {\n // Convert base64url → standard base64 and restore padding.\n const base64 = payloadSegment\n .replace(/-/g, \"+\")\n .replace(/_/g, \"/\");\n\n // Restore `=` padding so atob does not throw.\n const padded = base64 + \"===\".slice(0, (4 - (base64.length % 4)) % 4);\n\n const json = atob(padded);\n const parsed: unknown = JSON.parse(json);\n\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n {\n return {};\n }\n\n return parsed as Record<string, unknown>;\n }\n catch\n {\n return {};\n }\n}\n","/**\n * W5160 — `useAuth` FE gate (F-AUTH-SEAM FE face).\n *\n * # What this does\n * Wraps {@link useFrontendSessionToken} and decodes the returned compact JWT\n * to expose the caller's identity (`currentUserId`) and a short-code permission\n * gate (`hasPermission`).\n *\n * # Claim layout (host-minted, per FrontendSessionTokenIssuer)\n * - `user_id` (or `sub` as fallback) — the caller's user ID.\n * - `permissions` — a space-delimited string of short codes,\n * e.g. `\"timeslip.read timeslip.write\"`.\n *\n * # Fail-closed behaviour\n * While the token is loading, absent, or the bridge errors, `currentUserId`\n * is `null` and `hasPermission` always returns `false`.\n *\n * # Security note\n * The JWT signature is NOT re-validated here — the host validated it at mint.\n * The FE gate is advisory (UI gating); privileged execution is always\n * RBAC-gated host-side (§4.4 of the auth-seam spec).\n *\n * This is the canonical `useAuth` hook; `@ethisyscore/plugin-ui/token`\n * re-exports it so plugin authors take a single dependency on plugin-ui.\n */\nimport { useMemo } from \"react\";\nimport { useFrontendSessionToken } from \"./useFrontendSessionToken\";\nimport { decodeJwtPayload } from \"./decodeJwtPayload\";\nimport type { McpTransport } from \"./transport\";\n\nexport interface UseAuthResult\n{\n /** The caller's user ID from the FE-session JWT, or null while loading / on error. */\n currentUserId: string | null;\n /**\n * Returns true if the caller has the given short-code permission.\n * Always returns false while the token is loading or absent (fail-closed).\n *\n * @param shortCode The exact permission short code, e.g. `\"timeslip.read\"`.\n */\n hasPermission(shortCode: string): boolean;\n /** True while the first fetch (or a silent refresh) is in-flight. */\n isLoading: boolean;\n /** Set when the bridge rejects; null otherwise. */\n error: Error | null;\n}\n\n/**\n * Returns the caller's identity and permission gate derived from the\n * FE-session token.\n *\n * @param transport The {@link McpTransport} to use. Typically the context\n * transport from the extension runtime provider.\n */\nexport function useAuth(transport: McpTransport): UseAuthResult\n{\n const { token, isLoading, error } = useFrontendSessionToken(transport);\n\n const { currentUserId, permissionSet } = useMemo(() =>\n {\n const claims = decodeJwtPayload(token);\n\n const userId =\n (typeof claims[\"user_id\"] === \"string\" && claims[\"user_id\"] !== \"\" ? claims[\"user_id\"] : null) ??\n (typeof claims[\"sub\"] === \"string\" && claims[\"sub\"] !== \"\" ? claims[\"sub\"] : null);\n\n const permissionsRaw = typeof claims[\"permissions\"] === \"string\" ? claims[\"permissions\"] : \"\";\n const set = permissionsRaw.length > 0\n ? new Set(permissionsRaw.split(\" \").filter((s) => s.length > 0))\n : new Set<string>();\n\n return { currentUserId: userId, permissionSet: set };\n }, [token]);\n\n return {\n currentUserId,\n hasPermission: (shortCode: string): boolean => permissionSet.has(shortCode),\n isLoading,\n error,\n };\n}\n"]}
|