@ethisyscore/plugin-ui 1.47.0 → 1.49.0

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/platform-react/templateContext.ts","../../src/platform-react/injectStyles.ts","../../src/platform-react/surfaceUrl.ts","../../src/platform-react/hostSidebarActionsContext.ts","../../src/platform-react/definePlatformReactPluginPage.tsx","../../src/platform-react/MissingViewFallback.tsx","../../src/platform-react/useView.ts","../../src/platform-react/mcpService.ts","../../src/platform-react/useAuthenticatedQuery.ts","../../src/platform-react/reactRouterShim.tsx","../../src/platform-react/usePluginRealtime.ts","../../src/platform-react/useMcpUpload.ts","../../src/platform-react/overlayHostContext.ts","../../src/platform-react/useOverlayHost.ts","../../src/platform-react/definePlatformReactPluginOverlay.tsx","../../src/platform-react/managedLifecycle.ts","../../src/platform-react/useHostSidebarActions.ts","../../src/platform-react/toHostSidebarInput.ts"],"names":["createContext","useEffect","useContext","useHostIdentity","QueryClient","createElement","QueryClientProvider","definePlatformReactPage","useView","useMcpTool","useMemo","useQuery","useQueries","emitNavigation","usePluginRealtimeSource","useRef","useExtensionRuntimeTransport","useState","useCallback"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, type ComponentType } from \"react\";\n\n/** A view component resolved from the template manifest. Props are open. */\nexport type ViewComponent = ComponentType<Record<string, unknown>>;\n\n/**\n * Static port of the monolith's gogo-ui adapter mechanism for PlatformReact.\n *\n * A monolith feature picks its adapter view at runtime via `import.meta.glob`;\n * a PlatformReact page is a single self-contained ESM bundle where that glob is\n * unavailable, so each page declares its view slice statically and provides it\n * through this context. The shape is `manifest[module][viewName] = Component`.\n */\nexport type TemplateManifest = Record<string, Partial<Record<string, ViewComponent>>>;\n\nexport const TemplateContext = createContext<TemplateManifest | null>(null);\n","import { useEffect } from \"react\";\n\n/**\n * Injects a plugin's compiled CSS into the host document once, keyed by a\n * stable element id (idempotent across pages and re-mounts).\n *\n * Each PlatformReact page is a separate ESM bundle with no companion CSS asset\n * the host would load, so the styles travel inside the bundle (typically via a\n * `*.css?inline` import) and are mounted at runtime. Scope the CSS to a plugin\n * root class (never `:root`) so it cannot override the host theme.\n */\nexport function injectPluginStyles(id: string, css: string): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(id)) return;\n const el = document.createElement(\"style\");\n el.id = id;\n el.textContent = css;\n document.head.appendChild(el);\n}\n\n/** Component form of {@link injectPluginStyles}; renders nothing. */\nexport function PluginStyleScope({ id, css }: { id: string; css: string }): null {\n useEffect(() => {\n injectPluginStyles(id, css);\n }, [id, css]);\n return null;\n}\n","import { createContext, useContext } from \"react\";\nimport { useHostIdentity } from \"@ethisyscore/extension-runtime/plugin\";\n\n/** The surface's mount context, provided by definePlatformReactPluginPage. */\nexport interface SurfaceBaseValue {\n /** Host-provided mount path `/extensions/<slug>/<pageId>` (authoritative). */\n basePath?: string;\n /** The page id (from PlatformReactPageProps), used for the derived fallback. */\n pageId?: string;\n}\n\nexport const SurfaceBaseContext = createContext<SurfaceBaseValue | undefined>(undefined);\n\n/** Match the host's slug normalisation (useExtensionSurfaceShellPage `normaliseSlug`). */\nexport function normaliseSlug(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction stripTrailingSlash(p: string): string {\n return p.length > 1 && p.endsWith(\"/\") ? p.slice(0, -1) : p;\n}\n\n/**\n * Resolve the surface base. Precedence: host-provided basePath → derived from\n * normaliseSlug(extensionGroupCode) + pageId → null (caller decides how to fail).\n * Pure — callers read context + identity and pass the pieces in.\n */\nexport function resolveSurfaceBase(input: {\n basePath?: string;\n pageId?: string;\n extensionGroupCode?: string | null;\n}): { base: string; groupRoot: string } | null {\n if (input.basePath) {\n const base = stripTrailingSlash(input.basePath);\n const cut = base.lastIndexOf(\"/\");\n const groupRoot = cut > 0 ? base.slice(0, cut) : base;\n return { base, groupRoot };\n }\n const slug = input.extensionGroupCode ? normaliseSlug(input.extensionGroupCode) : \"\";\n const pageId = input.pageId ?? \"\";\n if (slug && pageId) {\n const groupRoot = `/extensions/${slug}`;\n return { base: `${groupRoot}/${pageId}`, groupRoot };\n }\n return null;\n}\n\n/** Join base + sub (leading slash stripped) + optional ?query/#hash suffix. */\nexport function buildSurfaceUrl(base: string, sub = \"\", suffix = \"\"): string {\n const b = stripTrailingSlash(base);\n const s = sub.replace(/^\\/+/, \"\");\n const path = s ? `${b}/${s}` : b;\n return suffix ? `${path}${suffix}` : path;\n}\n\n/**\n * Hook: build a URL relative to the CURRENT surface. Precedence per\n * resolveSurfaceBase; throws in dev when unresolved (never returns app-root).\n */\nexport function useSurfaceUrl(): (sub?: string, suffix?: string) => string {\n const ctx = useContext(SurfaceBaseContext);\n const identity = useHostIdentity();\n return (sub = \"\", suffix = \"\") => {\n const resolved = resolveSurfaceBase({\n basePath: ctx?.basePath,\n pageId: ctx?.pageId,\n extensionGroupCode: identity?.extensionGroupCode ?? null,\n });\n if (!resolved) {\n const msg =\n \"useSurfaceUrl: no surface base — SurfaceBaseContext (basePath/pageId) and \" +\n \"extensionGroupCode are both unavailable. Ensure the page is wrapped by \" +\n \"definePlatformReactPluginPage inside the host runtime.\";\n // Dev: fail loud. Prod: never navigate to app-root (that recreates the 404\n // class) — stay on the current path (a no-op) so a transient unresolved\n // state (e.g. identity still loading on a pre-basePath host) can't crash the\n // surface. Callers invoke this in event handlers, by which time identity has\n // loaded and the base resolves normally.\n // Guard `process` access: in a browser/ESM consumer where `process` is\n // undefined, reading `process.env.NODE_ENV` directly would throw a\n // ReferenceError before the prod fallback runs. `typeof` never throws on an\n // undeclared identifier. Fail SAFE — throw ONLY when we can positively\n // confirm a non-production env; an unknown env (no `process`) is treated as\n // production so a bundled surface never crashes here.\n const isDevEnv =\n typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\";\n if (isDevEnv) throw new Error(msg);\n if (typeof console !== \"undefined\") console.error(msg);\n // Prod no-op: stay on the FULL current URL (path + query + hash) — a true\n // no-op, and never app-root.\n return typeof window !== \"undefined\"\n ? window.location.pathname + window.location.search + window.location.hash\n : \"\";\n }\n return buildSurfaceUrl(resolved.base, sub, suffix);\n };\n}\n\n/** Pure: build a URL for ANOTHER surface (cross-surface, e.g. an overlay opening a page). */\nexport function surfacePathFor(opts: {\n slug: string;\n pageId: string;\n sub?: string;\n suffix?: string;\n}): string {\n const base = `/extensions/${normaliseSlug(opts.slug)}/${opts.pageId}`;\n return buildSurfaceUrl(base, opts.sub ?? \"\", opts.suffix ?? \"\");\n}\n","import { createContext } from \"react\";\nimport type { Context } from \"react\";\nimport type { HostSidebarActionsApiShape } from \"@ethisyscore/components-react\";\n\n// ── Cross-bundle singleton keys ───────────────────────────────────────────────\n// tsup may inline this module into BOTH the package-root entry and the subpath\n// entry, producing two distinct module copies. Storing shared state on globalThis\n// under Symbol.for(…) keys guarantees that every copy resolves the SAME object.\nconst CONTEXT_KEY = Symbol.for(\"ethisyscore.plugin-ui.hostSidebarActionsContext\");\nconst SINK_KEY = Symbol.for(\"ethisyscore.plugin-ui.hostChromeDiagnosticSink\");\n\n/** Host-chrome / sidebar-actions contract version (semver). Host MAJOR must equal the SDK\n * MAJOR before any publish/subscribe; otherwise the plugin hook fails closed (WI 5188 batch 2). */\nexport const HOST_CHROME_CONTRACT_VERSION = \"2.0.0\";\n\nexport type HostSidebarActionSlot = \"primary\" | \"overflow\" | \"sidebar-only\";\nexport type HostSidebarActionVariant = \"default\" | \"danger\";\n\n/** Fields common to every action. */\ninterface HostSidebarActionBase {\n id: string;\n label: string;\n icon?: string;\n slot?: HostSidebarActionSlot;\n variant?: HostSidebarActionVariant;\n disabled?: boolean;\n active?: boolean;\n /** Optional host-side double-gate: the mask is resolved against the surface extension groupCode. */\n requiredPermission?: number;\n}\n\n/** Strictly serializable action the plugin publishes to the host sidebar — NO functions cross\n * the boundary. Discriminated union so `href` is REQUIRED at the type level for a navigate\n * action and FORBIDDEN for a dispatch action (spec-gate MEDIUM R1-M1 — a navigate with a\n * missing href fails typecheck at authoring time rather than being silently dropped by the host).\n * `kind:\"dispatch\"` is clicked back to the plugin by `id` via the action-emit subscription. */\nexport type HostSidebarActionDescriptor =\n | (HostSidebarActionBase & { kind: \"dispatch\" })\n | (HostSidebarActionBase & { kind: \"navigate\"; href: string }); // extension-relative; host normalizes/validates\n\n/** A navigation leaf the plugin drives into the host detail sidebar. `href` is extension-relative;\n * the host canonicalises + confines it to `/extensions/<slug>/…` (host-owned, never trusted). */\nexport interface HostSidebarNavItem {\n id: string;\n label: string;\n icon?: string;\n href: string;\n}\n\n/** The single publish payload. `none` = contribute nothing (do NOT clobber the static manifest\n * actions); `actions` = batch-2 Quick Actions for a detail entity; `detail` = full detail-nav\n * REPLACEMENT (title + nav items + back-arrow + Quick Actions). `identityEpoch` is host-stamped\n * (plan D-1), so it is not part of this plugin-published shape. */\nexport type HostSidebarContribution =\n | { mode: \"none\" }\n | { mode: \"actions\"; entityToken: string; actions: HostSidebarActionDescriptor[] }\n | {\n mode: \"detail\";\n entityToken: string;\n title: string;\n navSectionLabel: string;\n navItems: HostSidebarNavItem[];\n backHref: string;\n backLabel: string;\n actions: HostSidebarActionDescriptor[];\n };\n\nexport interface HostSidebarActionsCapabilities {\n readonly contractVersion: string; // HOST_CHROME_CONTRACT_VERSION the host implements\n}\n\n/** Host-provided API, passed to a PlatformReact page as the `hostSidebar` prop and read via\n * the plugin-bundled context. Additive-only. */\nexport interface HostSidebarActionsApi {\n /** Replace the surface's sidebar contribution. The host rejects a publish whose entityToken ≠\n * its current location-derived token, rejects duplicate action ids, and — for mode:\"detail\" —\n * rejects/atomically-drops an invalid or href-unsafe context (falls back to main nav). */\n publish(contribution: HostSidebarContribution): void;\n /** Revert to the static-manifest fallback (publication state = unpublished). */\n clear(): void;\n /** Subscribe to click dispatch for `kind:\"dispatch\"` actions. The host emits the clicked\n * action id. Returns an unsubscribe. The plugin bridge subscribes exactly once. */\n subscribe(onAction: (actionId: string) => void): () => void;\n readonly capabilities: HostSidebarActionsCapabilities;\n}\n\n/** Props the host injects into a page for the sidebar-actions seam (merged into PlatformReactPageProps).\n * Typed against the dependency-neutral `HostSidebarActionsApiShape` — the SAME type\n * `PlatformReactPageProps.hostSidebar` uses in `@ethisyscore/components-react` — so this\n * convenience alias never diverges from the canonical page-props declaration. The concrete\n * `HostSidebarActionsApi` is structurally assignable to the shape; the page bridge narrows to\n * it internally. */\nexport interface HostSidebarActionsPageProps {\n hostSidebar?: HostSidebarActionsApiShape;\n}\n\ntype GlobalWithCtx = typeof globalThis & {\n [CONTEXT_KEY]?: Context<HostSidebarActionsApi | null>;\n};\nconst _g = globalThis as GlobalWithCtx;\nexport const HostSidebarActionsContext: Context<HostSidebarActionsApi | null> =\n _g[CONTEXT_KEY] ?? (_g[CONTEXT_KEY] = createContext<HostSidebarActionsApi | null>(null));\n\n// ── Diagnostics adapter (concrete, testable — spec-gate HIGH R1-H3) ─────────────\n// A single injectable sink so degradation is OBSERVABLE in all environments without inventing a\n// `globalThis` global. The host runtime calls `setHostChromeDiagnosticSink` once to route these to\n// its real telemetry; absent a sink it is a safe no-op. Every emission carries the page id tag.\n//\n// The sink is stored on the SINK_KEY globalThis slot so that setHostChromeDiagnosticSink and\n// reportHostChromeDiagnostic in any duplicate module copy read/write the SAME slot.\nexport interface HostChromeDiagnostic {\n event: string;\n pageId?: string;\n detail?: unknown;\n}\ntype DiagnosticSinkFn = (d: HostChromeDiagnostic) => void;\ntype GlobalWithSink = typeof globalThis & { [SINK_KEY]?: DiagnosticSinkFn };\nconst _gs = globalThis as GlobalWithSink;\nif (_gs[SINK_KEY] === undefined) {\n _gs[SINK_KEY] = () => {};\n}\nexport function setHostChromeDiagnosticSink(sink: DiagnosticSinkFn): void {\n _gs[SINK_KEY] = sink;\n}\nexport function reportHostChromeDiagnostic(d: HostChromeDiagnostic): void {\n try {\n _gs[SINK_KEY]!(d);\n } catch {\n /* diagnostics are best-effort — never throw into the plugin */\n }\n}\n\n/** Major-equality compatibility check; fail-closed on absent/unparseable input. */\nexport function isHostChromeCompatible(hostVersion: string | undefined): boolean {\n if (hostVersion === undefined) return false;\n const hostMajor = Number.parseInt(hostVersion.split(\".\")[0] ?? \"\", 10);\n const sdkMajor = Number.parseInt(HOST_CHROME_CONTRACT_VERSION.split(\".\")[0] ?? \"\", 10);\n if (Number.isNaN(hostMajor) || Number.isNaN(sdkMajor)) return false;\n return hostMajor === sdkMajor;\n}\n","import { createElement, type ComponentType, type ReactElement } from \"react\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport {\n definePlatformReactPage,\n type PlatformReactPageProps,\n} from \"@ethisyscore/components-react\";\nimport { TemplateContext, type TemplateManifest, type ViewComponent } from \"./templateContext\";\nimport { PluginStyleScope } from \"./injectStyles\";\nimport { SurfaceBaseContext } from \"./surfaceUrl\";\nimport { HostSidebarActionsContext, type HostSidebarActionsApi } from \"./hostSidebarActionsContext\";\n\n/**\n * Default per-page `QueryClient`. Each PlatformReact page is an isolated ESM\n * bundle, so this module-level singleton is scoped to that page — exactly the\n * lifetime a page wants. Plugins needing different options pass their own.\n */\nfunction makeDefaultQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n retry: 1,\n refetchOnWindowFocus: false,\n staleTime: 30_000,\n },\n },\n });\n}\n\nconst defaultQueryClient = makeDefaultQueryClient();\n\n/**\n * Shared root class applied to every plugin page wrapper, in addition to the\n * plugin's own `rootClassName`. The SDK's `plugin-base.css` (scoped resets +\n * component overrides) targets this class, so host-parity styling reaches a\n * plugin's surface deterministically regardless of its plugin-specific class.\n */\nexport const EHX_PLUGIN_ROOT_CLASS = \"ehx-plugin-root\";\n\nexport interface DefinePlatformReactPluginPageOptions {\n /** The page's static `useView` manifest slice (no `import.meta.glob` in a single-file bundle). */\n manifest?: TemplateManifest;\n /** Scoped style-root class wrapped around the page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element (idempotent across pages). */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n /**\n * Optional wrapper applied around the page element, inside the style root. Use\n * to add a plugin-specific in-page provider — e.g. `withPageHeader(config)` from\n * `@ethisyscore/plugin-ui/components/layout` to restore list-page header CTAs.\n *\n * The wrapper is supplied as a ready-made function (not a config object) so this\n * entry stays free of the page-header React context — the provider and the\n * page's `usePageHeader` must resolve to the SAME `components/layout` module\n * instance, which they only do when both come from that entry (bundlers inline a\n * separate context copy per entry otherwise).\n */\n wrapPage?: (page: ReactElement) => ReactElement;\n}\n\n/**\n * Wraps a lifted page as a PlatformReact entry with the providers the monolith\n * took for granted: react-query, the static `useView` manifest, the scoped style\n * root, and — when `wrapPage` is supplied — a plugin-specific in-page provider\n * (e.g. the page-header). The host already provides the MCP transport (it mounts\n * pages inside its own runtime provider), so this adds only the in-page providers.\n */\nexport function definePlatformReactPluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n options: DefinePlatformReactPluginPageOptions,\n): ComponentType<PlatformReactPageProps> {\n const {\n manifest = {},\n rootClassName,\n styleId,\n css,\n queryClient = defaultQueryClient,\n wrapPage,\n } = options;\n\n function PluginPage(props: PlatformReactPageProps) {\n const page = createElement(Page, props);\n const body = wrapPage ? wrapPage(page) : page;\n const surfaceTree = createElement(\n SurfaceBaseContext.Provider,\n { value: { basePath: props.basePath, pageId: props.pageId } },\n createElement(\n QueryClientProvider,\n { client: queryClient },\n createElement(\n TemplateContext.Provider,\n { value: manifest },\n createElement(PluginStyleScope, { id: styleId, css }),\n createElement(\n \"div\",\n { className: `${EHX_PLUGIN_ROOT_CLASS} ${rootClassName}` },\n body,\n ),\n ),\n ),\n );\n return createElement(\n HostSidebarActionsContext.Provider,\n { value: (props.hostSidebar as HostSidebarActionsApi | undefined) ?? null },\n surfaceTree,\n );\n }\n\n return definePlatformReactPage(PluginPage);\n}\n\nexport interface PluginPageDefinerConfig {\n /**\n * The plugin's `useView` module key (e.g. `\"timesheets\"`). Each page's `views`\n * are registered under it, so a page's `useView(module, name)` resolves them.\n */\n module: string;\n /** Scoped style-root class wrapped around every page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element. */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n /**\n * Optional wrapper applied around every page (see\n * {@link DefinePlatformReactPluginPageOptions.wrapPage}) — e.g.\n * `withPageHeader(<feature>MenuConfig)` to restore list-page header CTAs.\n */\n wrapPage?: (page: ReactElement) => ReactElement;\n}\n\n/**\n * Builds a plugin-bound `definePluginPage(Page, views)` from one config, so a\n * plugin writes the four plugin-specific values (module key, root class, style\n * id, CSS) ONCE instead of in every page entry. Each surface entry then calls\n * `definePluginPage(Page, { ViewName })`; the flat `views` map is wrapped into\n * the `{ [module]: views }` manifest slice the page's `useView(module, name)`\n * reads. The generic provider/manifest/style wrapping stays in\n * {@link definePlatformReactPluginPage}.\n */\nexport function createPluginPageDefiner(config: PluginPageDefinerConfig) {\n const { module, ...rest } = config;\n return function definePluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n views: Record<string, ViewComponent> = {},\n ): ComponentType<PlatformReactPageProps> {\n return definePlatformReactPluginPage(Page, { ...rest, manifest: { [module]: views } });\n };\n}\n","import { createElement, type ComponentType } from \"react\";\n\n/**\n * Dependency-light fallback rendered when a view is not present in the current\n * template manifest. Kept to plain elements + inline styles so the foundation\n * package carries no UI-library dependency; plugins that want a branded\n * fallback pass their own via {@link createUseView}.\n */\nexport const MissingViewFallback: ComponentType<Record<string, unknown>> = () =>\n createElement(\n \"div\",\n {\n style: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2rem\",\n },\n },\n createElement(\n \"div\",\n {\n style: {\n maxWidth: \"28rem\",\n padding: \"1.5rem\",\n border: \"1px solid rgba(0,0,0,0.12)\",\n borderRadius: \"0.5rem\",\n textAlign: \"center\",\n },\n },\n createElement(\"p\", { style: { margin: \"0 0 0.5rem\", fontWeight: 600 } }, \"View not available\"),\n createElement(\n \"p\",\n { style: { margin: 0, opacity: 0.7, fontSize: \"0.875rem\" } },\n \"This view has not been implemented by the current template.\",\n ),\n ),\n );\n","import { useContext, type ComponentType } from \"react\";\nimport { TemplateContext } from \"./templateContext\";\nimport { MissingViewFallback } from \"./MissingViewFallback\";\n\n// Renderable component object-types keyed by their `$$typeof` tag. Plain function\n// (and class) components are matched by `typeof === \"function\"`; the wrappers below\n// are objects, not functions, so they need an explicit tag check. `forwardRef` and\n// `memo` were previously omitted — a `forwardRef`/`memo` view registered in the\n// manifest failed `isComponent` and `useView` fell back to \"View not available\".\nconst RENDERABLE_COMPONENT_TYPEOFS = new Set<symbol>([\n Symbol.for(\"react.lazy\"),\n Symbol.for(\"react.forward_ref\"),\n Symbol.for(\"react.memo\"),\n]);\n\n/**\n * Matches component types renderable via `useView`: plain function/class\n * components plus the object wrappers `React.lazy`, `React.forwardRef`, and\n * `React.memo` (incl. `memo(forwardRef(...))`, whose outer tag is `react.memo`).\n * Deliberately narrower than `react-is`'s `isValidElementType`, which also accepts\n * host-tag strings and Fragment/Suspense symbols — a manifest view value is always\n * a component, never a bare string.\n */\nexport function isComponent(value: unknown): value is ComponentType {\n if (typeof value === \"function\") return true;\n if (typeof value === \"object\" && value !== null) {\n const tag = (value as Record<string, unknown>).$$typeof;\n return typeof tag === \"symbol\" && RENDERABLE_COMPONENT_TYPEOFS.has(tag);\n }\n return false;\n}\n\n/**\n * Builds a `useView` hook bound to a fallback component. The hook is a safe\n * view accessor that never throws: it returns `manifest[module][viewName]` when\n * present, or the supplied fallback when the manifest, module slice, or view is\n * missing or is not a component.\n */\nexport function createUseView(fallback: ComponentType<Record<string, unknown>>) {\n return function useView(\n module: string,\n viewName: string,\n ): ComponentType<Record<string, unknown>> {\n const manifest = useContext(TemplateContext);\n if (!manifest) {\n return fallback;\n }\n\n const slice = manifest[module];\n if (slice && typeof slice === \"object\" && viewName in (slice as Record<string, unknown>)) {\n const component = (slice as Record<string, unknown>)[viewName];\n if (isComponent(component)) {\n return component as ComponentType<Record<string, unknown>>;\n }\n }\n\n return fallback;\n };\n}\n\n/** Default `useView` bound to the built-in {@link MissingViewFallback}. */\nexport const useView = createUseView(MissingViewFallback);\n","import { useMemo } from \"react\";\nimport { useMcpTool } from \"@ethisyscore/extension-runtime/plugin\";\n\n/** Dispatches an MCP tool call by name. The seam a lifted REST service binds to. */\nexport type ToolInvoker = (toolName: string, args: unknown) => Promise<unknown>;\n\n/**\n * Base for service classes lifted from a monolith REST plane onto MCP.\n *\n * The monolith versions extend a REST base (`this.get`/`this.post` over axios).\n * The plugin runtime exposes no imperative MCP transport — only the\n * `useMcpTool` hook — so a `useXService()` hook composes per-tool invokers (see\n * {@link useToolInvokerMap}) into a single {@link ToolInvoker} and constructs\n * the class with it. Method bodies call `this.tool(\"<tool-name>\", args)`.\n */\nexport abstract class BaseMcpService {\n constructor(private readonly invoker: ToolInvoker) {}\n\n protected async tool<TRes>(name: string, args: unknown = {}): Promise<TRes> {\n return (await this.invoker(name, args)) as TRes;\n }\n}\n\n/**\n * Thrown by a lifted service method whose monolith REST route has no MCP tool\n * yet (typically because it needs client-side aggregation over a coarser tool).\n */\nexport function notViaMcp(serviceName: string, method: string): never {\n throw new Error(`${serviceName}.${method} has no MCP tool — pending per-surface aggregation.`);\n}\n\n/** Imperative invoker for a single MCP tool (the hook's `.invoke`, stable across renders). */\nexport function useToolInvoker<TReq, TRes>(toolName: string): (req: TReq) => Promise<TRes> {\n return useMcpTool<TReq, TRes>(toolName).invoke;\n}\n\n/**\n * Composes one {@link ToolInvoker} dispatching over every tool in `tools`, so a\n * service class can call tools by name.\n *\n * Rules of hooks: `tools` MUST be a frozen, constant-length list (e.g. a\n * module-level `as const` array) so the per-tool `useMcpTool` calls run in a\n * stable order on every render. Passing a list whose length varies between\n * renders is a violation and will break.\n */\nexport function useToolInvokerMap(tools: readonly string[]): ToolInvoker {\n const invokers: Record<string, (args: unknown) => Promise<unknown>> = {};\n for (const name of tools) {\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `tools` is required to be frozen; call order is stable.\n invokers[name] = useMcpTool(name).invoke;\n }\n return useMemo<ToolInvoker>(\n () => (name, args) => {\n const fn = invokers[name];\n if (!fn) {\n throw new Error(`No MCP invoker registered for tool \"${name}\"`);\n }\n return fn(args);\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- per-tool invoke refs are stable; rebuild only if one changes.\n tools.map((t) => invokers[t]),\n );\n}\n","import {\n useQueries,\n useQuery,\n type QueryKey,\n type UseQueryOptions,\n type UseQueryResult,\n} from \"@tanstack/react-query\";\n\n/**\n * Plugin shim for the monolith's `useAuthenticatedQuery`.\n *\n * In the host realm the MCP transport is already authenticated and scoped to\n * the active organisation, so there is no auth gate to apply. The hook keeps\n * the monolith's return shape (`isLoading` + `isAuthLoading`) so lifted data\n * hooks and pages consume it unchanged — `isAuthLoading` is always `false`.\n */\nexport interface AuthenticatedQueryResult<TData, TError>\n extends Omit<UseQueryResult<TData, TError>, \"isLoading\"> {\n isLoading: boolean;\n isAuthLoading: boolean;\n}\n\nexport function useAuthenticatedQuery<\n TQueryFnData = unknown,\n TError = Error,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n): AuthenticatedQueryResult<TData, TError> {\n const result = useQuery(options);\n return {\n ...result,\n isLoading: result.isLoading,\n isAuthLoading: false,\n };\n}\n\nexport function useAuthenticatedQueries<TQueryFnData = unknown, TError = Error>(\n queries: UseQueryOptions<TQueryFnData, TError>[],\n) {\n return useQueries({ queries });\n}\n","import { createElement, useContext, type AnchorHTMLAttributes, type MouseEvent, type ReactNode } from \"react\";\nimport { emitNavigation } from \"@ethisyscore/components-react\";\nimport { useHostIdentity } from \"@ethisyscore/extension-runtime/plugin\";\nimport { SurfaceBaseContext, resolveSurfaceBase } from \"./surfaceUrl\";\n\n/**\n * Minimal `react-router-dom` compatibility shim for lifted monolith code.\n *\n * A PlatformReact plugin has no client router — navigation is brokered to the\n * host via {@link emitNavigation}, and route params come from the surface URL's\n * trailing segment(s). {@link createReactRouterShim} returns a module-shaped\n * object of the hooks/components lifted code expects; alias `react-router-dom`\n * to a tiny file that re-exports this object's members so call sites resolve\n * unchanged.\n *\n * `paramKeys` names the keys the single trailing path segment is exposed under,\n * so a route like `/…/<page>/<value>` satisfies `useParams<{ id }>()` /\n * `useParams<{ date }>()` regardless of which key the lifted code destructures.\n */\nexport interface ReactRouterShimOptions {\n /** Keys the trailing path segment is exposed under (default: `[\"id\"]`). Used as\n * the fallback when `routes` is not supplied or no route matches. */\n paramKeys?: readonly string[];\n /**\n * Route patterns (sub-paths relative to `basePath`, e.g. `invoices/:id/edit`)\n * for precise param extraction. When set, `useParams` matches the current path\n * against them (most-specific wins) and returns the captured params by name —\n * so a param in the MIDDLE of the path (`:id/edit`) resolves correctly, unlike\n * the trailing-segment fallback.\n */\n routes?: readonly string[];\n /** Base path stripped before matching `routes` (e.g. `/extensions/finance`). */\n basePath?: string;\n}\n\n/**\n * Matches `pathname` (minus `basePath`) against `routes` (most literal segments\n * wins) and returns the captured `:param` values, or `null` when none match.\n */\nfunction matchRouteParams(\n pathname: string,\n routes: readonly string[],\n basePath: string,\n): Record<string, string> | null {\n const prefix = basePath.length > 1 && basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n const norm = pathname.length > 1 && pathname.endsWith(\"/\") ? pathname.slice(0, -1) : pathname;\n const rest = norm.startsWith(prefix) ? norm.slice(prefix.length).replace(/^\\/+/, \"\") : \"\";\n const segments = rest.length > 0 ? rest.split(\"/\") : [];\n\n let best: Record<string, string> | null = null;\n let bestLiterals = -1;\n for (const route of routes) {\n const pattern = route.split(\"/\").filter(Boolean);\n if (pattern.length !== segments.length) {\n continue;\n }\n const params: Record<string, string> = {};\n let literals = 0;\n let ok = true;\n for (let i = 0; i < pattern.length; i++) {\n if (pattern[i].startsWith(\":\")) {\n params[pattern[i].slice(1)] = segments[i];\n } else if (pattern[i] !== segments[i]) {\n ok = false;\n break;\n } else {\n literals++;\n }\n }\n if (ok && literals > bestLiterals) {\n best = params;\n bestLiterals = literals;\n }\n }\n return best;\n}\n\ntype LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {\n to: string;\n children?: ReactNode;\n};\n\nexport function createReactRouterShim(options: ReactRouterShimOptions = {}) {\n const paramKeys = options.paramKeys ?? [\"id\"];\n const routes = options.routes;\n const basePath = options.basePath ?? \"\";\n\n function useNavigate(): (to: string | number) => void {\n return (to) => {\n if (typeof to === \"number\") {\n if (typeof window !== \"undefined\") window.history.go(to);\n return;\n }\n emitNavigation(to);\n };\n }\n\n // NOTE: `useParams` is now a REAL React hook — it calls `useContext`/`useHostIdentity`\n // internally (to resolve the surface base). It therefore obeys the Rules of Hooks: call it\n // unconditionally at the top level of a component/hook render, never conditionally or outside\n // render. (Previously it was a plain path-parser with no hook calls.)\n function useParams<\n T extends Record<string, string | undefined> = Record<string, string | undefined>,\n >(): T {\n // Hooks FIRST — unconditional, so the hook order is identical on every render\n // regardless of which branch returns below.\n const ctx = useContext(SurfaceBaseContext);\n const identity = useHostIdentity();\n const resolved = resolveSurfaceBase({\n basePath: ctx?.basePath,\n pageId: ctx?.pageId,\n extensionGroupCode: identity?.extensionGroupCode ?? null,\n });\n\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"\";\n\n // Precise param extraction from declared route patterns (unchanged).\n if (routes != null && routes.length > 0) {\n const matched = matchRouteParams(path, routes, basePath);\n if (matched != null) return matched as T;\n }\n\n // Fallback: strip the surface base — the longest matching prefix of the\n // explicit base and the bare group root, so BOTH the explicit surface root\n // and the host's empty-subpath fallback yield no param — then take the\n // trailing segment of the remainder. Query/hash ignored. When no base\n // resolves (non-surface shim consumer / no provider+identity) `resolved` is\n // null and this reduces to the legacy trailing-segment behaviour — no\n // regression for existing consumers.\n const pathNoQuery = path.split(/[?#]/)[0];\n let remainder = pathNoQuery;\n if (resolved) {\n for (const prefix of [resolved.base, resolved.groupRoot].sort((a, b) => b.length - a.length)) {\n if (pathNoQuery === prefix || pathNoQuery.startsWith(prefix + \"/\")) {\n remainder = pathNoQuery.slice(prefix.length);\n break;\n }\n }\n }\n const segments = remainder.split(\"/\").filter(Boolean);\n const last = segments[segments.length - 1];\n const params: Record<string, string | undefined> = {};\n if (last) {\n for (const key of paramKeys) params[key] = last;\n }\n return params as T;\n }\n\n function useSearchParams(): [\n URLSearchParams,\n (\n next: URLSearchParams | Record<string, string>,\n navigateOptions?: { replace?: boolean; state?: unknown },\n ) => void,\n ] {\n const search = typeof window !== \"undefined\" ? window.location.search : \"\";\n const params = new URLSearchParams(search);\n // `navigateOptions` mirrors react-router's `setSearchParams(next, opts)` signature so\n // call sites that pass `{ replace: true, state }` typecheck. The shim has no router\n // history stack — it always writes via `history.replaceState` (i.e. behaves as\n // `replace: true`); `state`, when supplied, is persisted onto the history entry so the\n // signature is not misleading (Copilot review). `replace` is accepted for source-compat.\n const setSearchParams = (\n next: URLSearchParams | Record<string, string>,\n navigateOptions?: { replace?: boolean; state?: unknown },\n ) => {\n const usp = next instanceof URLSearchParams ? next : new URLSearchParams(next);\n const qs = usp.toString();\n if (typeof window !== \"undefined\") {\n window.history.replaceState(\n navigateOptions?.state ?? {},\n \"\",\n qs ? `${window.location.pathname}?${qs}` : window.location.pathname,\n );\n }\n };\n return [params, setSearchParams];\n }\n\n function useMatch(\n _pattern: string,\n ): { params: Record<string, string | undefined>; pathname: string } | null {\n return null;\n }\n\n function useLocation() {\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"/\";\n return { pathname: path, search: \"\", hash: \"\", state: null, key: \"default\" };\n }\n\n function Link({ to, children, onClick, ...rest }: LinkProps) {\n return createElement(\n \"a\",\n {\n href: to,\n onClick: (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n onClick?.(e);\n emitNavigation(to);\n },\n ...rest,\n },\n children,\n );\n }\n\n return { useNavigate, useParams, useSearchParams, useMatch, useLocation, Link, NavLink: Link };\n}\n","import { useEffect, useRef } from \"react\";\nimport { usePluginRealtimeSource } from \"@ethisyscore/extension-runtime/plugin\";\n\n/**\n * Subscribe to realtime push notifications of a given `typeCode`.\n *\n * Subscribes on mount and unsubscribes on unmount. The `handler` is kept in a\n * ref so re-renders with a new closure do not churn the underlying subscription\n * — the latest handler is always used without needing to re-subscribe.\n *\n * No-ops cleanly when the plugin runtime has no realtime source (dev/mock\n * environments, or plugins that have not been given a SignalR connection by\n * the host).\n *\n * @param typeCode Application-level event type to filter on (e.g.\n * `\"HelpdeskTicketCreated\"`). Filtering by extensionId is the\n * host's responsibility.\n * @param handler Called with the raw notification payload each time a\n * matching notification arrives.\n */\nexport function usePluginRealtime(\n typeCode: string,\n handler: (payload: unknown) => void,\n): void\n{\n const source = usePluginRealtimeSource();\n const ref = useRef(handler);\n ref.current = handler;\n\n useEffect(() =>\n {\n if (!source) return; // dev/mock: no connection — no-op\n\n return source.subscribe(typeCode, (payload: unknown) => ref.current(payload));\n }, [source, typeCode]);\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n useExtensionRuntimeTransport,\n type McpTransport,\n type UploadDocumentMeta,\n type UploadDocumentResult,\n} from \"@ethisyscore/extension-runtime/plugin\";\n\n/**\n * Advisory client-side upload ceiling (UX only). The kernel's streaming route\n * applies the authoritative size cap mid-stream; this default lets the hook\n * fail fast with a friendly message before reading a large file into memory.\n * Roughly matches the host's default request-body ceiling.\n */\nexport const DEFAULT_MAX_UPLOAD_BYTES = 30 * 1024 * 1024;\n\n/** Where the uploaded document belongs. `moduleKey` is validated against the caller's kernel allowlist. */\nexport interface UseMcpUploadTarget {\n /** Storage module key (e.g. \"hr\"). Server rejects a key outside the plugin's allowlist. */\n moduleKey: string;\n /** Owning entity name (e.g. \"Candidates\", \"CompanyDocuments\"). */\n entityName: string;\n /** Optional owning entity id. */\n entityId?: string;\n /** Logical sub-folder / category (e.g. \"cvs\"). */\n category: string;\n}\n\nexport interface UseMcpUploadOptions {\n /** Override the transport resolved from the runtime provider (tests / advanced hosts). */\n transport?: McpTransport;\n /** Advisory client-side max byte size. Defaults to {@link DEFAULT_MAX_UPLOAD_BYTES}. */\n maxSizeBytes?: number;\n}\n\nexport interface UseMcpUploadResult {\n /**\n * Read `file` into an ArrayBuffer and upload it to core storage via the host,\n * resolving the resulting `StoredDocumentId` + server-validated metadata. The\n * plugin then records the id with its own JSON command.\n */\n upload: (file: File | Blob, target: UseMcpUploadTarget) => Promise<UploadDocumentResult>;\n /**\n * Coarse progress in [0, 1]. NOTE: the FE→host leg is not streaming (the file\n * is transferred whole), so this is phase-based, not byte-accurate: 0 idle,\n * ~0.1 once the bytes are read + handed to the host, 1 on completion.\n */\n progress: number;\n loading: boolean;\n error: Error | undefined;\n}\n\n/**\n * Upload a binary file from a platform-react plugin surface to CORE storage.\n *\n * The bytes never touch the plugin backend and the plugin never sees the\n * capability token or kernel URL: the host shell mints the token and streams\n * the octet-stream to the kernel document-upload route, returning only the\n * `StoredDocumentId`. Filename/content-type are advisory — the kernel\n * sanitises them and binds the effective module key + organisation itself.\n */\nexport function useMcpUpload(opts?: UseMcpUploadOptions): UseMcpUploadResult {\n const transport = useExtensionRuntimeTransport(opts?.transport);\n const maxSizeBytes = opts?.maxSizeBytes ?? DEFAULT_MAX_UPLOAD_BYTES;\n\n const [progress, setProgress] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n\n const controllerRef = useRef<AbortController | null>(null);\n const mountedRef = useRef(true);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n controllerRef.current?.abort();\n };\n }, []);\n\n const upload = useCallback(\n async (file: File | Blob, target: UseMcpUploadTarget): Promise<UploadDocumentResult> => {\n const controller = new AbortController();\n controllerRef.current?.abort();\n controllerRef.current = controller;\n\n const applyState = (fn: () => void): void => {\n if (mountedRef.current && !controller.signal.aborted) {\n fn();\n }\n };\n\n applyState(() => {\n setLoading(true);\n setError(undefined);\n setProgress(0);\n });\n\n try {\n // `uploadDocument` is an optional transport capability. A transport that\n // predates the upload seam (or a custom host transport that omits it)\n // won't provide it — fail with a clear, actionable message instead of a\n // `transport.uploadDocument is not a function` TypeError.\n if (typeof transport.uploadDocument !== \"function\") {\n throw new Error(\n \"The active McpTransport does not support binary uploads (no `uploadDocument`). \" +\n \"The host runtime / transport must implement the upload capability for useMcpUpload to work.\",\n );\n }\n // Capture a bound reference now: property narrowing does not survive the\n // `await file.arrayBuffer()` below, and binding preserves `this` for\n // class-based transports (e.g. InMemoryMcpTransport).\n const uploadDocument = transport.uploadDocument.bind(transport);\n\n const size = file.size;\n if (size > maxSizeBytes) {\n throw new Error(\n `File is ${size} bytes, which exceeds the ${maxSizeBytes}-byte upload limit.`,\n );\n }\n\n // FE→host is NOT streaming: read the whole file into an ArrayBuffer,\n // which the transport hands to the host as a zero-copy transferable.\n const buffer = await file.arrayBuffer();\n applyState(() => setProgress(0.1));\n\n // Duck-type the name so this works for a real File, a named Blob, or a\n // File-like object (and does not depend on the `File` global existing).\n const maybeName = (file as File).name;\n const fileName =\n typeof maybeName === \"string\" && maybeName.length > 0 ? maybeName : \"upload.bin\";\n const contentType = file.type && file.type.length > 0\n ? file.type\n : \"application/octet-stream\";\n\n const meta: UploadDocumentMeta = {\n moduleKey: target.moduleKey,\n entityName: target.entityName,\n entityId: target.entityId,\n category: target.category,\n fileName,\n contentType,\n sizeBytes: buffer.byteLength,\n };\n\n const result = await uploadDocument(meta, buffer, controller.signal);\n\n applyState(() => {\n setProgress(1);\n setLoading(false);\n });\n return result;\n } catch (err) {\n const wrapped = err instanceof Error ? err : new Error(String(err));\n applyState(() => {\n setError(wrapped);\n setLoading(false);\n setProgress(0);\n });\n throw wrapped;\n }\n },\n [transport, maxSizeBytes],\n );\n\n return { upload, progress, loading, error };\n}\n","import { createContext } from \"react\";\n\nexport const OVERLAY_HOST_CONTRACT_VERSION = \"1.0.0\";\n\n/** Canonical inset-lease identity — the SAME shape the web host layout store uses (Codex R2 M1:\n * one lease shape across SDK/web/store, no adapter). The store's handle extends this with `source`. */\nexport interface InsetLease {\n readonly leaseId: string;\n readonly generation: number;\n}\n\nexport interface OverlayHostCapabilities {\n readonly contractVersion: string; // OVERLAY_HOST_CONTRACT_VERSION the host implements\n readonly contentInset: boolean; // side-by-side inset supported\n readonly focusHelpers: boolean; // focus-handoff + Escape helpers present\n readonly safeArea: boolean; // mobile safe-area insets provided\n}\n\n/** The host-provided API. The host constructs this object; the plugin receives it as a prop and\n * reads it via useOverlayHost(). Shape is additive-only — add optional fields, never remove. */\nexport interface OverlayHostApi {\n /** Reserve host content space. Returns a lease on grant, or NULL when the request is rejected\n * (invalid px) or YIELDED (a higher-precedence source holds the inset) — Codex R2: the contract\n * represents reject/yield deterministically so the overlay can fall back to float mode. */\n requestContentInset(inset: { side: \"right\"; px: number }): InsetLease | null;\n releaseContentInset(lease: InsetLease): void; // idempotent; stale handle = no-op\n readonly viewport: { width: number; height: number };\n readonly breakpoint: \"mobile\" | \"tablet\" | \"desktop\"; // host-authoritative\n readonly capabilities: OverlayHostCapabilities;\n /** Register a cancellation callback the host invokes during an identity transition (§6.1) BEFORE it\n * unmounts the overlay — the overlay uses it to cancel its in-flight server-side assistant turn so\n * no run continues under stale authority. Returns an unsubscribe. Additive; the host guarantees\n * every registered callback runs (awaited best-effort) before the remount. (Codex R1 H4 — this is\n * the concrete registration path Task 4.6's transition protocol invokes.) */\n registerActivityCancellation(cancel: () => void | Promise<void>): () => void;\n /** Focus-handoff helpers (§7.3). Return focus to the launcher on close/Escape. */\n readonly focus: {\n onEscape(handler: () => void): () => void; // returns an unsubscribe\n returnFocusToLauncher(): void;\n };\n}\n\n/** Props the host injects into the overlay component (mirrors PlatformReactPageProps + overlayHost). */\nexport interface PlatformReactOverlayProps {\n extensionId: string;\n organisationId: string;\n tenantId?: string;\n overlayId: string;\n overlayHost: OverlayHostApi;\n}\n\nexport const OverlayHostContext = createContext<OverlayHostApi | null>(null);\n","import { useContext } from \"react\";\nimport { OverlayHostContext, type OverlayHostApi } from \"./overlayHostContext\";\n\n/**\n * Reads the host-provided overlay API. Must be called inside a PlatformReact overlay mounted by the\n * host (the host injects the API via the `overlayHost` prop, which\n * `definePlatformReactPluginOverlay` bridges into {@link OverlayHostContext}).\n */\nexport function useOverlayHost(): OverlayHostApi {\n const api = useContext(OverlayHostContext);\n if (api === null) {\n throw new Error(\"useOverlayHost must be used within a host-mounted PlatformReact overlay.\");\n }\n return api;\n}\n","import { createElement, type ComponentType, type ReactElement } from \"react\";\nimport {\n OverlayHostContext,\n type PlatformReactOverlayProps,\n} from \"./overlayHostContext\";\nimport { TemplateContext, type ViewComponent } from \"./templateContext\";\nimport { PluginStyleScope } from \"./injectStyles\";\nimport { EHX_PLUGIN_ROOT_CLASS } from \"./definePlatformReactPluginPage\";\n\nexport interface DefinePlatformReactPluginOverlayOptions {\n rootClassName: string;\n styleId: string;\n css: string;\n manifest?: Record<string, Partial<Record<string, ViewComponent>>>;\n wrapOverlay?: (overlay: ReactElement) => ReactElement;\n}\n\n/**\n * Wraps an overlay component as a PlatformReact entry with the providers the host\n * needs: the `OverlayHostContext` (bridging the `overlayHost` prop into a plugin-side\n * context for `useOverlayHost()`), the static `useView` manifest, and the scoped style\n * root. Mirrors `definePlatformReactPluginPage`; the key difference is the\n * `OverlayHostContext.Provider` wrapping the `overlayHost` prop.\n *\n * @ethisyscore/plugin-ui is BUNDLED into the plugin (not externalized), so a React\n * context created in the host's module copy would not be identity-equal to the\n * plugin's copy. Passing the API as a prop and bridging it here is the correct seam\n * (D-2 design decision).\n */\nexport function definePlatformReactPluginOverlay(\n Overlay: ComponentType<PlatformReactOverlayProps>,\n options: DefinePlatformReactPluginOverlayOptions,\n): ComponentType<PlatformReactOverlayProps> {\n const { rootClassName, styleId, css, manifest = {}, wrapOverlay } = options;\n return function MountedOverlay(props: PlatformReactOverlayProps) {\n // Inject styles via <PluginStyleScope> (effect-based, idempotent under\n // StrictMode) rather than calling injectPluginStyles() during render — keeps\n // render pure and mirrors definePlatformReactPluginPage.\n const tree = createElement(\n OverlayHostContext.Provider,\n { value: props.overlayHost },\n createElement(\n TemplateContext.Provider,\n { value: manifest },\n createElement(PluginStyleScope, { id: styleId, css }),\n createElement(\n \"div\",\n { className: `${EHX_PLUGIN_ROOT_CLASS} ${rootClassName}` },\n createElement(Overlay, props),\n ),\n ),\n );\n return wrapOverlay ? wrapOverlay(tree) : tree;\n };\n}\n\nexport interface PluginOverlayDefinerConfig {\n module: string;\n rootClassName: string;\n styleId: string;\n css: string;\n wrapOverlay?: (overlay: ReactElement) => ReactElement;\n}\n\n/**\n * Builds a plugin-bound `definePluginOverlay(Overlay, views)` from one config, so a\n * plugin writes the four plugin-specific values (module key, root class, style id, CSS)\n * ONCE instead of in every overlay entry. The flat `views` map is wrapped into the\n * `{ [module]: views }` manifest slice that `useView(module, name)` reads. Mirrors\n * `createPluginPageDefiner`.\n */\nexport function createPluginOverlayDefiner(config: PluginOverlayDefinerConfig) {\n const { module, ...rest } = config;\n return function definePluginOverlay(\n Overlay: ComponentType<PlatformReactOverlayProps>,\n views: Record<string, ViewComponent> = {},\n ): ComponentType<PlatformReactOverlayProps> {\n return definePlatformReactPluginOverlay(Overlay, { ...rest, manifest: { [module]: views } });\n };\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\n/**\n * A managed cleanup registry the overlay uses to register event listeners,\n * timers, and AbortControllers. All registrations are torn down deterministically\n * when the host component unmounts.\n *\n * **Teardown semantics (R2 decision):** the HOST does NOT call `disposeAll()`.\n * Teardown rides on React's unmount cleanup (`useEffect(() => () => disposeAll(), [])`),\n * which the host guarantees by forcing unmount via the identity-key change at\n * the remount boundary. `disposeAll()` is exposed as an OPTIONAL early-teardown\n * affordance for the overlay's own use only — not the host's teardown path.\n *\n * @example\n * ```tsx\n * function MyOverlay() {\n * const lifecycle = useManagedLifecycle();\n * useEffect(() => {\n * lifecycle.addEventListener(window, \"resize\", handleResize);\n * const ctrl = lifecycle.createAbortController();\n * fetch(\"/api/data\", { signal: ctrl.signal });\n * }, [lifecycle]);\n * return <div />;\n * }\n * ```\n */\nexport interface ManagedLifecycle {\n /**\n * Registers an event listener on `target`. The listener is automatically\n * removed when the host component unmounts (or when `disposeAll()` is called).\n */\n addEventListener(\n target: Window | Document | HTMLElement,\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: AddEventListenerOptions,\n ): void;\n\n /**\n * Schedules a repeating callback. The interval is automatically cleared when\n * the host component unmounts (or when `disposeAll()` is called).\n *\n * @returns The interval ID (mirrors the native return value).\n */\n setInterval(handler: () => void, ms: number): number;\n\n /**\n * Schedules a one-shot callback. The timeout is automatically cleared when\n * the host component unmounts (or when `disposeAll()` is called).\n *\n * @returns The timeout ID (mirrors the native return value).\n */\n setTimeout(handler: () => void, ms: number): number;\n\n /**\n * Creates an AbortController whose signal is aborted when the host component\n * unmounts (or when `disposeAll()` is called).\n *\n * @returns The AbortController. The caller may pass `.signal` to fetch/streams.\n */\n createAbortController(): AbortController;\n\n /**\n * Optional early-teardown affordance for the overlay's own use. Removes all\n * registered listeners, clears all timers, and aborts all controllers\n * immediately.\n *\n * Idempotent: subsequent calls (including the React unmount cleanup) are safe\n * and do not throw.\n *\n * **The host does NOT call this.** Host-driven teardown is guaranteed by\n * React unmount cleanup via the identity-key remount mechanism.\n */\n disposeAll(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal registry entries\n// ---------------------------------------------------------------------------\n\ninterface ListenerEntry {\n kind: \"listener\";\n target: Window | Document | HTMLElement;\n type: string;\n listener: EventListenerOrEventListenerObject;\n options?: AddEventListenerOptions;\n}\n\ninterface IntervalEntry {\n kind: \"interval\";\n id: number;\n}\n\ninterface TimeoutEntry {\n kind: \"timeout\";\n id: number;\n}\n\ninterface ControllerEntry {\n kind: \"controller\";\n controller: AbortController;\n}\n\ntype RegistryEntry = ListenerEntry | IntervalEntry | TimeoutEntry | ControllerEntry;\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Returns a {@link ManagedLifecycle} registry. All registered event listeners,\n * timers, and AbortControllers are automatically torn down when the host\n * component unmounts.\n *\n * The returned object has stable identity across re-renders (backed by `useMemo`\n * / `useRef`) so it can be placed in dependency arrays without causing churn.\n */\nexport function useManagedLifecycle(): ManagedLifecycle {\n // Ref holds the live registry so it is shared across all renders without\n // causing re-renders. We use an Array (not a Set) for deterministic teardown\n // order and because entries are not deduplicated — the same listener may be\n // registered with different options.\n const entriesRef = useRef<RegistryEntry[]>([]);\n\n // Stable lifecycle object. `useMemo` with an empty dep array gives us a\n // single object identity for the lifetime of the component, which means\n // callers can safely include `lifecycle` in useEffect dependency arrays.\n const lifecycle = useMemo<ManagedLifecycle>(() => {\n const addEventListener = (\n target: Window | Document | HTMLElement,\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: AddEventListenerOptions,\n ): void => {\n target.addEventListener(type, listener, options);\n entriesRef.current.push({ kind: \"listener\", target, type, listener, options });\n };\n\n const setInterval = (handler: () => void, ms: number): number => {\n const id = globalThis.setInterval(handler, ms) as unknown as number;\n entriesRef.current.push({ kind: \"interval\", id });\n return id;\n };\n\n const setTimeout = (handler: () => void, ms: number): number => {\n const id = globalThis.setTimeout(handler, ms) as unknown as number;\n entriesRef.current.push({ kind: \"timeout\", id });\n return id;\n };\n\n const createAbortController = (): AbortController => {\n const controller = new AbortController();\n entriesRef.current.push({ kind: \"controller\", controller });\n return controller;\n };\n\n const disposeAll = (): void => {\n // Drain the registry atomically so concurrent / re-entrant calls are safe.\n const entries = entriesRef.current.splice(0);\n for (const entry of entries) {\n switch (entry.kind) {\n case \"listener\":\n entry.target.removeEventListener(entry.type, entry.listener, entry.options);\n break;\n case \"interval\":\n globalThis.clearInterval(entry.id);\n break;\n case \"timeout\":\n globalThis.clearTimeout(entry.id);\n break;\n case \"controller\":\n entry.controller.abort();\n break;\n }\n }\n };\n\n return { addEventListener, setInterval, setTimeout, createAbortController, disposeAll };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Teardown on unmount. The host guarantees unmount via the identity-key change;\n // `disposeAll()` is also an optional early-teardown path for the overlay itself.\n useEffect(() => () => lifecycle.disposeAll(), [lifecycle]);\n\n return lifecycle;\n}\n","import { useContext, useEffect, useRef } from \"react\";\nimport {\n HostSidebarActionsContext,\n isHostChromeCompatible,\n reportHostChromeDiagnostic,\n type HostSidebarActionDescriptor,\n type HostSidebarActionsApi,\n type HostSidebarContribution,\n type HostSidebarNavItem,\n} from \"./hostSidebarActionsContext\";\nimport { SurfaceBaseContext } from \"./surfaceUrl\";\n\n/** Authoring shape: a descriptor plus — ONLY for `kind:\"dispatch\"` — an inline handler.\n * A distributive union (not a blanket intersection over the whole descriptor union): `onSelect`\n * is permitted only on a dispatch action. A navigate action carries no handler (the host performs\n * the navigation from `href`), so supplying `onSelect` on a navigate action is a typecheck error\n * rather than a silently-ignored footgun. The hook strips `onSelect` before publishing (only\n * serializable descriptors cross to the host). */\nexport type HostSidebarAction =\n | (Extract<HostSidebarActionDescriptor, { kind: \"dispatch\" }> & { onSelect?: () => void })\n | Extract<HostSidebarActionDescriptor, { kind: \"navigate\" }>;\n\n/** Single source for the discriminant values used across the mode checks, `toContribution`, and\n * the content key — so the \"none\"/\"actions\"/\"detail\" strings live in one place. The union type\n * below intentionally keeps its literal members (they ARE the discriminant + drive IntelliSense);\n * `SIDEBAR_MODE.*` is `as const`, so it is type-identical to those literals. */\nexport const SIDEBAR_MODE = { none: \"none\", actions: \"actions\", detail: \"detail\" } as const;\n\n/** Discriminated input to `useHostSidebarActions`. `mode:\"none\"` suppresses all plugin-side\n * contributions without clobbering the static manifest actions. `mode:\"actions\"` is the\n * batch-2 Quick Actions shape. `mode:\"detail\"` is the full detail-nav replacement with title,\n * navigation items, back-arrow, and Quick Actions. */\nexport type HostSidebarInput =\n | { mode: \"none\" }\n | { mode: \"actions\"; entityToken: string; actions: HostSidebarAction[] }\n | {\n mode: \"detail\";\n entityToken: string;\n title: string;\n navSectionLabel: string;\n navItems: HostSidebarNavItem[];\n backHref: string;\n backLabel: string;\n actions: HostSidebarAction[];\n };\n\nfunction toDescriptor(a: HostSidebarAction): HostSidebarActionDescriptor {\n if (a.kind === \"dispatch\") {\n const { onSelect: _drop, ...rest } = a;\n return rest; // rest is the serializable dispatch descriptor\n }\n return a; // navigate descriptor is already serializable (no handler)\n}\n\n/** Map a plugin-authoring input to the serializable contribution published to the host.\n * `onSelect` handlers are stripped via `toDescriptor`; nav/back/title pass through for `detail`. */\nfunction toContribution(input: HostSidebarInput): HostSidebarContribution {\n if (input.mode === SIDEBAR_MODE.none) {\n return { mode: SIDEBAR_MODE.none };\n }\n if (input.mode === SIDEBAR_MODE.actions) {\n return {\n mode: SIDEBAR_MODE.actions,\n entityToken: input.entityToken,\n actions: input.actions.map(toDescriptor),\n };\n }\n // mode === \"detail\"\n return {\n mode: SIDEBAR_MODE.detail,\n entityToken: input.entityToken,\n title: input.title,\n navSectionLabel: input.navSectionLabel,\n navItems: input.navItems,\n backHref: input.backHref,\n backLabel: input.backLabel,\n actions: input.actions.map(toDescriptor),\n };\n}\n\n/** Content key so we republish only when the visible descriptor set changes. JSON-encodes an\n * ARRAY of ORDERED-tuple arrays: element order is stable (unlike object property insertion order,\n * which is why we avoid `JSON.stringify` over the raw descriptor objects — spec-gate MEDIUM R1-M2),\n * and JSON string-escaping makes the key delimiter-collision-proof, so a `label`/`href` that\n * happens to contain a separator character can no longer alias two distinct action sets to the\n * same key (which would skip a needed republish and leave the host sidebar stale). */\nfunction contributionKey(input: HostSidebarInput): string {\n if (input.mode === SIDEBAR_MODE.none) {\n return JSON.stringify([SIDEBAR_MODE.none]);\n }\n const actionTuples = input.actions.map((a) => [\n a.id,\n a.label,\n a.icon ?? \"\",\n a.slot ?? \"\",\n a.variant ?? \"\",\n a.disabled ? 1 : 0,\n a.active ? 1 : 0,\n a.requiredPermission ?? \"\",\n a.kind,\n a.kind === \"navigate\" ? a.href : \"\",\n ]);\n if (input.mode === SIDEBAR_MODE.actions) {\n return JSON.stringify([SIDEBAR_MODE.actions, input.entityToken, actionTuples]);\n }\n // mode === \"detail\"\n const navTuples = input.navItems.map((n) => [n.id, n.label, n.icon ?? \"\", n.href]);\n return JSON.stringify([\n SIDEBAR_MODE.detail,\n input.entityToken,\n input.title,\n input.navSectionLabel,\n input.backHref,\n input.backLabel,\n navTuples,\n actionTuples,\n ]);\n}\n\n/** Per-page dev-warn throttle (NOT a global boolean — a module singleton would suppress warnings\n * for every later surface in a shared bundle; spec-gate HIGH R1-H2). Telemetry fires every time. */\nconst devWarnedPages = new Set<string>();\nfunction reportNoHost(pageId: string | undefined): void {\n // Observable in ALL environments via the injected diagnostic sink, tagged by page id (R1-H3).\n reportHostChromeDiagnostic({ event: \"host_sidebar_actions_unavailable\", pageId });\n const key = pageId ?? \"*\";\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if ((import.meta as any).env?.DEV && !devWarnedPages.has(key)) {\n devWarnedPages.add(key);\n // eslint-disable-next-line no-console\n console.warn(\n `[plugin-ui] useHostSidebarActions (${key}): no compatible host sidebar-actions API — ` +\n \"the host predates the seam or its contract major differs. Quick Actions will not render.\",\n );\n }\n}\n\n/**\n * Publish contextual sidebar contributions from a PlatformReact page to the host sidebar, and\n * receive click dispatch by id. Accepts a discriminated `HostSidebarInput`:\n * - `mode:\"none\"` — suppress all plugin contributions without clobbering the static manifest.\n * - `mode:\"actions\"` — batch-2 Quick Actions for a detail entity (entity token + action list).\n * - `mode:\"detail\"` — full detail-nav replacement (title, nav items, back-arrow, Quick Actions).\n *\n * Serializable descriptors only cross to the host; the inline `onSelect` handlers stay in a\n * ref-map here and are always the latest closure. Fails closed (no publish/subscribe, no throw)\n * when the host predates or is incompatible with the seam. */\nexport function useHostSidebarActions(input: HostSidebarInput): void {\n const api = useContext(HostSidebarActionsContext);\n const pageId = useContext(SurfaceBaseContext)?.pageId; // diagnostic tag (per-surface)\n const compatible: HostSidebarActionsApi | null =\n api && isHostChromeCompatible(api.capabilities?.contractVersion) ? api : null;\n\n // Ref-map refreshed every render → dispatch always hits the latest handler.\n const actions = input.mode === SIDEBAR_MODE.none ? [] : input.actions;\n const handlers = useRef<Map<string, () => void>>(new Map());\n handlers.current = new Map(\n actions.flatMap((a) =>\n a.kind === \"dispatch\" && a.onSelect ? [[a.id, a.onSelect] as const] : [],\n ),\n );\n\n useEffect(() => {\n if (!compatible) {\n reportNoHost(pageId);\n return;\n }\n const unsub = compatible.subscribe((id) => {\n handlers.current.get(id)?.();\n });\n return () => {\n compatible.clear();\n unsub();\n };\n }, [compatible, pageId]);\n\n // Content key: mode + entityToken + descriptor tuples + (detail) nav/back/title.\n const key = contributionKey(input);\n useEffect(() => {\n if (!compatible) return;\n compatible.publish(toContribution(input));\n // key encodes all content that affects the published contribution; changes force republish.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [compatible, key]);\n}\n","import type { PluginDetailContext, PluginDetailContextItem } from \"../components/layout/pageHeaderTypes\";\nimport type { HostSidebarActionSlot, HostSidebarNavItem } from \"./hostSidebarActionsContext\";\nimport type { HostSidebarAction, HostSidebarInput } from \"./useHostSidebarActions\";\n\n/**\n * Options for {@link toHostSidebarInput}.\n */\nexport interface ToHostSidebarInputOptions {\n /** The plugin's mount base (e.g. `/extensions/projects`). Stripped from hrefs. */\n basePath: string;\n /**\n * The entity token the host matches to the current route. Use the bare entity id\n * — the host segment-matches it across an entity's sub-pages\n * (`deriveEntityToken`/`tokenMatches`). When absent the result is `{mode:\"none\"}`.\n */\n entityToken: string | undefined;\n /**\n * Dispatch bridge for `actionId` actions — pass a page's `useTriggerSidebarAction()`\n * so a sidebar click reaches the same `useSidebarActions` handler the header uses.\n * (Kept as a parameter rather than called internally so this stays a PURE mapper\n * and avoids the cross-entry `SidebarActionContext` singleton pitfall.)\n */\n onDispatch: (actionId: string) => void;\n /**\n * Slug-relative fallback for a back link that can't be confined (e.g. the plugin\n * root, whose path is empty and which the host rejects). Defaults to `\"\"` which,\n * if the primary back link is also unconfinable, yields no back link.\n */\n backHrefFallback?: string;\n}\n\n/**\n * Map a plugin {@link PluginDetailContext} to a {@link HostSidebarInput} for\n * `useHostSidebarActions(mode:\"detail\")` — the plugin-side of the host left-sidebar\n * detail panel (back link + sub-page nav + Quick actions), the companion of\n * `useDetailHeaderActions` (the header surface).\n *\n * Encodes the host-contract rules so no plugin has to re-derive them from host\n * source (getting them wrong SILENTLY BLANKS the panel):\n * - hrefs are made SLUG-RELATIVE (basePath stripped); absolute/cross-module hrefs\n * are dropped (the host rejects them and `mode:\"detail\"` atomic-drops on a bad\n * nav/back href).\n * - the host action contract is FLAT (no nested children), so children-bearing\n * actions (e.g. Lifecycle / Share) are flattened to their leaves.\n * - `actionId` → `kind:\"dispatch\"` with `onSelect` wired to `onDispatch`; `href` →\n * `kind:\"navigate\"`; a bare `onClick` → `kind:\"dispatch\"` with that handler.\n * - duplicate action ids are removed (the host rejects the whole contribution on a\n * duplicate id).\n * - no context / no entity token → `{mode:\"none\"}` (suppresses without clobbering\n * the static manifest).\n */\nexport function toHostSidebarInput(\n context: PluginDetailContext | null | undefined,\n opts: ToHostSidebarInputOptions,\n): HostSidebarInput {\n if (!context || !opts.entityToken) {\n return { mode: \"none\" };\n }\n const navItems: HostSidebarNavItem[] = context.navItems\n .map((n) => ({ id: n.id, label: n.label, icon: n.icon, href: slugRelative(n.href, opts.basePath) }))\n .filter((n) => isConfinable(n.href));\n\n // A non-confinable back link (empty root, absolute/cross-module, or schemed) would\n // atomic-drop the whole detail contribution — fall back instead of publishing it.\n const mappedBack = slugRelative(context.backHref, opts.basePath);\n const backHref = isConfinable(mappedBack) ? mappedBack : opts.backHrefFallback ?? \"\";\n\n return {\n mode: \"detail\",\n entityToken: opts.entityToken,\n title: context.title,\n navSectionLabel: context.navSectionLabel ?? \"Sections\",\n navItems,\n backHref,\n backLabel: context.backLabel ?? \"Back\",\n actions: toHostActions(context.actions ?? [], opts),\n };\n}\n\nfunction slugRelative(href: string | undefined, basePath: string): string {\n if (!href) return \"\";\n // Normalise a trailing slash on basePath (the host may provide one — see\n // platform-react/surfaceUrl.ts) so the prefix isn't `/extensions/x//`.\n const base = basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n if (href === base) return \"\";\n const prefix = `${base}/`;\n return href.startsWith(prefix) ? href.slice(prefix.length) : href;\n}\n\n/**\n * Whether a slug-relative href is one the host confiner accepts: non-empty, not\n * absolute (`/…` cross-module), no URI scheme (`http:`…), no fragment. A\n * non-confinable href must be dropped (nav/action) or replaced (back link) — the\n * host rejects it and `mode:\"detail\"` atomic-drops on a bad nav/back href.\n */\nfunction isConfinable(slugRelativeHref: string): boolean {\n return (\n slugRelativeHref.length > 0 &&\n !slugRelativeHref.startsWith(\"/\") &&\n !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(slugRelativeHref) &&\n !slugRelativeHref.includes(\"#\")\n );\n}\n\n/** Flatten children-bearing items to their leaves — the host action contract is FLAT. */\nfunction flattenLeaves(items: PluginDetailContextItem[]): PluginDetailContextItem[] {\n const out: PluginDetailContextItem[] = [];\n for (const item of items) {\n if (item.children?.length) out.push(...flattenLeaves(item.children));\n else out.push(item);\n }\n return out;\n}\n\nfunction toHostActions(\n actions: PluginDetailContextItem[],\n opts: ToHostSidebarInputOptions,\n): HostSidebarAction[] {\n const out: HostSidebarAction[] = [];\n for (const item of flattenLeaves(actions)) {\n const mapped = toHostAction(item, opts);\n if (mapped) out.push(mapped);\n }\n const seen = new Set<string>();\n return out.filter((a) => (seen.has(a.id) ? false : (seen.add(a.id), true)));\n}\n\nfunction toHostAction(\n item: PluginDetailContextItem,\n opts: ToHostSidebarInputOptions,\n): HostSidebarAction | null {\n const slot: HostSidebarActionSlot =\n item.slot === \"primary\" ? \"primary\" : item.slot === \"sidebar-only\" ? \"sidebar-only\" : \"overflow\";\n const variant = item.variant === \"danger\" ? (\"danger\" as const) : undefined;\n const shared = { id: item.id, label: item.label, icon: item.icon, slot, variant, disabled: item.disabled };\n\n if (item.href) {\n const href = slugRelative(item.href, opts.basePath);\n // Non-confinable (empty root, absolute/cross-module, schemed) → the host rejects\n // it; skip this action rather than risk it.\n if (!isConfinable(href)) return null;\n return { ...shared, kind: \"navigate\", href };\n }\n if (item.actionId) {\n const actionId = item.actionId;\n return { ...shared, kind: \"dispatch\", onSelect: () => opts.onDispatch(actionId) };\n }\n if (item.onClick) {\n return { ...shared, kind: \"dispatch\", onSelect: item.onClick };\n }\n return null;\n}\n"]}
1
+ {"version":3,"sources":["../../src/platform-react/templateContext.ts","../../src/platform-react/injectStyles.ts","../../src/platform-react/surfaceUrl.ts","../../src/platform-react/hostSidebarActionsContext.ts","../../src/platform-react/definePlatformReactPluginPage.tsx","../../src/platform-react/MissingViewFallback.tsx","../../src/platform-react/useView.ts","../../src/platform-react/mcpService.ts","../../src/platform-react/useAuthenticatedQuery.ts","../../src/platform-react/reactRouterShim.tsx","../../src/platform-react/usePluginRealtime.ts","../../src/platform-react/useMcpUpload.ts","../../src/platform-react/overlayHostContext.ts","../../src/platform-react/useOverlayHost.ts","../../src/platform-react/definePlatformReactPluginOverlay.tsx","../../src/platform-react/managedLifecycle.ts","../../src/platform-react/useHostSidebarActions.ts","../../src/platform-react/toHostSidebarInput.ts"],"names":["createContext","useEffect","useContext","useHostIdentity","QueryClient","createElement","QueryClientProvider","definePlatformReactPage","useView","useMcpTool","useMemo","useQuery","useQueries","emitNavigation","usePluginRealtimeSource","useRef","useExtensionRuntimeTransport","useState","useCallback"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, type ComponentType } from \"react\";\n\n/** A view component resolved from the template manifest. Props are open. */\nexport type ViewComponent = ComponentType<Record<string, unknown>>;\n\n/**\n * Static port of the monolith's gogo-ui adapter mechanism for PlatformReact.\n *\n * A monolith feature picks its adapter view at runtime via `import.meta.glob`;\n * a PlatformReact page is a single self-contained ESM bundle where that glob is\n * unavailable, so each page declares its view slice statically and provides it\n * through this context. The shape is `manifest[module][viewName] = Component`.\n */\nexport type TemplateManifest = Record<string, Partial<Record<string, ViewComponent>>>;\n\nexport const TemplateContext = createContext<TemplateManifest | null>(null);\n","import { useEffect } from \"react\";\n\n/**\n * Injects a plugin's compiled CSS into the host document once, keyed by a\n * stable element id (idempotent across pages and re-mounts).\n *\n * Each PlatformReact page is a separate ESM bundle with no companion CSS asset\n * the host would load, so the styles travel inside the bundle (typically via a\n * `*.css?inline` import) and are mounted at runtime. Scope the CSS to a plugin\n * root class (never `:root`) so it cannot override the host theme.\n */\nexport function injectPluginStyles(id: string, css: string): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(id)) return;\n const el = document.createElement(\"style\");\n el.id = id;\n el.textContent = css;\n document.head.appendChild(el);\n}\n\n/** Component form of {@link injectPluginStyles}; renders nothing. */\nexport function PluginStyleScope({ id, css }: { id: string; css: string }): null {\n useEffect(() => {\n injectPluginStyles(id, css);\n }, [id, css]);\n return null;\n}\n","import { createContext, useContext } from \"react\";\nimport { useHostIdentity } from \"@ethisyscore/extension-runtime/plugin\";\n\n/** The surface's mount context, provided by definePlatformReactPluginPage. */\nexport interface SurfaceBaseValue {\n /** Host-provided mount path `/extensions/<slug>/<pageId>` (authoritative). */\n basePath?: string;\n /** The page id (from PlatformReactPageProps), used for the derived fallback. */\n pageId?: string;\n}\n\nexport const SurfaceBaseContext = createContext<SurfaceBaseValue | undefined>(undefined);\n\n/** Match the host's slug normalisation (useExtensionSurfaceShellPage `normaliseSlug`). */\nexport function normaliseSlug(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction stripTrailingSlash(p: string): string {\n return p.length > 1 && p.endsWith(\"/\") ? p.slice(0, -1) : p;\n}\n\n/**\n * Resolve the surface base. Precedence: host-provided basePath → derived from\n * normaliseSlug(extensionGroupCode) + pageId → null (caller decides how to fail).\n * Pure — callers read context + identity and pass the pieces in.\n */\nexport function resolveSurfaceBase(input: {\n basePath?: string;\n pageId?: string;\n extensionGroupCode?: string | null;\n}): { base: string; groupRoot: string } | null {\n if (input.basePath) {\n const base = stripTrailingSlash(input.basePath);\n const cut = base.lastIndexOf(\"/\");\n const groupRoot = cut > 0 ? base.slice(0, cut) : base;\n return { base, groupRoot };\n }\n const slug = input.extensionGroupCode ? normaliseSlug(input.extensionGroupCode) : \"\";\n const pageId = input.pageId ?? \"\";\n if (slug && pageId) {\n const groupRoot = `/extensions/${slug}`;\n return { base: `${groupRoot}/${pageId}`, groupRoot };\n }\n return null;\n}\n\n/** Join base + sub (leading slash stripped) + optional ?query/#hash suffix. */\nexport function buildSurfaceUrl(base: string, sub = \"\", suffix = \"\"): string {\n const b = stripTrailingSlash(base);\n const s = sub.replace(/^\\/+/, \"\");\n const path = s ? `${b}/${s}` : b;\n return suffix ? `${path}${suffix}` : path;\n}\n\n/**\n * Hook: build a URL relative to the CURRENT surface. Precedence per\n * resolveSurfaceBase; throws in dev when unresolved (never returns app-root).\n */\nexport function useSurfaceUrl(): (sub?: string, suffix?: string) => string {\n const ctx = useContext(SurfaceBaseContext);\n const identity = useHostIdentity();\n return (sub = \"\", suffix = \"\") => {\n const resolved = resolveSurfaceBase({\n basePath: ctx?.basePath,\n pageId: ctx?.pageId,\n extensionGroupCode: identity?.extensionGroupCode ?? null,\n });\n if (!resolved) {\n const msg =\n \"useSurfaceUrl: no surface base — SurfaceBaseContext (basePath/pageId) and \" +\n \"extensionGroupCode are both unavailable. Ensure the page is wrapped by \" +\n \"definePlatformReactPluginPage inside the host runtime.\";\n // Dev: fail loud. Prod: never navigate to app-root (that recreates the 404\n // class) — stay on the current path (a no-op) so a transient unresolved\n // state (e.g. identity still loading on a pre-basePath host) can't crash the\n // surface. Callers invoke this in event handlers, by which time identity has\n // loaded and the base resolves normally.\n // Guard `process` access: in a browser/ESM consumer where `process` is\n // undefined, reading `process.env.NODE_ENV` directly would throw a\n // ReferenceError before the prod fallback runs. `typeof` never throws on an\n // undeclared identifier. Fail SAFE — throw ONLY when we can positively\n // confirm a non-production env; an unknown env (no `process`) is treated as\n // production so a bundled surface never crashes here.\n const isDevEnv =\n typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\";\n if (isDevEnv) throw new Error(msg);\n if (typeof console !== \"undefined\") console.error(msg);\n // Prod no-op: stay on the FULL current URL (path + query + hash) — a true\n // no-op, and never app-root.\n return typeof window !== \"undefined\"\n ? window.location.pathname + window.location.search + window.location.hash\n : \"\";\n }\n return buildSurfaceUrl(resolved.base, sub, suffix);\n };\n}\n\n/** Pure: build a URL for ANOTHER surface (cross-surface, e.g. an overlay opening a page). */\nexport function surfacePathFor(opts: {\n slug: string;\n pageId: string;\n sub?: string;\n suffix?: string;\n}): string {\n const base = `/extensions/${normaliseSlug(opts.slug)}/${opts.pageId}`;\n return buildSurfaceUrl(base, opts.sub ?? \"\", opts.suffix ?? \"\");\n}\n","import { createContext } from \"react\";\nimport type { Context } from \"react\";\nimport type { HostSidebarActionsApiShape } from \"@ethisyscore/components-react\";\n\n// ── Cross-bundle singleton keys ───────────────────────────────────────────────\n// tsup may inline this module into BOTH the package-root entry and the subpath\n// entry, producing two distinct module copies. Storing shared state on globalThis\n// under Symbol.for(…) keys guarantees that every copy resolves the SAME object.\nconst CONTEXT_KEY = Symbol.for(\"ethisyscore.plugin-ui.hostSidebarActionsContext\");\nconst SINK_KEY = Symbol.for(\"ethisyscore.plugin-ui.hostChromeDiagnosticSink\");\n\n/** Host-chrome / sidebar-actions contract version (semver). Host MAJOR must equal the SDK\n * MAJOR before any publish/subscribe; otherwise the plugin hook fails closed (WI 5188 batch 2).\n * 2.1.0 added the additive `kind:\"group\"` action variant (collapsible Lifecycle/Share); the\n * major-equality gate keeps 2.0 and 2.1 mutually compatible (older hosts ignore group children). */\nexport const HOST_CHROME_CONTRACT_VERSION = \"2.1.0\";\n\nexport type HostSidebarActionSlot = \"primary\" | \"overflow\" | \"sidebar-only\";\nexport type HostSidebarActionVariant = \"default\" | \"danger\";\n\n/** Fields common to every action. */\ninterface HostSidebarActionBase {\n id: string;\n label: string;\n icon?: string;\n slot?: HostSidebarActionSlot;\n variant?: HostSidebarActionVariant;\n disabled?: boolean;\n active?: boolean;\n /** Optional host-side double-gate: the mask is resolved against the surface extension groupCode. */\n requiredPermission?: number;\n}\n\n/** Strictly serializable action the plugin publishes to the host sidebar — NO functions cross\n * the boundary. Discriminated union so `href` is REQUIRED at the type level for a navigate\n * action and FORBIDDEN for a dispatch action (spec-gate MEDIUM R1-M1 — a navigate with a\n * missing href fails typecheck at authoring time rather than being silently dropped by the host).\n * `kind:\"dispatch\"` is clicked back to the plugin by `id` via the action-emit subscription. */\nexport type HostSidebarActionDescriptor =\n | (HostSidebarActionBase & { kind: \"dispatch\" })\n | (HostSidebarActionBase & { kind: \"navigate\"; href: string }) // extension-relative; host normalizes/validates\n // A collapsible parent (e.g. Lifecycle / Share). It carries no behaviour of its own — the host\n // renders it as an accordion and its `children` as the collapsed rows. `children` is recursive:\n // each child is a navigate/dispatch action OR a nested group.\n | (HostSidebarActionBase & { kind: \"group\"; children: HostSidebarActionDescriptor[] });\n\n/** A navigation leaf the plugin drives into the host detail sidebar. `href` is extension-relative;\n * the host canonicalises + confines it to `/extensions/<slug>/…` (host-owned, never trusted). */\nexport interface HostSidebarNavItem {\n id: string;\n label: string;\n icon?: string;\n href: string;\n}\n\n/** The single publish payload. `none` = contribute nothing (do NOT clobber the static manifest\n * actions); `actions` = batch-2 Quick Actions for a detail entity; `detail` = full detail-nav\n * REPLACEMENT (title + nav items + back-arrow + Quick Actions). `identityEpoch` is host-stamped\n * (plan D-1), so it is not part of this plugin-published shape. */\nexport type HostSidebarContribution =\n | { mode: \"none\" }\n | { mode: \"actions\"; entityToken: string; actions: HostSidebarActionDescriptor[] }\n | {\n mode: \"detail\";\n entityToken: string;\n title: string;\n navSectionLabel: string;\n navItems: HostSidebarNavItem[];\n backHref: string;\n backLabel: string;\n actions: HostSidebarActionDescriptor[];\n };\n\nexport interface HostSidebarActionsCapabilities {\n readonly contractVersion: string; // HOST_CHROME_CONTRACT_VERSION the host implements\n}\n\n/** Host-provided API, passed to a PlatformReact page as the `hostSidebar` prop and read via\n * the plugin-bundled context. Additive-only. */\nexport interface HostSidebarActionsApi {\n /** Replace the surface's sidebar contribution. The host rejects a publish whose entityToken ≠\n * its current location-derived token, rejects duplicate action ids, and — for mode:\"detail\" —\n * rejects/atomically-drops an invalid or href-unsafe context (falls back to main nav). */\n publish(contribution: HostSidebarContribution): void;\n /** Revert to the static-manifest fallback (publication state = unpublished). */\n clear(): void;\n /** Subscribe to click dispatch for `kind:\"dispatch\"` actions. The host emits the clicked\n * action id. Returns an unsubscribe. The plugin bridge subscribes exactly once. */\n subscribe(onAction: (actionId: string) => void): () => void;\n readonly capabilities: HostSidebarActionsCapabilities;\n}\n\n/** Props the host injects into a page for the sidebar-actions seam (merged into PlatformReactPageProps).\n * Typed against the dependency-neutral `HostSidebarActionsApiShape` — the SAME type\n * `PlatformReactPageProps.hostSidebar` uses in `@ethisyscore/components-react` — so this\n * convenience alias never diverges from the canonical page-props declaration. The concrete\n * `HostSidebarActionsApi` is structurally assignable to the shape; the page bridge narrows to\n * it internally. */\nexport interface HostSidebarActionsPageProps {\n hostSidebar?: HostSidebarActionsApiShape;\n}\n\ntype GlobalWithCtx = typeof globalThis & {\n [CONTEXT_KEY]?: Context<HostSidebarActionsApi | null>;\n};\nconst _g = globalThis as GlobalWithCtx;\nexport const HostSidebarActionsContext: Context<HostSidebarActionsApi | null> =\n _g[CONTEXT_KEY] ?? (_g[CONTEXT_KEY] = createContext<HostSidebarActionsApi | null>(null));\n\n// ── Diagnostics adapter (concrete, testable — spec-gate HIGH R1-H3) ─────────────\n// A single injectable sink so degradation is OBSERVABLE in all environments without inventing a\n// `globalThis` global. The host runtime calls `setHostChromeDiagnosticSink` once to route these to\n// its real telemetry; absent a sink it is a safe no-op. Every emission carries the page id tag.\n//\n// The sink is stored on the SINK_KEY globalThis slot so that setHostChromeDiagnosticSink and\n// reportHostChromeDiagnostic in any duplicate module copy read/write the SAME slot.\nexport interface HostChromeDiagnostic {\n event: string;\n pageId?: string;\n detail?: unknown;\n}\ntype DiagnosticSinkFn = (d: HostChromeDiagnostic) => void;\ntype GlobalWithSink = typeof globalThis & { [SINK_KEY]?: DiagnosticSinkFn };\nconst _gs = globalThis as GlobalWithSink;\nif (_gs[SINK_KEY] === undefined) {\n _gs[SINK_KEY] = () => {};\n}\nexport function setHostChromeDiagnosticSink(sink: DiagnosticSinkFn): void {\n _gs[SINK_KEY] = sink;\n}\nexport function reportHostChromeDiagnostic(d: HostChromeDiagnostic): void {\n try {\n _gs[SINK_KEY]!(d);\n } catch {\n /* diagnostics are best-effort — never throw into the plugin */\n }\n}\n\n/** Major-equality compatibility check; fail-closed on absent/unparseable input. */\nexport function isHostChromeCompatible(hostVersion: string | undefined): boolean {\n if (hostVersion === undefined) return false;\n const hostMajor = Number.parseInt(hostVersion.split(\".\")[0] ?? \"\", 10);\n const sdkMajor = Number.parseInt(HOST_CHROME_CONTRACT_VERSION.split(\".\")[0] ?? \"\", 10);\n if (Number.isNaN(hostMajor) || Number.isNaN(sdkMajor)) return false;\n return hostMajor === sdkMajor;\n}\n","import { createElement, type ComponentType, type ReactElement } from \"react\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport {\n definePlatformReactPage,\n type PlatformReactPageProps,\n} from \"@ethisyscore/components-react\";\nimport { TemplateContext, type TemplateManifest, type ViewComponent } from \"./templateContext\";\nimport { PluginStyleScope } from \"./injectStyles\";\nimport { SurfaceBaseContext } from \"./surfaceUrl\";\nimport { HostSidebarActionsContext, type HostSidebarActionsApi } from \"./hostSidebarActionsContext\";\n\n/**\n * Default per-page `QueryClient`. Each PlatformReact page is an isolated ESM\n * bundle, so this module-level singleton is scoped to that page — exactly the\n * lifetime a page wants. Plugins needing different options pass their own.\n */\nfunction makeDefaultQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n retry: 1,\n refetchOnWindowFocus: false,\n staleTime: 30_000,\n },\n },\n });\n}\n\nconst defaultQueryClient = makeDefaultQueryClient();\n\n/**\n * Shared root class applied to every plugin page wrapper, in addition to the\n * plugin's own `rootClassName`. The SDK's `plugin-base.css` (scoped resets +\n * component overrides) targets this class, so host-parity styling reaches a\n * plugin's surface deterministically regardless of its plugin-specific class.\n */\nexport const EHX_PLUGIN_ROOT_CLASS = \"ehx-plugin-root\";\n\nexport interface DefinePlatformReactPluginPageOptions {\n /** The page's static `useView` manifest slice (no `import.meta.glob` in a single-file bundle). */\n manifest?: TemplateManifest;\n /** Scoped style-root class wrapped around the page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element (idempotent across pages). */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n /**\n * Optional wrapper applied around the page element, inside the style root. Use\n * to add a plugin-specific in-page provider — e.g. `withPageHeader(config)` from\n * `@ethisyscore/plugin-ui/components/layout` to restore list-page header CTAs.\n *\n * The wrapper is supplied as a ready-made function (not a config object) so this\n * entry stays free of the page-header React context — the provider and the\n * page's `usePageHeader` must resolve to the SAME `components/layout` module\n * instance, which they only do when both come from that entry (bundlers inline a\n * separate context copy per entry otherwise).\n */\n wrapPage?: (page: ReactElement) => ReactElement;\n}\n\n/**\n * Wraps a lifted page as a PlatformReact entry with the providers the monolith\n * took for granted: react-query, the static `useView` manifest, the scoped style\n * root, and — when `wrapPage` is supplied — a plugin-specific in-page provider\n * (e.g. the page-header). The host already provides the MCP transport (it mounts\n * pages inside its own runtime provider), so this adds only the in-page providers.\n */\nexport function definePlatformReactPluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n options: DefinePlatformReactPluginPageOptions,\n): ComponentType<PlatformReactPageProps> {\n const {\n manifest = {},\n rootClassName,\n styleId,\n css,\n queryClient = defaultQueryClient,\n wrapPage,\n } = options;\n\n function PluginPage(props: PlatformReactPageProps) {\n const page = createElement(Page, props);\n const body = wrapPage ? wrapPage(page) : page;\n const surfaceTree = createElement(\n SurfaceBaseContext.Provider,\n { value: { basePath: props.basePath, pageId: props.pageId } },\n createElement(\n QueryClientProvider,\n { client: queryClient },\n createElement(\n TemplateContext.Provider,\n { value: manifest },\n createElement(PluginStyleScope, { id: styleId, css }),\n createElement(\n \"div\",\n { className: `${EHX_PLUGIN_ROOT_CLASS} ${rootClassName}` },\n body,\n ),\n ),\n ),\n );\n return createElement(\n HostSidebarActionsContext.Provider,\n { value: (props.hostSidebar as HostSidebarActionsApi | undefined) ?? null },\n surfaceTree,\n );\n }\n\n return definePlatformReactPage(PluginPage);\n}\n\nexport interface PluginPageDefinerConfig {\n /**\n * The plugin's `useView` module key (e.g. `\"timesheets\"`). Each page's `views`\n * are registered under it, so a page's `useView(module, name)` resolves them.\n */\n module: string;\n /** Scoped style-root class wrapped around every page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element. */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n /**\n * Optional wrapper applied around every page (see\n * {@link DefinePlatformReactPluginPageOptions.wrapPage}) — e.g.\n * `withPageHeader(<feature>MenuConfig)` to restore list-page header CTAs.\n */\n wrapPage?: (page: ReactElement) => ReactElement;\n}\n\n/**\n * Builds a plugin-bound `definePluginPage(Page, views)` from one config, so a\n * plugin writes the four plugin-specific values (module key, root class, style\n * id, CSS) ONCE instead of in every page entry. Each surface entry then calls\n * `definePluginPage(Page, { ViewName })`; the flat `views` map is wrapped into\n * the `{ [module]: views }` manifest slice the page's `useView(module, name)`\n * reads. The generic provider/manifest/style wrapping stays in\n * {@link definePlatformReactPluginPage}.\n */\nexport function createPluginPageDefiner(config: PluginPageDefinerConfig) {\n const { module, ...rest } = config;\n return function definePluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n views: Record<string, ViewComponent> = {},\n ): ComponentType<PlatformReactPageProps> {\n return definePlatformReactPluginPage(Page, { ...rest, manifest: { [module]: views } });\n };\n}\n","import { createElement, type ComponentType } from \"react\";\n\n/**\n * Dependency-light fallback rendered when a view is not present in the current\n * template manifest. Kept to plain elements + inline styles so the foundation\n * package carries no UI-library dependency; plugins that want a branded\n * fallback pass their own via {@link createUseView}.\n */\nexport const MissingViewFallback: ComponentType<Record<string, unknown>> = () =>\n createElement(\n \"div\",\n {\n style: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2rem\",\n },\n },\n createElement(\n \"div\",\n {\n style: {\n maxWidth: \"28rem\",\n padding: \"1.5rem\",\n border: \"1px solid rgba(0,0,0,0.12)\",\n borderRadius: \"0.5rem\",\n textAlign: \"center\",\n },\n },\n createElement(\"p\", { style: { margin: \"0 0 0.5rem\", fontWeight: 600 } }, \"View not available\"),\n createElement(\n \"p\",\n { style: { margin: 0, opacity: 0.7, fontSize: \"0.875rem\" } },\n \"This view has not been implemented by the current template.\",\n ),\n ),\n );\n","import { useContext, type ComponentType } from \"react\";\nimport { TemplateContext } from \"./templateContext\";\nimport { MissingViewFallback } from \"./MissingViewFallback\";\n\n// Renderable component object-types keyed by their `$$typeof` tag. Plain function\n// (and class) components are matched by `typeof === \"function\"`; the wrappers below\n// are objects, not functions, so they need an explicit tag check. `forwardRef` and\n// `memo` were previously omitted — a `forwardRef`/`memo` view registered in the\n// manifest failed `isComponent` and `useView` fell back to \"View not available\".\nconst RENDERABLE_COMPONENT_TYPEOFS = new Set<symbol>([\n Symbol.for(\"react.lazy\"),\n Symbol.for(\"react.forward_ref\"),\n Symbol.for(\"react.memo\"),\n]);\n\n/**\n * Matches component types renderable via `useView`: plain function/class\n * components plus the object wrappers `React.lazy`, `React.forwardRef`, and\n * `React.memo` (incl. `memo(forwardRef(...))`, whose outer tag is `react.memo`).\n * Deliberately narrower than `react-is`'s `isValidElementType`, which also accepts\n * host-tag strings and Fragment/Suspense symbols — a manifest view value is always\n * a component, never a bare string.\n */\nexport function isComponent(value: unknown): value is ComponentType {\n if (typeof value === \"function\") return true;\n if (typeof value === \"object\" && value !== null) {\n const tag = (value as Record<string, unknown>).$$typeof;\n return typeof tag === \"symbol\" && RENDERABLE_COMPONENT_TYPEOFS.has(tag);\n }\n return false;\n}\n\n/**\n * Builds a `useView` hook bound to a fallback component. The hook is a safe\n * view accessor that never throws: it returns `manifest[module][viewName]` when\n * present, or the supplied fallback when the manifest, module slice, or view is\n * missing or is not a component.\n */\nexport function createUseView(fallback: ComponentType<Record<string, unknown>>) {\n return function useView(\n module: string,\n viewName: string,\n ): ComponentType<Record<string, unknown>> {\n const manifest = useContext(TemplateContext);\n if (!manifest) {\n return fallback;\n }\n\n const slice = manifest[module];\n if (slice && typeof slice === \"object\" && viewName in (slice as Record<string, unknown>)) {\n const component = (slice as Record<string, unknown>)[viewName];\n if (isComponent(component)) {\n return component as ComponentType<Record<string, unknown>>;\n }\n }\n\n return fallback;\n };\n}\n\n/** Default `useView` bound to the built-in {@link MissingViewFallback}. */\nexport const useView = createUseView(MissingViewFallback);\n","import { useMemo } from \"react\";\nimport { useMcpTool } from \"@ethisyscore/extension-runtime/plugin\";\n\n/** Dispatches an MCP tool call by name. The seam a lifted REST service binds to. */\nexport type ToolInvoker = (toolName: string, args: unknown) => Promise<unknown>;\n\n/**\n * Base for service classes lifted from a monolith REST plane onto MCP.\n *\n * The monolith versions extend a REST base (`this.get`/`this.post` over axios).\n * The plugin runtime exposes no imperative MCP transport — only the\n * `useMcpTool` hook — so a `useXService()` hook composes per-tool invokers (see\n * {@link useToolInvokerMap}) into a single {@link ToolInvoker} and constructs\n * the class with it. Method bodies call `this.tool(\"<tool-name>\", args)`.\n */\nexport abstract class BaseMcpService {\n constructor(private readonly invoker: ToolInvoker) {}\n\n protected async tool<TRes>(name: string, args: unknown = {}): Promise<TRes> {\n return (await this.invoker(name, args)) as TRes;\n }\n}\n\n/**\n * Thrown by a lifted service method whose monolith REST route has no MCP tool\n * yet (typically because it needs client-side aggregation over a coarser tool).\n */\nexport function notViaMcp(serviceName: string, method: string): never {\n throw new Error(`${serviceName}.${method} has no MCP tool — pending per-surface aggregation.`);\n}\n\n/** Imperative invoker for a single MCP tool (the hook's `.invoke`, stable across renders). */\nexport function useToolInvoker<TReq, TRes>(toolName: string): (req: TReq) => Promise<TRes> {\n return useMcpTool<TReq, TRes>(toolName).invoke;\n}\n\n/**\n * Composes one {@link ToolInvoker} dispatching over every tool in `tools`, so a\n * service class can call tools by name.\n *\n * Rules of hooks: `tools` MUST be a frozen, constant-length list (e.g. a\n * module-level `as const` array) so the per-tool `useMcpTool` calls run in a\n * stable order on every render. Passing a list whose length varies between\n * renders is a violation and will break.\n */\nexport function useToolInvokerMap(tools: readonly string[]): ToolInvoker {\n const invokers: Record<string, (args: unknown) => Promise<unknown>> = {};\n for (const name of tools) {\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `tools` is required to be frozen; call order is stable.\n invokers[name] = useMcpTool(name).invoke;\n }\n return useMemo<ToolInvoker>(\n () => (name, args) => {\n const fn = invokers[name];\n if (!fn) {\n throw new Error(`No MCP invoker registered for tool \"${name}\"`);\n }\n return fn(args);\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- per-tool invoke refs are stable; rebuild only if one changes.\n tools.map((t) => invokers[t]),\n );\n}\n","import {\n useQueries,\n useQuery,\n type QueryKey,\n type UseQueryOptions,\n type UseQueryResult,\n} from \"@tanstack/react-query\";\n\n/**\n * Plugin shim for the monolith's `useAuthenticatedQuery`.\n *\n * In the host realm the MCP transport is already authenticated and scoped to\n * the active organisation, so there is no auth gate to apply. The hook keeps\n * the monolith's return shape (`isLoading` + `isAuthLoading`) so lifted data\n * hooks and pages consume it unchanged — `isAuthLoading` is always `false`.\n */\nexport interface AuthenticatedQueryResult<TData, TError>\n extends Omit<UseQueryResult<TData, TError>, \"isLoading\"> {\n isLoading: boolean;\n isAuthLoading: boolean;\n}\n\nexport function useAuthenticatedQuery<\n TQueryFnData = unknown,\n TError = Error,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n): AuthenticatedQueryResult<TData, TError> {\n const result = useQuery(options);\n return {\n ...result,\n isLoading: result.isLoading,\n isAuthLoading: false,\n };\n}\n\nexport function useAuthenticatedQueries<TQueryFnData = unknown, TError = Error>(\n queries: UseQueryOptions<TQueryFnData, TError>[],\n) {\n return useQueries({ queries });\n}\n","import { createElement, useContext, type AnchorHTMLAttributes, type MouseEvent, type ReactNode } from \"react\";\nimport { emitNavigation } from \"@ethisyscore/components-react\";\nimport { useHostIdentity } from \"@ethisyscore/extension-runtime/plugin\";\nimport { SurfaceBaseContext, resolveSurfaceBase } from \"./surfaceUrl\";\n\n/**\n * Minimal `react-router-dom` compatibility shim for lifted monolith code.\n *\n * A PlatformReact plugin has no client router — navigation is brokered to the\n * host via {@link emitNavigation}, and route params come from the surface URL's\n * trailing segment(s). {@link createReactRouterShim} returns a module-shaped\n * object of the hooks/components lifted code expects; alias `react-router-dom`\n * to a tiny file that re-exports this object's members so call sites resolve\n * unchanged.\n *\n * `paramKeys` names the keys the single trailing path segment is exposed under,\n * so a route like `/…/<page>/<value>` satisfies `useParams<{ id }>()` /\n * `useParams<{ date }>()` regardless of which key the lifted code destructures.\n */\nexport interface ReactRouterShimOptions {\n /** Keys the trailing path segment is exposed under (default: `[\"id\"]`). Used as\n * the fallback when `routes` is not supplied or no route matches. */\n paramKeys?: readonly string[];\n /**\n * Route patterns (sub-paths relative to `basePath`, e.g. `invoices/:id/edit`)\n * for precise param extraction. When set, `useParams` matches the current path\n * against them (most-specific wins) and returns the captured params by name —\n * so a param in the MIDDLE of the path (`:id/edit`) resolves correctly, unlike\n * the trailing-segment fallback.\n */\n routes?: readonly string[];\n /** Base path stripped before matching `routes` (e.g. `/extensions/finance`). */\n basePath?: string;\n}\n\n/**\n * Matches `pathname` (minus `basePath`) against `routes` (most literal segments\n * wins) and returns the captured `:param` values, or `null` when none match.\n */\nfunction matchRouteParams(\n pathname: string,\n routes: readonly string[],\n basePath: string,\n): Record<string, string> | null {\n const prefix = basePath.length > 1 && basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n const norm = pathname.length > 1 && pathname.endsWith(\"/\") ? pathname.slice(0, -1) : pathname;\n const rest = norm.startsWith(prefix) ? norm.slice(prefix.length).replace(/^\\/+/, \"\") : \"\";\n const segments = rest.length > 0 ? rest.split(\"/\") : [];\n\n let best: Record<string, string> | null = null;\n let bestLiterals = -1;\n for (const route of routes) {\n const pattern = route.split(\"/\").filter(Boolean);\n if (pattern.length !== segments.length) {\n continue;\n }\n const params: Record<string, string> = {};\n let literals = 0;\n let ok = true;\n for (let i = 0; i < pattern.length; i++) {\n if (pattern[i].startsWith(\":\")) {\n params[pattern[i].slice(1)] = segments[i];\n } else if (pattern[i] !== segments[i]) {\n ok = false;\n break;\n } else {\n literals++;\n }\n }\n if (ok && literals > bestLiterals) {\n best = params;\n bestLiterals = literals;\n }\n }\n return best;\n}\n\ntype LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {\n to: string;\n children?: ReactNode;\n};\n\nexport function createReactRouterShim(options: ReactRouterShimOptions = {}) {\n const paramKeys = options.paramKeys ?? [\"id\"];\n const routes = options.routes;\n const basePath = options.basePath ?? \"\";\n\n function useNavigate(): (to: string | number) => void {\n return (to) => {\n if (typeof to === \"number\") {\n if (typeof window !== \"undefined\") window.history.go(to);\n return;\n }\n emitNavigation(to);\n };\n }\n\n // NOTE: `useParams` is now a REAL React hook — it calls `useContext`/`useHostIdentity`\n // internally (to resolve the surface base). It therefore obeys the Rules of Hooks: call it\n // unconditionally at the top level of a component/hook render, never conditionally or outside\n // render. (Previously it was a plain path-parser with no hook calls.)\n function useParams<\n T extends Record<string, string | undefined> = Record<string, string | undefined>,\n >(): T {\n // Hooks FIRST — unconditional, so the hook order is identical on every render\n // regardless of which branch returns below.\n const ctx = useContext(SurfaceBaseContext);\n const identity = useHostIdentity();\n const resolved = resolveSurfaceBase({\n basePath: ctx?.basePath,\n pageId: ctx?.pageId,\n extensionGroupCode: identity?.extensionGroupCode ?? null,\n });\n\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"\";\n\n // Precise param extraction from declared route patterns (unchanged).\n if (routes != null && routes.length > 0) {\n const matched = matchRouteParams(path, routes, basePath);\n if (matched != null) return matched as T;\n }\n\n // Fallback: strip the surface base — the longest matching prefix of the\n // explicit base and the bare group root, so BOTH the explicit surface root\n // and the host's empty-subpath fallback yield no param — then take the\n // trailing segment of the remainder. Query/hash ignored. When no base\n // resolves (non-surface shim consumer / no provider+identity) `resolved` is\n // null and this reduces to the legacy trailing-segment behaviour — no\n // regression for existing consumers.\n const pathNoQuery = path.split(/[?#]/)[0];\n let remainder = pathNoQuery;\n if (resolved) {\n for (const prefix of [resolved.base, resolved.groupRoot].sort((a, b) => b.length - a.length)) {\n if (pathNoQuery === prefix || pathNoQuery.startsWith(prefix + \"/\")) {\n remainder = pathNoQuery.slice(prefix.length);\n break;\n }\n }\n }\n const segments = remainder.split(\"/\").filter(Boolean);\n const last = segments[segments.length - 1];\n const params: Record<string, string | undefined> = {};\n if (last) {\n for (const key of paramKeys) params[key] = last;\n }\n return params as T;\n }\n\n function useSearchParams(): [\n URLSearchParams,\n (\n next: URLSearchParams | Record<string, string>,\n navigateOptions?: { replace?: boolean; state?: unknown },\n ) => void,\n ] {\n const search = typeof window !== \"undefined\" ? window.location.search : \"\";\n const params = new URLSearchParams(search);\n // `navigateOptions` mirrors react-router's `setSearchParams(next, opts)` signature so\n // call sites that pass `{ replace: true, state }` typecheck. The shim has no router\n // history stack — it always writes via `history.replaceState` (i.e. behaves as\n // `replace: true`); `state`, when supplied, is persisted onto the history entry so the\n // signature is not misleading (Copilot review). `replace` is accepted for source-compat.\n const setSearchParams = (\n next: URLSearchParams | Record<string, string>,\n navigateOptions?: { replace?: boolean; state?: unknown },\n ) => {\n const usp = next instanceof URLSearchParams ? next : new URLSearchParams(next);\n const qs = usp.toString();\n if (typeof window !== \"undefined\") {\n window.history.replaceState(\n navigateOptions?.state ?? {},\n \"\",\n qs ? `${window.location.pathname}?${qs}` : window.location.pathname,\n );\n }\n };\n return [params, setSearchParams];\n }\n\n function useMatch(\n _pattern: string,\n ): { params: Record<string, string | undefined>; pathname: string } | null {\n return null;\n }\n\n function useLocation() {\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"/\";\n return { pathname: path, search: \"\", hash: \"\", state: null, key: \"default\" };\n }\n\n function Link({ to, children, onClick, ...rest }: LinkProps) {\n return createElement(\n \"a\",\n {\n href: to,\n onClick: (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n onClick?.(e);\n emitNavigation(to);\n },\n ...rest,\n },\n children,\n );\n }\n\n return { useNavigate, useParams, useSearchParams, useMatch, useLocation, Link, NavLink: Link };\n}\n","import { useEffect, useRef } from \"react\";\nimport { usePluginRealtimeSource } from \"@ethisyscore/extension-runtime/plugin\";\n\n/**\n * Subscribe to realtime push notifications of a given `typeCode`.\n *\n * Subscribes on mount and unsubscribes on unmount. The `handler` is kept in a\n * ref so re-renders with a new closure do not churn the underlying subscription\n * — the latest handler is always used without needing to re-subscribe.\n *\n * No-ops cleanly when the plugin runtime has no realtime source (dev/mock\n * environments, or plugins that have not been given a SignalR connection by\n * the host).\n *\n * @param typeCode Application-level event type to filter on (e.g.\n * `\"HelpdeskTicketCreated\"`). Filtering by extensionId is the\n * host's responsibility.\n * @param handler Called with the raw notification payload each time a\n * matching notification arrives.\n */\nexport function usePluginRealtime(\n typeCode: string,\n handler: (payload: unknown) => void,\n): void\n{\n const source = usePluginRealtimeSource();\n const ref = useRef(handler);\n ref.current = handler;\n\n useEffect(() =>\n {\n if (!source) return; // dev/mock: no connection — no-op\n\n return source.subscribe(typeCode, (payload: unknown) => ref.current(payload));\n }, [source, typeCode]);\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n useExtensionRuntimeTransport,\n type McpTransport,\n type UploadDocumentMeta,\n type UploadDocumentResult,\n} from \"@ethisyscore/extension-runtime/plugin\";\n\n/**\n * Advisory client-side upload ceiling (UX only). The kernel's streaming route\n * applies the authoritative size cap mid-stream; this default lets the hook\n * fail fast with a friendly message before reading a large file into memory.\n * Roughly matches the host's default request-body ceiling.\n */\nexport const DEFAULT_MAX_UPLOAD_BYTES = 30 * 1024 * 1024;\n\n/** Where the uploaded document belongs. `moduleKey` is validated against the caller's kernel allowlist. */\nexport interface UseMcpUploadTarget {\n /** Storage module key (e.g. \"hr\"). Server rejects a key outside the plugin's allowlist. */\n moduleKey: string;\n /** Owning entity name (e.g. \"Candidates\", \"CompanyDocuments\"). */\n entityName: string;\n /** Optional owning entity id. */\n entityId?: string;\n /** Logical sub-folder / category (e.g. \"cvs\"). */\n category: string;\n}\n\nexport interface UseMcpUploadOptions {\n /** Override the transport resolved from the runtime provider (tests / advanced hosts). */\n transport?: McpTransport;\n /** Advisory client-side max byte size. Defaults to {@link DEFAULT_MAX_UPLOAD_BYTES}. */\n maxSizeBytes?: number;\n}\n\nexport interface UseMcpUploadResult {\n /**\n * Read `file` into an ArrayBuffer and upload it to core storage via the host,\n * resolving the resulting `StoredDocumentId` + server-validated metadata. The\n * plugin then records the id with its own JSON command.\n */\n upload: (file: File | Blob, target: UseMcpUploadTarget) => Promise<UploadDocumentResult>;\n /**\n * Coarse progress in [0, 1]. NOTE: the FE→host leg is not streaming (the file\n * is transferred whole), so this is phase-based, not byte-accurate: 0 idle,\n * ~0.1 once the bytes are read + handed to the host, 1 on completion.\n */\n progress: number;\n loading: boolean;\n error: Error | undefined;\n}\n\n/**\n * Upload a binary file from a platform-react plugin surface to CORE storage.\n *\n * The bytes never touch the plugin backend and the plugin never sees the\n * capability token or kernel URL: the host shell mints the token and streams\n * the octet-stream to the kernel document-upload route, returning only the\n * `StoredDocumentId`. Filename/content-type are advisory — the kernel\n * sanitises them and binds the effective module key + organisation itself.\n */\nexport function useMcpUpload(opts?: UseMcpUploadOptions): UseMcpUploadResult {\n const transport = useExtensionRuntimeTransport(opts?.transport);\n const maxSizeBytes = opts?.maxSizeBytes ?? DEFAULT_MAX_UPLOAD_BYTES;\n\n const [progress, setProgress] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n\n const controllerRef = useRef<AbortController | null>(null);\n const mountedRef = useRef(true);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n controllerRef.current?.abort();\n };\n }, []);\n\n const upload = useCallback(\n async (file: File | Blob, target: UseMcpUploadTarget): Promise<UploadDocumentResult> => {\n const controller = new AbortController();\n controllerRef.current?.abort();\n controllerRef.current = controller;\n\n const applyState = (fn: () => void): void => {\n if (mountedRef.current && !controller.signal.aborted) {\n fn();\n }\n };\n\n applyState(() => {\n setLoading(true);\n setError(undefined);\n setProgress(0);\n });\n\n try {\n // `uploadDocument` is an optional transport capability. A transport that\n // predates the upload seam (or a custom host transport that omits it)\n // won't provide it — fail with a clear, actionable message instead of a\n // `transport.uploadDocument is not a function` TypeError.\n if (typeof transport.uploadDocument !== \"function\") {\n throw new Error(\n \"The active McpTransport does not support binary uploads (no `uploadDocument`). \" +\n \"The host runtime / transport must implement the upload capability for useMcpUpload to work.\",\n );\n }\n // Capture a bound reference now: property narrowing does not survive the\n // `await file.arrayBuffer()` below, and binding preserves `this` for\n // class-based transports (e.g. InMemoryMcpTransport).\n const uploadDocument = transport.uploadDocument.bind(transport);\n\n const size = file.size;\n if (size > maxSizeBytes) {\n throw new Error(\n `File is ${size} bytes, which exceeds the ${maxSizeBytes}-byte upload limit.`,\n );\n }\n\n // FE→host is NOT streaming: read the whole file into an ArrayBuffer,\n // which the transport hands to the host as a zero-copy transferable.\n const buffer = await file.arrayBuffer();\n applyState(() => setProgress(0.1));\n\n // Duck-type the name so this works for a real File, a named Blob, or a\n // File-like object (and does not depend on the `File` global existing).\n const maybeName = (file as File).name;\n const fileName =\n typeof maybeName === \"string\" && maybeName.length > 0 ? maybeName : \"upload.bin\";\n const contentType = file.type && file.type.length > 0\n ? file.type\n : \"application/octet-stream\";\n\n const meta: UploadDocumentMeta = {\n moduleKey: target.moduleKey,\n entityName: target.entityName,\n entityId: target.entityId,\n category: target.category,\n fileName,\n contentType,\n sizeBytes: buffer.byteLength,\n };\n\n const result = await uploadDocument(meta, buffer, controller.signal);\n\n applyState(() => {\n setProgress(1);\n setLoading(false);\n });\n return result;\n } catch (err) {\n const wrapped = err instanceof Error ? err : new Error(String(err));\n applyState(() => {\n setError(wrapped);\n setLoading(false);\n setProgress(0);\n });\n throw wrapped;\n }\n },\n [transport, maxSizeBytes],\n );\n\n return { upload, progress, loading, error };\n}\n","import { createContext } from \"react\";\n\nexport const OVERLAY_HOST_CONTRACT_VERSION = \"1.0.0\";\n\n/** Canonical inset-lease identity — the SAME shape the web host layout store uses (Codex R2 M1:\n * one lease shape across SDK/web/store, no adapter). The store's handle extends this with `source`. */\nexport interface InsetLease {\n readonly leaseId: string;\n readonly generation: number;\n}\n\nexport interface OverlayHostCapabilities {\n readonly contractVersion: string; // OVERLAY_HOST_CONTRACT_VERSION the host implements\n readonly contentInset: boolean; // side-by-side inset supported\n readonly focusHelpers: boolean; // focus-handoff + Escape helpers present\n readonly safeArea: boolean; // mobile safe-area insets provided\n}\n\n/** The host-provided API. The host constructs this object; the plugin receives it as a prop and\n * reads it via useOverlayHost(). Shape is additive-only — add optional fields, never remove. */\nexport interface OverlayHostApi {\n /** Reserve host content space. Returns a lease on grant, or NULL when the request is rejected\n * (invalid px) or YIELDED (a higher-precedence source holds the inset) — Codex R2: the contract\n * represents reject/yield deterministically so the overlay can fall back to float mode. */\n requestContentInset(inset: { side: \"right\"; px: number }): InsetLease | null;\n releaseContentInset(lease: InsetLease): void; // idempotent; stale handle = no-op\n readonly viewport: { width: number; height: number };\n readonly breakpoint: \"mobile\" | \"tablet\" | \"desktop\"; // host-authoritative\n readonly capabilities: OverlayHostCapabilities;\n /** Register a cancellation callback the host invokes during an identity transition (§6.1) BEFORE it\n * unmounts the overlay — the overlay uses it to cancel its in-flight server-side assistant turn so\n * no run continues under stale authority. Returns an unsubscribe. Additive; the host guarantees\n * every registered callback runs (awaited best-effort) before the remount. (Codex R1 H4 — this is\n * the concrete registration path Task 4.6's transition protocol invokes.) */\n registerActivityCancellation(cancel: () => void | Promise<void>): () => void;\n /** Focus-handoff helpers (§7.3). Return focus to the launcher on close/Escape. */\n readonly focus: {\n onEscape(handler: () => void): () => void; // returns an unsubscribe\n returnFocusToLauncher(): void;\n };\n}\n\n/** Props the host injects into the overlay component (mirrors PlatformReactPageProps + overlayHost). */\nexport interface PlatformReactOverlayProps {\n extensionId: string;\n organisationId: string;\n tenantId?: string;\n overlayId: string;\n overlayHost: OverlayHostApi;\n}\n\nexport const OverlayHostContext = createContext<OverlayHostApi | null>(null);\n","import { useContext } from \"react\";\nimport { OverlayHostContext, type OverlayHostApi } from \"./overlayHostContext\";\n\n/**\n * Reads the host-provided overlay API. Must be called inside a PlatformReact overlay mounted by the\n * host (the host injects the API via the `overlayHost` prop, which\n * `definePlatformReactPluginOverlay` bridges into {@link OverlayHostContext}).\n */\nexport function useOverlayHost(): OverlayHostApi {\n const api = useContext(OverlayHostContext);\n if (api === null) {\n throw new Error(\"useOverlayHost must be used within a host-mounted PlatformReact overlay.\");\n }\n return api;\n}\n","import { createElement, type ComponentType, type ReactElement } from \"react\";\nimport {\n OverlayHostContext,\n type PlatformReactOverlayProps,\n} from \"./overlayHostContext\";\nimport { TemplateContext, type ViewComponent } from \"./templateContext\";\nimport { PluginStyleScope } from \"./injectStyles\";\nimport { EHX_PLUGIN_ROOT_CLASS } from \"./definePlatformReactPluginPage\";\n\nexport interface DefinePlatformReactPluginOverlayOptions {\n rootClassName: string;\n styleId: string;\n css: string;\n manifest?: Record<string, Partial<Record<string, ViewComponent>>>;\n wrapOverlay?: (overlay: ReactElement) => ReactElement;\n}\n\n/**\n * Wraps an overlay component as a PlatformReact entry with the providers the host\n * needs: the `OverlayHostContext` (bridging the `overlayHost` prop into a plugin-side\n * context for `useOverlayHost()`), the static `useView` manifest, and the scoped style\n * root. Mirrors `definePlatformReactPluginPage`; the key difference is the\n * `OverlayHostContext.Provider` wrapping the `overlayHost` prop.\n *\n * @ethisyscore/plugin-ui is BUNDLED into the plugin (not externalized), so a React\n * context created in the host's module copy would not be identity-equal to the\n * plugin's copy. Passing the API as a prop and bridging it here is the correct seam\n * (D-2 design decision).\n */\nexport function definePlatformReactPluginOverlay(\n Overlay: ComponentType<PlatformReactOverlayProps>,\n options: DefinePlatformReactPluginOverlayOptions,\n): ComponentType<PlatformReactOverlayProps> {\n const { rootClassName, styleId, css, manifest = {}, wrapOverlay } = options;\n return function MountedOverlay(props: PlatformReactOverlayProps) {\n // Inject styles via <PluginStyleScope> (effect-based, idempotent under\n // StrictMode) rather than calling injectPluginStyles() during render — keeps\n // render pure and mirrors definePlatformReactPluginPage.\n const tree = createElement(\n OverlayHostContext.Provider,\n { value: props.overlayHost },\n createElement(\n TemplateContext.Provider,\n { value: manifest },\n createElement(PluginStyleScope, { id: styleId, css }),\n createElement(\n \"div\",\n { className: `${EHX_PLUGIN_ROOT_CLASS} ${rootClassName}` },\n createElement(Overlay, props),\n ),\n ),\n );\n return wrapOverlay ? wrapOverlay(tree) : tree;\n };\n}\n\nexport interface PluginOverlayDefinerConfig {\n module: string;\n rootClassName: string;\n styleId: string;\n css: string;\n wrapOverlay?: (overlay: ReactElement) => ReactElement;\n}\n\n/**\n * Builds a plugin-bound `definePluginOverlay(Overlay, views)` from one config, so a\n * plugin writes the four plugin-specific values (module key, root class, style id, CSS)\n * ONCE instead of in every overlay entry. The flat `views` map is wrapped into the\n * `{ [module]: views }` manifest slice that `useView(module, name)` reads. Mirrors\n * `createPluginPageDefiner`.\n */\nexport function createPluginOverlayDefiner(config: PluginOverlayDefinerConfig) {\n const { module, ...rest } = config;\n return function definePluginOverlay(\n Overlay: ComponentType<PlatformReactOverlayProps>,\n views: Record<string, ViewComponent> = {},\n ): ComponentType<PlatformReactOverlayProps> {\n return definePlatformReactPluginOverlay(Overlay, { ...rest, manifest: { [module]: views } });\n };\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\n/**\n * A managed cleanup registry the overlay uses to register event listeners,\n * timers, and AbortControllers. All registrations are torn down deterministically\n * when the host component unmounts.\n *\n * **Teardown semantics (R2 decision):** the HOST does NOT call `disposeAll()`.\n * Teardown rides on React's unmount cleanup (`useEffect(() => () => disposeAll(), [])`),\n * which the host guarantees by forcing unmount via the identity-key change at\n * the remount boundary. `disposeAll()` is exposed as an OPTIONAL early-teardown\n * affordance for the overlay's own use only — not the host's teardown path.\n *\n * @example\n * ```tsx\n * function MyOverlay() {\n * const lifecycle = useManagedLifecycle();\n * useEffect(() => {\n * lifecycle.addEventListener(window, \"resize\", handleResize);\n * const ctrl = lifecycle.createAbortController();\n * fetch(\"/api/data\", { signal: ctrl.signal });\n * }, [lifecycle]);\n * return <div />;\n * }\n * ```\n */\nexport interface ManagedLifecycle {\n /**\n * Registers an event listener on `target`. The listener is automatically\n * removed when the host component unmounts (or when `disposeAll()` is called).\n */\n addEventListener(\n target: Window | Document | HTMLElement,\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: AddEventListenerOptions,\n ): void;\n\n /**\n * Schedules a repeating callback. The interval is automatically cleared when\n * the host component unmounts (or when `disposeAll()` is called).\n *\n * @returns The interval ID (mirrors the native return value).\n */\n setInterval(handler: () => void, ms: number): number;\n\n /**\n * Schedules a one-shot callback. The timeout is automatically cleared when\n * the host component unmounts (or when `disposeAll()` is called).\n *\n * @returns The timeout ID (mirrors the native return value).\n */\n setTimeout(handler: () => void, ms: number): number;\n\n /**\n * Creates an AbortController whose signal is aborted when the host component\n * unmounts (or when `disposeAll()` is called).\n *\n * @returns The AbortController. The caller may pass `.signal` to fetch/streams.\n */\n createAbortController(): AbortController;\n\n /**\n * Optional early-teardown affordance for the overlay's own use. Removes all\n * registered listeners, clears all timers, and aborts all controllers\n * immediately.\n *\n * Idempotent: subsequent calls (including the React unmount cleanup) are safe\n * and do not throw.\n *\n * **The host does NOT call this.** Host-driven teardown is guaranteed by\n * React unmount cleanup via the identity-key remount mechanism.\n */\n disposeAll(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal registry entries\n// ---------------------------------------------------------------------------\n\ninterface ListenerEntry {\n kind: \"listener\";\n target: Window | Document | HTMLElement;\n type: string;\n listener: EventListenerOrEventListenerObject;\n options?: AddEventListenerOptions;\n}\n\ninterface IntervalEntry {\n kind: \"interval\";\n id: number;\n}\n\ninterface TimeoutEntry {\n kind: \"timeout\";\n id: number;\n}\n\ninterface ControllerEntry {\n kind: \"controller\";\n controller: AbortController;\n}\n\ntype RegistryEntry = ListenerEntry | IntervalEntry | TimeoutEntry | ControllerEntry;\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Returns a {@link ManagedLifecycle} registry. All registered event listeners,\n * timers, and AbortControllers are automatically torn down when the host\n * component unmounts.\n *\n * The returned object has stable identity across re-renders (backed by `useMemo`\n * / `useRef`) so it can be placed in dependency arrays without causing churn.\n */\nexport function useManagedLifecycle(): ManagedLifecycle {\n // Ref holds the live registry so it is shared across all renders without\n // causing re-renders. We use an Array (not a Set) for deterministic teardown\n // order and because entries are not deduplicated — the same listener may be\n // registered with different options.\n const entriesRef = useRef<RegistryEntry[]>([]);\n\n // Stable lifecycle object. `useMemo` with an empty dep array gives us a\n // single object identity for the lifetime of the component, which means\n // callers can safely include `lifecycle` in useEffect dependency arrays.\n const lifecycle = useMemo<ManagedLifecycle>(() => {\n const addEventListener = (\n target: Window | Document | HTMLElement,\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: AddEventListenerOptions,\n ): void => {\n target.addEventListener(type, listener, options);\n entriesRef.current.push({ kind: \"listener\", target, type, listener, options });\n };\n\n const setInterval = (handler: () => void, ms: number): number => {\n const id = globalThis.setInterval(handler, ms) as unknown as number;\n entriesRef.current.push({ kind: \"interval\", id });\n return id;\n };\n\n const setTimeout = (handler: () => void, ms: number): number => {\n const id = globalThis.setTimeout(handler, ms) as unknown as number;\n entriesRef.current.push({ kind: \"timeout\", id });\n return id;\n };\n\n const createAbortController = (): AbortController => {\n const controller = new AbortController();\n entriesRef.current.push({ kind: \"controller\", controller });\n return controller;\n };\n\n const disposeAll = (): void => {\n // Drain the registry atomically so concurrent / re-entrant calls are safe.\n const entries = entriesRef.current.splice(0);\n for (const entry of entries) {\n switch (entry.kind) {\n case \"listener\":\n entry.target.removeEventListener(entry.type, entry.listener, entry.options);\n break;\n case \"interval\":\n globalThis.clearInterval(entry.id);\n break;\n case \"timeout\":\n globalThis.clearTimeout(entry.id);\n break;\n case \"controller\":\n entry.controller.abort();\n break;\n }\n }\n };\n\n return { addEventListener, setInterval, setTimeout, createAbortController, disposeAll };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Teardown on unmount. The host guarantees unmount via the identity-key change;\n // `disposeAll()` is also an optional early-teardown path for the overlay itself.\n useEffect(() => () => lifecycle.disposeAll(), [lifecycle]);\n\n return lifecycle;\n}\n","import { useContext, useEffect, useRef } from \"react\";\nimport {\n HostSidebarActionsContext,\n isHostChromeCompatible,\n reportHostChromeDiagnostic,\n type HostSidebarActionDescriptor,\n type HostSidebarActionsApi,\n type HostSidebarContribution,\n type HostSidebarNavItem,\n} from \"./hostSidebarActionsContext\";\nimport { SurfaceBaseContext } from \"./surfaceUrl\";\n\n/** Authoring shape: a descriptor plus — ONLY for `kind:\"dispatch\"` — an inline handler.\n * A distributive union (not a blanket intersection over the whole descriptor union): `onSelect`\n * is permitted only on a dispatch action. A navigate action carries no handler (the host performs\n * the navigation from `href`), so supplying `onSelect` on a navigate action is a typecheck error\n * rather than a silently-ignored footgun. The hook strips `onSelect` before publishing (only\n * serializable descriptors cross to the host). */\nexport type HostSidebarAction =\n | (Extract<HostSidebarActionDescriptor, { kind: \"dispatch\" }> & { onSelect?: () => void })\n | Extract<HostSidebarActionDescriptor, { kind: \"navigate\" }>\n // A collapsible group whose `children` are themselves authoring actions (so a dispatch child can\n // still carry its inline `onSelect`). `children` overrides the descriptor's serializable children.\n | (Omit<Extract<HostSidebarActionDescriptor, { kind: \"group\" }>, \"children\"> & { children: HostSidebarAction[] });\n\n/** Single source for the discriminant values used across the mode checks, `toContribution`, and\n * the content key — so the \"none\"/\"actions\"/\"detail\" strings live in one place. The union type\n * below intentionally keeps its literal members (they ARE the discriminant + drive IntelliSense);\n * `SIDEBAR_MODE.*` is `as const`, so it is type-identical to those literals. */\nexport const SIDEBAR_MODE = { none: \"none\", actions: \"actions\", detail: \"detail\" } as const;\n\n/** Discriminated input to `useHostSidebarActions`. `mode:\"none\"` suppresses all plugin-side\n * contributions without clobbering the static manifest actions. `mode:\"actions\"` is the\n * batch-2 Quick Actions shape. `mode:\"detail\"` is the full detail-nav replacement with title,\n * navigation items, back-arrow, and Quick Actions. */\nexport type HostSidebarInput =\n | { mode: \"none\" }\n | { mode: \"actions\"; entityToken: string; actions: HostSidebarAction[] }\n | {\n mode: \"detail\";\n entityToken: string;\n title: string;\n navSectionLabel: string;\n navItems: HostSidebarNavItem[];\n backHref: string;\n backLabel: string;\n actions: HostSidebarAction[];\n };\n\nfunction toDescriptor(a: HostSidebarAction): HostSidebarActionDescriptor {\n if (a.kind === \"dispatch\") {\n const { onSelect: _drop, ...rest } = a;\n return rest; // rest is the serializable dispatch descriptor\n }\n if (a.kind === \"group\") {\n // Recurse: strip the children's `onSelect` handlers so only serializable descriptors cross.\n const { children, ...rest } = a;\n return { ...rest, children: children.map(toDescriptor) };\n }\n return a; // navigate descriptor is already serializable (no handler)\n}\n\n/** Flatten a (possibly nested) action tree to its `kind:\"dispatch\"` handler entries — a group's\n * child dispatch clicks must reach their `onSelect` just like a top-level dispatch action. */\nfunction collectDispatchHandlers(actions: HostSidebarAction[]): Array<readonly [string, () => void]> {\n return actions.flatMap((a) =>\n a.kind === \"dispatch\" && a.onSelect\n ? [[a.id, a.onSelect] as const]\n : a.kind === \"group\"\n ? collectDispatchHandlers(a.children)\n : [],\n );\n}\n\n/** Map a plugin-authoring input to the serializable contribution published to the host.\n * `onSelect` handlers are stripped via `toDescriptor`; nav/back/title pass through for `detail`. */\nfunction toContribution(input: HostSidebarInput): HostSidebarContribution {\n if (input.mode === SIDEBAR_MODE.none) {\n return { mode: SIDEBAR_MODE.none };\n }\n if (input.mode === SIDEBAR_MODE.actions) {\n return {\n mode: SIDEBAR_MODE.actions,\n entityToken: input.entityToken,\n actions: input.actions.map(toDescriptor),\n };\n }\n // mode === \"detail\"\n return {\n mode: SIDEBAR_MODE.detail,\n entityToken: input.entityToken,\n title: input.title,\n navSectionLabel: input.navSectionLabel,\n navItems: input.navItems,\n backHref: input.backHref,\n backLabel: input.backLabel,\n actions: input.actions.map(toDescriptor),\n };\n}\n\n/** Content key so we republish only when the visible descriptor set changes. JSON-encodes an\n * ARRAY of ORDERED-tuple arrays: element order is stable (unlike object property insertion order,\n * which is why we avoid `JSON.stringify` over the raw descriptor objects — spec-gate MEDIUM R1-M2),\n * and JSON string-escaping makes the key delimiter-collision-proof, so a `label`/`href` that\n * happens to contain a separator character can no longer alias two distinct action sets to the\n * same key (which would skip a needed republish and leave the host sidebar stale). */\nfunction contributionKey(input: HostSidebarInput): string {\n if (input.mode === SIDEBAR_MODE.none) {\n return JSON.stringify([SIDEBAR_MODE.none]);\n }\n const actionTuple = (a: HostSidebarAction): unknown[] => [\n a.id,\n a.label,\n a.icon ?? \"\",\n a.slot ?? \"\",\n a.variant ?? \"\",\n a.disabled ? 1 : 0,\n a.active ? 1 : 0,\n a.requiredPermission ?? \"\",\n a.kind,\n a.kind === \"navigate\" ? a.href : \"\",\n // Group children fold into the key so a change to any collapsed leaf forces a republish.\n a.kind === \"group\" ? a.children.map(actionTuple) : \"\",\n ];\n const actionTuples = input.actions.map(actionTuple);\n if (input.mode === SIDEBAR_MODE.actions) {\n return JSON.stringify([SIDEBAR_MODE.actions, input.entityToken, actionTuples]);\n }\n // mode === \"detail\"\n const navTuples = input.navItems.map((n) => [n.id, n.label, n.icon ?? \"\", n.href]);\n return JSON.stringify([\n SIDEBAR_MODE.detail,\n input.entityToken,\n input.title,\n input.navSectionLabel,\n input.backHref,\n input.backLabel,\n navTuples,\n actionTuples,\n ]);\n}\n\n/** Per-page dev-warn throttle (NOT a global boolean — a module singleton would suppress warnings\n * for every later surface in a shared bundle; spec-gate HIGH R1-H2). Telemetry fires every time. */\nconst devWarnedPages = new Set<string>();\nfunction reportNoHost(pageId: string | undefined): void {\n // Observable in ALL environments via the injected diagnostic sink, tagged by page id (R1-H3).\n reportHostChromeDiagnostic({ event: \"host_sidebar_actions_unavailable\", pageId });\n const key = pageId ?? \"*\";\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if ((import.meta as any).env?.DEV && !devWarnedPages.has(key)) {\n devWarnedPages.add(key);\n // eslint-disable-next-line no-console\n console.warn(\n `[plugin-ui] useHostSidebarActions (${key}): no compatible host sidebar-actions API — ` +\n \"the host predates the seam or its contract major differs. Quick Actions will not render.\",\n );\n }\n}\n\n/**\n * Publish contextual sidebar contributions from a PlatformReact page to the host sidebar, and\n * receive click dispatch by id. Accepts a discriminated `HostSidebarInput`:\n * - `mode:\"none\"` — suppress all plugin contributions without clobbering the static manifest.\n * - `mode:\"actions\"` — batch-2 Quick Actions for a detail entity (entity token + action list).\n * - `mode:\"detail\"` — full detail-nav replacement (title, nav items, back-arrow, Quick Actions).\n *\n * Serializable descriptors only cross to the host; the inline `onSelect` handlers stay in a\n * ref-map here and are always the latest closure. Fails closed (no publish/subscribe, no throw)\n * when the host predates or is incompatible with the seam. */\nexport function useHostSidebarActions(input: HostSidebarInput): void {\n const api = useContext(HostSidebarActionsContext);\n const pageId = useContext(SurfaceBaseContext)?.pageId; // diagnostic tag (per-surface)\n const compatible: HostSidebarActionsApi | null =\n api && isHostChromeCompatible(api.capabilities?.contractVersion) ? api : null;\n\n // Ref-map refreshed every render → dispatch always hits the latest handler (recursing groups).\n const actions = input.mode === SIDEBAR_MODE.none ? [] : input.actions;\n const handlers = useRef<Map<string, () => void>>(new Map());\n handlers.current = new Map(collectDispatchHandlers(actions));\n\n useEffect(() => {\n if (!compatible) {\n reportNoHost(pageId);\n return;\n }\n const unsub = compatible.subscribe((id) => {\n handlers.current.get(id)?.();\n });\n return () => {\n compatible.clear();\n unsub();\n };\n }, [compatible, pageId]);\n\n // Content key: mode + entityToken + descriptor tuples + (detail) nav/back/title.\n const key = contributionKey(input);\n useEffect(() => {\n if (!compatible) return;\n compatible.publish(toContribution(input));\n // key encodes all content that affects the published contribution; changes force republish.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [compatible, key]);\n}\n","import type { PluginDetailContext, PluginDetailContextItem } from \"../components/layout/pageHeaderTypes\";\nimport type { HostSidebarActionSlot, HostSidebarNavItem } from \"./hostSidebarActionsContext\";\nimport type { HostSidebarAction, HostSidebarInput } from \"./useHostSidebarActions\";\n\n/**\n * Options for {@link toHostSidebarInput}.\n */\nexport interface ToHostSidebarInputOptions {\n /** The plugin's mount base (e.g. `/extensions/projects`). Stripped from hrefs. */\n basePath: string;\n /**\n * The entity token the host matches to the current route. Use the bare entity id\n * — the host segment-matches it across an entity's sub-pages\n * (`deriveEntityToken`/`tokenMatches`). When absent the result is `{mode:\"none\"}`.\n */\n entityToken: string | undefined;\n /**\n * Dispatch bridge for `actionId` actions — pass a page's `useTriggerSidebarAction()`\n * so a sidebar click reaches the same `useSidebarActions` handler the header uses.\n * (Kept as a parameter rather than called internally so this stays a PURE mapper\n * and avoids the cross-entry `SidebarActionContext` singleton pitfall.)\n */\n onDispatch: (actionId: string) => void;\n /**\n * Slug-relative fallback for a back link that can't be confined (e.g. the plugin\n * root, whose path is empty and which the host rejects). Defaults to `\"\"` which,\n * if the primary back link is also unconfinable, yields no back link.\n */\n backHrefFallback?: string;\n}\n\n/**\n * Map a plugin {@link PluginDetailContext} to a {@link HostSidebarInput} for\n * `useHostSidebarActions(mode:\"detail\")` — the plugin-side of the host left-sidebar\n * detail panel (back link + sub-page nav + Quick actions), the companion of\n * `useDetailHeaderActions` (the header surface).\n *\n * Encodes the host-contract rules so no plugin has to re-derive them from host\n * source (getting them wrong SILENTLY BLANKS the panel):\n * - hrefs are made SLUG-RELATIVE (basePath stripped); absolute/cross-module hrefs\n * are dropped (the host rejects them and `mode:\"detail\"` atomic-drops on a bad\n * nav/back href).\n * - children-bearing actions (e.g. Lifecycle / Share) map to a `kind:\"group\"`\n * action so the host renders them as a collapsible accordion (contract ≥2.1.0);\n * a group with no renderable children is dropped.\n * - `actionId` → `kind:\"dispatch\"` with `onSelect` wired to `onDispatch`; `href` →\n * `kind:\"navigate\"`; a bare `onClick` → `kind:\"dispatch\"` with that handler.\n * - duplicate action ids are removed (the host rejects the whole contribution on a\n * duplicate id).\n * - no context / no entity token → `{mode:\"none\"}` (suppresses without clobbering\n * the static manifest).\n */\nexport function toHostSidebarInput(\n context: PluginDetailContext | null | undefined,\n opts: ToHostSidebarInputOptions,\n): HostSidebarInput {\n if (!context || !opts.entityToken) {\n return { mode: \"none\" };\n }\n const navItems: HostSidebarNavItem[] = context.navItems\n .map((n) => ({ id: n.id, label: n.label, icon: n.icon, href: slugRelative(n.href, opts.basePath) }))\n .filter((n) => isConfinable(n.href));\n\n // A non-confinable back link (empty root, absolute/cross-module, or schemed) would\n // atomic-drop the whole detail contribution — fall back instead of publishing it.\n const mappedBack = slugRelative(context.backHref, opts.basePath);\n const backHref = isConfinable(mappedBack) ? mappedBack : opts.backHrefFallback ?? \"\";\n\n return {\n mode: \"detail\",\n entityToken: opts.entityToken,\n title: context.title,\n navSectionLabel: context.navSectionLabel ?? \"Sections\",\n navItems,\n backHref,\n backLabel: context.backLabel ?? \"Back\",\n actions: toHostActions(context.actions ?? [], opts),\n };\n}\n\nfunction slugRelative(href: string | undefined, basePath: string): string {\n if (!href) return \"\";\n // Normalise a trailing slash on basePath (the host may provide one — see\n // platform-react/surfaceUrl.ts) so the prefix isn't `/extensions/x//`.\n const base = basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n if (href === base) return \"\";\n const prefix = `${base}/`;\n return href.startsWith(prefix) ? href.slice(prefix.length) : href;\n}\n\n/**\n * Whether a slug-relative href is one the host confiner accepts: non-empty, not\n * absolute (`/…` cross-module), no URI scheme (`http:`…), no fragment. A\n * non-confinable href must be dropped (nav/action) or replaced (back link) — the\n * host rejects it and `mode:\"detail\"` atomic-drops on a bad nav/back href.\n */\nfunction isConfinable(slugRelativeHref: string): boolean {\n return (\n slugRelativeHref.length > 0 &&\n !slugRelativeHref.startsWith(\"/\") &&\n !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(slugRelativeHref) &&\n !slugRelativeHref.includes(\"#\")\n );\n}\n\nfunction toHostActions(\n actions: PluginDetailContextItem[],\n opts: ToHostSidebarInputOptions,\n): HostSidebarAction[] {\n // A single `seen` set threaded through the WHOLE tree: the host rejects a\n // contribution containing ANY duplicate id (across groups, or between a group\n // child and a top-level action), and the dispatch ref-map is keyed by id — so\n // ids must be unique tree-wide, not just per level.\n const seen = new Set<string>();\n const out: HostSidebarAction[] = [];\n for (const item of actions) {\n const mapped = toHostAction(item, opts, seen);\n if (mapped) out.push(mapped);\n }\n return out;\n}\n\nfunction toHostAction(\n item: PluginDetailContextItem,\n opts: ToHostSidebarInputOptions,\n seen: Set<string>,\n): HostSidebarAction | null {\n if (seen.has(item.id)) return null; // tree-global dedup — first occurrence wins\n const slot: HostSidebarActionSlot =\n item.slot === \"primary\" ? \"primary\" : item.slot === \"sidebar-only\" ? \"sidebar-only\" : \"overflow\";\n const variant = item.variant === \"danger\" ? (\"danger\" as const) : undefined;\n const shared = { id: item.id, label: item.label, icon: item.icon, slot, variant, disabled: item.disabled };\n\n // A children-bearing item (Lifecycle / Share) becomes a collapsible group. Reserve the group's\n // own id before recursing (so a child can't collide with it), map each child against the shared\n // `seen`, and drop the whole group — freeing its id — if nothing renderable survives.\n if (item.children?.length) {\n seen.add(item.id);\n const children: HostSidebarAction[] = [];\n for (const child of item.children) {\n const mapped = toHostAction(child, opts, seen);\n if (mapped) children.push(mapped);\n }\n if (children.length === 0) {\n seen.delete(item.id);\n return null;\n }\n return { ...shared, kind: \"group\", children };\n }\n\n if (item.href) {\n const href = slugRelative(item.href, opts.basePath);\n // Non-confinable (empty root, absolute/cross-module, schemed) → the host rejects\n // it; skip this action rather than risk it (and don't consume its id).\n if (!isConfinable(href)) return null;\n seen.add(item.id);\n return { ...shared, kind: \"navigate\", href };\n }\n if (item.actionId) {\n const actionId = item.actionId;\n seen.add(item.id);\n return { ...shared, kind: \"dispatch\", onSelect: () => opts.onDispatch(actionId) };\n }\n if (item.onClick) {\n seen.add(item.id);\n return { ...shared, kind: \"dispatch\", onSelect: item.onClick };\n }\n return null;\n}\n"]}
@@ -5,8 +5,8 @@ import { QueryClient, UseQueryResult, UseQueryOptions, QueryKey } from '@tanstac
5
5
  import { PlatformReactPageProps } from '@ethisyscore/components-react';
