@ethisyscore/plugin-ui 1.135.0 → 1.137.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/components/shared/toast.ts","../../src/platform-react/pluginQueryClient.ts","../../src/platform-react/hostQueryClient.ts","../../src/platform-react/templateContext.ts","../../src/platform-react/injectStyles.ts","../../src/platform-react/surfaceUrl.ts","../../src/platform-react/hostSidebarActionsContext.ts","../../src/platform-react/pluginPortalScope.tsx","../../src/platform-react/useOrgFormatSync.ts","../../src/platform-react/useReportingCurrencySync.ts","../../src/platform-react/definePlatformReactPluginPage.tsx","../../src/platform-react/MissingViewFallback.tsx","../../src/platform-react/useView.ts","../../src/platform-react/mcpService.ts","../../src/platform-react/typedMcpService.ts","../../src/platform-react/useAuthenticatedQuery.ts","../../src/platform-react/useCurrency.ts","../../src/platform-react/reactRouterShim.tsx","../../src/platform-react/usePluginRealtime.ts","../../src/platform-react/operations/asStateChanged.ts","../../src/platform-react/operations/mergeOperation.ts","../../src/platform-react/operations/queryKeys.ts","../../src/platform-react/operations/status.ts","../../src/platform-react/operations/types.ts","../../src/platform-react/operations/useBackgroundOperation.ts","../../src/platform-react/operations/hasKind.ts","../../src/platform-react/operations/useOperationsByParent.ts","../../src/platform-react/operations/useLatestTerminalOperationByParent.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","../../src/platform-react/menuConfigHostSidebar.ts","../../src/components/layout/breadcrumbs.ts","../../src/platform-react/activeNavHref.ts"],"names":["QueryClient","QueryCache","MutationCache","reactQuery","createContext","useContext","useEffect","useHostIdentity","useState","useTheme","useMemo","jsxs","ThemeProvider","jsx","useOptionalExtensionRuntimeTransport","extensionRuntime","setOrgFormats","warnedAboutMissingAccessor","extensionRuntime2","useRef","setOrgReportingCurrency","createElement","QueryClientProvider","definePlatformReactPage","useView","useMcpTool","useQuery","useQueries","REPORTING_CURRENCY_TOOL","useCallback","MONEY_FALLBACK_LOCALE","formatMoney","emitNavigation","useSyncExternalStore","usePluginRealtimeSource","useQueryClient","useExtensionRuntimeTransport","stripTrailingSlash"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Host toast bridge — the ergonomic surface a lifted monolith `useToast()`\n * expects, shipped alongside the other shared composites so a plugin's\n * `components/shared/*` (and its `use-toast` alias) resolve here.\n *\n * PlatformReact pages can't import the host's toaster (it's an app-global\n * singleton, not in the host module registry), so these dispatch the\n * `ethisys:toast` CustomEvent the host's toast listener (`useExtensionToast`)\n * forwards to its toaster — the same wire contract as the lower-level\n * `@ethisyscore/components-react` `showToast` primitive (kept self-contained\n * here to avoid a load-bearing cross-package import for a one-line dispatch).\n * Outside a host that wired the listener (mock/dev), every call is a harmless\n * no-op, so a plugin keeps its lifted `showSuccessToast(...)` call sites.\n */\ntype ToastVariant = \"default\" | \"success\" | \"error\" | \"warning\";\n\nfunction dispatchToast(title: string, description: string | undefined, variant: ToastVariant): void {\n if (typeof window === \"undefined\" || title.length === 0) {\n return;\n }\n window.dispatchEvent(new CustomEvent(\"ethisys:toast\", { detail: { title, description, variant } }));\n}\n\nexport function showSuccessToast(title: string, description?: string): void {\n dispatchToast(title, description, \"success\");\n}\n\nexport function showErrorToast(title: string, description?: string): void {\n dispatchToast(title, description, \"error\");\n}\n\nexport function showWarningToast(title: string, description?: string): void {\n dispatchToast(title, description, \"warning\");\n}\n\n/**\n * Options accepted by the `toast(...)` returned from {@link useToast}. Both `title`\n * and `description` are optional, but at least one must be set — `toast({ description })`\n * shows the description as the message (mirroring the monolith); `toast({})` is a no-op.\n */\nexport interface ToastOptions {\n /** Primary line. Falls back to {@link ToastOptions.description} when absent/empty. */\n title?: string;\n /** Secondary line — or the message itself when no `title` is given. */\n description?: string;\n /** `\"destructive\"`/`\"error\"` → error; `\"warning\"`/`\"success\"` map directly; else default. */\n variant?: string;\n}\n\n/**\n * Hook mirror of the monolith's `useToast()` surface, so a lifted\n * `const { toast } = useToast()` (and `showSuccessToast`/`showErrorToast`) keeps\n * working unchanged once `@/hooks/common/utility/use-toast` is aliased here.\n */\nexport function useToast() {\n return {\n toast: (opts: ToastOptions): void => {\n const variant: ToastVariant =\n opts.variant === \"destructive\" || opts.variant === \"error\"\n ? \"error\"\n : opts.variant === \"warning\"\n ? \"warning\"\n : opts.variant === \"success\"\n ? \"success\"\n : \"default\";\n // `title` is the primary line; fall back to `description` so `toast({ description })`\n // shows it (mirroring the monolith) instead of silently no-op'ing. Both absent → no-op.\n if (opts.title) {\n dispatchToast(opts.title, opts.description, variant);\n } else if (opts.description) {\n dispatchToast(opts.description, undefined, variant);\n }\n },\n showSuccessToast,\n showErrorToast,\n showWarningToast,\n };\n}\n","import { MutationCache, QueryCache, QueryClient, type QueryClientConfig } from \"@tanstack/react-query\";\nimport { showErrorToast } from \"../components/shared/toast\";\n\n/**\n * The `QueryClient` a PlatformReact plugin surface falls back to when the host realm provides\n * none — see {@link useSurfaceQueryClient} for when that is, and what it costs.\n *\n * # Why this file exists\n *\n * It predates the react-query external. A plugin bundle could not see the host's app-wide\n * `QueryClientProvider` — react-query was bundle-local, which is why each page and overlay\n * created its own client. The host's client\n * carries cache-level `onError` handlers that turn a failed query or mutation into a toast, and\n * for a long time the plugin-side client carried none. The consequence was not a degraded\n * message: it was NO message. A command could be refused by the server, travel back through\n * every layer intact, and land on a page that showed the operator nothing at all.\n *\n * That silence was invisible from inside plugin code, because plugins were written as though the\n * handler existed — roughly 1,100 `meta.errorTitle` declarations across nine plugins, plus this\n * SDK's own shared connector-mapping hooks, all of them dead on arrival.\n *\n * # What gets shown\n *\n * `meta.errorTitle` is the title when a call site set one — that is the contract those 1,100 sites\n * were already written against. Otherwise the title is derived from the `[Status]` prefix the\n * SDK's `ResultMapper` puts on every failure, so an unlabelled call still says something truer\n * than \"Error\". BOTH bracketed prefixes are stripped from the description — the status one and the\n * plugin’s own [ERROR_CODE] behind it: they are our wire format, and an operator reading either is\n * reading our plumbing rather than their problem.\n *\n * A page that renders its own error UI opts out with `meta.suppressErrorToast`, matching the host.\n */\n\n/** The subset of react-query's `meta` this module reads. Both fields are optional by design. */\nexport interface PluginQueryMeta\n{\n /** Toast title for a failure from this call site. */\n errorTitle?: string;\n /** Set when the page shows its own error UI and a toast would double-report it. */\n suppressErrorToast?: boolean;\n}\n\n/**\n * Titles for the `ResultStatus` values a plugin can actually receive, phrased for an operator\n * rather than after the enum. Statuses absent here (`Invalid`, `Error`, `CriticalError`,\n * `Unavailable`) deliberately fall through to the generic title: their own messages already say\n * what went wrong, and a title like \"Critical Error\" adds alarm without adding information.\n */\nconst STATUS_TITLES = new Map<string, string>([\n [\"NotFound\", \"Not Found\"],\n [\"Unauthorized\", \"Authentication Error\"],\n [\"Forbidden\", \"Access Denied\"],\n [\"Conflict\", \"Conflict\"],\n]);\n\n/**\n * The remaining statuses `ResultMapper` can emit. Recognised so their prefix is still stripped,\n * but deliberately left to the generic title — see the note above.\n */\nconst UNTITLED_STATUSES = new Set([\"Invalid\", \"Error\", \"CriticalError\", \"Unavailable\", \"Accepted\"]);\n\nconst GENERIC_TITLE = \"Error\";\n\n/**\n * A `Map`/`Set` pair rather than an object literal with `in`: `in` walks the prototype chain, so\n * `\"[toString] …\"` or `\"[constructor] …\"` would read as a known status and get its prefix eaten.\n */\nfunction isKnownStatus(status: string): boolean\n{\n return STATUS_TITLES.has(status) || UNTITLED_STATUSES.has(status);\n}\n\n/**\n * Splits `\"[Conflict] message\"` into its status and message.\n *\n * Only a leading bracketed token is treated as a status, and only when it is one this SDK can\n * actually emit. A message that merely opens with a bracket — `\"[draft] copy not ready\"` — keeps\n * its text intact, because silently eating the first word of someone's error is worse than\n * failing to recognise a prefix.\n */\nfunction splitStatusPrefix(message: string): { status: string | null; body: string }\n{\n const match = /^\\[([A-Za-z]+)\\]\\s*(.*)$/s.exec(message);\n if (match === null)\n {\n return { status: null, body: message };\n }\n\n const [, status, body] = match;\n if (!isKnownStatus(status))\n {\n return { status: null, body: message };\n }\n\n // A prefix with nothing after it would leave an empty toast body; keep the original instead.\n return body.length > 0 ? { status, body } : { status: null, body: message };\n}\n\n/**\n * A plugin's OWN error-code prefix — `SALES_TARGET_COMPANY_HAS_COMMITTED_WORK`, `HR_STAFF_NOT_FOUND`.\n *\n * Every plugin's error factory shapes its messages as <c>[CODE] message</c> so the code stays\n * parseable from the free-text tail, and `ResultMapper` then wraps THAT in its own `[Status]`. So a\n * failure reaches the browser carrying two bracketed prefixes, and stripping only the outer one\n * left an operator reading `SALES_TARGET_COMPANY_HAS_COMMITTED_WORK]` — our plumbing, one layer in.\n *\n * SCREAMING_SNAKE with at least one underscore, deliberately: that is the shape every generated\n * code has, and it is narrow enough that a message legitimately opening with a bracketed word\n * (`\"[draft] copy not ready\"`) keeps its own text. Eating the first word of someone's prose is the\n * same class of mistake as showing them the code.\n */\nconst ErrorCodePrefix = new RegExp(String.raw`^\\[[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+\\]\\s*`);\n\n/**\n * Removes one leading error-code prefix, if there is one.\n *\n * At most one: two codes is not a shape anything produces, so a second bracketed token is far more\n * likely to be part of the message than another prefix to eat.\n */\nfunction stripErrorCodePrefix(body: string): string\n{\n const stripped = body.replace(ErrorCodePrefix, \"\");\n\n // Never strip the whole message away — an empty toast says less than an ugly one.\n return stripped.length > 0 ? stripped : body;\n}\n\n/**\n * Whether this failure is a request the caller themselves abandoned.\n *\n * Matched on the error NAME rather than on an `instanceof` against `CancelledError`, because that\n * type lives in `@tanstack/query-core` and importing it here would bind this file to a transitive\n * package it does not otherwise depend on. `AbortError` is what the browser raises when a fetch is\n * aborted; `CancelledError` is what react-query raises when it cancels one itself.\n */\nfunction wasAbandoned(error: unknown): boolean\n{\n return error instanceof Error\n && (error.name === \"AbortError\" || error.name === \"CancelledError\");\n}\n\n/** Raises the failure as a host toast, unless the call site asked to own its own error UI. */\nfunction surfaceError(error: unknown, meta: PluginQueryMeta | undefined): void\n{\n if (meta?.suppressErrorToast === true)\n {\n return;\n }\n\n // A request the operator's own navigation abandoned is not a failure they can act on, and telling\n // them about it is actively misleading: it attaches a red toast to an action that SUCCEEDED.\n //\n // Measured on prod. Deleting a podcast feed soft-deleted the row exactly as intended — the UPDATE\n // is in the logs, with the row version matched — and then navigated away, which tore down a list\n // query still in flight on the page being left. The operator saw \"Error fetching podcast\n // episodes\" and reasonably concluded the delete had failed. It had not, and there was nothing\n // wrong to fix.\n //\n // Deliberately NOT solved at the call site by disabling that one query: every delete-then-navigate\n // surface in every plugin has the same race, and fixing them one at a time means finding them one\n // at a time, from operator reports.\n if (wasAbandoned(error))\n {\n return;\n }\n\n const raw = error instanceof Error && error.message.length > 0\n ? error.message\n : \"An unexpected error occurred\";\n const { status, body } = splitStatusPrefix(raw);\n const reason = stripErrorCodePrefix(body);\n\n const title = meta?.errorTitle\n ?? (status !== null ? STATUS_TITLES.get(status) ?? GENERIC_TITLE : GENERIC_TITLE);\n\n showErrorToast(title, reason);\n}\n\n/**\n * Creates a plugin-surface `QueryClient` whose failures are visible to the operator.\n *\n * `config` is merged shallowly over the defaults, so a plugin can tune `defaultOptions` without\n * having to restate — or accidentally drop — the error handling. Passing an explicit `queryCache`\n * or `mutationCache` does replace ours, which is the escape hatch for a plugin that genuinely\n * wants different behaviour.\n */\nexport function createPluginQueryClient(config?: QueryClientConfig): QueryClient\n{\n return new QueryClient({\n queryCache: new QueryCache({\n onError: (error, query) => surfaceError(error, query.meta as PluginQueryMeta | undefined),\n }),\n mutationCache: new MutationCache({\n onError: (error, _variables, _context, mutation) =>\n surfaceError(error, mutation.meta as PluginQueryMeta | undefined),\n }),\n ...config,\n defaultOptions: {\n // Spread first so `mutations`, `dehydrate`, `hydrate` and anything else react-query\n // adds to `DefaultOptions` survive. Rebuilding this object from named keys instead\n // silently dropped them, which contradicted the merge promised just above.\n ...config?.defaultOptions,\n queries: {\n retry: 1,\n refetchOnWindowFocus: false,\n staleTime: 30_000,\n ...config?.defaultOptions?.queries,\n },\n },\n });\n}\n","import { createContext, useContext, type Context } from \"react\";\n\nimport * as reactQuery from \"@tanstack/react-query\";\nimport type { QueryClient } from \"@tanstack/react-query\";\n\nimport { createPluginQueryClient } from \"./pluginQueryClient\";\n\n/**\n * Resolves the `QueryClient` a PlatformReact surface runs on: the host's when the\n * host realm provides one, this bundle's own when it does not.\n *\n * # Why a resolution rather than a constant\n *\n * `@tanstack/react-query` is now a host external (`PLATFORM_REACT_EXTERNALS`), so\n * the copy answering a plugin's import is the DEPLOYED host's module instance —\n * the same one the host's own app root built its `QueryClientProvider` from. That\n * makes the host's client reachable from plugin code for the first time, and one\n * client is one cache: a delete on a detail surface finally invalidates the list\n * surface that is showing the row.\n *\n * Every page used to construct its own client instead, because a bundled copy of\n * react-query could not see the host's provider. That is the defect this replaces:\n * one cache per page bundle, so an invalidation reached only the surface that\n * issued it and a deleted row survived until the operator reloaded the browser.\n *\n * # Why the host's client is read from CONTEXT, and not from a host accessor\n *\n * The host serves the react-query module itself; nothing else is required of it.\n * Verified in `coreconnect-web`, by reading it rather than assuming: the app has\n * exactly one React root (`src/main.tsx` → `<App/>`), `App` wraps the whole\n * router in `<QueryClientProvider client={queryClient}>`, `PlatformReactSurfaceMount`\n * renders a plugin surface as an ordinary child of that tree (no second\n * `createRoot`), and the one portal in the extension runtime uses `createPortal`,\n * which preserves context ancestry. So the host's provider is an ancestor of every\n * plugin surface the moment the react-query MODULE instance is shared — and this\n * hook needs only the module it already imports.\n *\n * The host additionally exposes a `getHostQueryClient()` accessor under a bespoke\n * registry specifier. This file deliberately does not import it. Every bare\n * specifier a plugin bundle imports is one the deployed host MUST serve or the\n * bundle fails to load before any SDK code runs — a failure no fallback in this\n * file can catch. Taking a second such dependency, on a specifier invented for\n * this purpose and matching no published package, would widen that blast radius\n * to buy a case the verification above shows does not arise. The accessor remains\n * useful to a PLUGIN that wants to pass a client into a definer's `queryClient`\n * option explicitly; it is simply not how the default path resolves.\n *\n * # The fallback is the point\n *\n * An external resolves from whatever host is deployed, which is NOT necessarily\n * the host this SDK was released alongside. A host may serve the react-query\n * module while rendering plugin surfaces outside its `QueryClientProvider`, or\n * expose a module namespace that predates the `QueryClientContext` export. Both\n * must degrade to exactly today's behaviour — a per-bundle client from\n * {@link createPluginQueryClient} — and never throw. A surface that throws takes\n * the whole page down; a surface on its own cache merely has the old defect back.\n *\n * What the fallback does NOT cover, and must not be claimed to: a host that does\n * not register `@tanstack/react-query` in its module registry AT ALL. The bare\n * specifier is then unresolvable and the bundle fails to load before any SDK code\n * runs. No SDK-side tolerance can reach that, which is why the host change ships\n * first and plugins rebuild last.\n */\n\n/**\n * The host realm's react-query context, resolved ONCE at module scope.\n *\n * <b>Read off a NAMESPACE import, never a named one.</b> `@tanstack/react-query`\n * is a host external, so the copy answering this import is whatever the DEPLOYED\n * host exposes — and a named import of an export that copy lacks is a module\n * LINKING failure, raised before a single line of this file runs. The fallback\n * documented above for \"a namespace that predates the QueryClientContext export\"\n * would have been unreachable in exactly the case it was written for: the page\n * would go down instead of degrading. This is the same trap\n * `useOrgFormatSync` records, where a named import of an accessor added in SDK\n * 1.88.0 took down every page of every plugin built against a 1.86.0 host.\n *\n * Module scope rather than a check inside the hook, for the reason that file also\n * gives: the host realm cannot change identity for the life of the bundle, and a\n * conditional `useContext` would break the rules of hooks. A host realm missing\n * the export gets a private context that no provider ever fills, so the read below\n * is valid and always yields `undefined` — a graceful absence, not a crash.\n */\nconst hostRealmExposesContext = isReactContext(reactQuery.QueryClientContext);\n\nconst resolvedHostContext: Context<QueryClient | undefined> = hostRealmExposesContext\n ? (reactQuery.QueryClientContext as Context<QueryClient | undefined>)\n : createContext<QueryClient | undefined>(undefined);\n\nfunction isReactContext(value: unknown): value is Context<QueryClient | undefined> {\n return typeof value === \"object\" && value !== null && \"Provider\" in value;\n}\n\n/**\n * This bundle's own client, built on FIRST fallback rather than at module load.\n *\n * Lazy deliberately. Eager construction is what the definers used to do, and it\n * paid `createPluginQueryClient` — and the `QueryCache`/`MutationCache` wiring\n * behind it — on every surface of every plugin, including the surfaces that now\n * run on the host's client and never touch this at all.\n */\nlet bundleQueryClient: QueryClient | undefined;\n\nfunction pluginBundleQueryClient(): QueryClient {\n bundleQueryClient ??= createPluginQueryClient();\n return bundleQueryClient;\n}\n\nlet warnedAboutMissingHostClient = false;\n\nfunction warnOnceAboutMissingHostClient(): void {\n if (warnedAboutMissingHostClient) {\n return;\n }\n warnedAboutMissingHostClient = true;\n console.warn(\n \"[plugin-ui] The host realm provides no QueryClient\" +\n (hostRealmExposesContext\n ? \" (@tanstack/react-query is served, but this surface renders outside the host's QueryClientProvider)\"\n : \" (the host's @tanstack/react-query namespace has no QueryClientContext export)\") +\n \". This surface falls back to a client of its own, so a mutation here will not \" +\n \"invalidate another surface's cache. Deploy a host that mounts plugin surfaces \" +\n \"inside its QueryClientProvider to share one cache.\",\n );\n}\n\n/**\n * The client this surface should provide to its subtree.\n *\n * Precedence, and the order matters:\n *\n * 1. `override` — the definers' existing `queryClient` option. A plugin that asks\n * for a specific client gets it, and is never second-guessed or warned about.\n * 2. The host's client, read from the host realm's react-query context.\n * 3. This bundle's own client — today's behaviour, unchanged.\n *\n * @param override The `queryClient` a definer's caller supplied, if any.\n */\nexport function useSurfaceQueryClient(override?: QueryClient): QueryClient {\n // Unconditional, before any branching: it is a hook, and the rules of hooks do\n // not care that the answer is about to be discarded in favour of `override`.\n const hostQueryClient = useContext(resolvedHostContext);\n\n if (override !== undefined) {\n return override;\n }\n\n if (hostQueryClient !== undefined) {\n return hostQueryClient;\n }\n\n warnOnceAboutMissingHostClient();\n return pluginBundleQueryClient();\n}\n\n/** Test seam: clears the once-per-bundle state the fallback path accumulates. */\nexport function __resetSurfaceQueryClientForTests(): void {\n bundleQueryClient = undefined;\n warnedAboutMissingHostClient = false;\n}\n","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 *\n * Tailwind's generated layers (`utilities`, and the `@property` fallbacks in\n * `properties`) are emitted UNSCOPED and used to break that rule — a plugin's bare\n * `.text-sm` silently restyled host chrome. The build now scopes them:\n * `scopePluginCssPlugin` in `@ethisyscore/vite-plugin`, applied by both page-build\n * paths. Hand-written plugin CSS still has to scope itself.\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/**\n * Mirror of the HOST mount's `deriveEntityToken` (PlatformReactSurfaceMount): the\n * route sub-path relative to the PAGE-surface `basePath` — which is the page\n * route's STATIC leading segments, so a fully-static page (list routes) yields\n * `\"/\"` because there the pathname IS the basePath. The host silently drops a\n * publish whose `entityToken` doesn't match its own derivation, so this must stay\n * bit-identical to the host helper. No basePath (legacy host) falls back to the\n * full pathname, exactly like the host does.\n */\nexport function deriveHostEntityToken(pathname: string, basePath: string | undefined): string {\n if (!basePath) {\n return pathname;\n }\n const base = stripTrailingSlash(basePath);\n if (pathname === base) {\n return \"/\";\n }\n if (pathname.startsWith(`${base}/`)) {\n return pathname.slice(base.length);\n }\n return pathname;\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).\n * 2.2.0 added the additive, optional `activeNavHref` on every contribution mode; an older host\n * ignores the extra field (it builds its state field-by-field, never by spreading the\n * contribution), so a plugin declaring it against a 2.0/2.1 host is inert, not broken. */\nexport const HOST_CHROME_CONTRACT_VERSION = \"2.2.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\" } & HostSidebarActiveNav)\n | ({ mode: \"actions\"; entityToken: string; actions: HostSidebarActionDescriptor[] } & HostSidebarActiveNav)\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 } & HostSidebarActiveNav);\n\n/**\n * The optional active-nav declaration carried by EVERY contribution mode (contract ≥2.2.0).\n *\n * `activeNavHref` is a slug-relative href — the SAME vocabulary as nav-item and navigate-action\n * hrefs — naming the sidebar entry the plugin considers active for the current route. The host\n * resolves it to its own nav-item id and highlights that entry; when it is absent, or names\n * nothing the host is rendering, the host's URL election decides exactly as it always has.\n *\n * It exists because the host's election can only light an item whose href PREFIXES the current\n * URL, so a sub-page whose URL does not nest under its section (`/extensions/projects/people/:id`\n * belonging to \"People\" at `/extensions/projects/settings`) lights the module root instead. A\n * plugin's information architecture is not always recoverable from its URL shape, so the plugin\n * says so; {@link resolveActiveNavHref} derives it from the `routeMeta` parent chain the plugin\n * already declares for breadcrumbs.\n *\n * It rides on `mode:\"none\"` too — deliberately. `\"none\"` is what a plugin publishes on an\n * ordinary list route with no CTAs, and that is precisely the route shape whose highlight is most\n * often wrong; a declaration there still contributes no actions and still does not clobber the\n * static manifest.\n */\nexport interface HostSidebarActiveNav {\n activeNavHref?: string;\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 { useMemo, useState, type ReactElement, type ReactNode } from \"react\";\nimport { ThemeProvider, useTheme, type Theme } from \"@mui/material\";\n\n/**\n * Class on the portal container element rendered at the end of a plugin surface.\n * `plugin-base.css` positions it; the class is exported so a plugin can target it\n * (e.g. to raise the stacking order for one surface).\n */\nexport const EHX_PLUGIN_PORTAL_CLASS = \"ehx-plugin-portal-root\";\n\n/**\n * Marker attribute on any element acting as a plugin portal container. Scoped\n * plugin CSS keys off `.ehx-plugin-root` OR this attribute, so a plugin that\n * supplies its OWN container for a specific overlay (passing `container` on the\n * component, which beats the default set here) can mark it and keep utilities.\n */\nexport const EHX_PLUGIN_PORTAL_ATTR = \"data-ehx-plugin-portal\";\n\n/**\n * Returns `theme` with the portal container set on every slot that can decide\n * where an overlay mounts, preserving all other theme values and `defaultProps`.\n *\n * Which slots, and why the two base ones are not enough:\n *\n * MuiModal Modal, Dialog, Drawer - the base for anything full-screen.\n * MuiPopper Popper, Autocomplete - the base for anything anchored.\n * MuiPopover Popover, and Select via Menu. NOT covered by MuiModal: Popover\n * resolves its own container first (\"if the anchorEl prop is\n * provided, use its parent body element\"), so Modal receives a\n * non-`undefined` value and its own default never applies.\n * MuiMenu Menu passes its container down to Popover explicitly, so a\n * MuiMenu-level default outranks the MuiPopover one. The HOST sets\n * exactly that (below), so without this entry every plugin menu and\n * select escapes to the host's container.\n * MuiTooltip Tooltip passes `PopperProps.container` to its Popper, which\n * likewise outranks the MuiPopper default. The host sets that too.\n *\n * The container is applied LAST at each level so it beats the host's. The host is\n * not neutral here: `gogo-theme-provider.tsx` already redirects MUI portals into\n * a container inside its own `.gogo-ui` scope, setting MuiModal, MuiPopover,\n * MuiMenu, MuiPopper and MuiTooltip. That container is a SIBLING of the plugin\n * root, not an ancestor, so inheriting it leaves plugin overlays outside the\n * plugin's scoped CSS. Inside a plugin surface the plugin container has to win.\n *\n * A component's own explicit `container` prop still wins over all of this:\n * `resolveProps` only falls back to a default when the incoming value is\n * `undefined`.\n *\n * Written out per slot rather than looped because each slot's `defaultProps` is a\n * distinct props type; indexing with a union key widens them into one\n * unassignable intersection.\n */\nfunction withPortalContainer(theme: Theme, containerElement: HTMLElement): Theme {\n const slots = theme.components ?? {};\n const tooltip = slots.MuiTooltip;\n // A getter rather than the element itself: the theme gets serialized downstream\n // (MUI X DataGrid memo-keys its CSS variables on JSON.stringify of the theme), and\n // a raw DOM node's React expandos (__reactFiber$*) recurse until the stack\n // overflows. JSON.stringify skips functions, and MUI resolves callable containers.\n const container = () => containerElement;\n return {\n ...theme,\n components: {\n ...slots,\n MuiModal: { ...slots.MuiModal, defaultProps: { ...slots.MuiModal?.defaultProps, container } },\n MuiPopper: { ...slots.MuiPopper, defaultProps: { ...slots.MuiPopper?.defaultProps, container } },\n MuiPopover: { ...slots.MuiPopover, defaultProps: { ...slots.MuiPopover?.defaultProps, container } },\n MuiMenu: { ...slots.MuiMenu, defaultProps: { ...slots.MuiMenu?.defaultProps, container } },\n MuiTooltip: {\n ...tooltip,\n defaultProps: {\n ...tooltip?.defaultProps,\n PopperProps: { ...tooltip?.defaultProps?.PopperProps, container },\n },\n },\n },\n };\n}\n\n/**\n * Renders plugin content plus a portal container INSIDE the plugin's style root,\n * and defaults MUI's portalling components to that container.\n *\n * Why: a plugin's compiled CSS is injected into the host document and is scoped\n * to the plugin root, but MUI renders modals, menus, poppers and tooltips through\n * a portal, landing wherever the host theme sends them — a sibling of the plugin\n * root, not a descendant — so they fall outside every scoped rule. Containing\n * them first is what lets plugin CSS be scoped at all.\n *\n * This is a nested `ThemeProvider` over the HOST theme (MUI is a host external,\n * so `useTheme()` reads the host's provider through the same module instance).\n * It adds the container defaults and changes nothing else.\n */\nexport function PluginPortalScope({ children }: { children: ReactNode }): ReactElement {\n // Callback ref into state, not a ref object: the theme has to be rebuilt once\n // the container element exists, which needs a render pass.\n const [container, setContainer] = useState<HTMLElement | null>(null);\n const hostTheme = useTheme();\n const theme = useMemo(\n () => (container ? withPortalContainer(hostTheme, container) : hostTheme),\n [hostTheme, container],\n );\n\n return (\n <ThemeProvider theme={theme}>\n {children}\n <div ref={setContainer} className={EHX_PLUGIN_PORTAL_CLASS} {...{ [EHX_PLUGIN_PORTAL_ATTR]: \"\" }} />\n </ThemeProvider>\n );\n}\n","import { useEffect, useState } from \"react\";\n\nimport * as extensionRuntime from \"@ethisyscore/extension-runtime/plugin\";\nimport { setOrgFormats } from \"@ethisyscore/core-utils/date/org-format\";\n\n/**\n * Resolved from the host realm at module scope, NOT imported as a binding.\n *\n * `@ethisyscore/extension-runtime/plugin` is a host-provided external\n * (`PLATFORM_REACT_EXTERNALS`), so it is never bundled into a plugin page: the\n * copy that answers this import is whatever the DEPLOYED host exposes, which is\n * independent of the SDK version the plugin was built against. A plugin on a\n * newer SDK than the host therefore gets `undefined` for anything the host's\n * older copy does not export, and a named import of it fails as\n * \"X is not a function\" the moment a surface mounts.\n *\n * That is not hypothetical. `useOptionalExtensionRuntimeTransport` landed in SDK\n * 1.88.0, and calling it unconditionally took down every page of every plugin\n * built on 1.88.x against a 1.86.0 host, because\n * `definePlatformReactPluginPage` and `definePlatformReactPluginOverlay` call\n * `useOrgFormatSync` on every surface mount. The old comment below claimed this\n * hook fails open, and it did for a missing TRANSPORT while crashing hard for a\n * missing HOOK, which is the case that actually occurs in production.\n *\n * A module-scope resolution rather than a check inside the hook: the host realm\n * cannot change identity for the life of the bundle, so this is stable, and the\n * hook below stays an unconditional call rather than a conditional one that\n * would break the rules of hooks.\n */\nlet warnedAboutMissingAccessor = false;\n\nconst useOptionalExtensionRuntimeTransport: typeof extensionRuntime.useOptionalExtensionRuntimeTransport =\n typeof extensionRuntime.useOptionalExtensionRuntimeTransport === \"function\"\n ? extensionRuntime.useOptionalExtensionRuntimeTransport\n : () => {\n if (!warnedAboutMissingAccessor) {\n warnedAboutMissingAccessor = true;\n console.warn(\n \"[plugin-ui] The host realm does not expose useOptionalExtensionRuntimeTransport \" +\n \"(host SDK predates 1.88.0). Organisation date/time formats fall back to the UK \" +\n \"default for this bundle. Bump the host's @ethisyscore/extension-runtime pin to \" +\n \"restore them.\",\n );\n }\n return null;\n };\n\n/**\n * Reserved host resource carrying the caller organisation's effective date/time\n * display formats. The host proxies it (see coreconnect-web\n * `surfaceMcpTransport.ts`) from the permission-free\n * `/organisations/current/display-formats` endpoint. The URI string is hardcoded\n * on both sides of the runtime host↔plugin contract, exactly like\n * `ethisys:user-permissions`.\n */\nexport const ORGANISATION_SETTINGS_URI = \"ethisys:organisation-settings\";\n\ninterface OrganisationDisplayFormats {\n readonly dateFormat: string | null;\n readonly timeFormat: string | null;\n}\n\n/**\n * Reads the caller organisation's effective date/time display formats from the\n * reserved {@link ORGANISATION_SETTINGS_URI} host resource and stamps them into\n * the core-utils org-format refs, so THIS plugin bundle's org-aware formatters\n * (`@ethisyscore/core-utils/date/org-format`) and `DateInput` picker masks render\n * in the organisation's configured format.\n *\n * Each plugin bundle owns its own copy of those module refs, so this must run per\n * bundle — `definePlatformReactPluginPage` and `definePlatformReactPluginOverlay`\n * auto-call it once per surface mount, so plugins wire nothing.\n *\n * Reactive: the org format arrives asynchronously, after the surface (and its\n * `DateInput`s) have already rendered on the UK fallback. The module refs are\n * non-React, so a `useState` bump here re-renders the surface once the formats\n * resolve, letting already-mounted formatters and picker masks pick them up. The\n * effect is also keyed on the active organisation id, so switching organisation\n * without a remount refetches.\n *\n * Fails open in both directions, which is two distinct cases:\n *\n * - No TRANSPORT. It resolves the transport via the OPTIONAL accessor (not\n * `useMcpResource`, which throws when no provider is present) so a surface that\n * renders without a host transport — a standalone/mock-mode run or an isolated\n * unit test — degrades to a no-op and the formatters use the UK fallback.\n * - No ACCESSOR. A host realm older than SDK 1.88.0 does not expose the accessor\n * at all; see the module-scope resolution above for why an import binding\n * cannot be relied on here and what it cost when it was.\n *\n * On a rejected/failed fetch the refs are reset to the UK fallback rather than\n * left holding a previous organisation's format.\n */\nexport function useOrgFormatSync(): void {\n const transport = useOptionalExtensionRuntimeTransport();\n const organisationId = extensionRuntime.useHostIdentity()?.organisationId ?? null;\n const [, bumpVersion] = useState(0);\n\n useEffect(() => {\n // Reset to the deterministic UK fallback up front — before the no-transport\n // early return AND before this organisation's fetch settles. The refs are a\n // module-level singleton shared across surface mounts of the same bundle, so\n // without this a reused bundle (no transport) or an in-flight organisation\n // switch would keep rendering the PREVIOUS organisation's format until (or\n // unless) a new request resolved, exposing one tenant's mask under another.\n setOrgFormats(null, null);\n bumpVersion((v) => v + 1);\n\n if (!transport) return;\n\n const controller = new AbortController();\n transport\n .getResource<OrganisationDisplayFormats>(ORGANISATION_SETTINGS_URI, controller.signal)\n .then((result) => {\n if (controller.signal.aborted) return;\n setOrgFormats(result.data?.dateFormat ?? null, result.data?.timeFormat ?? null);\n bumpVersion((v) => v + 1);\n })\n .catch(() => {\n if (controller.signal.aborted) return;\n // Fail open, but do not keep a previous organisation's format on failure:\n // reset to the deterministic UK fallback.\n setOrgFormats(null, null);\n bumpVersion((v) => v + 1);\n });\n\n return () => controller.abort();\n }, [transport, organisationId]);\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nimport * as extensionRuntime from \"@ethisyscore/extension-runtime/plugin\";\nimport { setOrgReportingCurrency } from \"@ethisyscore/core-utils/money\";\n\n/**\n * Resolved from the host realm at module scope, NOT imported as a binding — see\n * {@link useOrgFormatSync} for the full reasoning. `@ethisyscore/extension-runtime/plugin`\n * is a host-provided external, so a host older than the SDK that introduced\n * `useOptionalExtensionRuntimeTransport` (1.88.0) does not expose it, and a named\n * import would crash every surface with \"X is not a function\". This hook is called\n * on every surface mount by `definePlatformReactPluginPage` /\n * `definePlatformReactPluginOverlay`, exactly like `useOrgFormatSync`, so it must\n * degrade to a no-op (currency falls back to GBP) rather than throw.\n */\nlet warnedAboutMissingAccessor = false;\n\nconst useOptionalExtensionRuntimeTransport: typeof extensionRuntime.useOptionalExtensionRuntimeTransport =\n typeof extensionRuntime.useOptionalExtensionRuntimeTransport === \"function\"\n ? extensionRuntime.useOptionalExtensionRuntimeTransport\n : () => {\n if (!warnedAboutMissingAccessor) {\n warnedAboutMissingAccessor = true;\n console.warn(\n \"[plugin-ui] The host realm does not expose useOptionalExtensionRuntimeTransport \" +\n \"(host SDK predates 1.88.0). The organisation reporting currency falls back to GBP \" +\n \"for this bundle. Bump the host's @ethisyscore/extension-runtime pin to restore it.\",\n );\n }\n return null;\n };\n\n/** Host tool resolving the caller org's reporting currency (default, else GBP). */\nexport const REPORTING_CURRENCY_TOOL = \"settings:get-organisation-reporting-currency\";\n\ninterface ReportingCurrencyInfo {\n readonly code: string;\n readonly decimalPlaces: number | null;\n}\n\ninterface ReportingCurrencyResponse {\n readonly currency: ReportingCurrencyInfo | null;\n}\n\n/**\n * Reads the caller organisation's reporting currency from the host\n * {@link REPORTING_CURRENCY_TOOL} and stamps it into the core-utils reporting-currency\n * ref (`@ethisyscore/core-utils/money`), so THIS plugin bundle's pure\n * `formatMoney(amount)` renders reporting-currency amounts in the org's currency\n * without threading a code.\n *\n * The org-currency sibling of {@link useOrgFormatSync}. Each plugin bundle owns its\n * own copy of the module ref, so this must run per bundle —\n * `definePlatformReactPluginPage` and `definePlatformReactPluginOverlay` auto-call\n * it once per surface mount, so plugins wire nothing.\n *\n * Reactive: the currency arrives asynchronously, after the surface has already\n * rendered on the GBP fallback. The module ref is non-React, so a `useState` bump\n * re-renders the surface once the currency resolves. Keyed on the active\n * organisation id so switching organisation without a remount refetches.\n *\n * Fails open in both directions, mirroring {@link useOrgFormatSync}:\n * - No TRANSPORT (standalone/mock run, unit test) — resolved via the OPTIONAL\n * accessor, degrades to a no-op and `formatMoney` uses the GBP fallback.\n * - No ACCESSOR (host realm older than SDK 1.88.0) — see the module-scope\n * resolution above.\n * On a rejected/failed fetch the ref is reset to the GBP fallback rather than left\n * holding a previous organisation's currency.\n */\nexport function useReportingCurrencySync(): void {\n const transport = useOptionalExtensionRuntimeTransport();\n const organisationId = extensionRuntime.useHostIdentity()?.organisationId ?? null;\n const [, bumpVersion] = useState(0);\n\n // Clear the ref SYNCHRONOUSLY during render the moment the organisation changes,\n // before any child in this render reads it. The effect below runs AFTER this\n // render commits, so clearing only there lets a child's `formatMoney()` observe the\n // PREVIOUS organisation's currency for the first paint of a switch-without-remount.\n // Tracking the previous org id in a ref is the \"reset derived state on prop change\n // during render\" pattern; the effect still owns the fetch + re-stamp.\n const previousOrganisationId = useRef<string | null | undefined>(undefined);\n if (previousOrganisationId.current !== organisationId) {\n previousOrganisationId.current = organisationId;\n setOrgReportingCurrency(null);\n }\n\n useEffect(() => {\n // Reset to the deterministic GBP fallback up front — before the no-transport\n // early return AND before this organisation's fetch settles. The ref is a\n // module-level singleton shared across surface mounts of the same bundle, so\n // without this a reused bundle (no transport) or an in-flight organisation\n // switch would keep rendering the PREVIOUS organisation's currency. (The\n // synchronous during-render clear above covers the first paint; this covers a\n // remount/no-transport reuse where the org id did not change.)\n setOrgReportingCurrency(null);\n bumpVersion((v) => v + 1);\n\n if (!transport) return;\n\n const controller = new AbortController();\n transport\n .invokeTool<Record<string, never>, ReportingCurrencyResponse>(\n REPORTING_CURRENCY_TOOL,\n {},\n controller.signal,\n )\n .then((result) => {\n if (controller.signal.aborted) return;\n const currency = result?.currency ?? null;\n setOrgReportingCurrency(\n currency ? { code: currency.code, decimalPlaces: currency.decimalPlaces } : null,\n );\n bumpVersion((v) => v + 1);\n })\n .catch(() => {\n if (controller.signal.aborted) return;\n // Fail open, but do not keep a previous organisation's currency on failure:\n // reset to the deterministic GBP fallback.\n setOrgReportingCurrency(null);\n bumpVersion((v) => v + 1);\n });\n\n return () => controller.abort();\n }, [transport, organisationId]);\n}\n","import { createElement, type ComponentType, type ReactElement } from \"react\";\nimport { QueryClientProvider, type QueryClient } from \"@tanstack/react-query\";\nimport { useSurfaceQueryClient } from \"./hostQueryClient\";\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\";\nimport { PluginPortalScope } from \"./pluginPortalScope\";\nimport { useOrgFormatSync } from \"./useOrgFormatSync\";\nimport { useReportingCurrencySync } from \"./useReportingCurrencySync\";\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 /**\n * Override the `QueryClient` this page runs on.\n *\n * Left unset, the page runs on the host's client when the host realm provides\n * one, and on a per-bundle client of its own when it does not — see\n * {@link useSurfaceQueryClient}. Setting it opts out of both.\n */\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: queryClientOverride,\n wrapPage,\n } = options;\n\n function PluginPage(props: PlatformReactPageProps) {\n // The host's client when the host realm provides one, this bundle's own when\n // it does not, and `queryClientOverride` ahead of both. Resolved per render\n // rather than once at module load: the answer depends on where this surface\n // is mounted, which module scope cannot see.\n const queryClient = useSurfaceQueryClient(queryClientOverride);\n // Stamp this bundle's org date/time format refs from the reserved host\n // resource so the plugin's org-aware formatters + DateInput masks render in\n // the org's configured format. Runs once per surface mount; fails open to the\n // UK fallback. The host mounts pages inside its runtime provider, so the MCP\n // transport useMcpResource needs is already in scope here.\n useOrgFormatSync();\n // Stamp this bundle's org reporting-currency ref so the plugin's pure\n // formatMoney(amount) renders reporting-currency amounts in the org's currency.\n // Same once-per-mount, fail-open-to-GBP sync as useOrgFormatSync.\n useReportingCurrencySync();\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 // PluginPortalScope sits INSIDE the style root, so the portal container\n // it renders is a descendant of it and MUI overlays (dialogs, menus,\n // poppers, tooltips) open inside the plugin's scoped CSS.\n createElement(PluginPortalScope, null, 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 { useMemo } from \"react\";\n\nimport { type ToolInvoker, useToolInvokerMap } from \"./mcpService\";\n\n/**\n * A single tool's wire contract: the arguments it takes and the payload it returns.\n *\n * ## The gap this closes\n *\n * `BaseMcpService.tool<TRes>(name: string, args?: unknown)` checks nothing. The tool name is any\n * string, the arguments are `unknown`, and `TRes` is whatever the caller asserts. Every part of the\n * boundary is a claim rather than a check, and all three parts have been wrong in production within\n * a single module:\n *\n * - **The result lied.** `tech-list-tool-mappings` returned a bare array while the caller read\n * `response.items`, so the table rendered its headers with no rows. No error anywhere.\n * - **The result lied again.** `tech-auto-match-tool-mappings` returned no payload at all while the\n * caller read `response.items.filter(...)`.\n * - **The arguments were mislabelled.** A service sent `{ connectorId, moduleKey }` where the\n * command declared `ConnectorSlug`, so the command rejected every call. Both values were strings,\n * so even the service interface it implemented could not catch the swap.\n *\n * None of those produced a type error, and none produced a runtime error either - the surface just\n * quietly did nothing, which is the most expensive failure mode there is.\n *\n * ## Phantom, not runtime\n *\n * The contract carries no values. `__args` and `__result` exist only so a declaration can name\n * types; {@link mcpTool} returns a frozen empty object, so both are ABSENT at runtime whatever their\n * declared types say. Reading one gets `undefined`, never a value to build on - that absence is the\n * safety property here, not the types themselves.\n */\nexport interface McpToolContract<TArgs = unknown, TResult = unknown> {\n /** Phantom: the arguments this tool accepts. Never present at runtime. */\n readonly __args: TArgs;\n /** Phantom: the payload this tool returns. Never present at runtime. */\n readonly __result: TResult;\n}\n\n/** A plugin's declared tool surface: tool name to contract. */\nexport type McpToolContracts = Record<string, McpToolContract>;\n\n/** The arguments type for one tool in a contract map. */\nexport type McpToolArgs<TTools extends McpToolContracts, K extends keyof TTools>\n = TTools[K][\"__args\"];\n\n/** The result type for one tool in a contract map. */\nexport type McpToolResult<TTools extends McpToolContracts, K extends keyof TTools>\n = TTools[K][\"__result\"];\n\n/**\n * Declares one tool's contract.\n *\n * Returns a marker object rather than nothing so the declaration can be a real value: the same\n * object literal drives the types AND the hook registration (see {@link useTypedToolInvokerMap}),\n * which is what stops the registered tool list from drifting away from the declared surface.\n *\n * @example\n * export const TOOLS = {\n * \"tech-list-tool-mappings\": mcpTool<{ connectorId: string; moduleKey: string }, MappingsResponse>(),\n * \"tech-auto-match-tool-mappings\": mcpTool<{ connectorId: string }, AutoMatchResponse>(),\n * } as const;\n */\nexport function mcpTool<TArgs, TResult>(): McpToolContract<TArgs, TResult>\n{\n // The cast is the point: there is nothing to construct. Freezing it makes an accidental write\n // fail loudly rather than silently mutating a shared module-level marker.\n return Object.freeze({}) as McpToolContract<TArgs, TResult>;\n}\n\n/**\n * Base class for a service whose tool calls are checked against a declared contract map.\n *\n * Deliberately a SEPARATE class from `BaseMcpService` rather than an overload on it. An added\n * overload would have to keep accepting `(name: string, args?: unknown)` for the services that have\n * not adopted contracts, and TypeScript would then resolve a mistyped call to that looser signature\n * instead of rejecting it - so a typo in a tool name would still compile. Opting in by changing base\n * class means the check cannot be silently bypassed.\n *\n * `BaseMcpService` is untouched; nothing has to migrate.\n *\n * @example\n * class ConnectorMappingService extends TypedMcpService<typeof TOOLS> {\n * async getMappings(connectorId: string, moduleKey: string) {\n * // name, args and result all checked against TOOLS\n * return this.tool(\"tech-list-tool-mappings\", { connectorId, moduleKey });\n * }\n * }\n */\nexport abstract class TypedMcpService<TTools extends McpToolContracts>\n{\n protected constructor(private readonly invoker: ToolInvoker) {}\n\n /**\n * Invokes a declared tool. The name must be a key of the contract map, the arguments must match\n * its declared shape, and the result is its declared type - no call-site assertion.\n */\n protected async tool<K extends keyof TTools & string>(\n name: K,\n args: McpToolArgs<TTools, K>,\n ): Promise<McpToolResult<TTools, K>>\n {\n return (await this.invoker(name, args)) as McpToolResult<TTools, K>;\n }\n}\n\n/**\n * Composes a {@link ToolInvoker} over every tool in a contract declaration.\n *\n * Takes the declaration OBJECT, not a list of names, and that is the point: `useToolInvokerMap`\n * throws `No MCP invoker registered for tool \"x\"` at runtime when a service calls a tool the list\n * forgot, and the list and the service were two places to keep in step. Deriving the names from the\n * contract map makes that drift impossible - if a tool is declared it is registered, and if it is\n * not declared the call does not typecheck.\n *\n * Rules of hooks: pass a module-level `as const` declaration. `Object.keys` on a stable object\n * yields a stable, constant-length list, which is what the underlying hook requires. Building the\n * declaration inside a component would vary the hook count between renders and break.\n */\nexport function useTypedToolInvokerMap<TTools extends McpToolContracts>(\n contracts: TTools,\n): ToolInvoker\n{\n // Sorted so the hook call order does not depend on key insertion order, which a merge or a\n // reformat can change without anyone considering it a behavioural edit. Frozen because\n // `useToolInvokerMap` requires a stable, constant-length list - mutating this array would\n // reorder or resize the per-tool hook calls, and React's hook rules make that corruption rather\n // than an error.\n const names = useMemo(() => Object.freeze(Object.keys(contracts).sort()), [contracts]);\n return useToolInvokerMap(names);\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 { useCallback, useEffect, useMemo, useState } from \"react\";\n\nimport { useHostIdentity, useOptionalExtensionRuntimeTransport } from \"@ethisyscore/extension-runtime/plugin\";\nimport { formatMoney, MONEY_FALLBACK_LOCALE } from \"@ethisyscore/core-utils/money\";\n\n/** Host tool that resolves the caller org's reporting currency (default, else GBP). */\nexport const REPORTING_CURRENCY_TOOL = \"settings:get-organisation-reporting-currency\";\n/** Host tool that lists the currency catalogue. */\nexport const LIST_CURRENCIES_TOOL = \"settings:list-currencies\";\n\n/** A currency as returned by the host currency tools. */\nexport interface CurrencyInfo {\n readonly id: string;\n readonly name: string;\n readonly code: string;\n readonly symbol: string | null;\n /** Host decimal-places override, or null to use the currency's ISO default. */\n readonly decimalPlaces: number | null;\n}\n\n/** What a plugin surface may pass to {@link UseCurrencyResult.format} to name a currency. */\nexport type CurrencySelector = CurrencyInfo | string | null | undefined;\n\nexport interface UseCurrencyResult {\n /** The org's reporting currency, or null while loading / when unresolved. */\n reportingCurrency: CurrencyInfo | null;\n /** The currency catalogue keyed by id (empty while loading / on a fail-open). */\n currencies: ReadonlyMap<string, CurrencyInfo>;\n /** True once the initial fetch has settled (whether it succeeded or failed open). */\n ready: boolean;\n /**\n * Formats an amount in a currency. `currency` may be a {@link CurrencyInfo}, a\n * currency id or ISO code (resolved via the catalogue), or null/undefined to use\n * the org's reporting currency. When no currency can be resolved it falls back to a\n * plain localized number so a render never throws or shows a wrong symbol.\n */\n format: (amount: number, currency?: CurrencySelector) => string;\n}\n\ninterface ReportingCurrencyResponse {\n readonly currency: CurrencyInfo | null;\n}\n\n/**\n * A FRESH empty catalogue per call - deliberately not a shared constant. Returning one\n * shared `Map` as a `ReadonlyMap` still exposes the mutating `Map` methods at runtime, so a\n * caller (or a stale closure) could mutate it and leak state across hook instances and\n * organisation switches. A new instance each time keeps the empty state truly inert.\n */\nconst emptyCatalogue = (): ReadonlyMap<string, CurrencyInfo> => new Map();\n\n/** `PromiseSettledResult.status` value for a resolved promise, avoiding a magic string. */\nconst SETTLED_FULFILLED = \"fulfilled\" as const;\n\n/**\n * Host-aware currency access for a plugin surface: the org's reporting currency, the\n * currency catalogue, and a `format` helper that renders through the pure\n * `@ethisyscore/core-utils/money` formatter honouring each currency's decimal-places\n * override.\n *\n * Fails open, like {@link useOrgFormatSync}: it resolves the transport via the OPTIONAL\n * accessor, so a surface rendered without a host transport (standalone/mock run, unit\n * test) degrades to an empty catalogue and a plain-number fallback rather than crashing.\n * Re-fetches when the active organisation changes.\n */\nexport function useCurrency(): UseCurrencyResult {\n const transport = useOptionalExtensionRuntimeTransport();\n const organisationId = useHostIdentity()?.organisationId ?? null;\n\n const [reportingCurrency, setReportingCurrency] = useState<CurrencyInfo | null>(null);\n const [currencies, setCurrencies] = useState<ReadonlyMap<string, CurrencyInfo>>(emptyCatalogue);\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n // Reset up front so an in-flight org switch never renders the previous org's\n // reporting currency, and a no-transport surface settles into the fallback.\n setReportingCurrency(null);\n setCurrencies(emptyCatalogue());\n setReady(false);\n\n if (!transport) {\n setReady(true);\n return;\n }\n\n const controller = new AbortController();\n\n Promise.allSettled([\n transport.invokeTool<Record<string, never>, ReportingCurrencyResponse>(\n REPORTING_CURRENCY_TOOL,\n {},\n controller.signal,\n ),\n transport.invokeTool<Record<string, never>, CurrencyInfo[]>(\n LIST_CURRENCIES_TOOL,\n {},\n controller.signal,\n ),\n ]).then(([reporting, list]) => {\n if (controller.signal.aborted) return;\n\n if (reporting.status === SETTLED_FULFILLED) {\n const resolved = reporting.value?.currency ?? null;\n setReportingCurrency(resolved);\n // NOTE: this hook does NOT stamp the org reporting-currency ref. The single\n // authoritative stamper is useReportingCurrencySync (auto-run by the page\n // definers). Stamping here too would leave a stale ref on the paths where\n // this hook fails open locally (rejected/no-transport/org switch) but never\n // re-stamps, diverging the pure formatMoney(amount) default from this hook.\n }\n if (list.status === SETTLED_FULFILLED && Array.isArray(list.value)) {\n setCurrencies(new Map(list.value.map((currency) => [currency.id, currency])));\n }\n setReady(true);\n });\n\n return () => controller.abort();\n }, [transport, organisationId]);\n\n const format = useCallback(\n (amount: number, selector?: CurrencySelector): string => {\n const currency = resolveCurrency(selector, currencies, reportingCurrency);\n\n if (currency === null) {\n // Nothing resolvable - render a plain localized number rather than guess a currency.\n return new Intl.NumberFormat(MONEY_FALLBACK_LOCALE).format(amount);\n }\n\n if (typeof currency === \"string\") {\n // A bare code the catalogue did not know - let Intl format it (or fall back internally).\n return formatMoney(amount, { currencyCode: currency });\n }\n\n return formatMoney(amount, {\n currencyCode: currency.code,\n decimalPlaces: currency.decimalPlaces,\n symbol: currency.symbol,\n });\n },\n [currencies, reportingCurrency],\n );\n\n return useMemo(\n () => ({ reportingCurrency, currencies, ready, format }),\n [reportingCurrency, currencies, ready, format],\n );\n}\n\n/**\n * Resolves a selector to a {@link CurrencyInfo}, a bare ISO code (string) when only a\n * code is known, or null when nothing is resolvable.\n */\nfunction resolveCurrency(\n selector: CurrencySelector,\n currencies: ReadonlyMap<string, CurrencyInfo>,\n reportingCurrency: CurrencyInfo | null,\n): CurrencyInfo | string | null {\n if (selector === null || selector === undefined) {\n return reportingCurrency;\n }\n\n if (typeof selector !== \"string\") {\n return selector;\n }\n\n // A string is either a currency id (catalogue key) or an ISO code.\n const byId = currencies.get(selector);\n if (byId) {\n return byId;\n }\n\n const upper = selector.toUpperCase();\n for (const currency of currencies.values()) {\n if (currency.code.toUpperCase() === upper) {\n return currency;\n }\n }\n\n // Unknown to the catalogue - treat it as a raw ISO code for Intl to try.\n return selector;\n}\n","import {\n createElement,\n useContext,\n useSyncExternalStore,\n type AnchorHTMLAttributes,\n type MouseEvent,\n type ReactNode,\n} 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\n// `setSearchParams` writes via history.replaceState, which fires no event — so subscribers\n// are told to re-read via this custom event. `popstate` covers browser back/forward.\nconst SEARCH_PARAMS_EVENT = \"ethisys:searchparams\";\n\nfunction subscribeToSearch(callback: () => void): () => void {\n if (typeof window === \"undefined\") {\n return () => {};\n }\n window.addEventListener(\"popstate\", callback);\n window.addEventListener(SEARCH_PARAMS_EVENT, callback);\n return () => {\n window.removeEventListener(\"popstate\", callback);\n window.removeEventListener(SEARCH_PARAMS_EVENT, callback);\n };\n}\n\nfunction getSearchSnapshot(): string {\n return typeof window !== \"undefined\" ? window.location.search : \"\";\n}\n\nfunction getServerSearchSnapshot(): string {\n return \"\";\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 // Reactive: subscribe to the URL's query string so inputs bound to a param and any\n // data derived from it re-render when it changes (previously read once per render with\n // no subscription, so setSearchParams updated the URL but nothing re-rendered).\n const search = useSyncExternalStore(subscribeToSearch, getSearchSnapshot, getServerSearchSnapshot);\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 // replaceState fires no event — notify subscribers so they re-read the query string.\n window.dispatchEvent(new Event(SEARCH_PARAMS_EVENT));\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 type { PluginOperationStateChanged } from \"./types\";\n\n/**\n * Narrows a raw push payload to a state change. The realtime source hands payloads over untyped,\n * so anything without an operation id and a numeric sequence is ignored rather than merged.\n */\nexport function asStateChanged(payload: unknown): PluginOperationStateChanged | null {\n if (payload == null || typeof payload !== \"object\") {\n return null;\n }\n\n const candidate = payload as Partial<PluginOperationStateChanged>;\n return typeof candidate.operationId === \"string\" && typeof candidate.eventSequence === \"number\"\n ? (candidate as PluginOperationStateChanged)\n : null;\n}\n","import type { PluginOperation, PluginOperationStateChanged } from \"./types\";\n\n/**\n * Applies a pushed state change to a cached operation. The push carries no result payload, so a\n * cached one is kept. An event that is not newer (by `eventSequence`) than the cached row is\n * ignored, so a late, out-of-order delivery can never move an operation backwards.\n */\nexport function mergeOperation(\n current: PluginOperation | null | undefined,\n event: PluginOperationStateChanged,\n): PluginOperation {\n if (current && current.eventSequence >= event.eventSequence) {\n return current;\n }\n\n // Drop the push-only fields; everything else is the same shape as a read.\n const { extensionId: _extensionId, previousStatus: _previousStatus, ...fields } = event;\n return { ...fields, resultPayloadJson: current?.resultPayloadJson ?? null };\n}\n\n/**\n * Applies a pushed state change to a cached list for one parent: replaces the row with the same\n * `operationId` when the event is newer, or prepends it when the list does not hold it yet.\n */\nexport function mergeIntoList(\n current: readonly PluginOperation[] | null | undefined,\n event: PluginOperationStateChanged,\n): PluginOperation[] {\n const rows = current ?? [];\n const index = rows.findIndex((r) => r.operationId === event.operationId);\n if (index < 0) {\n return [mergeOperation(null, event), ...rows];\n }\n\n const merged = mergeOperation(rows[index], event);\n if (merged === rows[index]) {\n return rows as PluginOperation[];\n }\n\n const next = rows.slice();\n next[index] = merged;\n return next;\n}\n","/** Query keys for the operation hooks. The plugin's own query client already isolates the realm. */\nexport const pluginOperationKeys = {\n all: [\"plugin-operations\"] as const,\n byId: (operationId: string) => [\"plugin-operations\", \"get\", operationId] as const,\n byParent: (parentEntityType: string, parentEntityId: string) =>\n [\"plugin-operations\", \"by-parent\", parentEntityType, parentEntityId] as const,\n latestTerminalByParent: (parentEntityType: string, parentEntityId: string) =>\n [\"plugin-operations\", \"latest-terminal\", parentEntityType, parentEntityId] as const,\n};\n","const TERMINAL = new Set([\"Succeeded\", \"Failed\", \"Cancelled\"]);\n\n/** Whether an operation has finished: succeeded, failed or cancelled. */\nexport function isTerminalOperationStatus(status: string | null | undefined): boolean {\n return status != null && TERMINAL.has(status);\n}\n","/**\n * A background operation as a plugin surface sees it through the host's `plugin-operations:*`\n * tools and the plugin-group push. `kind` and `parentEntityType` are the plugin's own LOCAL names:\n * the host strips its namespacing before anything reaches a surface.\n */\nexport interface PluginOperation {\n operationId: string;\n kind: string;\n status: PluginOperationStatus | string;\n currentStageCode?: string | null;\n currentStageLabel?: string | null;\n stageIndex?: number | null;\n currentStageOrdinal?: number | null;\n totalStages?: number | null;\n stageProgressCurrent?: number | null;\n stageProgressTotal?: number | null;\n message?: string | null;\n errorMessage?: string | null;\n errorCode?: string | null;\n parentEntityType?: string | null;\n parentEntityId?: string | null;\n retryAttempt?: number;\n startedUtc?: string | null;\n completedUtc?: string | null;\n lastUpdatedUtc?: string;\n estimatedCompletionUtc?: string | null;\n eventSequence: number;\n /** Only populated by `plugin-operations:get` (and so by {@link useBackgroundOperation}). */\n resultPayloadJson?: string | null;\n}\n\n/** The statuses the host records. Terminal ones are `Succeeded`, `Failed` and `Cancelled`. */\nexport type PluginOperationStatus =\n | \"Pending\"\n | \"Running\"\n | \"PausedRateLimited\"\n | \"Succeeded\"\n | \"Failed\"\n | \"Cancelled\";\n\n/** Payload of the `operations.plugin-operation.state-changed` push. */\nexport interface PluginOperationStateChanged extends Omit<PluginOperation, \"resultPayloadJson\"> {\n extensionId: string;\n previousStatus: string;\n}\n\n/** Type code of the host's plugin-group push for a plugin's own operation state changes. */\nexport const PLUGIN_OPERATION_STATE_CHANGED = \"operations.plugin-operation.state-changed\";\n\n/** The host tools the hooks read through. Declare the ones you use under `McpToolDependencies`. */\nexport const PLUGIN_OPERATION_TOOLS = {\n get: \"plugin-operations:get\",\n listByParent: \"plugin-operations:list-by-parent\",\n listActive: \"plugin-operations:list-active\",\n latestTerminalByParent: \"plugin-operations:latest-terminal-by-parent\",\n} as const;\n\n/** Poll cadence used ONLY when the host supplies no realtime source (matches the host web). */\nexport const OPERATION_FALLBACK_POLL_MS = 8_000;\n","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useMcpTool, usePluginRealtimeSource } from \"@ethisyscore/extension-runtime/plugin\";\nimport { usePluginRealtime } from \"../usePluginRealtime\";\nimport { asStateChanged } from \"./asStateChanged\";\nimport { mergeOperation } from \"./mergeOperation\";\nimport { pluginOperationKeys } from \"./queryKeys\";\nimport { isTerminalOperationStatus } from \"./status\";\nimport {\n OPERATION_FALLBACK_POLL_MS,\n PLUGIN_OPERATION_STATE_CHANGED,\n PLUGIN_OPERATION_TOOLS,\n type PluginOperation,\n} from \"./types\";\n\n/**\n * Follows one of this plugin's own background operations by id: its stage, progress, outcome and,\n * once it has finished, the result payload the plugin recorded.\n *\n * Reads through `plugin-operations:get` and then follows the host's push, merging each state change\n * by `eventSequence` so a late delivery cannot move the operation backwards. When the operation\n * reaches a terminal state it refetches once, because the push carries no result payload. Polls\n * every 8 s only when the host supplied no realtime source and the operation is still running.\n *\n * The data is `PluginOperation | null`; a missing operation, or one that belongs to another app,\n * surfaces as the query's error.\n */\nexport function useBackgroundOperation(operationId: string | null | undefined) {\n const queryClient = useQueryClient();\n const realtime = usePluginRealtimeSource();\n const { invoke } = useMcpTool<{ operationId: string }, PluginOperation | null>(PLUGIN_OPERATION_TOOLS.get);\n\n const query = useQuery({\n queryKey: pluginOperationKeys.byId(operationId ?? \"\"),\n enabled: Boolean(operationId),\n // Never undefined: TanStack Query rejects an undefined result.\n queryFn: async () => (await invoke({ operationId: operationId! })) ?? null,\n refetchInterval: (q) =>\n realtime === null && q.state.data != null && !isTerminalOperationStatus(q.state.data.status)\n ? OPERATION_FALLBACK_POLL_MS\n : false,\n });\n\n usePluginRealtime(PLUGIN_OPERATION_STATE_CHANGED, (payload) => {\n const event = asStateChanged(payload);\n if (!operationId || event?.operationId !== operationId) {\n return;\n }\n\n const key = pluginOperationKeys.byId(operationId);\n const before = queryClient.getQueryData<PluginOperation | null>(key);\n const merged = mergeOperation(before, event);\n queryClient.setQueryData(key, merged);\n\n if (merged !== before && isTerminalOperationStatus(merged.status)) {\n void queryClient.invalidateQueries({ queryKey: key });\n }\n });\n\n return query;\n}\n","const KIND_SCOPE = /^p:[0-9a-f]{32}:/i;\nconst PARENT_SCOPE = /^po:[0-9A-F]{32}:/i;\n\n/**\n * Matches an operation's kind against the plugin's own (unscoped) kind. Mirrors the .NET\n * `HasKind` / `BackgroundOperationScope.Matches` rule (analyser ECP0016): the host-namespaced\n * form `p:{extensionId}:` is stripped before comparing, so the check is right whether the value\n * came from the de-namespacing `plugin-operations:*` tools or anywhere else. Never compare kinds\n * with `===`.\n */\nexport function hasKind(operation: { kind: string } | string | null | undefined, unscopedKind: string): boolean {\n const kind = typeof operation === \"string\" ? operation : operation?.kind;\n return kind != null && kind.replace(KIND_SCOPE, \"\") === unscopedKind;\n}\n\n/** The parent-type counterpart of {@link hasKind}; strips `po:{scopeHash}:` before comparing. */\nexport function hasParentEntityType(\n operation: { parentEntityType?: string | null } | string | null | undefined,\n unscopedParentEntityType: string,\n): boolean {\n const parentType = typeof operation === \"string\" ? operation : operation?.parentEntityType;\n return parentType != null && parentType.replace(PARENT_SCOPE, \"\") === unscopedParentEntityType;\n}\n","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useMcpTool, usePluginRealtimeSource } from \"@ethisyscore/extension-runtime/plugin\";\nimport { usePluginRealtime } from \"../usePluginRealtime\";\nimport { asStateChanged } from \"./asStateChanged\";\nimport { hasParentEntityType } from \"./hasKind\";\nimport { mergeIntoList } from \"./mergeOperation\";\nimport { pluginOperationKeys } from \"./queryKeys\";\nimport { isTerminalOperationStatus } from \"./status\";\nimport {\n OPERATION_FALLBACK_POLL_MS,\n PLUGIN_OPERATION_STATE_CHANGED,\n PLUGIN_OPERATION_TOOLS,\n type PluginOperation,\n} from \"./types\";\n\ninterface ByParentArgs {\n parentEntityType: string;\n parentEntityId: string;\n}\n\n/**\n * Follows this plugin's own background operations against one parent entity (for example every\n * import of one repository), newest first, at most 50.\n *\n * `parentEntityType` is the plugin's LOCAL type name, the one it passed when starting the\n * operation. Reads through `plugin-operations:list-by-parent`, then merges each pushed state change\n * for this parent by `eventSequence`, prepending operations that start after the first read. Polls\n * every 8 s only when the host supplied no realtime source and some operation is still running.\n */\nexport function useOperationsByParent(parentEntityType: string, parentEntityId: string | null | undefined) {\n const queryClient = useQueryClient();\n const realtime = usePluginRealtimeSource();\n const { invoke } = useMcpTool<ByParentArgs, { items?: PluginOperation[] } | null>(\n PLUGIN_OPERATION_TOOLS.listByParent,\n );\n\n const query = useQuery({\n queryKey: pluginOperationKeys.byParent(parentEntityType, parentEntityId ?? \"\"),\n enabled: Boolean(parentEntityId),\n queryFn: async () =>\n (await invoke({ parentEntityType, parentEntityId: parentEntityId! }))?.items ?? [],\n refetchInterval: (q) =>\n realtime === null && (q.state.data ?? []).some((op) => !isTerminalOperationStatus(op.status))\n ? OPERATION_FALLBACK_POLL_MS\n : false,\n });\n\n usePluginRealtime(PLUGIN_OPERATION_STATE_CHANGED, (payload) => {\n const event = asStateChanged(payload);\n if (\n !parentEntityId\n || event == null\n || event.parentEntityId !== parentEntityId\n || !hasParentEntityType(event, parentEntityType)\n ) {\n return;\n }\n\n queryClient.setQueryData<PluginOperation[]>(\n pluginOperationKeys.byParent(parentEntityType, parentEntityId),\n (current) => mergeIntoList(current, event),\n );\n });\n\n return query;\n}\n","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useMcpTool } from \"@ethisyscore/extension-runtime/plugin\";\nimport { usePluginRealtime } from \"../usePluginRealtime\";\nimport { asStateChanged } from \"./asStateChanged\";\nimport { hasParentEntityType } from \"./hasKind\";\nimport { pluginOperationKeys } from \"./queryKeys\";\nimport { isTerminalOperationStatus } from \"./status\";\nimport { PLUGIN_OPERATION_STATE_CHANGED, PLUGIN_OPERATION_TOOLS, type PluginOperation } from \"./types\";\n\ninterface ByParentArgs {\n parentEntityType: string;\n parentEntityId: string;\n}\n\n/**\n * The newest FINISHED operation (succeeded, failed or cancelled) of this plugin against one parent\n * entity, or null: the \"last import failed at 10:42\" line on a detail page.\n *\n * Reads through `plugin-operations:latest-terminal-by-parent` and refetches when a pushed state\n * change for this parent reaches a terminal status. A finished row cannot change, so there is no\n * fallback poll; without a realtime source the value refreshes on the query's own refetches.\n */\nexport function useLatestTerminalOperationByParent(\n parentEntityType: string,\n parentEntityId: string | null | undefined,\n) {\n const queryClient = useQueryClient();\n const { invoke } = useMcpTool<ByParentArgs, { item?: PluginOperation | null } | null>(\n PLUGIN_OPERATION_TOOLS.latestTerminalByParent,\n );\n\n const key = pluginOperationKeys.latestTerminalByParent(parentEntityType, parentEntityId ?? \"\");\n\n const query = useQuery({\n queryKey: key,\n enabled: Boolean(parentEntityId),\n queryFn: async () =>\n (await invoke({ parentEntityType, parentEntityId: parentEntityId! }))?.item ?? null,\n });\n\n usePluginRealtime(PLUGIN_OPERATION_STATE_CHANGED, (payload) => {\n const event = asStateChanged(payload);\n if (\n parentEntityId\n && event != null\n && event.parentEntityId === parentEntityId\n && hasParentEntityType(event, parentEntityType)\n && isTerminalOperationStatus(event.status)\n ) {\n void queryClient.invalidateQueries({ queryKey: key });\n }\n });\n\n return query;\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 { QueryClientProvider, type QueryClient } from \"@tanstack/react-query\";\nimport { useSurfaceQueryClient } from \"./hostQueryClient\";\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\";\nimport { PluginPortalScope } from \"./pluginPortalScope\";\nimport { useOrgFormatSync } from \"./useOrgFormatSync\";\nimport { useReportingCurrencySync } from \"./useReportingCurrencySync\";\n\nexport interface DefinePlatformReactPluginOverlayOptions {\n rootClassName: string;\n styleId: string;\n css: string;\n manifest?: Record<string, Partial<Record<string, ViewComponent>>>;\n /**\n * Override the `QueryClient` this overlay runs on.\n *\n * Left unset, the overlay runs on the host's client when the host realm\n * provides one, and on a per-bundle client of its own when it does not — see\n * {@link useSurfaceQueryClient}. Setting it opts out of both.\n */\n queryClient?: QueryClient;\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 = {}, queryClient: queryClientOverride, wrapOverlay } = options;\n return function MountedOverlay(props: PlatformReactOverlayProps) {\n // The host's client when the host realm provides one, this overlay bundle's\n // own when it does not, and `queryClientOverride` ahead of both. See\n // definePlatformReactPluginPage for why this is resolved per render.\n const queryClient = useSurfaceQueryClient(queryClientOverride);\n // Stamp this bundle's org date/time format refs so an overlay's DateInputs and\n // read-only formatters honour the org format too (not just pages). Same\n // once-per-mount, fail-open sync as definePlatformReactPluginPage.\n useOrgFormatSync();\n // Stamp this bundle's org reporting-currency ref so an overlay's formatMoney\n // renders reporting-currency amounts in the org's currency too (not just pages).\n // Same once-per-mount, fail-open-to-GBP sync as definePlatformReactPluginPage.\n useReportingCurrencySync();\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 // react-query provider. Normally re-providing the host's own client, which\n // is a no-op for the subtree and deliberate: the tree shape stays identical\n // whichever client `useSurfaceQueryClient` returned, so the fallback path is\n // not a second code path through the renderer.\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 // Overlay surfaces open their own dialogs and menus, so they need the same\n // portal containment as pages (see definePlatformReactPluginPage).\n createElement(PluginPortalScope, null, createElement(Overlay, props)),\n ),\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 HostSidebarActiveNav,\n type HostSidebarContribution,\n type HostSidebarNavItem,\n} from \"./hostSidebarActionsContext\";\nimport { SurfaceBaseContext, deriveHostEntityToken } 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.\n *\n * `entityToken` is REQUIRED here — this is the published shape existing consumers narrow\n * against (`input.entityToken` is a `string` after a mode check), unchanged. To let the\n * hook derive the token instead, OMIT the property via {@link HostSidebarDerivedInput}. */\nexport type HostSidebarInput =\n | ({ mode: \"none\" } & HostSidebarActiveNav)\n | ({ mode: \"actions\"; entityToken: string; actions: HostSidebarAction[] } & HostSidebarActiveNav)\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 } & HostSidebarActiveNav);\n\n/** Token-less authoring variants: the hook derives the host-mirrored entity token from the\n * surface base + current location (`deriveHostEntityToken`) — correct by construction, since\n * the host's token guard silently drops a mismatching publish. Prefer these unless you need\n * the bare-entity-id form (which the host segment-matches across an entity's sub-pages).\n * `entityToken?: undefined` (not just absence) is deliberate: an EXPLICIT `undefined`\n * (e.g. `entityToken: maybeToken` with an unresolved token) keeps the long-standing\n * \"unresolved token → contribute nothing\" semantic and is treated as `mode:\"none\"`, NOT\n * derived — only a truly ABSENT property derives. */\nexport type HostSidebarDerivedInput =\n | ({ mode: \"actions\"; entityToken?: undefined; actions: HostSidebarAction[] } & HostSidebarActiveNav)\n | ({\n mode: \"detail\";\n entityToken?: undefined;\n title: string;\n navSectionLabel: string;\n navItems: HostSidebarNavItem[];\n backHref: string;\n backLabel: string;\n actions: HostSidebarAction[];\n } & HostSidebarActiveNav);\n\n/** What `useHostSidebarActions` accepts: the classic explicit-token shape or a derived-token variant. */\nexport type HostSidebarHookInput = HostSidebarInput | HostSidebarDerivedInput;\n\n/** Input with the entity token resolved (supplied or derived) — the only shape that crosses\n * to the host, whose contribution contract requires the token. */\ntype ResolvedHostSidebarInput = HostSidebarInput;\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: ResolvedHostSidebarInput): HostSidebarContribution {\n // `activeNavHref` is spread only when the plugin declared one, so a contribution from a plugin\n // that says nothing is byte-identical to the pre-2.2.0 shape.\n const activeNav: HostSidebarActiveNav = input.activeNavHref ? { activeNavHref: input.activeNavHref } : {};\n if (input.mode === SIDEBAR_MODE.none) {\n return { mode: SIDEBAR_MODE.none, ...activeNav };\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 ...activeNav,\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 ...activeNav,\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: ResolvedHostSidebarInput, hostToken: string): string {\n // The declaration is part of the visible result, so it belongs in the key: without it a\n // navigation that changes ONLY the declared item would not republish and the highlight would\n // stay on the previous page's entry.\n const activeNav = input.activeNavHref ?? \"\";\n if (input.mode === SIDEBAR_MODE.none) {\n // A DECLARING `mode:\"none\"` also keys on the host's entity token, which the other two modes\n // already carry through `input.entityToken`. The host clears its whole sidebar state\n // synchronously whenever that token changes (`setCurrentToken`), and it must: one mount\n // instance is reused across page ids, so a declaration that survived the clear would light\n // the previous PAGE's entry on the next one. But the plugin has no reason to re-send a\n // declaration that has not changed, so without the token here the key stayed constant across\n // an entity change, no republish happened, and the section went dark on a sibling entity\n // (e.g. `…/fix-detail/1` -> `…/fix-detail/2`). Keying on it makes the plugin republish\n // exactly when the host discards.\n //\n // Included ONLY when there IS a declaration: an undeclared `mode:\"none\"` keeps its constant\n // key and so republishes on nothing, exactly as before. That matters — its publish is a\n // `setState(EMPTY)` that notifies every subscriber, so keying it on the location would add a\n // host re-render to every navigation of every plugin that declares nothing.\n return JSON.stringify(activeNav ? [SIDEBAR_MODE.none, activeNav, hostToken] : [SIDEBAR_MODE.none, activeNav]);\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, activeNav]);\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 activeNav,\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: HostSidebarHookInput): void {\n const api = useContext(HostSidebarActionsContext);\n const surfaceBase = useContext(SurfaceBaseContext);\n const pageId = surfaceBase?.pageId; // diagnostic tag (per-surface)\n const compatible: HostSidebarActionsApi | null =\n api && isHostChromeCompatible(api.capabilities?.contractVersion) ? api : null;\n\n // Resolve the entity token. Three cases, in order:\n // 1. Explicit string token → publish as-is (the pre-existing behaviour, unchanged).\n // 2. Property PRESENT but undefined (`entityToken: maybeToken` with an unresolved\n // token) → mode:\"none\". This preserves the long-standing \"unresolved token →\n // contribute nothing\" gate (`toHostSidebarInput` has always mapped an undefined\n // token to none) — deriving here would surface actions for an entity the page\n // has not resolved yet.\n // 3. Property ABSENT → derive the host-mirrored token from the surface base +\n // current location, so the host's (silent) token guard can never drop the\n // publish over a token-shape mismatch. The derived token re-computes on every\n // render; the content key below re-publishes when it changes (a location change\n // also makes the HOST clear its state synchronously, so a republish is required\n // with explicit tokens too — this path is no less reactive than they are).\n const resolved: ResolvedHostSidebarInput =\n input.mode === SIDEBAR_MODE.none\n ? input\n : typeof input.entityToken === \"string\"\n ? (input as ResolvedHostSidebarInput)\n : \"entityToken\" in input\n ? // An unresolved entity token suppresses the CONTRIBUTION, not the page's identity:\n // the sidebar entry the page belongs to is known before its entity has loaded, so the\n // declaration (when there is one) is carried through rather than dropped, and the\n // section stays lit while the detail data is in flight.\n { mode: SIDEBAR_MODE.none, ...(input.activeNavHref ? { activeNavHref: input.activeNavHref } : {}) }\n : {\n ...input,\n entityToken: deriveHostEntityToken(\n typeof window !== \"undefined\" ? window.location.pathname : \"\",\n surfaceBase?.basePath,\n ),\n };\n\n // Ref-map refreshed every render → dispatch always hits the latest handler (recursing groups).\n // Built from RESOLVED (not raw input): when resolution collapses to mode:\"none\" (explicit\n // undefined token), the handlers must be empty too — a stale host dispatch arriving before\n // the none-publish clears the sidebar must not invoke an unresolved-entity action.\n const actions = resolved.mode === SIDEBAR_MODE.none ? [] : resolved.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, plus — for a\n // DECLARING `mode:\"none\"` only — the host-mirrored entity token, which that mode has no\n // `entityToken` field to carry. Derived the same way the token-less authoring variants above\n // derive theirs, and re-read every render: the host mount re-renders on every location change\n // (it reads `useLocation`), so this sees the current path whenever the host's own clear fires.\n const key = contributionKey(\n resolved,\n deriveHostEntityToken(\n typeof window !== \"undefined\" ? window.location.pathname : \"\",\n surfaceBase?.basePath,\n ),\n );\n useEffect(() => {\n if (!compatible) return;\n compatible.publish(toContribution(resolved));\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, no `.`/`..`\n * path segments (the host's traversal guard rejects them after canonicalisation).\n * A 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 !slugRelativeHref\n .split(\"?\")[0]\n .split(\"/\")\n .some((seg) => seg === \".\" || seg === \"..\")\n );\n}\n\n/** The subset of {@link ToHostSidebarInputOptions} the action mapper needs — lets other\n * input builders (e.g. `menuConfigToHostSidebarInput`) reuse the SAME mapping rules\n * (slug-relative confinement, group recursion, tree-global id dedupe, dispatch wiring). */\nexport type ToHostActionsOptions = Pick<ToHostSidebarInputOptions, \"basePath\" | \"onDispatch\">;\n\nexport function toHostActions(\n actions: PluginDetailContextItem[],\n opts: ToHostActionsOptions,\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: ToHostActionsOptions,\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","import type { HeaderAction, HeaderMenuItem } from \"../components/layout/pageHeaderTypes\";\nimport type { PluginDetailContextItem } from \"../components/layout/pageHeaderTypes\";\nimport type { HostSidebarHookInput } from \"./useHostSidebarActions\";\nimport { toHostActions } from \"./toHostSidebarInput\";\n\n/**\n * Options for {@link menuConfigToHostSidebarInput}.\n */\nexport interface MenuConfigHostSidebarOptions {\n /** The CURRENT pathname (full host path, e.g. `/extensions/projects/roles`). */\n pathname: string;\n /**\n * Dispatch bridge for `actionId` actions — pass the plugin's\n * `useTriggerSidebarAction()` (from `@ethisyscore/plugin-ui/components/layout`)\n * so a sidebar click reaches the same `useSidebarActions` handler the header\n * uses. (A parameter rather than an internal hook call: the sidebar-action\n * context must come from the SAME `components/layout` module instance the\n * plugin's provider uses — the cross-entry singleton pitfall.)\n */\n onDispatch: (actionId: string) => void;\n /**\n * The plugin's mount base (e.g. `/extensions/projects`), stripped from action\n * hrefs to make them slug-relative for the host confiner. Defaults to the\n * `/extensions/<slug>` prefix of `pathname`.\n */\n mountBase?: string;\n}\n\n/** Strip a trailing slash (except root) — same normalisation as `PageHeaderProvider`. */\nfunction normalise(pathname: string): string {\n return pathname.length > 1 && pathname.endsWith(\"/\") ? pathname.slice(0, -1) : pathname;\n}\n\n/** Pre-order search for the item whose `href` exactly matches — mirrors the provider's `findByHref`. */\nfunction findByHref(items: HeaderMenuItem[], pathname: string): HeaderMenuItem | null {\n for (const item of items) {\n if (item.href != null && normalise(item.href) === pathname) {\n return item;\n }\n if (item.children) {\n const nested = findByHref(item.children, pathname);\n if (nested) {\n return nested;\n }\n }\n }\n return null;\n}\n\n/** Adapt a menu-config `HeaderAction` to the detail-context item shape `toHostActions`\n * maps (the shapes differ only in the `variant` vocabulary). */\nfunction toItem(a: HeaderAction): PluginDetailContextItem {\n return {\n id: a.id,\n label: a.label,\n icon: a.icon,\n href: a.href,\n actionId: a.actionId,\n onClick: a.onClick,\n variant: a.variant === \"danger\" ? \"danger\" : undefined,\n slot: a.slot,\n disabled: a.disabled,\n children: a.children?.map(toItem),\n };\n}\n\n/**\n * Map the ACTIVE menu item's `actions` to a `useHostSidebarActions` input — the\n * runtime analogue of the monolith shell surfacing the active `menuConfig` item's\n * actions as the sidebar \"Quick actions\" section. Detail routes are covered by\n * `toHostSidebarInput` over a `*DetailContext`; THIS covers the menu-item (list)\n * routes, which otherwise contribute nothing and render an empty Quick Actions\n * panel (static manifest `NavigationActionItem`s can only navigate — they cannot\n * cover dialog/`actionId` CTAs at all).\n *\n * Typical bridge shape:\n * ```ts\n * const input = detailCtx\n * ? toHostSidebarInput(detailCtx, { basePath, entityToken, onDispatch })\n * : menuConfigToHostSidebarInput(menuConfig, { pathname, onDispatch });\n * useHostSidebarActions(input);\n * ```\n *\n * The result carries NO `entityToken` — `useHostSidebarActions` derives the\n * host-mirrored token from the surface base, which is correct by construction.\n * Routes without a matching menu item (or with no mappable actions) yield\n * `{mode:\"none\"}`, preserving the static-manifest fallback.\n */\nexport function menuConfigToHostSidebarInput(\n config: HeaderMenuItem[],\n opts: MenuConfigHostSidebarOptions,\n): HostSidebarHookInput {\n const item = findByHref(config, normalise(opts.pathname));\n if (!item?.actions?.length) {\n return { mode: \"none\" };\n }\n const mountBase = opts.mountBase ?? /^\\/extensions\\/[^/]+/.exec(opts.pathname)?.[0];\n if (!mountBase) {\n return { mode: \"none\" };\n }\n const actions = toHostActions(item.actions.map(toItem), {\n basePath: mountBase,\n onDispatch: opts.onDispatch,\n });\n if (actions.length === 0) {\n return { mode: \"none\" };\n }\n return { mode: \"actions\", actions };\n}\n","import type { Breadcrumb, RouteMeta, RouteMetaEntry } from \"./pageHeaderTypes\";\n\n/** Synthesised root crumb — mirrors the monolith's virtual `{ pattern: \"/\", label: \"Home\" }`. */\nconst HOME_CRUMB: Breadcrumb = { label: \"Home\", href: \"/\" };\n\nfunction stripTrailingSlash(value: string): string {\n return value.length > 1 && value.endsWith(\"/\") ? value.slice(0, -1) : value;\n}\n\n/**\n * Orders routes most-specific first: more segments win, then at equal depth prefer\n * literal segments over `:param` ones — identical to the monolith's `SORTED_ROUTE_META`.\n */\nexport function sortBySpecificity(routeMeta: RouteMeta): RouteMetaEntry[] {\n return [...routeMeta].sort((a, b) => {\n const aDepth = a.pattern.split(\"/\").length;\n const bDepth = b.pattern.split(\"/\").length;\n if (bDepth !== aDepth) {\n return bDepth - aDepth;\n }\n const aDynamic = (a.pattern.match(/:/g) ?? []).length;\n const bDynamic = (b.pattern.match(/:/g) ?? []).length;\n return aDynamic - bDynamic;\n });\n}\n\n/**\n * Matches a pathname against the sorted routes segment-by-segment, capturing\n * `:param` values. Returns the first (most-specific) match, or `null`. Port of the\n * monolith's `matchRoute`.\n */\nexport function matchRoute(\n pathname: string,\n sorted: RouteMetaEntry[],\n): { entry: RouteMetaEntry; params: Record<string, string> } | null {\n const pathSegments = stripTrailingSlash(pathname).split(\"/\");\n for (const entry of sorted) {\n const patternSegments = entry.pattern.split(\"/\");\n if (patternSegments.length !== pathSegments.length) {\n continue;\n }\n const params: Record<string, string> = {};\n let matched = true;\n for (let i = 0; i < patternSegments.length; i++) {\n const pSeg = patternSegments[i];\n const vSeg = pathSegments[i];\n if (pSeg.startsWith(\":\")) {\n params[pSeg.slice(1)] = vSeg;\n } else if (pSeg !== vSeg) {\n matched = false;\n break;\n }\n }\n if (matched) {\n return { entry, params };\n }\n }\n return null;\n}\n\n/**\n * Replaces `:param` segments in a pattern with captured values. Port of\n * `resolvePattern`. The param-name class matches the identifiers `matchRoute`\n * captures (letters/digits/underscore, e.g. `:user_id`, `:id2`); when a value is\n * missing the original `:param` token is preserved rather than dropping the colon.\n */\nfunction resolvePattern(pattern: string, params: Record<string, string>): string {\n return pattern.replace(/:([A-Za-z0-9_]+)/g, (_, name: string) => params[name] ?? `:${name}`);\n}\n\n/**\n * Builds the `Home / … / <page>` breadcrumb trail — a faithful port of the\n * monolith's `buildBreadcrumbs`. Matches the current path against the `routeMeta`\n * patterns (most-specific wins), walks the `parent` chain, prepends Home, and maps\n * each ancestor to its resolved-pattern href (the current page gets no href). The\n * app/module crumb (e.g. \"Finance\") is just the top-level `routeMeta` entry — no\n * separate config, exactly like the monolith's `/finance` root entry.\n */\nexport function buildBreadcrumbs(pathname: string, routeMeta: RouteMeta): Breadcrumb[] {\n const match = matchRoute(pathname, sortBySpecificity(routeMeta));\n if (match == null) {\n return [HOME_CRUMB];\n }\n\n const { entry: current, params } = match;\n\n // Walk the parent chain (leaf → root), guarding against cycles.\n const chain: RouteMetaEntry[] = [current];\n const seen = new Set<string>([current.pattern]);\n let node = current;\n while (node.parent != null) {\n const parent = routeMeta.find((e) => e.pattern === node.parent);\n if (parent == null || seen.has(parent.pattern)) {\n break;\n }\n seen.add(parent.pattern);\n chain.unshift(parent);\n node = parent;\n }\n\n const crumbs: Breadcrumb[] = [HOME_CRUMB];\n chain.forEach((entry, index) => {\n const isLast = index === chain.length - 1;\n crumbs.push(isLast ? { label: entry.label } : { label: entry.label, href: resolvePattern(entry.pattern, params) });\n });\n return crumbs;\n}\n","import { matchRoute, sortBySpecificity } from \"../components/layout/breadcrumbs\";\nimport type { RouteMeta, RouteMetaEntry } from \"../components/layout/pageHeaderTypes\";\nimport type { HostSidebarHookInput } from \"./useHostSidebarActions\";\n\n/**\n * Options for {@link resolveActiveNavHref}.\n */\nexport interface ResolveActiveNavHrefOptions {\n /**\n * The plugin's mount base (e.g. `/extensions/projects`), stripped to make the result\n * slug-relative for the host confiner. Defaults to the mount prefix of the pathname — either\n * `/extensions/<slug>` or `/super-admin/extensions/<slug>`, the two roots the host mounts a\n * plugin surface under. Prefer passing the host-supplied `SurfaceBaseContext.basePath`\n * (resolved via `resolveSurfaceBase`) when the plugin has it: that is authoritative for the\n * page actually rendering, where this default is inferred from the URL.\n */\n basePath?: string;\n}\n\n/**\n * The plugin mount roots, longest first.\n *\n * `/super-admin/extensions/<slug>` has to be tried BEFORE `/extensions/<slug>`: the super-admin\n * URL contains the substring `/extensions/`, so an unanchored tenant-first match would slice the\n * base in the wrong place. Anchoring both at `^` and ordering longest-first makes that\n * impossible. Without the super-admin entry the helper simply returned `undefined` for every\n * super-admin surface, so its declaration never reached the host at all.\n */\nconst MOUNT_BASE_PATTERN = /^(?:\\/super-admin)?\\/extensions\\/[^/]+/;\n\n/** A pattern with no `:param` segments resolves to exactly one concrete URL, so it can name a\n * nav item. A parameterised one cannot: it stands for a family of URLs, not a menu entry. */\nfunction isConcrete(pattern: string): boolean {\n return !pattern.split(\"/\").some((seg) => seg.startsWith(\":\"));\n}\n\n/**\n * Derive the sidebar entry that owns the current route, as a slug-relative href for\n * `activeNavHref` on a `useHostSidebarActions` input.\n *\n * The source of truth is the plugin's own `routeMeta` — the `{ pattern, label, parent }` chain it\n * already declares for breadcrumbs. That chain IS the plugin's information architecture: it\n * records that `/extensions/projects/people/:id` belongs under `/extensions/projects/settings`,\n * which no amount of URL inspection can recover, because the two share no path. So this walks the\n * matched route up its `parent` chain and returns the nearest CONCRETE (param-free) ancestor:\n *\n * - `/extensions/projects/people/abc` → `settings` (via parent `/extensions/projects/settings`)\n * - `/extensions/tech/technologies/7/edit` → `technologies` (two parents up, past `…/:id`)\n * - `/extensions/projects/all` → `all` (the route is its own nav item)\n *\n * It serves both mounts: a plugin surface is mounted at `/extensions/<slug>` for tenant/admin\n * audiences and at `/super-admin/extensions/<slug>` for super-admin ones, and the base is\n * stripped from whichever the pathname is under. That matters most at the super-admin mount,\n * where the host's URL election has no detail-context path to fall back on at all.\n *\n * Returns `undefined` — meaning \"no opinion, let the host's URL election decide\" — when the path\n * matches no route, when no ancestor is concrete, or when the answer is the plugin ROOT. The root\n * is deliberately not declarable: its href prefixes every URL in the plugin, so the URL election\n * already picks it whenever nothing deeper matches, and there is no case where declaring it\n * changes an outcome.\n *\n * The result is a HINT. The host validates it against the nav items it is actually rendering and\n * falls back to its URL election if it names nothing, so a stale or mistaken `routeMeta` entry\n * degrades to today's behaviour rather than blanking the sidebar.\n */\nexport function resolveActiveNavHref(\n pathname: string,\n routeMeta: RouteMeta,\n opts: ResolveActiveNavHrefOptions = {},\n): string | undefined {\n const base = opts.basePath ?? MOUNT_BASE_PATTERN.exec(pathname)?.[0];\n if (!base) return undefined;\n const mountBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n\n const match = matchRoute(pathname, sortBySpecificity(routeMeta));\n if (match == null) return undefined;\n\n // Walk leaf → root, guarding against a cyclic `parent` chain exactly as `buildBreadcrumbs` does.\n const seen = new Set<string>([match.entry.pattern]);\n let node: RouteMetaEntry | undefined = match.entry;\n while (node != null) {\n if (isConcrete(node.pattern)) {\n const owner = node.pattern.replace(/\\/$/, \"\");\n if (owner === mountBase) return undefined; // the plugin root — nothing to declare\n const prefix = `${mountBase}/`;\n return owner.startsWith(prefix) ? owner.slice(prefix.length) : undefined;\n }\n const parentPattern: string | undefined = node.parent;\n if (parentPattern == null || seen.has(parentPattern)) return undefined;\n seen.add(parentPattern);\n node = routeMeta.find((e) => e.pattern === parentPattern);\n }\n return undefined;\n}\n\n/**\n * Attach an active-nav declaration to an existing `useHostSidebarActions` input.\n *\n * The input union is discriminated on `mode`, so a plugin cannot just spread the field on without\n * re-narrowing; this does it once, correctly, for every mode — including `mode:\"none\"`, which is\n * what the input builders return on an ordinary list route and is exactly where the declaration\n * matters most. Passing `undefined` returns the input untouched, so the call site needs no branch.\n *\n * ```ts\n * const input = withActiveNavHref(\n * menuConfigToHostSidebarInput(menuConfig, { pathname, onDispatch }),\n * resolveActiveNavHref(pathname, routeMeta),\n * );\n * useHostSidebarActions(input);\n * ```\n */\nexport function withActiveNavHref(\n input: HostSidebarHookInput,\n activeNavHref: string | undefined,\n): HostSidebarHookInput {\n if (!activeNavHref) return input;\n return { ...input, activeNavHref };\n}\n"]}
1
+ {"version":3,"sources":["../../src/components/shared/toast.ts","../../src/platform-react/pluginQueryClient.ts","../../src/platform-react/hostQueryClient.ts","../../src/platform-react/templateContext.ts","../../src/platform-react/injectStyles.ts","../../src/platform-react/surfaceUrl.ts","../../src/platform-react/hostSidebarActionsContext.ts","../../src/platform-react/pluginPortalScope.tsx","../../src/platform-react/useOrgFormatSync.ts","../../src/platform-react/useReportingCurrencySync.ts","../../src/platform-react/definePlatformReactPluginPage.tsx","../../src/platform-react/MissingViewFallback.tsx","../../src/platform-react/useView.ts","../../src/platform-react/mcpService.ts","../../src/platform-react/typedMcpService.ts","../../src/platform-react/useAuthenticatedQuery.ts","../../src/platform-react/useCurrency.ts","../../src/platform-react/reactRouterShim.tsx","../../src/platform-react/usePluginRealtime.ts","../../src/platform-react/operations/asStateChanged.ts","../../src/platform-react/operations/mergeOperation.ts","../../src/platform-react/operations/queryKeys.ts","../../src/platform-react/operations/status.ts","../../src/platform-react/operations/types.ts","../../src/platform-react/operations/useBackgroundOperation.ts","../../src/platform-react/operations/hasKind.ts","../../src/platform-react/operations/useOperationsByParent.ts","../../src/platform-react/operations/useLatestTerminalOperationByParent.ts","../../src/platform-react/useMcpUpload.ts","../../src/platform-react/useUploadToStorage.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","../../src/platform-react/menuConfigHostSidebar.ts","../../src/components/layout/breadcrumbs.ts","../../src/platform-react/activeNavHref.ts"],"names":["QueryClient","QueryCache","MutationCache","reactQuery","createContext","useContext","useEffect","useHostIdentity","useState","useTheme","useMemo","jsxs","ThemeProvider","jsx","useOptionalExtensionRuntimeTransport","extensionRuntime","setOrgFormats","warnedAboutMissingAccessor","extensionRuntime2","useRef","setOrgReportingCurrency","createElement","QueryClientProvider","definePlatformReactPage","useView","useMcpTool","useQuery","useQueries","REPORTING_CURRENCY_TOOL","useCallback","MONEY_FALLBACK_LOCALE","formatMoney","emitNavigation","useSyncExternalStore","usePluginRealtimeSource","useQueryClient","useExtensionRuntimeTransport","uploadFileToPluginStorage","toPluginStorageUploadError","stripTrailingSlash"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Host toast bridge — the ergonomic surface a lifted monolith `useToast()`\n * expects, shipped alongside the other shared composites so a plugin's\n * `components/shared/*` (and its `use-toast` alias) resolve here.\n *\n * PlatformReact pages can't import the host's toaster (it's an app-global\n * singleton, not in the host module registry), so these dispatch the\n * `ethisys:toast` CustomEvent the host's toast listener (`useExtensionToast`)\n * forwards to its toaster — the same wire contract as the lower-level\n * `@ethisyscore/components-react` `showToast` primitive (kept self-contained\n * here to avoid a load-bearing cross-package import for a one-line dispatch).\n * Outside a host that wired the listener (mock/dev), every call is a harmless\n * no-op, so a plugin keeps its lifted `showSuccessToast(...)` call sites.\n */\ntype ToastVariant = \"default\" | \"success\" | \"error\" | \"warning\";\n\nfunction dispatchToast(title: string, description: string | undefined, variant: ToastVariant): void {\n if (typeof window === \"undefined\" || title.length === 0) {\n return;\n }\n window.dispatchEvent(new CustomEvent(\"ethisys:toast\", { detail: { title, description, variant } }));\n}\n\nexport function showSuccessToast(title: string, description?: string): void {\n dispatchToast(title, description, \"success\");\n}\n\nexport function showErrorToast(title: string, description?: string): void {\n dispatchToast(title, description, \"error\");\n}\n\nexport function showWarningToast(title: string, description?: string): void {\n dispatchToast(title, description, \"warning\");\n}\n\n/**\n * Options accepted by the `toast(...)` returned from {@link useToast}. Both `title`\n * and `description` are optional, but at least one must be set — `toast({ description })`\n * shows the description as the message (mirroring the monolith); `toast({})` is a no-op.\n */\nexport interface ToastOptions {\n /** Primary line. Falls back to {@link ToastOptions.description} when absent/empty. */\n title?: string;\n /** Secondary line — or the message itself when no `title` is given. */\n description?: string;\n /** `\"destructive\"`/`\"error\"` → error; `\"warning\"`/`\"success\"` map directly; else default. */\n variant?: string;\n}\n\n/**\n * Hook mirror of the monolith's `useToast()` surface, so a lifted\n * `const { toast } = useToast()` (and `showSuccessToast`/`showErrorToast`) keeps\n * working unchanged once `@/hooks/common/utility/use-toast` is aliased here.\n */\nexport function useToast() {\n return {\n toast: (opts: ToastOptions): void => {\n const variant: ToastVariant =\n opts.variant === \"destructive\" || opts.variant === \"error\"\n ? \"error\"\n : opts.variant === \"warning\"\n ? \"warning\"\n : opts.variant === \"success\"\n ? \"success\"\n : \"default\";\n // `title` is the primary line; fall back to `description` so `toast({ description })`\n // shows it (mirroring the monolith) instead of silently no-op'ing. Both absent → no-op.\n if (opts.title) {\n dispatchToast(opts.title, opts.description, variant);\n } else if (opts.description) {\n dispatchToast(opts.description, undefined, variant);\n }\n },\n showSuccessToast,\n showErrorToast,\n showWarningToast,\n };\n}\n","import { MutationCache, QueryCache, QueryClient, type QueryClientConfig } from \"@tanstack/react-query\";\nimport { showErrorToast } from \"../components/shared/toast\";\n\n/**\n * The `QueryClient` a PlatformReact plugin surface falls back to when the host realm provides\n * none — see {@link useSurfaceQueryClient} for when that is, and what it costs.\n *\n * # Why this file exists\n *\n * It predates the react-query external. A plugin bundle could not see the host's app-wide\n * `QueryClientProvider` — react-query was bundle-local, which is why each page and overlay\n * created its own client. The host's client\n * carries cache-level `onError` handlers that turn a failed query or mutation into a toast, and\n * for a long time the plugin-side client carried none. The consequence was not a degraded\n * message: it was NO message. A command could be refused by the server, travel back through\n * every layer intact, and land on a page that showed the operator nothing at all.\n *\n * That silence was invisible from inside plugin code, because plugins were written as though the\n * handler existed — roughly 1,100 `meta.errorTitle` declarations across nine plugins, plus this\n * SDK's own shared connector-mapping hooks, all of them dead on arrival.\n *\n * # What gets shown\n *\n * `meta.errorTitle` is the title when a call site set one — that is the contract those 1,100 sites\n * were already written against. Otherwise the title is derived from the `[Status]` prefix the\n * SDK's `ResultMapper` puts on every failure, so an unlabelled call still says something truer\n * than \"Error\". BOTH bracketed prefixes are stripped from the description — the status one and the\n * plugin’s own [ERROR_CODE] behind it: they are our wire format, and an operator reading either is\n * reading our plumbing rather than their problem.\n *\n * A page that renders its own error UI opts out with `meta.suppressErrorToast`, matching the host.\n */\n\n/** The subset of react-query's `meta` this module reads. Both fields are optional by design. */\nexport interface PluginQueryMeta\n{\n /** Toast title for a failure from this call site. */\n errorTitle?: string;\n /** Set when the page shows its own error UI and a toast would double-report it. */\n suppressErrorToast?: boolean;\n}\n\n/**\n * Titles for the `ResultStatus` values a plugin can actually receive, phrased for an operator\n * rather than after the enum. Statuses absent here (`Invalid`, `Error`, `CriticalError`,\n * `Unavailable`) deliberately fall through to the generic title: their own messages already say\n * what went wrong, and a title like \"Critical Error\" adds alarm without adding information.\n */\nconst STATUS_TITLES = new Map<string, string>([\n [\"NotFound\", \"Not Found\"],\n [\"Unauthorized\", \"Authentication Error\"],\n [\"Forbidden\", \"Access Denied\"],\n [\"Conflict\", \"Conflict\"],\n]);\n\n/**\n * The remaining statuses `ResultMapper` can emit. Recognised so their prefix is still stripped,\n * but deliberately left to the generic title — see the note above.\n */\nconst UNTITLED_STATUSES = new Set([\"Invalid\", \"Error\", \"CriticalError\", \"Unavailable\", \"Accepted\"]);\n\nconst GENERIC_TITLE = \"Error\";\n\n/**\n * A `Map`/`Set` pair rather than an object literal with `in`: `in` walks the prototype chain, so\n * `\"[toString] …\"` or `\"[constructor] …\"` would read as a known status and get its prefix eaten.\n */\nfunction isKnownStatus(status: string): boolean\n{\n return STATUS_TITLES.has(status) || UNTITLED_STATUSES.has(status);\n}\n\n/**\n * Splits `\"[Conflict] message\"` into its status and message.\n *\n * Only a leading bracketed token is treated as a status, and only when it is one this SDK can\n * actually emit. A message that merely opens with a bracket — `\"[draft] copy not ready\"` — keeps\n * its text intact, because silently eating the first word of someone's error is worse than\n * failing to recognise a prefix.\n */\nfunction splitStatusPrefix(message: string): { status: string | null; body: string }\n{\n const match = /^\\[([A-Za-z]+)\\]\\s*(.*)$/s.exec(message);\n if (match === null)\n {\n return { status: null, body: message };\n }\n\n const [, status, body] = match;\n if (!isKnownStatus(status))\n {\n return { status: null, body: message };\n }\n\n // A prefix with nothing after it would leave an empty toast body; keep the original instead.\n return body.length > 0 ? { status, body } : { status: null, body: message };\n}\n\n/**\n * A plugin's OWN error-code prefix — `SALES_TARGET_COMPANY_HAS_COMMITTED_WORK`, `HR_STAFF_NOT_FOUND`.\n *\n * Every plugin's error factory shapes its messages as <c>[CODE] message</c> so the code stays\n * parseable from the free-text tail, and `ResultMapper` then wraps THAT in its own `[Status]`. So a\n * failure reaches the browser carrying two bracketed prefixes, and stripping only the outer one\n * left an operator reading `SALES_TARGET_COMPANY_HAS_COMMITTED_WORK]` — our plumbing, one layer in.\n *\n * SCREAMING_SNAKE with at least one underscore, deliberately: that is the shape every generated\n * code has, and it is narrow enough that a message legitimately opening with a bracketed word\n * (`\"[draft] copy not ready\"`) keeps its own text. Eating the first word of someone's prose is the\n * same class of mistake as showing them the code.\n */\nconst ErrorCodePrefix = new RegExp(String.raw`^\\[[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+\\]\\s*`);\n\n/**\n * Removes one leading error-code prefix, if there is one.\n *\n * At most one: two codes is not a shape anything produces, so a second bracketed token is far more\n * likely to be part of the message than another prefix to eat.\n */\nfunction stripErrorCodePrefix(body: string): string\n{\n const stripped = body.replace(ErrorCodePrefix, \"\");\n\n // Never strip the whole message away — an empty toast says less than an ugly one.\n return stripped.length > 0 ? stripped : body;\n}\n\n/**\n * Whether this failure is a request the caller themselves abandoned.\n *\n * Matched on the error NAME rather than on an `instanceof` against `CancelledError`, because that\n * type lives in `@tanstack/query-core` and importing it here would bind this file to a transitive\n * package it does not otherwise depend on. `AbortError` is what the browser raises when a fetch is\n * aborted; `CancelledError` is what react-query raises when it cancels one itself.\n */\nfunction wasAbandoned(error: unknown): boolean\n{\n return error instanceof Error\n && (error.name === \"AbortError\" || error.name === \"CancelledError\");\n}\n\n/** Raises the failure as a host toast, unless the call site asked to own its own error UI. */\nfunction surfaceError(error: unknown, meta: PluginQueryMeta | undefined): void\n{\n if (meta?.suppressErrorToast === true)\n {\n return;\n }\n\n // A request the operator's own navigation abandoned is not a failure they can act on, and telling\n // them about it is actively misleading: it attaches a red toast to an action that SUCCEEDED.\n //\n // Measured on prod. Deleting a podcast feed soft-deleted the row exactly as intended — the UPDATE\n // is in the logs, with the row version matched — and then navigated away, which tore down a list\n // query still in flight on the page being left. The operator saw \"Error fetching podcast\n // episodes\" and reasonably concluded the delete had failed. It had not, and there was nothing\n // wrong to fix.\n //\n // Deliberately NOT solved at the call site by disabling that one query: every delete-then-navigate\n // surface in every plugin has the same race, and fixing them one at a time means finding them one\n // at a time, from operator reports.\n if (wasAbandoned(error))\n {\n return;\n }\n\n const raw = error instanceof Error && error.message.length > 0\n ? error.message\n : \"An unexpected error occurred\";\n const { status, body } = splitStatusPrefix(raw);\n const reason = stripErrorCodePrefix(body);\n\n const title = meta?.errorTitle\n ?? (status !== null ? STATUS_TITLES.get(status) ?? GENERIC_TITLE : GENERIC_TITLE);\n\n showErrorToast(title, reason);\n}\n\n/**\n * Creates a plugin-surface `QueryClient` whose failures are visible to the operator.\n *\n * `config` is merged shallowly over the defaults, so a plugin can tune `defaultOptions` without\n * having to restate — or accidentally drop — the error handling. Passing an explicit `queryCache`\n * or `mutationCache` does replace ours, which is the escape hatch for a plugin that genuinely\n * wants different behaviour.\n */\nexport function createPluginQueryClient(config?: QueryClientConfig): QueryClient\n{\n return new QueryClient({\n queryCache: new QueryCache({\n onError: (error, query) => surfaceError(error, query.meta as PluginQueryMeta | undefined),\n }),\n mutationCache: new MutationCache({\n onError: (error, _variables, _context, mutation) =>\n surfaceError(error, mutation.meta as PluginQueryMeta | undefined),\n }),\n ...config,\n defaultOptions: {\n // Spread first so `mutations`, `dehydrate`, `hydrate` and anything else react-query\n // adds to `DefaultOptions` survive. Rebuilding this object from named keys instead\n // silently dropped them, which contradicted the merge promised just above.\n ...config?.defaultOptions,\n queries: {\n retry: 1,\n refetchOnWindowFocus: false,\n staleTime: 30_000,\n ...config?.defaultOptions?.queries,\n },\n },\n });\n}\n","import { createContext, useContext, type Context } from \"react\";\n\nimport * as reactQuery from \"@tanstack/react-query\";\nimport type { QueryClient } from \"@tanstack/react-query\";\n\nimport { createPluginQueryClient } from \"./pluginQueryClient\";\n\n/**\n * Resolves the `QueryClient` a PlatformReact surface runs on: the host's when the\n * host realm provides one, this bundle's own when it does not.\n *\n * # Why a resolution rather than a constant\n *\n * `@tanstack/react-query` is now a host external (`PLATFORM_REACT_EXTERNALS`), so\n * the copy answering a plugin's import is the DEPLOYED host's module instance —\n * the same one the host's own app root built its `QueryClientProvider` from. That\n * makes the host's client reachable from plugin code for the first time, and one\n * client is one cache: a delete on a detail surface finally invalidates the list\n * surface that is showing the row.\n *\n * Every page used to construct its own client instead, because a bundled copy of\n * react-query could not see the host's provider. That is the defect this replaces:\n * one cache per page bundle, so an invalidation reached only the surface that\n * issued it and a deleted row survived until the operator reloaded the browser.\n *\n * # Why the host's client is read from CONTEXT, and not from a host accessor\n *\n * The host serves the react-query module itself; nothing else is required of it.\n * Verified in `coreconnect-web`, by reading it rather than assuming: the app has\n * exactly one React root (`src/main.tsx` → `<App/>`), `App` wraps the whole\n * router in `<QueryClientProvider client={queryClient}>`, `PlatformReactSurfaceMount`\n * renders a plugin surface as an ordinary child of that tree (no second\n * `createRoot`), and the one portal in the extension runtime uses `createPortal`,\n * which preserves context ancestry. So the host's provider is an ancestor of every\n * plugin surface the moment the react-query MODULE instance is shared — and this\n * hook needs only the module it already imports.\n *\n * The host additionally exposes a `getHostQueryClient()` accessor under a bespoke\n * registry specifier. This file deliberately does not import it. Every bare\n * specifier a plugin bundle imports is one the deployed host MUST serve or the\n * bundle fails to load before any SDK code runs — a failure no fallback in this\n * file can catch. Taking a second such dependency, on a specifier invented for\n * this purpose and matching no published package, would widen that blast radius\n * to buy a case the verification above shows does not arise. The accessor remains\n * useful to a PLUGIN that wants to pass a client into a definer's `queryClient`\n * option explicitly; it is simply not how the default path resolves.\n *\n * # The fallback is the point\n *\n * An external resolves from whatever host is deployed, which is NOT necessarily\n * the host this SDK was released alongside. A host may serve the react-query\n * module while rendering plugin surfaces outside its `QueryClientProvider`, or\n * expose a module namespace that predates the `QueryClientContext` export. Both\n * must degrade to exactly today's behaviour — a per-bundle client from\n * {@link createPluginQueryClient} — and never throw. A surface that throws takes\n * the whole page down; a surface on its own cache merely has the old defect back.\n *\n * What the fallback does NOT cover, and must not be claimed to: a host that does\n * not register `@tanstack/react-query` in its module registry AT ALL. The bare\n * specifier is then unresolvable and the bundle fails to load before any SDK code\n * runs. No SDK-side tolerance can reach that, which is why the host change ships\n * first and plugins rebuild last.\n */\n\n/**\n * The host realm's react-query context, resolved ONCE at module scope.\n *\n * <b>Read off a NAMESPACE import, never a named one.</b> `@tanstack/react-query`\n * is a host external, so the copy answering this import is whatever the DEPLOYED\n * host exposes — and a named import of an export that copy lacks is a module\n * LINKING failure, raised before a single line of this file runs. The fallback\n * documented above for \"a namespace that predates the QueryClientContext export\"\n * would have been unreachable in exactly the case it was written for: the page\n * would go down instead of degrading. This is the same trap\n * `useOrgFormatSync` records, where a named import of an accessor added in SDK\n * 1.88.0 took down every page of every plugin built against a 1.86.0 host.\n *\n * Module scope rather than a check inside the hook, for the reason that file also\n * gives: the host realm cannot change identity for the life of the bundle, and a\n * conditional `useContext` would break the rules of hooks. A host realm missing\n * the export gets a private context that no provider ever fills, so the read below\n * is valid and always yields `undefined` — a graceful absence, not a crash.\n */\nconst hostRealmExposesContext = isReactContext(reactQuery.QueryClientContext);\n\nconst resolvedHostContext: Context<QueryClient | undefined> = hostRealmExposesContext\n ? (reactQuery.QueryClientContext as Context<QueryClient | undefined>)\n : createContext<QueryClient | undefined>(undefined);\n\nfunction isReactContext(value: unknown): value is Context<QueryClient | undefined> {\n return typeof value === \"object\" && value !== null && \"Provider\" in value;\n}\n\n/**\n * This bundle's own client, built on FIRST fallback rather than at module load.\n *\n * Lazy deliberately. Eager construction is what the definers used to do, and it\n * paid `createPluginQueryClient` — and the `QueryCache`/`MutationCache` wiring\n * behind it — on every surface of every plugin, including the surfaces that now\n * run on the host's client and never touch this at all.\n */\nlet bundleQueryClient: QueryClient | undefined;\n\nfunction pluginBundleQueryClient(): QueryClient {\n bundleQueryClient ??= createPluginQueryClient();\n return bundleQueryClient;\n}\n\nlet warnedAboutMissingHostClient = false;\n\nfunction warnOnceAboutMissingHostClient(): void {\n if (warnedAboutMissingHostClient) {\n return;\n }\n warnedAboutMissingHostClient = true;\n console.warn(\n \"[plugin-ui] The host realm provides no QueryClient\" +\n (hostRealmExposesContext\n ? \" (@tanstack/react-query is served, but this surface renders outside the host's QueryClientProvider)\"\n : \" (the host's @tanstack/react-query namespace has no QueryClientContext export)\") +\n \". This surface falls back to a client of its own, so a mutation here will not \" +\n \"invalidate another surface's cache. Deploy a host that mounts plugin surfaces \" +\n \"inside its QueryClientProvider to share one cache.\",\n );\n}\n\n/**\n * The client this surface should provide to its subtree.\n *\n * Precedence, and the order matters:\n *\n * 1. `override` — the definers' existing `queryClient` option. A plugin that asks\n * for a specific client gets it, and is never second-guessed or warned about.\n * 2. The host's client, read from the host realm's react-query context.\n * 3. This bundle's own client — today's behaviour, unchanged.\n *\n * @param override The `queryClient` a definer's caller supplied, if any.\n */\nexport function useSurfaceQueryClient(override?: QueryClient): QueryClient {\n // Unconditional, before any branching: it is a hook, and the rules of hooks do\n // not care that the answer is about to be discarded in favour of `override`.\n const hostQueryClient = useContext(resolvedHostContext);\n\n if (override !== undefined) {\n return override;\n }\n\n if (hostQueryClient !== undefined) {\n return hostQueryClient;\n }\n\n warnOnceAboutMissingHostClient();\n return pluginBundleQueryClient();\n}\n\n/** Test seam: clears the once-per-bundle state the fallback path accumulates. */\nexport function __resetSurfaceQueryClientForTests(): void {\n bundleQueryClient = undefined;\n warnedAboutMissingHostClient = false;\n}\n","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 *\n * Tailwind's generated layers (`utilities`, and the `@property` fallbacks in\n * `properties`) are emitted UNSCOPED and used to break that rule — a plugin's bare\n * `.text-sm` silently restyled host chrome. The build now scopes them:\n * `scopePluginCssPlugin` in `@ethisyscore/vite-plugin`, applied by both page-build\n * paths. Hand-written plugin CSS still has to scope itself.\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/**\n * Mirror of the HOST mount's `deriveEntityToken` (PlatformReactSurfaceMount): the\n * route sub-path relative to the PAGE-surface `basePath` — which is the page\n * route's STATIC leading segments, so a fully-static page (list routes) yields\n * `\"/\"` because there the pathname IS the basePath. The host silently drops a\n * publish whose `entityToken` doesn't match its own derivation, so this must stay\n * bit-identical to the host helper. No basePath (legacy host) falls back to the\n * full pathname, exactly like the host does.\n */\nexport function deriveHostEntityToken(pathname: string, basePath: string | undefined): string {\n if (!basePath) {\n return pathname;\n }\n const base = stripTrailingSlash(basePath);\n if (pathname === base) {\n return \"/\";\n }\n if (pathname.startsWith(`${base}/`)) {\n return pathname.slice(base.length);\n }\n return pathname;\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).\n * 2.2.0 added the additive, optional `activeNavHref` on every contribution mode; an older host\n * ignores the extra field (it builds its state field-by-field, never by spreading the\n * contribution), so a plugin declaring it against a 2.0/2.1 host is inert, not broken. */\nexport const HOST_CHROME_CONTRACT_VERSION = \"2.2.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\" } & HostSidebarActiveNav)\n | ({ mode: \"actions\"; entityToken: string; actions: HostSidebarActionDescriptor[] } & HostSidebarActiveNav)\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 } & HostSidebarActiveNav);\n\n/**\n * The optional active-nav declaration carried by EVERY contribution mode (contract ≥2.2.0).\n *\n * `activeNavHref` is a slug-relative href — the SAME vocabulary as nav-item and navigate-action\n * hrefs — naming the sidebar entry the plugin considers active for the current route. The host\n * resolves it to its own nav-item id and highlights that entry; when it is absent, or names\n * nothing the host is rendering, the host's URL election decides exactly as it always has.\n *\n * It exists because the host's election can only light an item whose href PREFIXES the current\n * URL, so a sub-page whose URL does not nest under its section (`/extensions/projects/people/:id`\n * belonging to \"People\" at `/extensions/projects/settings`) lights the module root instead. A\n * plugin's information architecture is not always recoverable from its URL shape, so the plugin\n * says so; {@link resolveActiveNavHref} derives it from the `routeMeta` parent chain the plugin\n * already declares for breadcrumbs.\n *\n * It rides on `mode:\"none\"` too — deliberately. `\"none\"` is what a plugin publishes on an\n * ordinary list route with no CTAs, and that is precisely the route shape whose highlight is most\n * often wrong; a declaration there still contributes no actions and still does not clobber the\n * static manifest.\n */\nexport interface HostSidebarActiveNav {\n activeNavHref?: string;\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 { useMemo, useState, type ReactElement, type ReactNode } from \"react\";\nimport { ThemeProvider, useTheme, type Theme } from \"@mui/material\";\n\n/**\n * Class on the portal container element rendered at the end of a plugin surface.\n * `plugin-base.css` positions it; the class is exported so a plugin can target it\n * (e.g. to raise the stacking order for one surface).\n */\nexport const EHX_PLUGIN_PORTAL_CLASS = \"ehx-plugin-portal-root\";\n\n/**\n * Marker attribute on any element acting as a plugin portal container. Scoped\n * plugin CSS keys off `.ehx-plugin-root` OR this attribute, so a plugin that\n * supplies its OWN container for a specific overlay (passing `container` on the\n * component, which beats the default set here) can mark it and keep utilities.\n */\nexport const EHX_PLUGIN_PORTAL_ATTR = \"data-ehx-plugin-portal\";\n\n/**\n * Returns `theme` with the portal container set on every slot that can decide\n * where an overlay mounts, preserving all other theme values and `defaultProps`.\n *\n * Which slots, and why the two base ones are not enough:\n *\n * MuiModal Modal, Dialog, Drawer - the base for anything full-screen.\n * MuiPopper Popper, Autocomplete - the base for anything anchored.\n * MuiPopover Popover, and Select via Menu. NOT covered by MuiModal: Popover\n * resolves its own container first (\"if the anchorEl prop is\n * provided, use its parent body element\"), so Modal receives a\n * non-`undefined` value and its own default never applies.\n * MuiMenu Menu passes its container down to Popover explicitly, so a\n * MuiMenu-level default outranks the MuiPopover one. The HOST sets\n * exactly that (below), so without this entry every plugin menu and\n * select escapes to the host's container.\n * MuiTooltip Tooltip passes `PopperProps.container` to its Popper, which\n * likewise outranks the MuiPopper default. The host sets that too.\n *\n * The container is applied LAST at each level so it beats the host's. The host is\n * not neutral here: `gogo-theme-provider.tsx` already redirects MUI portals into\n * a container inside its own `.gogo-ui` scope, setting MuiModal, MuiPopover,\n * MuiMenu, MuiPopper and MuiTooltip. That container is a SIBLING of the plugin\n * root, not an ancestor, so inheriting it leaves plugin overlays outside the\n * plugin's scoped CSS. Inside a plugin surface the plugin container has to win.\n *\n * A component's own explicit `container` prop still wins over all of this:\n * `resolveProps` only falls back to a default when the incoming value is\n * `undefined`.\n *\n * Written out per slot rather than looped because each slot's `defaultProps` is a\n * distinct props type; indexing with a union key widens them into one\n * unassignable intersection.\n */\nfunction withPortalContainer(theme: Theme, containerElement: HTMLElement): Theme {\n const slots = theme.components ?? {};\n const tooltip = slots.MuiTooltip;\n // A getter rather than the element itself: the theme gets serialized downstream\n // (MUI X DataGrid memo-keys its CSS variables on JSON.stringify of the theme), and\n // a raw DOM node's React expandos (__reactFiber$*) recurse until the stack\n // overflows. JSON.stringify skips functions, and MUI resolves callable containers.\n const container = () => containerElement;\n return {\n ...theme,\n components: {\n ...slots,\n MuiModal: { ...slots.MuiModal, defaultProps: { ...slots.MuiModal?.defaultProps, container } },\n MuiPopper: { ...slots.MuiPopper, defaultProps: { ...slots.MuiPopper?.defaultProps, container } },\n MuiPopover: { ...slots.MuiPopover, defaultProps: { ...slots.MuiPopover?.defaultProps, container } },\n MuiMenu: { ...slots.MuiMenu, defaultProps: { ...slots.MuiMenu?.defaultProps, container } },\n MuiTooltip: {\n ...tooltip,\n defaultProps: {\n ...tooltip?.defaultProps,\n PopperProps: { ...tooltip?.defaultProps?.PopperProps, container },\n },\n },\n },\n };\n}\n\n/**\n * Renders plugin content plus a portal container INSIDE the plugin's style root,\n * and defaults MUI's portalling components to that container.\n *\n * Why: a plugin's compiled CSS is injected into the host document and is scoped\n * to the plugin root, but MUI renders modals, menus, poppers and tooltips through\n * a portal, landing wherever the host theme sends them — a sibling of the plugin\n * root, not a descendant — so they fall outside every scoped rule. Containing\n * them first is what lets plugin CSS be scoped at all.\n *\n * This is a nested `ThemeProvider` over the HOST theme (MUI is a host external,\n * so `useTheme()` reads the host's provider through the same module instance).\n * It adds the container defaults and changes nothing else.\n */\nexport function PluginPortalScope({ children }: { children: ReactNode }): ReactElement {\n // Callback ref into state, not a ref object: the theme has to be rebuilt once\n // the container element exists, which needs a render pass.\n const [container, setContainer] = useState<HTMLElement | null>(null);\n const hostTheme = useTheme();\n const theme = useMemo(\n () => (container ? withPortalContainer(hostTheme, container) : hostTheme),\n [hostTheme, container],\n );\n\n return (\n <ThemeProvider theme={theme}>\n {children}\n <div ref={setContainer} className={EHX_PLUGIN_PORTAL_CLASS} {...{ [EHX_PLUGIN_PORTAL_ATTR]: \"\" }} />\n </ThemeProvider>\n );\n}\n","import { useEffect, useState } from \"react\";\n\nimport * as extensionRuntime from \"@ethisyscore/extension-runtime/plugin\";\nimport { setOrgFormats } from \"@ethisyscore/core-utils/date/org-format\";\n\n/**\n * Resolved from the host realm at module scope, NOT imported as a binding.\n *\n * `@ethisyscore/extension-runtime/plugin` is a host-provided external\n * (`PLATFORM_REACT_EXTERNALS`), so it is never bundled into a plugin page: the\n * copy that answers this import is whatever the DEPLOYED host exposes, which is\n * independent of the SDK version the plugin was built against. A plugin on a\n * newer SDK than the host therefore gets `undefined` for anything the host's\n * older copy does not export, and a named import of it fails as\n * \"X is not a function\" the moment a surface mounts.\n *\n * That is not hypothetical. `useOptionalExtensionRuntimeTransport` landed in SDK\n * 1.88.0, and calling it unconditionally took down every page of every plugin\n * built on 1.88.x against a 1.86.0 host, because\n * `definePlatformReactPluginPage` and `definePlatformReactPluginOverlay` call\n * `useOrgFormatSync` on every surface mount. The old comment below claimed this\n * hook fails open, and it did for a missing TRANSPORT while crashing hard for a\n * missing HOOK, which is the case that actually occurs in production.\n *\n * A module-scope resolution rather than a check inside the hook: the host realm\n * cannot change identity for the life of the bundle, so this is stable, and the\n * hook below stays an unconditional call rather than a conditional one that\n * would break the rules of hooks.\n */\nlet warnedAboutMissingAccessor = false;\n\nconst useOptionalExtensionRuntimeTransport: typeof extensionRuntime.useOptionalExtensionRuntimeTransport =\n typeof extensionRuntime.useOptionalExtensionRuntimeTransport === \"function\"\n ? extensionRuntime.useOptionalExtensionRuntimeTransport\n : () => {\n if (!warnedAboutMissingAccessor) {\n warnedAboutMissingAccessor = true;\n console.warn(\n \"[plugin-ui] The host realm does not expose useOptionalExtensionRuntimeTransport \" +\n \"(host SDK predates 1.88.0). Organisation date/time formats fall back to the UK \" +\n \"default for this bundle. Bump the host's @ethisyscore/extension-runtime pin to \" +\n \"restore them.\",\n );\n }\n return null;\n };\n\n/**\n * Reserved host resource carrying the caller organisation's effective date/time\n * display formats. The host proxies it (see coreconnect-web\n * `surfaceMcpTransport.ts`) from the permission-free\n * `/organisations/current/display-formats` endpoint. The URI string is hardcoded\n * on both sides of the runtime host↔plugin contract, exactly like\n * `ethisys:user-permissions`.\n */\nexport const ORGANISATION_SETTINGS_URI = \"ethisys:organisation-settings\";\n\ninterface OrganisationDisplayFormats {\n readonly dateFormat: string | null;\n readonly timeFormat: string | null;\n}\n\n/**\n * Reads the caller organisation's effective date/time display formats from the\n * reserved {@link ORGANISATION_SETTINGS_URI} host resource and stamps them into\n * the core-utils org-format refs, so THIS plugin bundle's org-aware formatters\n * (`@ethisyscore/core-utils/date/org-format`) and `DateInput` picker masks render\n * in the organisation's configured format.\n *\n * Each plugin bundle owns its own copy of those module refs, so this must run per\n * bundle — `definePlatformReactPluginPage` and `definePlatformReactPluginOverlay`\n * auto-call it once per surface mount, so plugins wire nothing.\n *\n * Reactive: the org format arrives asynchronously, after the surface (and its\n * `DateInput`s) have already rendered on the UK fallback. The module refs are\n * non-React, so a `useState` bump here re-renders the surface once the formats\n * resolve, letting already-mounted formatters and picker masks pick them up. The\n * effect is also keyed on the active organisation id, so switching organisation\n * without a remount refetches.\n *\n * Fails open in both directions, which is two distinct cases:\n *\n * - No TRANSPORT. It resolves the transport via the OPTIONAL accessor (not\n * `useMcpResource`, which throws when no provider is present) so a surface that\n * renders without a host transport — a standalone/mock-mode run or an isolated\n * unit test — degrades to a no-op and the formatters use the UK fallback.\n * - No ACCESSOR. A host realm older than SDK 1.88.0 does not expose the accessor\n * at all; see the module-scope resolution above for why an import binding\n * cannot be relied on here and what it cost when it was.\n *\n * On a rejected/failed fetch the refs are reset to the UK fallback rather than\n * left holding a previous organisation's format.\n */\nexport function useOrgFormatSync(): void {\n const transport = useOptionalExtensionRuntimeTransport();\n const organisationId = extensionRuntime.useHostIdentity()?.organisationId ?? null;\n const [, bumpVersion] = useState(0);\n\n useEffect(() => {\n // Reset to the deterministic UK fallback up front — before the no-transport\n // early return AND before this organisation's fetch settles. The refs are a\n // module-level singleton shared across surface mounts of the same bundle, so\n // without this a reused bundle (no transport) or an in-flight organisation\n // switch would keep rendering the PREVIOUS organisation's format until (or\n // unless) a new request resolved, exposing one tenant's mask under another.\n setOrgFormats(null, null);\n bumpVersion((v) => v + 1);\n\n if (!transport) return;\n\n const controller = new AbortController();\n transport\n .getResource<OrganisationDisplayFormats>(ORGANISATION_SETTINGS_URI, controller.signal)\n .then((result) => {\n if (controller.signal.aborted) return;\n setOrgFormats(result.data?.dateFormat ?? null, result.data?.timeFormat ?? null);\n bumpVersion((v) => v + 1);\n })\n .catch(() => {\n if (controller.signal.aborted) return;\n // Fail open, but do not keep a previous organisation's format on failure:\n // reset to the deterministic UK fallback.\n setOrgFormats(null, null);\n bumpVersion((v) => v + 1);\n });\n\n return () => controller.abort();\n }, [transport, organisationId]);\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nimport * as extensionRuntime from \"@ethisyscore/extension-runtime/plugin\";\nimport { setOrgReportingCurrency } from \"@ethisyscore/core-utils/money\";\n\n/**\n * Resolved from the host realm at module scope, NOT imported as a binding — see\n * {@link useOrgFormatSync} for the full reasoning. `@ethisyscore/extension-runtime/plugin`\n * is a host-provided external, so a host older than the SDK that introduced\n * `useOptionalExtensionRuntimeTransport` (1.88.0) does not expose it, and a named\n * import would crash every surface with \"X is not a function\". This hook is called\n * on every surface mount by `definePlatformReactPluginPage` /\n * `definePlatformReactPluginOverlay`, exactly like `useOrgFormatSync`, so it must\n * degrade to a no-op (currency falls back to GBP) rather than throw.\n */\nlet warnedAboutMissingAccessor = false;\n\nconst useOptionalExtensionRuntimeTransport: typeof extensionRuntime.useOptionalExtensionRuntimeTransport =\n typeof extensionRuntime.useOptionalExtensionRuntimeTransport === \"function\"\n ? extensionRuntime.useOptionalExtensionRuntimeTransport\n : () => {\n if (!warnedAboutMissingAccessor) {\n warnedAboutMissingAccessor = true;\n console.warn(\n \"[plugin-ui] The host realm does not expose useOptionalExtensionRuntimeTransport \" +\n \"(host SDK predates 1.88.0). The organisation reporting currency falls back to GBP \" +\n \"for this bundle. Bump the host's @ethisyscore/extension-runtime pin to restore it.\",\n );\n }\n return null;\n };\n\n/** Host tool resolving the caller org's reporting currency (default, else GBP). */\nexport const REPORTING_CURRENCY_TOOL = \"settings:get-organisation-reporting-currency\";\n\ninterface ReportingCurrencyInfo {\n readonly code: string;\n readonly decimalPlaces: number | null;\n}\n\ninterface ReportingCurrencyResponse {\n readonly currency: ReportingCurrencyInfo | null;\n}\n\n/**\n * Reads the caller organisation's reporting currency from the host\n * {@link REPORTING_CURRENCY_TOOL} and stamps it into the core-utils reporting-currency\n * ref (`@ethisyscore/core-utils/money`), so THIS plugin bundle's pure\n * `formatMoney(amount)` renders reporting-currency amounts in the org's currency\n * without threading a code.\n *\n * The org-currency sibling of {@link useOrgFormatSync}. Each plugin bundle owns its\n * own copy of the module ref, so this must run per bundle —\n * `definePlatformReactPluginPage` and `definePlatformReactPluginOverlay` auto-call\n * it once per surface mount, so plugins wire nothing.\n *\n * Reactive: the currency arrives asynchronously, after the surface has already\n * rendered on the GBP fallback. The module ref is non-React, so a `useState` bump\n * re-renders the surface once the currency resolves. Keyed on the active\n * organisation id so switching organisation without a remount refetches.\n *\n * Fails open in both directions, mirroring {@link useOrgFormatSync}:\n * - No TRANSPORT (standalone/mock run, unit test) — resolved via the OPTIONAL\n * accessor, degrades to a no-op and `formatMoney` uses the GBP fallback.\n * - No ACCESSOR (host realm older than SDK 1.88.0) — see the module-scope\n * resolution above.\n * On a rejected/failed fetch the ref is reset to the GBP fallback rather than left\n * holding a previous organisation's currency.\n */\nexport function useReportingCurrencySync(): void {\n const transport = useOptionalExtensionRuntimeTransport();\n const organisationId = extensionRuntime.useHostIdentity()?.organisationId ?? null;\n const [, bumpVersion] = useState(0);\n\n // Clear the ref SYNCHRONOUSLY during render the moment the organisation changes,\n // before any child in this render reads it. The effect below runs AFTER this\n // render commits, so clearing only there lets a child's `formatMoney()` observe the\n // PREVIOUS organisation's currency for the first paint of a switch-without-remount.\n // Tracking the previous org id in a ref is the \"reset derived state on prop change\n // during render\" pattern; the effect still owns the fetch + re-stamp.\n const previousOrganisationId = useRef<string | null | undefined>(undefined);\n if (previousOrganisationId.current !== organisationId) {\n previousOrganisationId.current = organisationId;\n setOrgReportingCurrency(null);\n }\n\n useEffect(() => {\n // Reset to the deterministic GBP fallback up front — before the no-transport\n // early return AND before this organisation's fetch settles. The ref is a\n // module-level singleton shared across surface mounts of the same bundle, so\n // without this a reused bundle (no transport) or an in-flight organisation\n // switch would keep rendering the PREVIOUS organisation's currency. (The\n // synchronous during-render clear above covers the first paint; this covers a\n // remount/no-transport reuse where the org id did not change.)\n setOrgReportingCurrency(null);\n bumpVersion((v) => v + 1);\n\n if (!transport) return;\n\n const controller = new AbortController();\n transport\n .invokeTool<Record<string, never>, ReportingCurrencyResponse>(\n REPORTING_CURRENCY_TOOL,\n {},\n controller.signal,\n )\n .then((result) => {\n if (controller.signal.aborted) return;\n const currency = result?.currency ?? null;\n setOrgReportingCurrency(\n currency ? { code: currency.code, decimalPlaces: currency.decimalPlaces } : null,\n );\n bumpVersion((v) => v + 1);\n })\n .catch(() => {\n if (controller.signal.aborted) return;\n // Fail open, but do not keep a previous organisation's currency on failure:\n // reset to the deterministic GBP fallback.\n setOrgReportingCurrency(null);\n bumpVersion((v) => v + 1);\n });\n\n return () => controller.abort();\n }, [transport, organisationId]);\n}\n","import { createElement, type ComponentType, type ReactElement } from \"react\";\nimport { QueryClientProvider, type QueryClient } from \"@tanstack/react-query\";\nimport { useSurfaceQueryClient } from \"./hostQueryClient\";\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\";\nimport { PluginPortalScope } from \"./pluginPortalScope\";\nimport { useOrgFormatSync } from \"./useOrgFormatSync\";\nimport { useReportingCurrencySync } from \"./useReportingCurrencySync\";\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 /**\n * Override the `QueryClient` this page runs on.\n *\n * Left unset, the page runs on the host's client when the host realm provides\n * one, and on a per-bundle client of its own when it does not — see\n * {@link useSurfaceQueryClient}. Setting it opts out of both.\n */\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: queryClientOverride,\n wrapPage,\n } = options;\n\n function PluginPage(props: PlatformReactPageProps) {\n // The host's client when the host realm provides one, this bundle's own when\n // it does not, and `queryClientOverride` ahead of both. Resolved per render\n // rather than once at module load: the answer depends on where this surface\n // is mounted, which module scope cannot see.\n const queryClient = useSurfaceQueryClient(queryClientOverride);\n // Stamp this bundle's org date/time format refs from the reserved host\n // resource so the plugin's org-aware formatters + DateInput masks render in\n // the org's configured format. Runs once per surface mount; fails open to the\n // UK fallback. The host mounts pages inside its runtime provider, so the MCP\n // transport useMcpResource needs is already in scope here.\n useOrgFormatSync();\n // Stamp this bundle's org reporting-currency ref so the plugin's pure\n // formatMoney(amount) renders reporting-currency amounts in the org's currency.\n // Same once-per-mount, fail-open-to-GBP sync as useOrgFormatSync.\n useReportingCurrencySync();\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 // PluginPortalScope sits INSIDE the style root, so the portal container\n // it renders is a descendant of it and MUI overlays (dialogs, menus,\n // poppers, tooltips) open inside the plugin's scoped CSS.\n createElement(PluginPortalScope, null, 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 { useMemo } from \"react\";\n\nimport { type ToolInvoker, useToolInvokerMap } from \"./mcpService\";\n\n/**\n * A single tool's wire contract: the arguments it takes and the payload it returns.\n *\n * ## The gap this closes\n *\n * `BaseMcpService.tool<TRes>(name: string, args?: unknown)` checks nothing. The tool name is any\n * string, the arguments are `unknown`, and `TRes` is whatever the caller asserts. Every part of the\n * boundary is a claim rather than a check, and all three parts have been wrong in production within\n * a single module:\n *\n * - **The result lied.** `tech-list-tool-mappings` returned a bare array while the caller read\n * `response.items`, so the table rendered its headers with no rows. No error anywhere.\n * - **The result lied again.** `tech-auto-match-tool-mappings` returned no payload at all while the\n * caller read `response.items.filter(...)`.\n * - **The arguments were mislabelled.** A service sent `{ connectorId, moduleKey }` where the\n * command declared `ConnectorSlug`, so the command rejected every call. Both values were strings,\n * so even the service interface it implemented could not catch the swap.\n *\n * None of those produced a type error, and none produced a runtime error either - the surface just\n * quietly did nothing, which is the most expensive failure mode there is.\n *\n * ## Phantom, not runtime\n *\n * The contract carries no values. `__args` and `__result` exist only so a declaration can name\n * types; {@link mcpTool} returns a frozen empty object, so both are ABSENT at runtime whatever their\n * declared types say. Reading one gets `undefined`, never a value to build on - that absence is the\n * safety property here, not the types themselves.\n */\nexport interface McpToolContract<TArgs = unknown, TResult = unknown> {\n /** Phantom: the arguments this tool accepts. Never present at runtime. */\n readonly __args: TArgs;\n /** Phantom: the payload this tool returns. Never present at runtime. */\n readonly __result: TResult;\n}\n\n/** A plugin's declared tool surface: tool name to contract. */\nexport type McpToolContracts = Record<string, McpToolContract>;\n\n/** The arguments type for one tool in a contract map. */\nexport type McpToolArgs<TTools extends McpToolContracts, K extends keyof TTools>\n = TTools[K][\"__args\"];\n\n/** The result type for one tool in a contract map. */\nexport type McpToolResult<TTools extends McpToolContracts, K extends keyof TTools>\n = TTools[K][\"__result\"];\n\n/**\n * Declares one tool's contract.\n *\n * Returns a marker object rather than nothing so the declaration can be a real value: the same\n * object literal drives the types AND the hook registration (see {@link useTypedToolInvokerMap}),\n * which is what stops the registered tool list from drifting away from the declared surface.\n *\n * @example\n * export const TOOLS = {\n * \"tech-list-tool-mappings\": mcpTool<{ connectorId: string; moduleKey: string }, MappingsResponse>(),\n * \"tech-auto-match-tool-mappings\": mcpTool<{ connectorId: string }, AutoMatchResponse>(),\n * } as const;\n */\nexport function mcpTool<TArgs, TResult>(): McpToolContract<TArgs, TResult>\n{\n // The cast is the point: there is nothing to construct. Freezing it makes an accidental write\n // fail loudly rather than silently mutating a shared module-level marker.\n return Object.freeze({}) as McpToolContract<TArgs, TResult>;\n}\n\n/**\n * Base class for a service whose tool calls are checked against a declared contract map.\n *\n * Deliberately a SEPARATE class from `BaseMcpService` rather than an overload on it. An added\n * overload would have to keep accepting `(name: string, args?: unknown)` for the services that have\n * not adopted contracts, and TypeScript would then resolve a mistyped call to that looser signature\n * instead of rejecting it - so a typo in a tool name would still compile. Opting in by changing base\n * class means the check cannot be silently bypassed.\n *\n * `BaseMcpService` is untouched; nothing has to migrate.\n *\n * @example\n * class ConnectorMappingService extends TypedMcpService<typeof TOOLS> {\n * async getMappings(connectorId: string, moduleKey: string) {\n * // name, args and result all checked against TOOLS\n * return this.tool(\"tech-list-tool-mappings\", { connectorId, moduleKey });\n * }\n * }\n */\nexport abstract class TypedMcpService<TTools extends McpToolContracts>\n{\n protected constructor(private readonly invoker: ToolInvoker) {}\n\n /**\n * Invokes a declared tool. The name must be a key of the contract map, the arguments must match\n * its declared shape, and the result is its declared type - no call-site assertion.\n */\n protected async tool<K extends keyof TTools & string>(\n name: K,\n args: McpToolArgs<TTools, K>,\n ): Promise<McpToolResult<TTools, K>>\n {\n return (await this.invoker(name, args)) as McpToolResult<TTools, K>;\n }\n}\n\n/**\n * Composes a {@link ToolInvoker} over every tool in a contract declaration.\n *\n * Takes the declaration OBJECT, not a list of names, and that is the point: `useToolInvokerMap`\n * throws `No MCP invoker registered for tool \"x\"` at runtime when a service calls a tool the list\n * forgot, and the list and the service were two places to keep in step. Deriving the names from the\n * contract map makes that drift impossible - if a tool is declared it is registered, and if it is\n * not declared the call does not typecheck.\n *\n * Rules of hooks: pass a module-level `as const` declaration. `Object.keys` on a stable object\n * yields a stable, constant-length list, which is what the underlying hook requires. Building the\n * declaration inside a component would vary the hook count between renders and break.\n */\nexport function useTypedToolInvokerMap<TTools extends McpToolContracts>(\n contracts: TTools,\n): ToolInvoker\n{\n // Sorted so the hook call order does not depend on key insertion order, which a merge or a\n // reformat can change without anyone considering it a behavioural edit. Frozen because\n // `useToolInvokerMap` requires a stable, constant-length list - mutating this array would\n // reorder or resize the per-tool hook calls, and React's hook rules make that corruption rather\n // than an error.\n const names = useMemo(() => Object.freeze(Object.keys(contracts).sort()), [contracts]);\n return useToolInvokerMap(names);\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 { useCallback, useEffect, useMemo, useState } from \"react\";\n\nimport { useHostIdentity, useOptionalExtensionRuntimeTransport } from \"@ethisyscore/extension-runtime/plugin\";\nimport { formatMoney, MONEY_FALLBACK_LOCALE } from \"@ethisyscore/core-utils/money\";\n\n/** Host tool that resolves the caller org's reporting currency (default, else GBP). */\nexport const REPORTING_CURRENCY_TOOL = \"settings:get-organisation-reporting-currency\";\n/** Host tool that lists the currency catalogue. */\nexport const LIST_CURRENCIES_TOOL = \"settings:list-currencies\";\n\n/** A currency as returned by the host currency tools. */\nexport interface CurrencyInfo {\n readonly id: string;\n readonly name: string;\n readonly code: string;\n readonly symbol: string | null;\n /** Host decimal-places override, or null to use the currency's ISO default. */\n readonly decimalPlaces: number | null;\n}\n\n/** What a plugin surface may pass to {@link UseCurrencyResult.format} to name a currency. */\nexport type CurrencySelector = CurrencyInfo | string | null | undefined;\n\nexport interface UseCurrencyResult {\n /** The org's reporting currency, or null while loading / when unresolved. */\n reportingCurrency: CurrencyInfo | null;\n /** The currency catalogue keyed by id (empty while loading / on a fail-open). */\n currencies: ReadonlyMap<string, CurrencyInfo>;\n /** True once the initial fetch has settled (whether it succeeded or failed open). */\n ready: boolean;\n /**\n * Formats an amount in a currency. `currency` may be a {@link CurrencyInfo}, a\n * currency id or ISO code (resolved via the catalogue), or null/undefined to use\n * the org's reporting currency. When no currency can be resolved it falls back to a\n * plain localized number so a render never throws or shows a wrong symbol.\n */\n format: (amount: number, currency?: CurrencySelector) => string;\n}\n\ninterface ReportingCurrencyResponse {\n readonly currency: CurrencyInfo | null;\n}\n\n/**\n * A FRESH empty catalogue per call - deliberately not a shared constant. Returning one\n * shared `Map` as a `ReadonlyMap` still exposes the mutating `Map` methods at runtime, so a\n * caller (or a stale closure) could mutate it and leak state across hook instances and\n * organisation switches. A new instance each time keeps the empty state truly inert.\n */\nconst emptyCatalogue = (): ReadonlyMap<string, CurrencyInfo> => new Map();\n\n/** `PromiseSettledResult.status` value for a resolved promise, avoiding a magic string. */\nconst SETTLED_FULFILLED = \"fulfilled\" as const;\n\n/**\n * Host-aware currency access for a plugin surface: the org's reporting currency, the\n * currency catalogue, and a `format` helper that renders through the pure\n * `@ethisyscore/core-utils/money` formatter honouring each currency's decimal-places\n * override.\n *\n * Fails open, like {@link useOrgFormatSync}: it resolves the transport via the OPTIONAL\n * accessor, so a surface rendered without a host transport (standalone/mock run, unit\n * test) degrades to an empty catalogue and a plain-number fallback rather than crashing.\n * Re-fetches when the active organisation changes.\n */\nexport function useCurrency(): UseCurrencyResult {\n const transport = useOptionalExtensionRuntimeTransport();\n const organisationId = useHostIdentity()?.organisationId ?? null;\n\n const [reportingCurrency, setReportingCurrency] = useState<CurrencyInfo | null>(null);\n const [currencies, setCurrencies] = useState<ReadonlyMap<string, CurrencyInfo>>(emptyCatalogue);\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n // Reset up front so an in-flight org switch never renders the previous org's\n // reporting currency, and a no-transport surface settles into the fallback.\n setReportingCurrency(null);\n setCurrencies(emptyCatalogue());\n setReady(false);\n\n if (!transport) {\n setReady(true);\n return;\n }\n\n const controller = new AbortController();\n\n Promise.allSettled([\n transport.invokeTool<Record<string, never>, ReportingCurrencyResponse>(\n REPORTING_CURRENCY_TOOL,\n {},\n controller.signal,\n ),\n transport.invokeTool<Record<string, never>, CurrencyInfo[]>(\n LIST_CURRENCIES_TOOL,\n {},\n controller.signal,\n ),\n ]).then(([reporting, list]) => {\n if (controller.signal.aborted) return;\n\n if (reporting.status === SETTLED_FULFILLED) {\n const resolved = reporting.value?.currency ?? null;\n setReportingCurrency(resolved);\n // NOTE: this hook does NOT stamp the org reporting-currency ref. The single\n // authoritative stamper is useReportingCurrencySync (auto-run by the page\n // definers). Stamping here too would leave a stale ref on the paths where\n // this hook fails open locally (rejected/no-transport/org switch) but never\n // re-stamps, diverging the pure formatMoney(amount) default from this hook.\n }\n if (list.status === SETTLED_FULFILLED && Array.isArray(list.value)) {\n setCurrencies(new Map(list.value.map((currency) => [currency.id, currency])));\n }\n setReady(true);\n });\n\n return () => controller.abort();\n }, [transport, organisationId]);\n\n const format = useCallback(\n (amount: number, selector?: CurrencySelector): string => {\n const currency = resolveCurrency(selector, currencies, reportingCurrency);\n\n if (currency === null) {\n // Nothing resolvable - render a plain localized number rather than guess a currency.\n return new Intl.NumberFormat(MONEY_FALLBACK_LOCALE).format(amount);\n }\n\n if (typeof currency === \"string\") {\n // A bare code the catalogue did not know - let Intl format it (or fall back internally).\n return formatMoney(amount, { currencyCode: currency });\n }\n\n return formatMoney(amount, {\n currencyCode: currency.code,\n decimalPlaces: currency.decimalPlaces,\n symbol: currency.symbol,\n });\n },\n [currencies, reportingCurrency],\n );\n\n return useMemo(\n () => ({ reportingCurrency, currencies, ready, format }),\n [reportingCurrency, currencies, ready, format],\n );\n}\n\n/**\n * Resolves a selector to a {@link CurrencyInfo}, a bare ISO code (string) when only a\n * code is known, or null when nothing is resolvable.\n */\nfunction resolveCurrency(\n selector: CurrencySelector,\n currencies: ReadonlyMap<string, CurrencyInfo>,\n reportingCurrency: CurrencyInfo | null,\n): CurrencyInfo | string | null {\n if (selector === null || selector === undefined) {\n return reportingCurrency;\n }\n\n if (typeof selector !== \"string\") {\n return selector;\n }\n\n // A string is either a currency id (catalogue key) or an ISO code.\n const byId = currencies.get(selector);\n if (byId) {\n return byId;\n }\n\n const upper = selector.toUpperCase();\n for (const currency of currencies.values()) {\n if (currency.code.toUpperCase() === upper) {\n return currency;\n }\n }\n\n // Unknown to the catalogue - treat it as a raw ISO code for Intl to try.\n return selector;\n}\n","import {\n createElement,\n useContext,\n useSyncExternalStore,\n type AnchorHTMLAttributes,\n type MouseEvent,\n type ReactNode,\n} 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\n// `setSearchParams` writes via history.replaceState, which fires no event — so subscribers\n// are told to re-read via this custom event. `popstate` covers browser back/forward.\nconst SEARCH_PARAMS_EVENT = \"ethisys:searchparams\";\n\nfunction subscribeToSearch(callback: () => void): () => void {\n if (typeof window === \"undefined\") {\n return () => {};\n }\n window.addEventListener(\"popstate\", callback);\n window.addEventListener(SEARCH_PARAMS_EVENT, callback);\n return () => {\n window.removeEventListener(\"popstate\", callback);\n window.removeEventListener(SEARCH_PARAMS_EVENT, callback);\n };\n}\n\nfunction getSearchSnapshot(): string {\n return typeof window !== \"undefined\" ? window.location.search : \"\";\n}\n\nfunction getServerSearchSnapshot(): string {\n return \"\";\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 // Reactive: subscribe to the URL's query string so inputs bound to a param and any\n // data derived from it re-render when it changes (previously read once per render with\n // no subscription, so setSearchParams updated the URL but nothing re-rendered).\n const search = useSyncExternalStore(subscribeToSearch, getSearchSnapshot, getServerSearchSnapshot);\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 // replaceState fires no event — notify subscribers so they re-read the query string.\n window.dispatchEvent(new Event(SEARCH_PARAMS_EVENT));\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 type { PluginOperationStateChanged } from \"./types\";\n\n/**\n * Narrows a raw push payload to a state change. The realtime source hands payloads over untyped,\n * so anything without an operation id and a numeric sequence is ignored rather than merged.\n */\nexport function asStateChanged(payload: unknown): PluginOperationStateChanged | null {\n if (payload == null || typeof payload !== \"object\") {\n return null;\n }\n\n const candidate = payload as Partial<PluginOperationStateChanged>;\n return typeof candidate.operationId === \"string\" && typeof candidate.eventSequence === \"number\"\n ? (candidate as PluginOperationStateChanged)\n : null;\n}\n","import type { PluginOperation, PluginOperationStateChanged } from \"./types\";\n\n/**\n * Applies a pushed state change to a cached operation. The push carries no result payload, so a\n * cached one is kept. An event that is not newer (by `eventSequence`) than the cached row is\n * ignored, so a late, out-of-order delivery can never move an operation backwards.\n */\nexport function mergeOperation(\n current: PluginOperation | null | undefined,\n event: PluginOperationStateChanged,\n): PluginOperation {\n if (current && current.eventSequence >= event.eventSequence) {\n return current;\n }\n\n // Drop the push-only fields; everything else is the same shape as a read.\n const { extensionId: _extensionId, previousStatus: _previousStatus, ...fields } = event;\n return { ...fields, resultPayloadJson: current?.resultPayloadJson ?? null };\n}\n\n/**\n * Applies a pushed state change to a cached list for one parent: replaces the row with the same\n * `operationId` when the event is newer, or prepends it when the list does not hold it yet.\n */\nexport function mergeIntoList(\n current: readonly PluginOperation[] | null | undefined,\n event: PluginOperationStateChanged,\n): PluginOperation[] {\n const rows = current ?? [];\n const index = rows.findIndex((r) => r.operationId === event.operationId);\n if (index < 0) {\n return [mergeOperation(null, event), ...rows];\n }\n\n const merged = mergeOperation(rows[index], event);\n if (merged === rows[index]) {\n return rows as PluginOperation[];\n }\n\n const next = rows.slice();\n next[index] = merged;\n return next;\n}\n","/** Query keys for the operation hooks. The plugin's own query client already isolates the realm. */\nexport const pluginOperationKeys = {\n all: [\"plugin-operations\"] as const,\n byId: (operationId: string) => [\"plugin-operations\", \"get\", operationId] as const,\n byParent: (parentEntityType: string, parentEntityId: string) =>\n [\"plugin-operations\", \"by-parent\", parentEntityType, parentEntityId] as const,\n latestTerminalByParent: (parentEntityType: string, parentEntityId: string) =>\n [\"plugin-operations\", \"latest-terminal\", parentEntityType, parentEntityId] as const,\n};\n","const TERMINAL = new Set([\"Succeeded\", \"Failed\", \"Cancelled\"]);\n\n/** Whether an operation has finished: succeeded, failed or cancelled. */\nexport function isTerminalOperationStatus(status: string | null | undefined): boolean {\n return status != null && TERMINAL.has(status);\n}\n","/**\n * A background operation as a plugin surface sees it through the host's `plugin-operations:*`\n * tools and the plugin-group push. `kind` and `parentEntityType` are the plugin's own LOCAL names:\n * the host strips its namespacing before anything reaches a surface.\n */\nexport interface PluginOperation {\n operationId: string;\n kind: string;\n status: PluginOperationStatus | string;\n currentStageCode?: string | null;\n currentStageLabel?: string | null;\n stageIndex?: number | null;\n currentStageOrdinal?: number | null;\n totalStages?: number | null;\n stageProgressCurrent?: number | null;\n stageProgressTotal?: number | null;\n message?: string | null;\n errorMessage?: string | null;\n errorCode?: string | null;\n parentEntityType?: string | null;\n parentEntityId?: string | null;\n retryAttempt?: number;\n startedUtc?: string | null;\n completedUtc?: string | null;\n lastUpdatedUtc?: string;\n estimatedCompletionUtc?: string | null;\n eventSequence: number;\n /** Only populated by `plugin-operations:get` (and so by {@link useBackgroundOperation}). */\n resultPayloadJson?: string | null;\n}\n\n/** The statuses the host records. Terminal ones are `Succeeded`, `Failed` and `Cancelled`. */\nexport type PluginOperationStatus =\n | \"Pending\"\n | \"Running\"\n | \"PausedRateLimited\"\n | \"Succeeded\"\n | \"Failed\"\n | \"Cancelled\";\n\n/** Payload of the `operations.plugin-operation.state-changed` push. */\nexport interface PluginOperationStateChanged extends Omit<PluginOperation, \"resultPayloadJson\"> {\n extensionId: string;\n previousStatus: string;\n}\n\n/** Type code of the host's plugin-group push for a plugin's own operation state changes. */\nexport const PLUGIN_OPERATION_STATE_CHANGED = \"operations.plugin-operation.state-changed\";\n\n/** The host tools the hooks read through. Declare the ones you use under `McpToolDependencies`. */\nexport const PLUGIN_OPERATION_TOOLS = {\n get: \"plugin-operations:get\",\n listByParent: \"plugin-operations:list-by-parent\",\n listActive: \"plugin-operations:list-active\",\n latestTerminalByParent: \"plugin-operations:latest-terminal-by-parent\",\n} as const;\n\n/** Poll cadence used ONLY when the host supplies no realtime source (matches the host web). */\nexport const OPERATION_FALLBACK_POLL_MS = 8_000;\n","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useMcpTool, usePluginRealtimeSource } from \"@ethisyscore/extension-runtime/plugin\";\nimport { usePluginRealtime } from \"../usePluginRealtime\";\nimport { asStateChanged } from \"./asStateChanged\";\nimport { mergeOperation } from \"./mergeOperation\";\nimport { pluginOperationKeys } from \"./queryKeys\";\nimport { isTerminalOperationStatus } from \"./status\";\nimport {\n OPERATION_FALLBACK_POLL_MS,\n PLUGIN_OPERATION_STATE_CHANGED,\n PLUGIN_OPERATION_TOOLS,\n type PluginOperation,\n} from \"./types\";\n\n/**\n * Follows one of this plugin's own background operations by id: its stage, progress, outcome and,\n * once it has finished, the result payload the plugin recorded.\n *\n * Reads through `plugin-operations:get` and then follows the host's push, merging each state change\n * by `eventSequence` so a late delivery cannot move the operation backwards. When the operation\n * reaches a terminal state it refetches once, because the push carries no result payload. Polls\n * every 8 s only when the host supplied no realtime source and the operation is still running.\n *\n * The data is `PluginOperation | null`; a missing operation, or one that belongs to another app,\n * surfaces as the query's error.\n */\nexport function useBackgroundOperation(operationId: string | null | undefined) {\n const queryClient = useQueryClient();\n const realtime = usePluginRealtimeSource();\n const { invoke } = useMcpTool<{ operationId: string }, PluginOperation | null>(PLUGIN_OPERATION_TOOLS.get);\n\n const query = useQuery({\n queryKey: pluginOperationKeys.byId(operationId ?? \"\"),\n enabled: Boolean(operationId),\n // Never undefined: TanStack Query rejects an undefined result.\n queryFn: async () => (await invoke({ operationId: operationId! })) ?? null,\n refetchInterval: (q) =>\n realtime === null && q.state.data != null && !isTerminalOperationStatus(q.state.data.status)\n ? OPERATION_FALLBACK_POLL_MS\n : false,\n });\n\n usePluginRealtime(PLUGIN_OPERATION_STATE_CHANGED, (payload) => {\n const event = asStateChanged(payload);\n if (!operationId || event?.operationId !== operationId) {\n return;\n }\n\n const key = pluginOperationKeys.byId(operationId);\n const before = queryClient.getQueryData<PluginOperation | null>(key);\n const merged = mergeOperation(before, event);\n queryClient.setQueryData(key, merged);\n\n if (merged !== before && isTerminalOperationStatus(merged.status)) {\n void queryClient.invalidateQueries({ queryKey: key });\n }\n });\n\n return query;\n}\n","const KIND_SCOPE = /^p:[0-9a-f]{32}:/i;\nconst PARENT_SCOPE = /^po:[0-9A-F]{32}:/i;\n\n/**\n * Matches an operation's kind against the plugin's own (unscoped) kind. Mirrors the .NET\n * `HasKind` / `BackgroundOperationScope.Matches` rule (analyser ECP0016): the host-namespaced\n * form `p:{extensionId}:` is stripped before comparing, so the check is right whether the value\n * came from the de-namespacing `plugin-operations:*` tools or anywhere else. Never compare kinds\n * with `===`.\n */\nexport function hasKind(operation: { kind: string } | string | null | undefined, unscopedKind: string): boolean {\n const kind = typeof operation === \"string\" ? operation : operation?.kind;\n return kind != null && kind.replace(KIND_SCOPE, \"\") === unscopedKind;\n}\n\n/** The parent-type counterpart of {@link hasKind}; strips `po:{scopeHash}:` before comparing. */\nexport function hasParentEntityType(\n operation: { parentEntityType?: string | null } | string | null | undefined,\n unscopedParentEntityType: string,\n): boolean {\n const parentType = typeof operation === \"string\" ? operation : operation?.parentEntityType;\n return parentType != null && parentType.replace(PARENT_SCOPE, \"\") === unscopedParentEntityType;\n}\n","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useMcpTool, usePluginRealtimeSource } from \"@ethisyscore/extension-runtime/plugin\";\nimport { usePluginRealtime } from \"../usePluginRealtime\";\nimport { asStateChanged } from \"./asStateChanged\";\nimport { hasParentEntityType } from \"./hasKind\";\nimport { mergeIntoList } from \"./mergeOperation\";\nimport { pluginOperationKeys } from \"./queryKeys\";\nimport { isTerminalOperationStatus } from \"./status\";\nimport {\n OPERATION_FALLBACK_POLL_MS,\n PLUGIN_OPERATION_STATE_CHANGED,\n PLUGIN_OPERATION_TOOLS,\n type PluginOperation,\n} from \"./types\";\n\ninterface ByParentArgs {\n parentEntityType: string;\n parentEntityId: string;\n}\n\n/**\n * Follows this plugin's own background operations against one parent entity (for example every\n * import of one repository), newest first, at most 50.\n *\n * `parentEntityType` is the plugin's LOCAL type name, the one it passed when starting the\n * operation. Reads through `plugin-operations:list-by-parent`, then merges each pushed state change\n * for this parent by `eventSequence`, prepending operations that start after the first read. Polls\n * every 8 s only when the host supplied no realtime source and some operation is still running.\n */\nexport function useOperationsByParent(parentEntityType: string, parentEntityId: string | null | undefined) {\n const queryClient = useQueryClient();\n const realtime = usePluginRealtimeSource();\n const { invoke } = useMcpTool<ByParentArgs, { items?: PluginOperation[] } | null>(\n PLUGIN_OPERATION_TOOLS.listByParent,\n );\n\n const query = useQuery({\n queryKey: pluginOperationKeys.byParent(parentEntityType, parentEntityId ?? \"\"),\n enabled: Boolean(parentEntityId),\n queryFn: async () =>\n (await invoke({ parentEntityType, parentEntityId: parentEntityId! }))?.items ?? [],\n refetchInterval: (q) =>\n realtime === null && (q.state.data ?? []).some((op) => !isTerminalOperationStatus(op.status))\n ? OPERATION_FALLBACK_POLL_MS\n : false,\n });\n\n usePluginRealtime(PLUGIN_OPERATION_STATE_CHANGED, (payload) => {\n const event = asStateChanged(payload);\n if (\n !parentEntityId\n || event == null\n || event.parentEntityId !== parentEntityId\n || !hasParentEntityType(event, parentEntityType)\n ) {\n return;\n }\n\n queryClient.setQueryData<PluginOperation[]>(\n pluginOperationKeys.byParent(parentEntityType, parentEntityId),\n (current) => mergeIntoList(current, event),\n );\n });\n\n return query;\n}\n","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useMcpTool } from \"@ethisyscore/extension-runtime/plugin\";\nimport { usePluginRealtime } from \"../usePluginRealtime\";\nimport { asStateChanged } from \"./asStateChanged\";\nimport { hasParentEntityType } from \"./hasKind\";\nimport { pluginOperationKeys } from \"./queryKeys\";\nimport { isTerminalOperationStatus } from \"./status\";\nimport { PLUGIN_OPERATION_STATE_CHANGED, PLUGIN_OPERATION_TOOLS, type PluginOperation } from \"./types\";\n\ninterface ByParentArgs {\n parentEntityType: string;\n parentEntityId: string;\n}\n\n/**\n * The newest FINISHED operation (succeeded, failed or cancelled) of this plugin against one parent\n * entity, or null: the \"last import failed at 10:42\" line on a detail page.\n *\n * Reads through `plugin-operations:latest-terminal-by-parent` and refetches when a pushed state\n * change for this parent reaches a terminal status. A finished row cannot change, so there is no\n * fallback poll; without a realtime source the value refreshes on the query's own refetches.\n */\nexport function useLatestTerminalOperationByParent(\n parentEntityType: string,\n parentEntityId: string | null | undefined,\n) {\n const queryClient = useQueryClient();\n const { invoke } = useMcpTool<ByParentArgs, { item?: PluginOperation | null } | null>(\n PLUGIN_OPERATION_TOOLS.latestTerminalByParent,\n );\n\n const key = pluginOperationKeys.latestTerminalByParent(parentEntityType, parentEntityId ?? \"\");\n\n const query = useQuery({\n queryKey: key,\n enabled: Boolean(parentEntityId),\n queryFn: async () =>\n (await invoke({ parentEntityType, parentEntityId: parentEntityId! }))?.item ?? null,\n });\n\n usePluginRealtime(PLUGIN_OPERATION_STATE_CHANGED, (payload) => {\n const event = asStateChanged(payload);\n if (\n parentEntityId\n && event != null\n && event.parentEntityId === parentEntityId\n && hasParentEntityType(event, parentEntityType)\n && isTerminalOperationStatus(event.status)\n ) {\n void queryClient.invalidateQueries({ queryKey: key });\n }\n });\n\n return query;\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 { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n useExtensionRuntimeTransport,\n uploadFileToPluginStorage,\n toPluginStorageUploadError,\n type McpTransport,\n type PluginStorageUploadError,\n type UploadToStorageResult,\n} from \"@ethisyscore/extension-runtime/plugin\";\n\n/** Per-call options for {@link UseUploadToStorageResult.upload}. */\nexport interface UploadToStorageTarget {\n /**\n * Folder inside the app's own storage namespace: `/`-separated segments of `[A-Za-z0-9_-]`, at\n * most 84 characters, no leading or trailing `/` (HR: `cvs`; Legal:\n * `governance/{documentId}/{versionId}`). Validated here with the server's rules so a bad prefix\n * fails before the file is read; it is refused, never repaired, and sent exactly as given.\n */\n pathPrefix: string;\n /** Overrides the file's own name (required for an `ArrayBuffer`). Its extension must be on the allow-list. */\n fileName?: string;\n /** Overrides the file's own MIME type. */\n contentType?: string;\n}\n\nexport interface UseUploadToStorageOptions {\n /** Override the transport resolved from the runtime provider (tests / advanced hosts). */\n transport?: McpTransport;\n /** Client-side max byte size, never above the server's 30 MB cap (the default). */\n maxSizeBytes?: number;\n}\n\nexport interface UseUploadToStorageResult {\n /**\n * Stream `file` into this app's own plugin storage and resolve the server-chosen `path`\n * (`{pathPrefix}/{guid}{ext}`), the exact string the app's backend reads with `IPluginStorage`.\n * Record it with the app's own command. Rejects with a {@link PluginStorageUploadError}: branch on\n * `reason` (`too_large`, `quota_exceeded`, `unsupported_type`, `invalid_prefix`, `timeout`,\n * `busy`, ...); `message` is the server's when the server answered. A file whose extension is\n * not on `PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES` fails with `unsupported_type` before it is read.\n */\n upload: (file: File | Blob | ArrayBuffer, target: UploadToStorageTarget) => Promise<UploadToStorageResult>;\n /** Phase-based progress in [0, 1]: 0 idle, 1 on completion. The FE→host leg is not byte-streamed. */\n progress: number;\n loading: boolean;\n error: PluginStorageUploadError | undefined;\n}\n\n/**\n * Upload a browser file from a platform-react plugin surface into the app's OWN plugin storage.\n *\n * Browser uploads go through this (the `uploadToStorage` request kind); never base64 a file\n * through a tool call. The bytes never touch the plugin backend and the plugin never sees the\n * capability token: the host streams them to `extensions/storage/upload-stream`, and the kernel\n * derives the namespace from the token. For the legacy CORE `StoredDocument` route, see\n * `useMcpUpload`.\n *\n * Unmounting, or starting another upload, aborts the one in flight.\n */\nexport function useUploadToStorage(opts?: UseUploadToStorageOptions): UseUploadToStorageResult {\n const transport = useExtensionRuntimeTransport(opts?.transport);\n const maxSizeBytes = opts?.maxSizeBytes;\n\n const [progress, setProgress] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<PluginStorageUploadError | 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 | ArrayBuffer, target: UploadToStorageTarget): Promise<UploadToStorageResult> => {\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 const result = await uploadFileToPluginStorage(transport, file, {\n pathPrefix: target.pathPrefix,\n fileName: target.fileName,\n contentType: target.contentType,\n maxSizeBytes,\n signal: controller.signal,\n });\n applyState(() => {\n setProgress(1);\n setLoading(false);\n });\n return result;\n } catch (err) {\n const typed = toPluginStorageUploadError(err);\n applyState(() => {\n setError(typed);\n setLoading(false);\n setProgress(0);\n });\n throw typed;\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 { QueryClientProvider, type QueryClient } from \"@tanstack/react-query\";\nimport { useSurfaceQueryClient } from \"./hostQueryClient\";\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\";\nimport { PluginPortalScope } from \"./pluginPortalScope\";\nimport { useOrgFormatSync } from \"./useOrgFormatSync\";\nimport { useReportingCurrencySync } from \"./useReportingCurrencySync\";\n\nexport interface DefinePlatformReactPluginOverlayOptions {\n rootClassName: string;\n styleId: string;\n css: string;\n manifest?: Record<string, Partial<Record<string, ViewComponent>>>;\n /**\n * Override the `QueryClient` this overlay runs on.\n *\n * Left unset, the overlay runs on the host's client when the host realm\n * provides one, and on a per-bundle client of its own when it does not — see\n * {@link useSurfaceQueryClient}. Setting it opts out of both.\n */\n queryClient?: QueryClient;\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 = {}, queryClient: queryClientOverride, wrapOverlay } = options;\n return function MountedOverlay(props: PlatformReactOverlayProps) {\n // The host's client when the host realm provides one, this overlay bundle's\n // own when it does not, and `queryClientOverride` ahead of both. See\n // definePlatformReactPluginPage for why this is resolved per render.\n const queryClient = useSurfaceQueryClient(queryClientOverride);\n // Stamp this bundle's org date/time format refs so an overlay's DateInputs and\n // read-only formatters honour the org format too (not just pages). Same\n // once-per-mount, fail-open sync as definePlatformReactPluginPage.\n useOrgFormatSync();\n // Stamp this bundle's org reporting-currency ref so an overlay's formatMoney\n // renders reporting-currency amounts in the org's currency too (not just pages).\n // Same once-per-mount, fail-open-to-GBP sync as definePlatformReactPluginPage.\n useReportingCurrencySync();\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 // react-query provider. Normally re-providing the host's own client, which\n // is a no-op for the subtree and deliberate: the tree shape stays identical\n // whichever client `useSurfaceQueryClient` returned, so the fallback path is\n // not a second code path through the renderer.\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 // Overlay surfaces open their own dialogs and menus, so they need the same\n // portal containment as pages (see definePlatformReactPluginPage).\n createElement(PluginPortalScope, null, createElement(Overlay, props)),\n ),\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 HostSidebarActiveNav,\n type HostSidebarContribution,\n type HostSidebarNavItem,\n} from \"./hostSidebarActionsContext\";\nimport { SurfaceBaseContext, deriveHostEntityToken } 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.\n *\n * `entityToken` is REQUIRED here — this is the published shape existing consumers narrow\n * against (`input.entityToken` is a `string` after a mode check), unchanged. To let the\n * hook derive the token instead, OMIT the property via {@link HostSidebarDerivedInput}. */\nexport type HostSidebarInput =\n | ({ mode: \"none\" } & HostSidebarActiveNav)\n | ({ mode: \"actions\"; entityToken: string; actions: HostSidebarAction[] } & HostSidebarActiveNav)\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 } & HostSidebarActiveNav);\n\n/** Token-less authoring variants: the hook derives the host-mirrored entity token from the\n * surface base + current location (`deriveHostEntityToken`) — correct by construction, since\n * the host's token guard silently drops a mismatching publish. Prefer these unless you need\n * the bare-entity-id form (which the host segment-matches across an entity's sub-pages).\n * `entityToken?: undefined` (not just absence) is deliberate: an EXPLICIT `undefined`\n * (e.g. `entityToken: maybeToken` with an unresolved token) keeps the long-standing\n * \"unresolved token → contribute nothing\" semantic and is treated as `mode:\"none\"`, NOT\n * derived — only a truly ABSENT property derives. */\nexport type HostSidebarDerivedInput =\n | ({ mode: \"actions\"; entityToken?: undefined; actions: HostSidebarAction[] } & HostSidebarActiveNav)\n | ({\n mode: \"detail\";\n entityToken?: undefined;\n title: string;\n navSectionLabel: string;\n navItems: HostSidebarNavItem[];\n backHref: string;\n backLabel: string;\n actions: HostSidebarAction[];\n } & HostSidebarActiveNav);\n\n/** What `useHostSidebarActions` accepts: the classic explicit-token shape or a derived-token variant. */\nexport type HostSidebarHookInput = HostSidebarInput | HostSidebarDerivedInput;\n\n/** Input with the entity token resolved (supplied or derived) — the only shape that crosses\n * to the host, whose contribution contract requires the token. */\ntype ResolvedHostSidebarInput = HostSidebarInput;\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: ResolvedHostSidebarInput): HostSidebarContribution {\n // `activeNavHref` is spread only when the plugin declared one, so a contribution from a plugin\n // that says nothing is byte-identical to the pre-2.2.0 shape.\n const activeNav: HostSidebarActiveNav = input.activeNavHref ? { activeNavHref: input.activeNavHref } : {};\n if (input.mode === SIDEBAR_MODE.none) {\n return { mode: SIDEBAR_MODE.none, ...activeNav };\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 ...activeNav,\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 ...activeNav,\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: ResolvedHostSidebarInput, hostToken: string): string {\n // The declaration is part of the visible result, so it belongs in the key: without it a\n // navigation that changes ONLY the declared item would not republish and the highlight would\n // stay on the previous page's entry.\n const activeNav = input.activeNavHref ?? \"\";\n if (input.mode === SIDEBAR_MODE.none) {\n // A DECLARING `mode:\"none\"` also keys on the host's entity token, which the other two modes\n // already carry through `input.entityToken`. The host clears its whole sidebar state\n // synchronously whenever that token changes (`setCurrentToken`), and it must: one mount\n // instance is reused across page ids, so a declaration that survived the clear would light\n // the previous PAGE's entry on the next one. But the plugin has no reason to re-send a\n // declaration that has not changed, so without the token here the key stayed constant across\n // an entity change, no republish happened, and the section went dark on a sibling entity\n // (e.g. `…/fix-detail/1` -> `…/fix-detail/2`). Keying on it makes the plugin republish\n // exactly when the host discards.\n //\n // Included ONLY when there IS a declaration: an undeclared `mode:\"none\"` keeps its constant\n // key and so republishes on nothing, exactly as before. That matters — its publish is a\n // `setState(EMPTY)` that notifies every subscriber, so keying it on the location would add a\n // host re-render to every navigation of every plugin that declares nothing.\n return JSON.stringify(activeNav ? [SIDEBAR_MODE.none, activeNav, hostToken] : [SIDEBAR_MODE.none, activeNav]);\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, activeNav]);\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 activeNav,\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: HostSidebarHookInput): void {\n const api = useContext(HostSidebarActionsContext);\n const surfaceBase = useContext(SurfaceBaseContext);\n const pageId = surfaceBase?.pageId; // diagnostic tag (per-surface)\n const compatible: HostSidebarActionsApi | null =\n api && isHostChromeCompatible(api.capabilities?.contractVersion) ? api : null;\n\n // Resolve the entity token. Three cases, in order:\n // 1. Explicit string token → publish as-is (the pre-existing behaviour, unchanged).\n // 2. Property PRESENT but undefined (`entityToken: maybeToken` with an unresolved\n // token) → mode:\"none\". This preserves the long-standing \"unresolved token →\n // contribute nothing\" gate (`toHostSidebarInput` has always mapped an undefined\n // token to none) — deriving here would surface actions for an entity the page\n // has not resolved yet.\n // 3. Property ABSENT → derive the host-mirrored token from the surface base +\n // current location, so the host's (silent) token guard can never drop the\n // publish over a token-shape mismatch. The derived token re-computes on every\n // render; the content key below re-publishes when it changes (a location change\n // also makes the HOST clear its state synchronously, so a republish is required\n // with explicit tokens too — this path is no less reactive than they are).\n const resolved: ResolvedHostSidebarInput =\n input.mode === SIDEBAR_MODE.none\n ? input\n : typeof input.entityToken === \"string\"\n ? (input as ResolvedHostSidebarInput)\n : \"entityToken\" in input\n ? // An unresolved entity token suppresses the CONTRIBUTION, not the page's identity:\n // the sidebar entry the page belongs to is known before its entity has loaded, so the\n // declaration (when there is one) is carried through rather than dropped, and the\n // section stays lit while the detail data is in flight.\n { mode: SIDEBAR_MODE.none, ...(input.activeNavHref ? { activeNavHref: input.activeNavHref } : {}) }\n : {\n ...input,\n entityToken: deriveHostEntityToken(\n typeof window !== \"undefined\" ? window.location.pathname : \"\",\n surfaceBase?.basePath,\n ),\n };\n\n // Ref-map refreshed every render → dispatch always hits the latest handler (recursing groups).\n // Built from RESOLVED (not raw input): when resolution collapses to mode:\"none\" (explicit\n // undefined token), the handlers must be empty too — a stale host dispatch arriving before\n // the none-publish clears the sidebar must not invoke an unresolved-entity action.\n const actions = resolved.mode === SIDEBAR_MODE.none ? [] : resolved.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, plus — for a\n // DECLARING `mode:\"none\"` only — the host-mirrored entity token, which that mode has no\n // `entityToken` field to carry. Derived the same way the token-less authoring variants above\n // derive theirs, and re-read every render: the host mount re-renders on every location change\n // (it reads `useLocation`), so this sees the current path whenever the host's own clear fires.\n const key = contributionKey(\n resolved,\n deriveHostEntityToken(\n typeof window !== \"undefined\" ? window.location.pathname : \"\",\n surfaceBase?.basePath,\n ),\n );\n useEffect(() => {\n if (!compatible) return;\n compatible.publish(toContribution(resolved));\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, no `.`/`..`\n * path segments (the host's traversal guard rejects them after canonicalisation).\n * A 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 !slugRelativeHref\n .split(\"?\")[0]\n .split(\"/\")\n .some((seg) => seg === \".\" || seg === \"..\")\n );\n}\n\n/** The subset of {@link ToHostSidebarInputOptions} the action mapper needs — lets other\n * input builders (e.g. `menuConfigToHostSidebarInput`) reuse the SAME mapping rules\n * (slug-relative confinement, group recursion, tree-global id dedupe, dispatch wiring). */\nexport type ToHostActionsOptions = Pick<ToHostSidebarInputOptions, \"basePath\" | \"onDispatch\">;\n\nexport function toHostActions(\n actions: PluginDetailContextItem[],\n opts: ToHostActionsOptions,\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: ToHostActionsOptions,\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","import type { HeaderAction, HeaderMenuItem } from \"../components/layout/pageHeaderTypes\";\nimport type { PluginDetailContextItem } from \"../components/layout/pageHeaderTypes\";\nimport type { HostSidebarHookInput } from \"./useHostSidebarActions\";\nimport { toHostActions } from \"./toHostSidebarInput\";\n\n/**\n * Options for {@link menuConfigToHostSidebarInput}.\n */\nexport interface MenuConfigHostSidebarOptions {\n /** The CURRENT pathname (full host path, e.g. `/extensions/projects/roles`). */\n pathname: string;\n /**\n * Dispatch bridge for `actionId` actions — pass the plugin's\n * `useTriggerSidebarAction()` (from `@ethisyscore/plugin-ui/components/layout`)\n * so a sidebar click reaches the same `useSidebarActions` handler the header\n * uses. (A parameter rather than an internal hook call: the sidebar-action\n * context must come from the SAME `components/layout` module instance the\n * plugin's provider uses — the cross-entry singleton pitfall.)\n */\n onDispatch: (actionId: string) => void;\n /**\n * The plugin's mount base (e.g. `/extensions/projects`), stripped from action\n * hrefs to make them slug-relative for the host confiner. Defaults to the\n * `/extensions/<slug>` prefix of `pathname`.\n */\n mountBase?: string;\n}\n\n/** Strip a trailing slash (except root) — same normalisation as `PageHeaderProvider`. */\nfunction normalise(pathname: string): string {\n return pathname.length > 1 && pathname.endsWith(\"/\") ? pathname.slice(0, -1) : pathname;\n}\n\n/** Pre-order search for the item whose `href` exactly matches — mirrors the provider's `findByHref`. */\nfunction findByHref(items: HeaderMenuItem[], pathname: string): HeaderMenuItem | null {\n for (const item of items) {\n if (item.href != null && normalise(item.href) === pathname) {\n return item;\n }\n if (item.children) {\n const nested = findByHref(item.children, pathname);\n if (nested) {\n return nested;\n }\n }\n }\n return null;\n}\n\n/** Adapt a menu-config `HeaderAction` to the detail-context item shape `toHostActions`\n * maps (the shapes differ only in the `variant` vocabulary). */\nfunction toItem(a: HeaderAction): PluginDetailContextItem {\n return {\n id: a.id,\n label: a.label,\n icon: a.icon,\n href: a.href,\n actionId: a.actionId,\n onClick: a.onClick,\n variant: a.variant === \"danger\" ? \"danger\" : undefined,\n slot: a.slot,\n disabled: a.disabled,\n children: a.children?.map(toItem),\n };\n}\n\n/**\n * Map the ACTIVE menu item's `actions` to a `useHostSidebarActions` input — the\n * runtime analogue of the monolith shell surfacing the active `menuConfig` item's\n * actions as the sidebar \"Quick actions\" section. Detail routes are covered by\n * `toHostSidebarInput` over a `*DetailContext`; THIS covers the menu-item (list)\n * routes, which otherwise contribute nothing and render an empty Quick Actions\n * panel (static manifest `NavigationActionItem`s can only navigate — they cannot\n * cover dialog/`actionId` CTAs at all).\n *\n * Typical bridge shape:\n * ```ts\n * const input = detailCtx\n * ? toHostSidebarInput(detailCtx, { basePath, entityToken, onDispatch })\n * : menuConfigToHostSidebarInput(menuConfig, { pathname, onDispatch });\n * useHostSidebarActions(input);\n * ```\n *\n * The result carries NO `entityToken` — `useHostSidebarActions` derives the\n * host-mirrored token from the surface base, which is correct by construction.\n * Routes without a matching menu item (or with no mappable actions) yield\n * `{mode:\"none\"}`, preserving the static-manifest fallback.\n */\nexport function menuConfigToHostSidebarInput(\n config: HeaderMenuItem[],\n opts: MenuConfigHostSidebarOptions,\n): HostSidebarHookInput {\n const item = findByHref(config, normalise(opts.pathname));\n if (!item?.actions?.length) {\n return { mode: \"none\" };\n }\n const mountBase = opts.mountBase ?? /^\\/extensions\\/[^/]+/.exec(opts.pathname)?.[0];\n if (!mountBase) {\n return { mode: \"none\" };\n }\n const actions = toHostActions(item.actions.map(toItem), {\n basePath: mountBase,\n onDispatch: opts.onDispatch,\n });\n if (actions.length === 0) {\n return { mode: \"none\" };\n }\n return { mode: \"actions\", actions };\n}\n","import type { Breadcrumb, RouteMeta, RouteMetaEntry } from \"./pageHeaderTypes\";\n\n/** Synthesised root crumb — mirrors the monolith's virtual `{ pattern: \"/\", label: \"Home\" }`. */\nconst HOME_CRUMB: Breadcrumb = { label: \"Home\", href: \"/\" };\n\nfunction stripTrailingSlash(value: string): string {\n return value.length > 1 && value.endsWith(\"/\") ? value.slice(0, -1) : value;\n}\n\n/**\n * Orders routes most-specific first: more segments win, then at equal depth prefer\n * literal segments over `:param` ones — identical to the monolith's `SORTED_ROUTE_META`.\n */\nexport function sortBySpecificity(routeMeta: RouteMeta): RouteMetaEntry[] {\n return [...routeMeta].sort((a, b) => {\n const aDepth = a.pattern.split(\"/\").length;\n const bDepth = b.pattern.split(\"/\").length;\n if (bDepth !== aDepth) {\n return bDepth - aDepth;\n }\n const aDynamic = (a.pattern.match(/:/g) ?? []).length;\n const bDynamic = (b.pattern.match(/:/g) ?? []).length;\n return aDynamic - bDynamic;\n });\n}\n\n/**\n * Matches a pathname against the sorted routes segment-by-segment, capturing\n * `:param` values. Returns the first (most-specific) match, or `null`. Port of the\n * monolith's `matchRoute`.\n */\nexport function matchRoute(\n pathname: string,\n sorted: RouteMetaEntry[],\n): { entry: RouteMetaEntry; params: Record<string, string> } | null {\n const pathSegments = stripTrailingSlash(pathname).split(\"/\");\n for (const entry of sorted) {\n const patternSegments = entry.pattern.split(\"/\");\n if (patternSegments.length !== pathSegments.length) {\n continue;\n }\n const params: Record<string, string> = {};\n let matched = true;\n for (let i = 0; i < patternSegments.length; i++) {\n const pSeg = patternSegments[i];\n const vSeg = pathSegments[i];\n if (pSeg.startsWith(\":\")) {\n params[pSeg.slice(1)] = vSeg;\n } else if (pSeg !== vSeg) {\n matched = false;\n break;\n }\n }\n if (matched) {\n return { entry, params };\n }\n }\n return null;\n}\n\n/**\n * Replaces `:param` segments in a pattern with captured values. Port of\n * `resolvePattern`. The param-name class matches the identifiers `matchRoute`\n * captures (letters/digits/underscore, e.g. `:user_id`, `:id2`); when a value is\n * missing the original `:param` token is preserved rather than dropping the colon.\n */\nfunction resolvePattern(pattern: string, params: Record<string, string>): string {\n return pattern.replace(/:([A-Za-z0-9_]+)/g, (_, name: string) => params[name] ?? `:${name}`);\n}\n\n/**\n * Builds the `Home / … / <page>` breadcrumb trail — a faithful port of the\n * monolith's `buildBreadcrumbs`. Matches the current path against the `routeMeta`\n * patterns (most-specific wins), walks the `parent` chain, prepends Home, and maps\n * each ancestor to its resolved-pattern href (the current page gets no href). The\n * app/module crumb (e.g. \"Finance\") is just the top-level `routeMeta` entry — no\n * separate config, exactly like the monolith's `/finance` root entry.\n */\nexport function buildBreadcrumbs(pathname: string, routeMeta: RouteMeta): Breadcrumb[] {\n const match = matchRoute(pathname, sortBySpecificity(routeMeta));\n if (match == null) {\n return [HOME_CRUMB];\n }\n\n const { entry: current, params } = match;\n\n // Walk the parent chain (leaf → root), guarding against cycles.\n const chain: RouteMetaEntry[] = [current];\n const seen = new Set<string>([current.pattern]);\n let node = current;\n while (node.parent != null) {\n const parent = routeMeta.find((e) => e.pattern === node.parent);\n if (parent == null || seen.has(parent.pattern)) {\n break;\n }\n seen.add(parent.pattern);\n chain.unshift(parent);\n node = parent;\n }\n\n const crumbs: Breadcrumb[] = [HOME_CRUMB];\n chain.forEach((entry, index) => {\n const isLast = index === chain.length - 1;\n crumbs.push(isLast ? { label: entry.label } : { label: entry.label, href: resolvePattern(entry.pattern, params) });\n });\n return crumbs;\n}\n","import { matchRoute, sortBySpecificity } from \"../components/layout/breadcrumbs\";\nimport type { RouteMeta, RouteMetaEntry } from \"../components/layout/pageHeaderTypes\";\nimport type { HostSidebarHookInput } from \"./useHostSidebarActions\";\n\n/**\n * Options for {@link resolveActiveNavHref}.\n */\nexport interface ResolveActiveNavHrefOptions {\n /**\n * The plugin's mount base (e.g. `/extensions/projects`), stripped to make the result\n * slug-relative for the host confiner. Defaults to the mount prefix of the pathname — either\n * `/extensions/<slug>` or `/super-admin/extensions/<slug>`, the two roots the host mounts a\n * plugin surface under. Prefer passing the host-supplied `SurfaceBaseContext.basePath`\n * (resolved via `resolveSurfaceBase`) when the plugin has it: that is authoritative for the\n * page actually rendering, where this default is inferred from the URL.\n */\n basePath?: string;\n}\n\n/**\n * The plugin mount roots, longest first.\n *\n * `/super-admin/extensions/<slug>` has to be tried BEFORE `/extensions/<slug>`: the super-admin\n * URL contains the substring `/extensions/`, so an unanchored tenant-first match would slice the\n * base in the wrong place. Anchoring both at `^` and ordering longest-first makes that\n * impossible. Without the super-admin entry the helper simply returned `undefined` for every\n * super-admin surface, so its declaration never reached the host at all.\n */\nconst MOUNT_BASE_PATTERN = /^(?:\\/super-admin)?\\/extensions\\/[^/]+/;\n\n/** A pattern with no `:param` segments resolves to exactly one concrete URL, so it can name a\n * nav item. A parameterised one cannot: it stands for a family of URLs, not a menu entry. */\nfunction isConcrete(pattern: string): boolean {\n return !pattern.split(\"/\").some((seg) => seg.startsWith(\":\"));\n}\n\n/**\n * Derive the sidebar entry that owns the current route, as a slug-relative href for\n * `activeNavHref` on a `useHostSidebarActions` input.\n *\n * The source of truth is the plugin's own `routeMeta` — the `{ pattern, label, parent }` chain it\n * already declares for breadcrumbs. That chain IS the plugin's information architecture: it\n * records that `/extensions/projects/people/:id` belongs under `/extensions/projects/settings`,\n * which no amount of URL inspection can recover, because the two share no path. So this walks the\n * matched route up its `parent` chain and returns the nearest CONCRETE (param-free) ancestor:\n *\n * - `/extensions/projects/people/abc` → `settings` (via parent `/extensions/projects/settings`)\n * - `/extensions/tech/technologies/7/edit` → `technologies` (two parents up, past `…/:id`)\n * - `/extensions/projects/all` → `all` (the route is its own nav item)\n *\n * It serves both mounts: a plugin surface is mounted at `/extensions/<slug>` for tenant/admin\n * audiences and at `/super-admin/extensions/<slug>` for super-admin ones, and the base is\n * stripped from whichever the pathname is under. That matters most at the super-admin mount,\n * where the host's URL election has no detail-context path to fall back on at all.\n *\n * Returns `undefined` — meaning \"no opinion, let the host's URL election decide\" — when the path\n * matches no route, when no ancestor is concrete, or when the answer is the plugin ROOT. The root\n * is deliberately not declarable: its href prefixes every URL in the plugin, so the URL election\n * already picks it whenever nothing deeper matches, and there is no case where declaring it\n * changes an outcome.\n *\n * The result is a HINT. The host validates it against the nav items it is actually rendering and\n * falls back to its URL election if it names nothing, so a stale or mistaken `routeMeta` entry\n * degrades to today's behaviour rather than blanking the sidebar.\n */\nexport function resolveActiveNavHref(\n pathname: string,\n routeMeta: RouteMeta,\n opts: ResolveActiveNavHrefOptions = {},\n): string | undefined {\n const base = opts.basePath ?? MOUNT_BASE_PATTERN.exec(pathname)?.[0];\n if (!base) return undefined;\n const mountBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n\n const match = matchRoute(pathname, sortBySpecificity(routeMeta));\n if (match == null) return undefined;\n\n // Walk leaf → root, guarding against a cyclic `parent` chain exactly as `buildBreadcrumbs` does.\n const seen = new Set<string>([match.entry.pattern]);\n let node: RouteMetaEntry | undefined = match.entry;\n while (node != null) {\n if (isConcrete(node.pattern)) {\n const owner = node.pattern.replace(/\\/$/, \"\");\n if (owner === mountBase) return undefined; // the plugin root — nothing to declare\n const prefix = `${mountBase}/`;\n return owner.startsWith(prefix) ? owner.slice(prefix.length) : undefined;\n }\n const parentPattern: string | undefined = node.parent;\n if (parentPattern == null || seen.has(parentPattern)) return undefined;\n seen.add(parentPattern);\n node = routeMeta.find((e) => e.pattern === parentPattern);\n }\n return undefined;\n}\n\n/**\n * Attach an active-nav declaration to an existing `useHostSidebarActions` input.\n *\n * The input union is discriminated on `mode`, so a plugin cannot just spread the field on without\n * re-narrowing; this does it once, correctly, for every mode — including `mode:\"none\"`, which is\n * what the input builders return on an ordinary list route and is exactly where the declaration\n * matters most. Passing `undefined` returns the input untouched, so the call site needs no branch.\n *\n * ```ts\n * const input = withActiveNavHref(\n * menuConfigToHostSidebarInput(menuConfig, { pathname, onDispatch }),\n * resolveActiveNavHref(pathname, routeMeta),\n * );\n * useHostSidebarActions(input);\n * ```\n */\nexport function withActiveNavHref(\n input: HostSidebarHookInput,\n activeNavHref: string | undefined,\n): HostSidebarHookInput {\n if (!activeNavHref) return input;\n return { ...input, activeNavHref };\n}\n"]}