6
6
  import * as _tanstack_query_core from '@tanstack/query-core';
7
7
  import { McpTransport, UploadDocumentResult } from '@ethisyscore/extension-runtime/plugin';
8
- import { k as HostSidebarInput } from '../useHostSidebarActions-DFZPen7T.cjs';
9
- export { H as HOST_CHROME_CONTRACT_VERSION, a as HostChromeDiagnostic, b as HostSidebarAction, c as HostSidebarActionDescriptor, d as HostSidebarActionSlot, e as HostSidebarActionVariant, f as HostSidebarActionsApi, g as HostSidebarActionsCapabilities, h as HostSidebarActionsContext, i as HostSidebarActionsPageProps, j as HostSidebarContribution, l as HostSidebarNavItem, S as SIDEBAR_MODE, m as isHostChromeCompatible, r as reportHostChromeDiagnostic, s as setHostChromeDiagnosticSink, u as useHostSidebarActions } from '../useHostSidebarActions-DFZPen7T.cjs';
8
+ import { k as HostSidebarInput } from '../useHostSidebarActions-CyNYWKiv.cjs';
9
+ export { H as HOST_CHROME_CONTRACT_VERSION, a as HostChromeDiagnostic, b as HostSidebarAction, c as HostSidebarActionDescriptor, d as HostSidebarActionSlot, e as HostSidebarActionVariant, f as HostSidebarActionsApi, g as HostSidebarActionsCapabilities, h as HostSidebarActionsContext, i as HostSidebarActionsPageProps, j as HostSidebarContribution, l as HostSidebarNavItem, S as SIDEBAR_MODE, m as isHostChromeCompatible, r as reportHostChromeDiagnostic, s as setHostChromeDiagnosticSink, u as useHostSidebarActions } from '../useHostSidebarActions-CyNYWKiv.cjs';
10
10
  import { g as PluginDetailContext } from '../pageHeaderTypes-PWZpiwp6.cjs';
11
11
 
12
12
  /** A view component resolved from the template manifest. Props are open. */
@@ -1102,8 +1102,9 @@ interface ToHostSidebarInputOptions {
1102
1102
  * - hrefs are made SLUG-RELATIVE (basePath stripped); absolute/cross-module hrefs
1103
1103
  * are dropped (the host rejects them and `mode:"detail"` atomic-drops on a bad
1104
1104
  * nav/back href).
1105
- * - the host action contract is FLAT (no nested children), so children-bearing
1106
- * actions (e.g. Lifecycle / Share) are flattened to their leaves.
1105
+ * - children-bearing actions (e.g. Lifecycle / Share) map to a `kind:"group"`
1106
+ * action so the host renders them as a collapsible accordion (contract ≥2.1.0);
1107
+ * a group with no renderable children is dropped.
1107
1108
  * - `actionId` → `kind:"dispatch"` with `onSelect` wired to `onDispatch`; `href` →
1108
1109
  * `kind:"navigate"`; a bare `onClick` → `kind:"dispatch"` with that handler.
1109
1110
  * - duplicate action ids are removed (the host rejects the whole contribution on a
@@ -5,8 +5,8 @@ import { QueryClient, UseQueryResult, UseQueryOptions, QueryKey } from '@tanstac
5
5
  import { PlatformReactPageProps } from '@ethisyscore/components-react';
6
6
  import * as _tanstack_query_core from '@tanstack/query-core';
7
7
  import { McpTransport, UploadDocumentResult } from '@ethisyscore/extension-runtime/plugin';
8
- import { k as HostSidebarInput } from '../useHostSidebarActions-DFZPen7T.js';
9
- export { H as HOST_CHROME_CONTRACT_VERSION, a as HostChromeDiagnostic, b as HostSidebarAction, c as HostSidebarActionDescriptor, d as HostSidebarActionSlot, e as HostSidebarActionVariant, f as HostSidebarActionsApi, g as HostSidebarActionsCapabilities, h as HostSidebarActionsContext, i as HostSidebarActionsPageProps, j as HostSidebarContribution, l as HostSidebarNavItem, S as SIDEBAR_MODE, m as isHostChromeCompatible, r as reportHostChromeDiagnostic, s as setHostChromeDiagnosticSink, u as useHostSidebarActions } from '../useHostSidebarActions-DFZPen7T.js';
8
+ import { k as HostSidebarInput } from '../useHostSidebarActions-CyNYWKiv.js';
9
+ export { H as HOST_CHROME_CONTRACT_VERSION, a as HostChromeDiagnostic, b as HostSidebarAction, c as HostSidebarActionDescriptor, d as HostSidebarActionSlot, e as HostSidebarActionVariant, f as HostSidebarActionsApi, g as HostSidebarActionsCapabilities, h as HostSidebarActionsContext, i as HostSidebarActionsPageProps, j as HostSidebarContribution, l as HostSidebarNavItem, S as SIDEBAR_MODE, m as isHostChromeCompatible, r as reportHostChromeDiagnostic, s as setHostChromeDiagnosticSink, u as useHostSidebarActions } from '../useHostSidebarActions-CyNYWKiv.js';
10
10
  import { g as PluginDetailContext } from '../pageHeaderTypes-PWZpiwp6.js';
11
11
 
12
12
  /** A view component resolved from the template manifest. Props are open. */
@@ -1102,8 +1102,9 @@ interface ToHostSidebarInputOptions {
1102
1102
  * - hrefs are made SLUG-RELATIVE (basePath stripped); absolute/cross-module hrefs
1103
1103
  * are dropped (the host rejects them and `mode:"detail"` atomic-drops on a bad
1104
1104
  * nav/back href).
1105
- * - the host action contract is FLAT (no nested children), so children-bearing
1106
- * actions (e.g. Lifecycle / Share) are flattened to their leaves.
1105
+ * - children-bearing actions (e.g. Lifecycle / Share) map to a `kind:"group"`
1106
+ * action so the host renders them as a collapsible accordion (contract ≥2.1.0);
1107
+ * a group with no renderable children is dropped.
1107
1108
  * - `actionId` → `kind:"dispatch"` with `onSelect` wired to `onDispatch`; `href` →
1108
1109
  * `kind:"navigate"`; a bare `onClick` → `kind:"dispatch"` with that handler.
1109
1110
  * - duplicate action ids are removed (the host rejects the whole contribution on a