@sanity/sdk 3.7.0 → 3.8.0-next.20260930225713

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,7 +1,7 @@
1
1
  import { m as SanityConfig, s as DocumentResource, t as SanityInstance } from "../createSanityInstance--Lrldto8.js";
2
2
  import { n as StateSource, r as BoundStoreAction } from "../createStateSourceAction-CICvr0ZE.js";
3
3
  import { $t as FavoriteStatusResponse, Ai as CacheTag, Cr as createLogger, Et as GetUsersOptions, Fi as MutationDefinition, G as getQueryKey, Ii as MutationResult, Li as defineFetcher, Mi as Fetcher, Ni as FetcherDefinition, Nr as parseCommentsOptionsKey, Pi as FetcherSnapshot, Qt as FavoriteDocumentContext, Ri as defineMutation, ct as PreviewValue, en as SetFavoriteInput, ji as CacheWriter, jr as getCommentsOptionsKey, q as parseQueryKey, r as ApplicationBase, st as PreviewQueryResult } from "../applications-Bq3Z9Piv.js";
4
- import { D as ApplicationStatus, L as MessageBusTopics, O as ApplicationStatusUpdate, S as resetMessageBus, i as resolveTopic, o as InstallMessageBusOptions, r as getTopicState, u as MessageBusConnection, x as installMessageBus } from "../topicStore-CkO7ocKC.js";
4
+ import { D as ApplicationStatus, L as MessageBusTopics, O as ApplicationStatusUpdate, S as resetMessageBus, i as resolveTopic, o as InstallMessageBusOptions, r as getTopicState, u as MessageBusConnection, x as installMessageBus } from "../topicStore-CpQbrxAA.js";
5
5
  import { ClientError, SanityClient } from "@sanity/client";
6
6
  import { Observable } from "rxjs";
7
7
  import { CurrentUser } from "@sanity/types";
@@ -1,5 +1,5 @@
1
1
  import { C as getClientErrorApiBody, L as createLogger, O as isProjectUserNotFoundClientError, R as __exportAll, T as getClientErrorApiType, j as isStudioConfig, n as getClient, w as getClientErrorApiDescription } from "../clientStore-Cq3vrK4Q.js";
2
- import { D as parseCommentsOptionsKey, J as defineMutation, S as toFavoriteDocument, T as getCommentsOptionsKey, X as randomUuid, Y as randomId, d as getUsersKey, i as parseQueryKey, n as getQueryKey, p as parseUsersKey, q as defineFetcher, t as CORE_SDK_VERSION } from "../version-CB-4z0ps.js";
2
+ import { D as parseCommentsOptionsKey, J as defineMutation, S as toFavoriteDocument, T as getCommentsOptionsKey, X as randomUuid, Y as randomId, d as getUsersKey, i as parseQueryKey, n as getQueryKey, p as parseUsersKey, q as defineFetcher, t as CORE_SDK_VERSION } from "../version-iOE6pYps.js";
3
3
  import { i as pickProperties, n as isObject, t as isDeepEqual } from "../object-CxXlSVg7.js";
4
4
  import { a as isDashboardEnvironment, i as getDashboardMessageBus, l as installMessageBus, n as getTopicState, o as requireDashboardMessageBus, r as resolveTopic, u as resetMessageBus } from "../topicStore-BTwUymdG.js";
5
5
  import "@sanity/client";
@@ -1,2 +1,2 @@
1
- import { A as CapabilityRecord, B as PayloadOf, C as ApplicationActivity, E as ApplicationContext, F as FavoriteDocument, G as TopicName, H as ReplyOf, I as LocalApplication, J as ValueOf, K as TopicResult, M as DocumentActivity, N as EventTopic, P as EventTopicDef, R as NavigationLocation, T as ApplicationConfigAppType, U as StateTopic, V as RemoteModuleRef, W as StateTopicDef, Y as capabilities, _ as MessageBusMeta, a as ConnectMessageBusOptions, b as connectMessageBus, c as MessageBusAbortOptions, d as MessageBusEmitOptions, f as MessageBusEmitResult, g as MessageBusMessage, h as MessageBusHost, j as DashboardTopics, k as Capability, l as MessageBusClient, m as MessageBusErrorCode, n as TopicError, p as MessageBusError, q as Topics, s as MessageBus, t as TopicData, u as MessageBusConnection, v as MessageBusQueryOptions, w as ApplicationConfig, y as MessageBusStateSource, z as NavigationTarget } from "../topicStore-CkO7ocKC.js";
1
+ import { A as CapabilityRecord, B as PayloadOf, C as ApplicationActivity, E as ApplicationContext, F as FavoriteDocument, G as TopicName, H as ReplyOf, I as LocalApplication, J as ValueOf, K as TopicResult, M as DocumentActivity, N as EventTopic, P as EventTopicDef, R as NavigationLocation, T as ApplicationConfigAppType, U as StateTopic, V as RemoteModuleRef, W as StateTopicDef, Y as capabilities, _ as MessageBusMeta, a as ConnectMessageBusOptions, b as connectMessageBus, c as MessageBusAbortOptions, d as MessageBusEmitOptions, f as MessageBusEmitResult, g as MessageBusMessage, h as MessageBusHost, j as DashboardTopics, k as Capability, l as MessageBusClient, m as MessageBusErrorCode, n as TopicError, p as MessageBusError, q as Topics, s as MessageBus, t as TopicData, u as MessageBusConnection, v as MessageBusQueryOptions, w as ApplicationConfig, y as MessageBusStateSource, z as NavigationTarget } from "../topicStore-CpQbrxAA.js";
2
2
  export { type ApplicationActivity, type ApplicationConfig, type ApplicationConfigAppType, type ApplicationContext, type Capability, type CapabilityRecord, type ConnectMessageBusOptions, type DashboardTopics, type DocumentActivity, type EventTopic, type EventTopicDef, type FavoriteDocument, type LocalApplication, type MessageBus, type MessageBusAbortOptions, type MessageBusClient, type MessageBusConnection, type MessageBusEmitOptions, type MessageBusEmitResult, MessageBusError, type MessageBusErrorCode, type MessageBusHost, type MessageBusMessage, type MessageBusMeta, type MessageBusQueryOptions, type MessageBusStateSource, type NavigationLocation, type NavigationTarget, type PayloadOf, type RemoteModuleRef, type ReplyOf, type StateTopic, type StateTopicDef, type TopicData, TopicError, type TopicName, type TopicResult, type Topics, type ValueOf, capabilities, connectMessageBus };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { D as getTokenFromLocation, E as getDefaultLocation, F as getAuthLogger, I as configureLogging$1, L as createLogger, M as DEFAULT_API_VERSION, N as DEFAULT_BASE, P as REQUEST_TAG_PREFIX, S as getCleanedUrl, _ as postTokenRequest, b as createLoggedInAuthState, c as getDashboardOrganizationId, d as getTokenState, f as setAuthToken, g as getOAuthOptions, h as createOAuthClient, i as authStore, k as AuthStateType, l as getIsInDashboardState, m as appendResourceIndicator, n as getClient, o as getAuthState, p as runOAuthTokenRefresh, r as getClientState, s as getCurrentUserState, u as getLoginUrlState, v as serializeTokens, x as getAuthCode, y as toOAuthTokens } from "./clientStore-Cq3vrK4Q.js";
2
2
  import { c as cleanupTimer, f as isReleasePerspective, h as isMediaLibraryResource, i as bindActionGlobally, m as isDatasetResource, n as bindActionByResource, o as UPSTREAM_CLOSE_DELAY_MS, p as isCanvasResource, r as bindActionByResourceAndPerspective, s as defineStore, t as createStateSourceAction, u as setCleanupTimeout } from "./createStateSourceAction-Bn0po7Z3.js";
3
- import { A as toCommentsKeyParts, B as setPendingTransaction, C as commentsStore, E as getCommentsState, F as receiveComment, G as provisionAddonDataset, H as toCommentFieldPath, I as removeCommentById, J as defineMutation, K as COMMENTS_API_VERSION, L as restoreComments, M as applyCommentUpdate, N as clearPendingTransaction, O as resolveCommentThreads, P as getCommentsKey, R as rollbackCommentUpdate, U as assertDatasetResource, V as normalizeComment, W as getAddonDatasetState, X as randomUuid, Y as randomId, _ as updateLastLoadMoreRequest, a as resolveQuery, b as favorites, c as getAllReleasesState, d as getUsersKey, f as initializeRequest, g as setUsersError, h as setUsersData, j as addComment, k as resolveComments, l as addSubscription, m as removeSubscription, o as getPerspectiveState, p as parseUsersKey, q as defineFetcher, r as getQueryState, s as getActiveReleasesState, t as CORE_SDK_VERSION, u as cancelRequest, v as PROJECT_API_VERSION, w as getCommentThreadsState, x as setFavorite, y as USERS_STATE_CLEAR_DELAY, z as setCommentCreateError } from "./version-CB-4z0ps.js";
3
+ import { A as toCommentsKeyParts, B as setPendingTransaction, C as commentsStore, E as getCommentsState, F as receiveComment, G as provisionAddonDataset, H as toCommentFieldPath, I as removeCommentById, J as defineMutation, K as COMMENTS_API_VERSION, L as restoreComments, M as applyCommentUpdate, N as clearPendingTransaction, O as resolveCommentThreads, P as getCommentsKey, R as rollbackCommentUpdate, U as assertDatasetResource, V as normalizeComment, W as getAddonDatasetState, X as randomUuid, Y as randomId, _ as updateLastLoadMoreRequest, a as resolveQuery, b as favorites, c as getAllReleasesState, d as getUsersKey, f as initializeRequest, g as setUsersError, h as setUsersData, j as addComment, k as resolveComments, l as addSubscription, m as removeSubscription, o as getPerspectiveState, p as parseUsersKey, q as defineFetcher, r as getQueryState, s as getActiveReleasesState, t as CORE_SDK_VERSION, u as cancelRequest, v as PROJECT_API_VERSION, w as getCommentThreadsState, x as setFavorite, y as USERS_STATE_CLEAR_DELAY, z as setCommentCreateError } from "./version-iOE6pYps.js";
4
4
  import { r as omitProperty, t as isDeepEqual } from "./object-CxXlSVg7.js";
5
5
  import { t as _defineProperty } from "./defineProperty-Bho9Ngsi.js";
6
6
  import { ClientError, CorsOriginError, ServerError } from "@sanity/client";
@@ -1 +1 @@
1
- {"version":3,"file":"topicStore-BTwUymdG.js","names":["bindActionByInstance"],"sources":["../src/dashboard/messageBus/topics.ts","../src/dashboard/messageBus/bus.ts","../src/dashboard/messageBus/store.ts","../src/dashboard/messageBus/topicStore.ts"],"sourcesContent":["import {type CurrentUser} from '@sanity/types'\n\nimport {type AccessResourceType} from '../../access/checkPermissions'\nimport {type Application, type ApplicationInclude} from '../../applications/applications'\nimport {type Installation, type InstallationInclude} from '../../installations/installations'\nimport {type OrganizationBase} from '../../organization/organization'\n\n/**\n * Identifies a module federation expose and the manifest that serves it.\n * @public\n */\nexport interface RemoteModuleRef {\n readonly entry: string\n readonly moduleId: string\n readonly version: string\n}\n\n/**\n * Application types that can receive configuration modules.\n *\n * The dashboard supplies this value over the message bus, so any string is accepted; known\n * types are listed for autocomplete. `& {}` keeps the literal from collapsing into `string`.\n * @public\n */\nexport type ApplicationConfigAppType = 'media-library' | (string & {})\n\n/**\n * Identifies a configuration module for an application or application type.\n * @public\n */\nexport interface ApplicationConfig extends RemoteModuleRef {\n readonly appId?: Application['id']\n readonly appType: ApplicationConfigAppType\n}\n\n/**\n * The capabilities a host can provide.\n *\n * Reuses the Studio's rendering-context identifiers so an application can hide its own\n * implementation of anything the host provides.\n * @public\n */\nexport const capabilities = ['globalUserMenu', 'favorites', 'history'] as const\n\n/**\n * A capability a host can provide.\n * @public\n */\nexport type Capability = (typeof capabilities)[number]\n\n/**\n * The capabilities the host provides. A missing key means the host does not provide it.\n * @public\n */\nexport type CapabilityRecord = Partial<Record<Capability, boolean>>\n\n/**\n * A document a user viewed, edited, created, or deleted in an application.\n * @public\n */\nexport interface DocumentActivity {\n readonly kind: 'document'\n readonly eventType: 'viewed' | 'edited' | 'created' | 'deleted'\n readonly document: {\n readonly id: string\n readonly type: string\n readonly resource: {\n readonly id: string\n readonly type: 'dataset' | 'media-library' | 'canvas'\n /** Schema collection the document belongs to; the workspace name for studios. */\n readonly schemaName?: string\n }\n }\n}\n\n/**\n * Activity an application reports to the host. Discriminated on `kind`.\n * @public\n */\nexport type ApplicationActivity = DocumentActivity\n\n/**\n * A document a user favorited, addressed like {@link DocumentActivity}'s document.\n * @public\n */\nexport type FavoriteDocument = DocumentActivity['document']\n\n/**\n * A label rendered for an application interface; `null` clears it.\n * @internal\n */\nexport type ApplicationStatus = {label: string | null}\n\n/**\n * Updates the status rendered for an application interface.\n * @internal\n */\nexport type ApplicationStatusUpdate = {\n /** The application interface name. */\n name: string\n /** The status to render. */\n value: ApplicationStatus\n}\n\n/**\n * What an application is currently showing: the Comlink agent resource context, made generic.\n * @public\n */\nexport interface ApplicationContext {\n /** The resource the application is working in. Types follow `@sanity/client`'s resource config. */\n readonly resource: {\n readonly id: string\n readonly type: 'dataset' | 'media-library' | 'canvas'\n }\n /** The open document, or `null` when none is open. */\n readonly document: {readonly id: string} | null\n}\n\n/**\n * An application served by a CLI dev server. The workbench publishes it in the same shape as a\n * deployed application; `local` marks it and names the dev server it is served from.\n * @public\n */\nexport type LocalApplication = Application<ApplicationInclude> & {\n readonly local: {readonly host: string; readonly port: number}\n}\n\n/**\n * Declares a topic that stores and replays its current value.\n * @public\n */\nexport type StateTopicDef<T> = {kind: 'state'; value: T}\n\n/**\n * Declares a topic that delivers events and an optional reply.\n * @public\n */\nexport type EventTopicDef<P, R = never> = {\n kind: 'event'\n payload: P\n reply?: R\n}\n\n/**\n * Represents a successful topic value or a failed topic operation.\n * @public\n */\nexport type TopicResult<T> = {ok: true; value: T} | {ok: false}\n\n/**\n * Identifies a dashboard application and a route within it.\n * @public\n */\nexport type NavigationTarget = {\n /** The application ID, or `null` for dashboard-level routes. */\n appId: Application['id'] | null\n /** The route path, including its query string and fragment. */\n path: string\n}\n\n/**\n * Describes the current dashboard location and an active navigation.\n * @public\n */\nexport type NavigationLocation = NavigationTarget & {\n /** The active navigation, or `null` when navigation is idle. */\n transition: {\n /** Whether the navigation pushes or replaces browser history. */\n navigationType: 'push' | 'replace'\n /** The requested destination. */\n to: NavigationTarget\n } | null\n}\n\n/**\n * Declares the topics provided by the dashboard.\n * @public\n */\nexport interface DashboardTopics {\n /**\n * Asks the dashboard to show its access request prompt for a resource the user can't access,\n * scoped like the Access API's `/access/{resourceType}/{resourceId}/requests`. Replies once the\n * prompt is shown. The prompt handles the Access API outcome (SSO enforcement, pending or\n * declined requests, request limits), so the reply doesn't report it.\n *\n * `ok: false` reasons:\n * - `unsupported`: the dashboard doesn't show access requests for the sending application\n */\n 'access.request': EventTopicDef<\n {resourceType: Extract<AccessResourceType, 'organization' | 'project'>; resourceId: string},\n {ok: true} | {ok: false; reason: 'unsupported'}\n >\n /** Reports user activity in an application, e.g. for the recents feed. Fire-and-forget. */\n 'applications.activity': EventTopicDef<ApplicationActivity>\n /**\n * The base path for an application.\n *\n * An unknown application returns `{ok: false}`.\n */\n 'applications.base-path': StateTopicDef<TopicResult<string>>\n /** The capabilities the host provides. A missing key means the host does not provide it. */\n 'applications.capabilities': StateTopicDef<CapabilityRecord>\n /** The available application configuration modules. */\n 'applications.config': StateTopicDef<ApplicationConfig[] | null>\n /** The foreground application's context, or `null` when it has published none. */\n 'applications.context': StateTopicDef<\n (ApplicationContext & {readonly appId: Application['id']}) | null\n >\n /** Publishes the sending application's context. `null` clears it. Fire-and-forget. */\n 'applications.context.update': EventTopicDef<ApplicationContext | null>\n /** The foreground application ID, or `null` on dashboard-level routes. */\n 'applications.foreground': StateTopicDef<Application['id'] | null>\n /**\n * The dashboard applications and installations available to the current user. Each member\n * publishes its own raw serialised record, so the kinds are distinguished by shape: an\n * {@link Application} has a `type` of `'studio' | 'coreApp'`, a {@link LocalApplication} adds\n * `local`, and an {@link Installation} has no `type` and carries `applicationId` with a nested\n * `application` object.\n */\n 'applications.list': StateTopicDef<TopicResult<\n (Application<ApplicationInclude> | LocalApplication | Installation<InstallationInclude>)[]\n > | null>\n 'applications.status.update': EventTopicDef<ApplicationStatusUpdate>\n /**\n * The session token for the reading connection, or `null` while signed out. The host\n * writes it to each connection separately, so one application never sees another's token.\n */\n 'auth.token': StateTopicDef<string | null>\n /** Requests a dashboard session token. */\n 'auth.token.refresh': EventTopicDef<void, string>\n /** The signed-in user's favorited documents, published while the host provides `favorites`. */\n 'favorites.documents': StateTopicDef<FavoriteDocument[]>\n /**\n * Favorites or unfavorites a document. The host replies once `favorites.documents` reflects\n * the change, so a read after the reply sees it. A favorite is identified by its published\n * document ID, type, resource and `schemaName` (workspace).\n */\n 'favorites.update': EventTopicDef<{document: FavoriteDocument; favorited: boolean}, void>\n /** The current dashboard location and active navigation. */\n 'navigation.location': StateTopicDef<NavigationLocation | null>\n /**\n * Requests navigation and replies when the location commits.\n *\n * `ok: false` reasons:\n * - `not-navigable`: the URL cannot be handled by a dashboard application\n * - `interrupted`: another navigation superseded the request\n * - `failed`: the router rejected or did not commit the request\n */\n 'navigation.location.update': EventTopicDef<\n {\n url: string\n history?: 'push' | 'replace'\n },\n {ok: true} | {ok: false; reason: 'not-navigable' | 'interrupted' | 'failed'}\n >\n /** The current organization, or `null` without an active organization. */\n 'organizations.current': StateTopicDef<Pick<OrganizationBase, 'id' | 'name' | 'slug'> | null>\n /** The open panel, its application, display mode, and optional width in pixels. */\n 'panels.mode': StateTopicDef<TopicResult<\n | {appId: string; name: string; mode: 'aside'; size?: number}\n | {appId: string; name: string; mode: 'full'}\n | null\n > | null>\n /** Opens, updates, resizes, or closes an application's panel. */\n 'panels.mode.set': EventTopicDef<\n {name: string; mode: 'aside' | 'full'} | {name: string; size: number} | null\n >\n /** The resolved dashboard color scheme. */\n 'preferences.color-scheme': StateTopicDef<'light' | 'dark'>\n /** Whether the dashboard dock is pinned open. */\n 'preferences.dock-locked': StateTopicDef<boolean>\n /** The signed-in user, or `null` while signed out. */\n 'users.current': StateTopicDef<CurrentUser | null>\n}\n\n/**\n * Declares every topic available through the message bus.\n * @public\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type -- Declaration merging extends the SDK manifest.\nexport interface Topics extends DashboardTopics {}\n\n/**\n * Hides provisional topics from the public message bus methods.\n * @internal\n */\nexport type MessageBusTopics = Omit<Topics, 'applications.status.update'>\n\n/**\n * Every declared topic name.\n * @public\n */\nexport type TopicName<TTopics = Topics> = keyof TTopics\n\ntype StateTopicsOf<T> = {\n [K in keyof T]: T[K] extends {kind: 'state'} ? K : never\n}[keyof T]\n\n/**\n * Names of state topics exposed by message bus methods.\n * @public\n */\nexport type StateTopic<TTopics = MessageBusTopics> = StateTopicsOf<TTopics>\n\ntype TopicOwnership = {readonly type: 'same_app'} | {readonly type: 'any_app'}\n\n// State is only ever written by the host, one connection at a time, so it carries no ownership.\ntype TopicManifestEntry<T> = T extends {kind: 'state'; value: infer V}\n ? {readonly kind: 'state'; readonly seed: V | undefined}\n : {readonly kind: 'event'; readonly ownership: TopicOwnership}\n\n// The value a connection's copy of the topic starts with; `undefined` means unpublished.\nconst stateTopic = <const V>(seed: V) => ({kind: 'state', seed}) as const\n\n// `same_app` restricts responding to the application that installed the bus.\nconst dashboardEvent = {kind: 'event', ownership: {type: 'same_app'}} as const\n\ntype DashboardTopicManifest = {\n readonly [K in keyof DashboardTopics]: TopicManifestEntry<DashboardTopics[K]>\n}\n\n/**\n * Defines the runtime kind, ownership, and initial value of dashboard topics.\n * @internal\n */\nexport const DASHBOARD_TOPIC_MANIFEST: DashboardTopicManifest = {\n 'access.request': dashboardEvent,\n 'applications.activity': dashboardEvent,\n 'applications.base-path': stateTopic(undefined),\n 'applications.capabilities': stateTopic(undefined),\n 'applications.config': stateTopic(undefined),\n 'applications.context': stateTopic(undefined),\n 'applications.context.update': dashboardEvent,\n 'applications.foreground': stateTopic(undefined),\n 'applications.list': stateTopic(undefined),\n 'applications.status.update': dashboardEvent,\n 'auth.token': stateTopic(undefined),\n 'auth.token.refresh': dashboardEvent,\n 'favorites.documents': stateTopic(undefined),\n 'favorites.update': dashboardEvent,\n 'navigation.location': stateTopic(undefined),\n 'navigation.location.update': dashboardEvent,\n 'organizations.current': stateTopic(undefined),\n 'panels.mode': stateTopic({ok: true, value: null}),\n 'panels.mode.set': dashboardEvent,\n 'preferences.color-scheme': stateTopic(undefined),\n 'preferences.dock-locked': stateTopic(undefined),\n 'users.current': stateTopic(undefined),\n}\n\n/**\n * Defines the runtime manifest accepted from any message bus version.\n * @internal\n */\nexport type TopicManifest = Readonly<\n Record<\n string,\n | {readonly kind: 'state'; readonly seed: unknown}\n | {readonly kind: 'event'; readonly ownership: TopicOwnership}\n >\n>\n\n/**\n * Names of event topics exposed by message bus methods.\n * @public\n */\nexport type EventTopic<TTopics = MessageBusTopics> = {\n [K in keyof TTopics]: TTopics[K] extends {kind: 'event'} ? K : never\n}[keyof TTopics]\n\n/**\n * The value type of a state topic.\n * @public\n */\nexport type ValueOf<K extends StateTopic<TTopics>, TTopics = Topics> =\n TTopics[K] extends StateTopicDef<infer T> ? T : never\n\n/**\n * The payload type of an event topic.\n * @public\n */\nexport type PayloadOf<K extends EventTopic<TTopics>, TTopics = Topics> =\n TTopics[K] extends EventTopicDef<infer P, infer _R> ? P : never\n\n/**\n * The reply type of an event topic (`never` if it declares none).\n * @public\n */\nexport type ReplyOf<K extends EventTopic<TTopics>, TTopics = Topics> =\n TTopics[K] extends EventTopicDef<infer _P, infer R> ? R : never\n\n/**\n * Converts a topic value between 2 adjacent versions.\n * @internal\n */\nexport interface TopicMigration {\n /** The older version. */\n readonly from: number\n /** The newer version. */\n readonly to: number\n /** Converts an older state value or event payload to the newer version. */\n up(older: unknown): unknown\n /** Converts a newer state value or event payload to the older version. */\n down(newer: unknown): unknown\n /** Converts event replies between these versions. */\n readonly reply?: {\n /** Converts an older reply to the newer version. */\n up(older: unknown): unknown\n /** Converts a newer reply to the older version. */\n down(newer: unknown): unknown\n }\n}\n\n/**\n * Defines the bundled migration chain for each topic.\n * @internal\n */\nexport const topicMigrations: Partial<Record<TopicName, readonly TopicMigration[]>> = {}\n","/* eslint-disable no-console -- The bus can initialize before the SDK logger exists. */\nimport {\n BehaviorSubject,\n concat,\n defer,\n filter,\n firstValueFrom,\n from,\n map,\n type Observable,\n ReplaySubject,\n Subject,\n type Subscription,\n takeUntil,\n} from 'rxjs'\n\nimport {type Application} from '../../applications/applications'\nimport {\n DASHBOARD_TOPIC_MANIFEST,\n type EventTopic,\n type MessageBusTopics,\n type PayloadOf,\n type ReplyOf,\n type StateTopic,\n type TopicManifest,\n type TopicMigration,\n topicMigrations,\n type TopicName,\n type Topics,\n type ValueOf,\n} from './topics'\n\n/**\n * A message bus protocol error code.\n * @public\n */\nexport type MessageBusErrorCode =\n | 'NO_RESPONDER'\n | 'TIMEOUT'\n | 'ABORTED'\n | 'HANDLER_THREW'\n | 'PROTOCOL_MISMATCH'\n | 'OWNERSHIP_MISMATCH'\n | 'MISSING_APP_ID'\n | 'REFUSED'\n\n/**\n * An error raised by the message bus protocol.\n * @public\n */\nexport class MessageBusError extends Error {\n /** The machine-readable protocol error code. */\n readonly code: MessageBusErrorCode\n\n /** Creates a message bus protocol error. */\n constructor(code: MessageBusErrorCode, message?: string, options?: {cause?: unknown}) {\n super(message ?? code, options)\n this.name = 'MessageBusError'\n this.code = code\n }\n}\n\n/**\n * Metadata that travels with each message bus event.\n * @public\n */\nexport interface MessageBusMeta {\n /** The application that produced the message. */\n appId: Application['id']\n /**\n * The federation module id of the connection that sent the message (e.g.\n * `favorites/views/list/panel`). Absent from older copies and when the host\n * supplied none, so readers normalise with `message.meta.moduleId ?? message.meta.appId`.\n */\n moduleId?: string\n /** The Unix timestamp in milliseconds when the message was produced. */\n timestamp: number\n}\n\n/**\n * A message delivered to an event topic responder.\n * @public\n */\nexport interface MessageBusMessage<T, R = never, K extends PropertyKey = TopicName> {\n /** The topic that carries the message. */\n type: K\n /** The event payload. */\n payload: T\n /** The message provenance. */\n meta: MessageBusMeta\n /** Replies to an awaiting sender. */\n reply(value: R): void\n /**\n * Refuses the message: the sender's awaited `emit()` rejects with a\n * {@link MessageBusError} whose `code` is `'REFUSED'`. Ignored, with a warning,\n * once the reply has already settled.\n */\n reject(message?: string): void\n /** Aborts when the sender stops waiting for a reply. */\n readonly signal: AbortSignal\n}\n\n/**\n * An observable state topic with access to its current and first values.\n * @public\n */\nexport interface MessageBusStateSource<T> extends Observable<T> {\n /** Returns the current value, or `undefined` before the first value is published. */\n getCurrent(): T | undefined\n /** Resolves with the first published value. */\n readonly firstValue: Promise<T>\n}\n\n/**\n * Options for aborting a message bus operation.\n * @public\n */\nexport interface MessageBusAbortOptions {\n /** Aborts the operation when signaled. */\n signal?: AbortSignal\n}\n\n/**\n * Options for reading a state topic.\n * @public\n */\nexport interface MessageBusQueryOptions extends MessageBusAbortOptions {\n /** Wait timeout in milliseconds. Defaults to 5 seconds; `null` disables it. */\n timeout?: number | null\n}\n\n/**\n * Options for emitting an event topic.\n * @public\n */\nexport interface MessageBusEmitOptions extends MessageBusAbortOptions {\n /** Reply timeout in milliseconds. Defaults to 5 seconds; `null` disables it. */\n timeout?: number | null\n}\n\n/**\n * A lazily awaited event reply.\n * @public\n */\nexport interface MessageBusEmitResult<R> extends PromiseLike<R> {\n /** Handles a rejected event reply. */\n catch<T = never>(onRejected?: (reason: unknown) => T | PromiseLike<T>): Promise<R | T>\n /** Runs after the event reply settles. */\n finally(onFinally?: () => void): Promise<R>\n}\n\n/**\n * Sends events and reads typed state and event topics.\n *\n * @remarks\n * State topics are written by the host to each connection separately through\n * {@link MessageBusClient.emit}; a connection only ever reads its own values.\n * @public\n */\nexport interface MessageBus<TTopics = MessageBusTopics> {\n /** Emits an event topic and provides its reply when awaited. */\n emit<K extends EventTopic<TTopics>>(\n type: K,\n ...rest: PayloadOf<K, TTopics> extends void\n ? [payload?: void, options?: MessageBusEmitOptions]\n : [payload: PayloadOf<K, TTopics>, options?: MessageBusEmitOptions]\n ): MessageBusEmitResult<ReplyOf<K, TTopics>>\n /** Reads the current or next value of a state topic. */\n query<K extends StateTopic<TTopics>>(\n type: K,\n options?: MessageBusQueryOptions,\n ): Promise<ValueOf<K, TTopics>>\n /** Runs a responder for each event until its signal aborts. */\n subscribe<K extends EventTopic<TTopics>>(\n type: K,\n handler: (message: MessageBusMessage<PayloadOf<K, TTopics>, ReplyOf<K, TTopics>, K>) => void,\n options?: MessageBusAbortOptions,\n ): void\n /** Runs a handler for each state value until its signal aborts. */\n subscribe<K extends StateTopic<TTopics>>(\n type: K,\n handler: (value: ValueOf<K, TTopics>) => void,\n options?: MessageBusAbortOptions,\n ): void\n /** Returns a state topic as a `MessageBusStateSource`. */\n subscribe<K extends StateTopic<TTopics>>(type: K): MessageBusStateSource<ValueOf<K, TTopics>>\n /** Returns an event topic as an observable of its payloads. */\n subscribe<K extends EventTopic<TTopics>>(type: K): Observable<PayloadOf<K, TTopics>>\n}\n\n/**\n * A message bus connection that can be torn down independently of its siblings.\n * @public\n */\nexport interface MessageBusConnection<TTopics = MessageBusTopics> extends MessageBus<TTopics> {\n /** The application this connection belongs to. */\n readonly appId: Application['id']\n /**\n * Tears down this connection: pending requests reject `ABORTED`, subscriptions\n * complete, and later `emit`, `query`, and `subscribe` calls fail with `ABORTED`.\n * Siblings and the shared bus are untouched.\n */\n disconnect(): void\n}\n\n/**\n * The host's handle to one connection: who it is, when it closes, and where to write its state.\n * @public\n */\nexport interface MessageBusClient {\n /** The application the connection belongs to. */\n readonly appId: Application['id']\n /** The federation module id of the connection. Equals `appId` when the host supplied none. */\n readonly moduleId: string\n /** Aborts when the connection disconnects. Drop per-client state here. */\n readonly closed: AbortSignal\n /** Writes a state value to this connection only. Ignored once the connection has closed. */\n emit<K extends StateTopic>(type: K, value: ValueOf<K>): void\n /**\n * Rejects a state topic for this connection: its pending and later {@link MessageBus.query}\n * calls reject with a {@link MessageBusError} whose `code` is `'REFUSED'`, until the host next\n * writes a value with {@link MessageBusClient.emit}, which clears the rejection.\n *\n * @remarks\n * A rejected promise cannot be un-rejected and an errored subject cannot resume, so the\n * rejection is tracked per connection alongside the topic's subject rather than by erroring\n * it: `query` consults it before awaiting, and {@link MessageBus.subscribe} and the topic's\n * {@link MessageBusStateSource} stay silent (they never error) until a value is written.\n * Ignored once the connection has closed, the same as {@link MessageBusClient.emit}.\n */\n reject<K extends StateTopic>(type: K, message?: string): void\n}\n\n/**\n * The host's connection to the bus.\n *\n * @remarks\n * State reaches an application only through {@link MessageBusClient.emit}. There is no\n * broadcast: sending the same value to everyone is a loop over `connections`, and giving a\n * new connection the current values is done when it appears there, as an SSE server writes\n * a snapshot to each new client.\n * @public\n */\nexport interface MessageBusHost<TTopics = MessageBusTopics> extends MessageBusConnection<TTopics> {\n /**\n * Every open connection other than this one first, then each new one as it joins. The same\n * {@link MessageBusClient} object is handed out for a connection across subscriptions, so it\n * can be kept in a `Set`. A connection stays listed until it calls `disconnect()`; the SDK\n * does that when the owning `SanityInstance` is disposed.\n */\n readonly connections: Observable<MessageBusClient>\n}\n\nconst MESSAGE_BUS_KEY = Symbol.for('sanity.os.bus')\nconst MESSAGE_BUS_PROTOCOL_KEY = Symbol.for('sanity.os.protocol')\nconst MESSAGE_BUS_REGISTRY_KEY = Symbol.for('sanity.os.registry')\nconst MESSAGE_BUS_PENDING_REPLY_KEY = Symbol.for('sanity.os.request')\n\nconst MESSAGE_BUS_PROTOCOL = 1\n\nconst DEFAULT_TIMEOUT_MS = 5000\n\n// Distinguishes an unpublished topic from a published `undefined` value.\nconst NO_VALUE = Symbol.for('sanity.os.no-value')\n\ntype ReplyOutcome =\n | {readonly ok: true; readonly value: unknown}\n | {readonly ok: false; readonly error: unknown}\n\n// Defers Promise creation until the reply is awaited to preserve fire-and-forget emission.\ninterface PendingReply {\n readonly responderAbort: AbortController\n readonly responderSignal: AbortSignal\n settled: boolean\n outcomeBeforeAwait?: ReplyOutcome\n settlePromise?: (outcome: ReplyOutcome) => void\n replyPromise?: Promise<unknown>\n}\n\n// One connection: its identity, its own copy of every state topic, and its lifetime.\ninterface ConnectionRecord {\n readonly appId: string\n readonly moduleId: string\n readonly stateSubjects: Map<string, BehaviorSubject<unknown>>\n // Topics the host has rejected for this connection, with the reason, until the next write.\n readonly rejections: Map<string, string | undefined>\n // Fires the rejected topic name so pending queries on that topic can reject at once.\n readonly rejected$: Subject<string>\n readonly abort: AbortController\n}\n\ninterface MessageBusRegistry {\n readonly appId: string\n readonly topics: Map<string, TopicManifest[string]>\n readonly connections: Set<ConnectionRecord>\n readonly connected$: Subject<ConnectionRecord>\n readonly eventSubjects: Map<string, Subject<MessageBusMessage<unknown, unknown>>>\n readonly responderCounts: Map<string, number>\n readonly migrations: ReadonlyMap<string, readonly TopicMigration[]>\n resetAbort: AbortController\n generation: number\n}\n\ntype InternalMessageBus = MessageBus & {\n [MESSAGE_BUS_REGISTRY_KEY]: MessageBusRegistry\n [MESSAGE_BUS_PROTOCOL_KEY]: number\n}\n\n// A connection's box for a state topic, created on first use from the topic's manifest seed.\nfunction resolveStateSubject(\n registry: MessageBusRegistry,\n connection: ConnectionRecord,\n type: string,\n): BehaviorSubject<unknown> {\n // Callers gate on the connection's lifetime; this keeps a slipped call from recreating a\n // box on a closed connection that nothing would ever complete.\n if (connection.abort.signal.aborted) throw new MessageBusError('ABORTED')\n let subject = connection.stateSubjects.get(type)\n if (!subject) {\n const topic = registry.topics.get(type)\n if (!topic) {\n console.warn(`[sanity-sdk:message-bus] state topic \"${type}\" is not declared`)\n }\n const seed = topic?.kind === 'state' ? topic.seed : undefined\n subject = new BehaviorSubject<unknown>(seed === undefined ? NO_VALUE : seed)\n connection.stateSubjects.set(type, subject)\n }\n return subject\n}\n\nfunction toStateSource(\n values: Observable<unknown>,\n getCurrent: () => unknown,\n): MessageBusStateSource<unknown> {\n const source = values as MessageBusStateSource<unknown>\n source.getCurrent = getCurrent\n // Unread sources must not create promises that reject when reset completes them.\n let firstValue: Promise<unknown> | undefined\n Object.defineProperty(source, 'firstValue', {\n get: () => (firstValue ??= firstValueFrom(values)),\n })\n return source\n}\n\nfunction assertCompatibleTopicManifest(\n registry: MessageBusRegistry,\n manifest: TopicManifest,\n): void {\n for (const [type, entry] of Object.entries(manifest)) {\n const existing = registry.topics.get(type)\n if (existing && existing.kind !== entry.kind) {\n throw new MessageBusError(\n 'PROTOCOL_MISMATCH',\n `topic \"${type}\" is declared \"${entry.kind}\" but the installed bus knows it as \"${existing.kind}\"`,\n )\n }\n if (\n existing?.kind === 'event' &&\n entry.kind === 'event' &&\n existing.ownership.type !== entry.ownership.type\n ) {\n throw new MessageBusError('OWNERSHIP_MISMATCH', `topic \"${type}\" has conflicting ownership`)\n }\n }\n}\n\nfunction mergeTopicManifest(registry: MessageBusRegistry, manifest: TopicManifest): void {\n assertCompatibleTopicManifest(registry, manifest)\n for (const [type, entry] of Object.entries(manifest)) registry.topics.set(type, entry)\n}\n\nfunction resolveEventSubject(\n registry: MessageBusRegistry,\n type: string,\n): Subject<MessageBusMessage<unknown, unknown>> {\n let subject = registry.eventSubjects.get(type)\n if (!subject) {\n subject = new Subject<MessageBusMessage<unknown, unknown>>()\n registry.eventSubjects.set(type, subject)\n }\n return subject\n}\n\nfunction settleReply(reply: PendingReply, outcome: ReplyOutcome): void {\n if (reply.settled) return\n reply.settled = true\n if (reply.settlePromise) reply.settlePromise(outcome)\n else reply.outcomeBeforeAwait = outcome\n}\n\nfunction createEventMessage(\n appId: string,\n moduleId: string,\n type: string,\n payload: unknown,\n pendingReply: PendingReply,\n): MessageBusMessage<unknown, unknown> {\n const message: MessageBusMessage<unknown, unknown> & {\n [MESSAGE_BUS_PENDING_REPLY_KEY]?: PendingReply\n } = {\n type: type as TopicName,\n payload,\n meta: {appId, moduleId, timestamp: Date.now()},\n reply: (value) => {\n if (pendingReply.settled) {\n console.warn(\n `[sanity-sdk:message-bus] reply ignored for \"${type}\": no waiting caller or already replied`,\n )\n return\n }\n settleReply(pendingReply, {ok: true, value})\n },\n reject: (reason) => {\n if (pendingReply.settled) {\n console.warn(\n `[sanity-sdk:message-bus] reject ignored for \"${type}\": no waiting caller or already replied`,\n )\n return\n }\n settleReply(pendingReply, {ok: false, error: new MessageBusError('REFUSED', reason)})\n },\n get signal() {\n return pendingReply.responderSignal\n },\n }\n\n message[MESSAGE_BUS_PENDING_REPLY_KEY] = pendingReply\n return message\n}\n\nfunction createReplyPromise(\n pendingReply: PendingReply,\n options: MessageBusEmitOptions & {hadResponderAtEmission: boolean},\n): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n const settlePromise = (outcome: ReplyOutcome) =>\n outcome.ok ? resolve(outcome.value) : reject(outcome.error)\n\n if (pendingReply.outcomeBeforeAwait) {\n pendingReply.responderAbort.abort()\n settlePromise(pendingReply.outcomeBeforeAwait)\n return\n }\n if (!options.hadResponderAtEmission) {\n reject(new MessageBusError('NO_RESPONDER'))\n return\n }\n\n const timeoutMs = options.timeout === undefined ? DEFAULT_TIMEOUT_MS : options.timeout\n let timer: ReturnType<typeof setTimeout> | undefined\n const onAbort = () =>\n settleReply(pendingReply, {ok: false, error: new MessageBusError('ABORTED')})\n\n pendingReply.settlePromise = (outcome) => {\n if (timer !== undefined) clearTimeout(timer)\n options.signal?.removeEventListener('abort', onAbort)\n pendingReply.responderAbort.abort()\n settlePromise(outcome)\n }\n\n if (timeoutMs !== null) {\n timer = setTimeout(\n () => settleReply(pendingReply, {ok: false, error: new MessageBusError('TIMEOUT')}),\n timeoutMs,\n )\n }\n if (options.signal) {\n if (options.signal.aborted) onAbort()\n else options.signal.addEventListener('abort', onAbort, {once: true})\n }\n })\n}\n\nfunction createLazyReply<R>(awaitReply: () => Promise<R>): MessageBusEmitResult<R> {\n return {\n // oxlint-disable-next-line unicorn/no-thenable -- awaiting arms reply failure handling; fire-and-forget does not.\n then: (onFulfilled, onRejected) => awaitReply().then(onFulfilled, onRejected),\n catch: (onRejected) => awaitReply().then(undefined, onRejected),\n finally: (onFinally) => awaitReply().finally(onFinally),\n }\n}\n\nfunction emitEvent(\n registry: MessageBusRegistry,\n type: string,\n payload: unknown,\n options: MessageBusEmitOptions | undefined,\n appId: string,\n moduleId: string,\n): MessageBusEmitResult<unknown> {\n const hadResponderAtEmission = (registry.responderCounts.get(type) ?? 0) > 0\n const responderAbort = new AbortController()\n const pendingReply: PendingReply = {\n responderAbort,\n responderSignal: scopeSignal(options?.signal, responderAbort.signal),\n settled: false,\n }\n\n resolveEventSubject(registry, type).next(\n createEventMessage(appId, moduleId, type, payload, pendingReply),\n )\n\n const awaitReply = () =>\n (pendingReply.replyPromise ??= createReplyPromise(pendingReply, {\n ...options,\n hadResponderAtEmission,\n }))\n return createLazyReply(awaitReply)\n}\n\nconst isStateTopic = (registry: MessageBusRegistry, type: string): boolean =>\n registry.topics.get(type)?.kind === 'state'\n\nconst canRespond = (\n registry: MessageBusRegistry,\n ownership: {readonly type: 'same_app' | 'any_app'},\n appId: string,\n): boolean => ownership.type === 'any_app' || appId === registry.appId\n\nfunction emit(\n registry: MessageBusRegistry,\n type: string,\n payload: unknown,\n options: MessageBusEmitOptions | undefined,\n appId: string,\n moduleId: string,\n connectionSignal: AbortSignal,\n): MessageBusEmitResult<unknown> {\n if (isStateTopic(registry, type)) {\n throw new MessageBusError(\n 'OWNERSHIP_MISMATCH',\n `Cannot emit state topic \"${type}\" from app \"${appId}\". The host writes state to each connection through its client handle. Read it with query() or subscribe().`,\n )\n }\n return emitEvent(\n registry,\n type,\n payload,\n {\n ...options,\n signal: scopeSignal(options?.signal, registry.resetAbort.signal, connectionSignal),\n },\n appId,\n moduleId,\n )\n}\n\nfunction emitState(\n registry: MessageBusRegistry,\n connection: ConnectionRecord,\n type: string,\n value: unknown,\n): void {\n // A written value clears any rejection so later queries resolve normally.\n connection.rejections.delete(type)\n const subject = resolveStateSubject(registry, connection, type)\n if (!Object.is(subject.getValue(), value)) subject.next(value)\n}\n\nfunction query(\n source: MessageBusStateSource<unknown>,\n type: string,\n options: MessageBusQueryOptions | undefined,\n connectionSignal: AbortSignal,\n resetSignal: AbortSignal,\n connection: ConnectionRecord,\n): Promise<unknown> {\n const rejection = () => new MessageBusError('REFUSED', connection.rejections.get(type))\n // A rejection outranks any earlier value: it is cleared only when the host writes a new one.\n if (connection.rejections.has(type)) return Promise.reject(rejection())\n const current = source.getCurrent()\n if (current !== undefined) return Promise.resolve(current)\n\n const signal = scopeSignal(options?.signal, resetSignal, connectionSignal)\n if (signal.aborted) return Promise.reject(new MessageBusError('ABORTED'))\n const timeoutMs = options?.timeout === undefined ? DEFAULT_TIMEOUT_MS : options.timeout\n return new Promise((resolve, reject) => {\n const timer =\n timeoutMs === null\n ? undefined\n : setTimeout(() => {\n clear()\n signal?.removeEventListener('abort', onAbort)\n reject(new MessageBusError('TIMEOUT', `query(\"${type}\") timed out`))\n }, timeoutMs)\n // A rejection that arrives while waiting rejects the pending query at once.\n const rejectedSub = connection.rejected$.subscribe((rejectedType) => {\n if (rejectedType !== type) return\n clear()\n signal?.removeEventListener('abort', onAbort)\n reject(rejection())\n })\n const clear = () => {\n if (timer !== undefined) clearTimeout(timer)\n rejectedSub.unsubscribe()\n }\n const onAbort = () => {\n clear()\n reject(new MessageBusError('ABORTED'))\n }\n signal?.addEventListener('abort', onAbort, {once: true})\n void source.firstValue.then(\n (value) => {\n clear()\n signal?.removeEventListener('abort', onAbort)\n resolve(value)\n },\n (error) => {\n clear()\n signal?.removeEventListener('abort', onAbort)\n reject(error)\n },\n )\n })\n}\n\nfunction unsubscribeOnAbort(subscription: Subscription, signal: AbortSignal | undefined): void {\n if (!signal) return\n if (signal.aborted) {\n subscription.unsubscribe()\n return\n }\n signal.addEventListener('abort', () => subscription.unsubscribe(), {\n once: true,\n })\n}\n\nfunction scopeSignal(signal: AbortSignal | undefined, ...lifecycles: AbortSignal[]): AbortSignal {\n return signal ? AbortSignal.any([signal, ...lifecycles]) : AbortSignal.any(lifecycles)\n}\n\nfunction invokeResponder(\n handler: (message: MessageBusMessage<unknown, unknown>) => unknown,\n message: MessageBusMessage<unknown, unknown> & {\n [MESSAGE_BUS_PENDING_REPLY_KEY]?: PendingReply\n },\n): void {\n const pendingReply = message[MESSAGE_BUS_PENDING_REPLY_KEY]\n const fail = (error: unknown) => {\n if (pendingReply) {\n settleReply(pendingReply, {\n ok: false,\n error: new MessageBusError('HANDLER_THREW', undefined, {\n cause: error,\n }),\n })\n }\n }\n try {\n const result = handler(message)\n if (result instanceof Promise) result.catch(fail)\n } catch (error) {\n fail(error)\n }\n}\n\nfunction respond(\n registry: MessageBusRegistry,\n type: string,\n handler: (message: MessageBusMessage<unknown, unknown>) => unknown,\n options: MessageBusAbortOptions | undefined,\n appId: string,\n connectionSignal: AbortSignal,\n): void {\n const topic = registry.topics.get(type)\n if (topic?.kind === 'event' && !canRespond(registry, topic.ownership, appId)) {\n throw new MessageBusError(\n 'OWNERSHIP_MISMATCH',\n `Cannot register a handler for event topic \"${type}\" from app \"${appId}\". Only the app that owns this topic can respond to it. Other apps can send it with emit().`,\n )\n }\n\n registry.responderCounts.set(type, (registry.responderCounts.get(type) ?? 0) + 1)\n const subscription = resolveEventSubject(registry, type).subscribe((message) =>\n invokeResponder(handler, message),\n )\n subscription.add(() => {\n const count = registry.responderCounts.get(type) ?? 0\n if (count > 0) registry.responderCounts.set(type, count - 1)\n })\n unsubscribeOnAbort(\n subscription,\n scopeSignal(options?.signal, registry.resetAbort.signal, connectionSignal),\n )\n}\n\nconst bundledMigrations = () => new Map(Object.entries(topicMigrations))\n\n/**\n * Creates an isolated message bus without installing it globally and returns the host's\n * connection to it.\n * @internal\n */\nexport function createIsolatedMessageBus(\n appId: string,\n config: {\n migrations?: ReadonlyMap<string, readonly TopicMigration[]>\n } = {},\n): MessageBusHost<Topics> {\n if (!appId) throwMissingAppId()\n\n const registry: MessageBusRegistry = {\n appId,\n topics: new Map(),\n connections: new Set(),\n connected$: new Subject(),\n eventSubjects: new Map(),\n responderCounts: new Map(),\n migrations: config.migrations ?? bundledMigrations(),\n resetAbort: new AbortController(),\n generation: 0,\n }\n\n mergeTopicManifest(registry, DASHBOARD_TOPIC_MANIFEST)\n\n return createConnection(registry, {\n appId,\n migrations: config.migrations,\n }) as MessageBusHost<Topics>\n}\n\ntype TopicVersionAdapter = {\n toInstalled: (type: string, value: unknown) => unknown\n toApplication: (type: string, value: unknown) => unknown\n}\n\ntype TopicCompatibility = {\n toInstalledEmission: TopicVersionAdapter['toInstalled']\n toApplicationStateValue: TopicVersionAdapter['toApplication']\n toApplicationEventPayload: TopicVersionAdapter['toApplication']\n toInstalledEventReply: TopicVersionAdapter['toInstalled']\n toApplicationEventReply: TopicVersionAdapter['toApplication']\n}\n\ntype TopicMigrationTransform = Pick<TopicMigration, 'up' | 'down'>\n\nconst identityMigration: TopicMigrationTransform = {\n up: (value) => value,\n down: (value) => value,\n}\n\nfunction effectiveTopicVersions(\n migrations: ReadonlyMap<string, readonly TopicMigration[]>,\n): Map<string, number> {\n const versions = new Map<string, number>()\n for (const [topic, steps] of migrations) {\n let version = 1\n for (const step of steps) if (step.to > version) version = step.to\n versions.set(topic, version)\n }\n return versions\n}\n\nfunction migrationTransformAt(\n steps: readonly TopicMigration[] | undefined,\n version: number,\n select: (step: TopicMigration) => TopicMigrationTransform | undefined,\n): TopicMigrationTransform {\n const step = steps?.find((candidate) => candidate.from === version)\n return (step ? select(step) : undefined) ?? identityMigration\n}\n\nfunction migrateVersionedValue(\n steps: readonly TopicMigration[] | undefined,\n value: unknown,\n from: number,\n to: number,\n select: (step: TopicMigration) => TopicMigrationTransform | undefined,\n): unknown {\n if (from === to) return value\n\n let current = value\n if (from < to) {\n for (let version = from; version < to; version++) {\n current = migrationTransformAt(steps, version, select).up(current)\n }\n } else {\n for (let version = from; version > to; version--) {\n current = migrationTransformAt(steps, version - 1, select).down(current)\n }\n }\n return current\n}\n\nfunction createTopicCompatibility(\n installedMigrations: ReadonlyMap<string, readonly TopicMigration[]>,\n applicationMigrations: ReadonlyMap<string, readonly TopicMigration[]>,\n): TopicCompatibility {\n const installedVersions = effectiveTopicVersions(installedMigrations)\n const applicationVersions = effectiveTopicVersions(applicationMigrations)\n const applicationVersion = (type: string) => applicationVersions.get(type) ?? 1\n const installedVersion = (type: string) => installedVersions.get(type) ?? 1\n const migrationChainFor = (type: string) =>\n (applicationVersion(type) > installedVersion(type)\n ? applicationMigrations\n : installedMigrations\n ).get(type)\n const createVersionAdapter = (\n select: (step: TopicMigration) => TopicMigrationTransform | undefined,\n ): TopicVersionAdapter => ({\n toInstalled: (type, value) =>\n migrateVersionedValue(\n migrationChainFor(type),\n value,\n applicationVersion(type),\n installedVersion(type),\n select,\n ),\n toApplication: (type, value) =>\n migrateVersionedValue(\n migrationChainFor(type),\n value,\n installedVersion(type),\n applicationVersion(type),\n select,\n ),\n })\n\n const stateValueAndEventPayload = createVersionAdapter((step) => step)\n const eventReply = createVersionAdapter((step) => step.reply)\n return {\n toInstalledEmission: stateValueAndEventPayload.toInstalled,\n toApplicationStateValue: stateValueAndEventPayload.toApplication,\n toApplicationEventPayload: stateValueAndEventPayload.toApplication,\n toInstalledEventReply: eventReply.toInstalled,\n toApplicationEventReply: eventReply.toApplication,\n }\n}\n\n// Cache projections by input reference to keep state snapshots stable.\nfunction projectCurrent(input: () => unknown, project: (value: unknown) => unknown): () => unknown {\n let lastInput: unknown\n let lastOutput: unknown\n let cached = false\n return () => {\n const current = input()\n if (current === undefined) return undefined\n if (!cached || current !== lastInput) {\n lastInput = current\n lastOutput = project(current)\n cached = true\n }\n return lastOutput\n }\n}\n\n// The connection's view of one of its state boxes, in the connection's topic version.\nfunction createStateSource(\n subject: BehaviorSubject<unknown>,\n project: (value: unknown) => unknown,\n completeOn: Observable<unknown>,\n): MessageBusStateSource<unknown> {\n const getCurrent = () => {\n const current = subject.getValue()\n return current === NO_VALUE ? undefined : current\n }\n return toStateSource(\n subject.pipe(\n filter((value) => value !== NO_VALUE),\n map(project),\n takeUntil(completeOn),\n ),\n projectCurrent(getCurrent, project),\n )\n}\n\nfunction migrateEventMessage(\n message: MessageBusMessage<unknown, unknown>,\n payload: (value: unknown) => unknown,\n reply: (value: unknown) => unknown,\n): MessageBusMessage<unknown, unknown> {\n return {\n type: message.type,\n payload: payload(message.payload),\n meta: message.meta,\n reply: (value) => message.reply(reply(value)),\n reject: (reason) => message.reject(reason),\n get signal() {\n return message.signal\n },\n }\n}\n\nfunction migrateEventReply(\n result: MessageBusEmitResult<unknown>,\n project: (value: unknown) => unknown,\n): MessageBusEmitResult<unknown> {\n let projected: Promise<unknown> | undefined\n return createLazyReply(() => (projected ??= Promise.resolve(result).then(project)))\n}\n\nfunction createRejectedConnection(\n appId: string,\n connectionError: () => unknown,\n): MessageBusConnection {\n const throwConnectionError = (): never => {\n throw connectionError()\n }\n return {\n appId,\n emit: throwConnectionError,\n // `query` is typed as a Promise, so it must reject rather than throw synchronously.\n query: () => Promise.reject(connectionError()),\n subscribe: throwConnectionError,\n disconnect: () => {},\n } as unknown as MessageBusConnection\n}\n\n/**\n * Options for connecting an application to an isolated message bus.\n * @internal\n */\nexport interface ConnectApplicationToMessageBusOptions {\n /** The application ID stamped on emitted messages. */\n appId: string\n /** The federation module id stamped on emitted messages. Defaults to `appId`. */\n moduleId?: string\n /** The topic migrations supported by the application. */\n migrations?: ReadonlyMap<string, readonly TopicMigration[]>\n}\n\n/**\n * Connects an application to an isolated message bus.\n * @internal\n */\nexport function connectApplicationToMessageBus(\n installedMessageBus: MessageBus,\n config: ConnectApplicationToMessageBusOptions,\n): MessageBusConnection<Topics> {\n if (!config.appId) throwMissingAppId()\n\n const {appId} = config\n const installedProtocol = (installedMessageBus as Partial<InternalMessageBus>)[\n MESSAGE_BUS_PROTOCOL_KEY\n ]\n if (installedProtocol !== MESSAGE_BUS_PROTOCOL) {\n console.error(\n `[sanity-sdk:message-bus] protocol mismatch for \"${appId}\": installed ${String(installedProtocol)}, this copy speaks ${MESSAGE_BUS_PROTOCOL}`,\n )\n return createRejectedConnection(\n appId,\n () =>\n new MessageBusError(\n 'PROTOCOL_MISMATCH',\n `installed message bus speaks protocol ${String(installedProtocol)}, this copy speaks ${MESSAGE_BUS_PROTOCOL}`,\n ),\n )\n }\n\n const registry = (installedMessageBus as Partial<InternalMessageBus>)[MESSAGE_BUS_REGISTRY_KEY]\n if (!registry) {\n console.error(`[sanity-sdk:message-bus] incompatible message bus for \"${appId}\"`)\n return createRejectedConnection(\n appId,\n () =>\n new MessageBusError(\n 'PROTOCOL_MISMATCH',\n 'installed message bus does not expose a compatible registry',\n ),\n )\n }\n\n try {\n mergeTopicManifest(registry, DASHBOARD_TOPIC_MANIFEST)\n } catch (error) {\n console.error(`[sanity-sdk:message-bus] topic manifest conflict for \"${appId}\"`, {error})\n return createRejectedConnection(appId, () => error)\n }\n\n return createConnection(registry, config) as MessageBusConnection<Topics>\n}\n\n// Hands the host the same client object for a connection across `connections` subscriptions.\nfunction createClient(\n registry: MessageBusRegistry,\n record: ConnectionRecord,\n compatibility: TopicCompatibility,\n): MessageBusClient {\n return {\n appId: record.appId,\n moduleId: record.moduleId,\n closed: record.abort.signal,\n emit: (type: string, value: unknown) => {\n if (record.abort.signal.aborted) {\n console.warn(\n `[sanity-sdk:message-bus] \"${type}\" not written: connection \"${record.moduleId}\" has closed`,\n )\n return\n }\n emitState(registry, record, type, compatibility.toInstalledEmission(type, value))\n },\n reject: (type: string, message?: string) => {\n if (record.abort.signal.aborted) return\n record.rejections.set(type, message)\n record.rejected$.next(type)\n },\n } as MessageBusClient\n}\n\n// Open connections other than the host's own first, then each new one; one client object per\n// connection so the host can keep them in a Set. Completes when the host connection disconnects.\nfunction createConnectionsSource(\n registry: MessageBusRegistry,\n self: ConnectionRecord,\n compatibility: TopicCompatibility,\n completeOn: Observable<unknown>,\n): Observable<MessageBusClient> {\n const clients = new WeakMap<ConnectionRecord, MessageBusClient>()\n const clientFor = (record: ConnectionRecord) => {\n let client = clients.get(record)\n if (!client) {\n client = createClient(registry, record, compatibility)\n clients.set(record, client)\n }\n return client\n }\n return defer(() => concat(from([...registry.connections]), registry.connected$)).pipe(\n filter((record) => record !== self),\n map(clientFor),\n takeUntil(completeOn),\n )\n}\n\nfunction createConnection(\n registry: MessageBusRegistry,\n config: ConnectApplicationToMessageBusOptions,\n): MessageBusConnection {\n const {appId} = config\n const moduleId = config.moduleId ?? appId\n // Each connection owns its lifetime; disconnect() aborts it without touching siblings.\n const connectionAbort = new AbortController()\n const connectionSignal = connectionAbort.signal\n // ReplaySubject, not fromEvent: `abort` fires once, so a cold listener attached by a\n // stream subscribed after disconnect() would never see it and never complete. Replaying\n // the notification lets those late subscribers complete immediately.\n const connectionAborted$ = new ReplaySubject<void>(1)\n connectionSignal.addEventListener('abort', () => connectionAborted$.next(), {once: true})\n\n const record: ConnectionRecord = {\n appId,\n moduleId,\n stateSubjects: new Map(),\n rejections: new Map(),\n rejected$: new Subject<string>(),\n abort: connectionAbort,\n }\n\n const compatibility = createTopicCompatibility(\n registry.migrations,\n config.migrations ?? bundledMigrations(),\n )\n const isState = (type: string) => isStateTopic(registry, type)\n\n // Reuse topic streams because React external-store snapshots require stable references.\n const applicationStreams = new Map<string, MessageBusStateSource<unknown> | Observable<unknown>>()\n let streamGeneration = registry.generation\n const cachedStream = <T extends MessageBusStateSource<unknown> | Observable<unknown>>(\n type: string,\n create: () => T,\n ): T => {\n if (streamGeneration !== registry.generation) {\n applicationStreams.clear()\n streamGeneration = registry.generation\n }\n let stream = applicationStreams.get(type)\n if (!stream) {\n stream = create()\n applicationStreams.set(type, stream)\n }\n return stream as T\n }\n const stateSource = (type: string) =>\n cachedStream(type, () =>\n createStateSource(\n resolveStateSubject(registry, record, type),\n (value) => compatibility.toApplicationStateValue(type, value),\n connectionAborted$,\n ),\n )\n const eventStream = (type: string) =>\n cachedStream(type, () =>\n resolveEventSubject(registry, type).pipe(\n map((message) => compatibility.toApplicationEventPayload(type, message.payload)),\n takeUntil(connectionAborted$),\n ),\n )\n\n // A disconnected connection must not reach siblings, so its operations fail before touching\n // the shared registry rather than only tearing down pending requests and subscriptions.\n const throwIfDisconnected = () => {\n if (connectionSignal.aborted) throw new MessageBusError('ABORTED')\n }\n\n const connection = {\n appId,\n emit: (type: string, payload: unknown, options?: MessageBusEmitOptions) => {\n throwIfDisconnected()\n return migrateEventReply(\n emit(\n registry,\n type,\n compatibility.toInstalledEmission(type, payload),\n options,\n appId,\n moduleId,\n connectionSignal,\n ),\n (value) => compatibility.toApplicationEventReply(type, value),\n )\n },\n query: (type: string, options?: MessageBusQueryOptions) => {\n if (connectionSignal.aborted) return Promise.reject(new MessageBusError('ABORTED'))\n return query(\n stateSource(type),\n type,\n options,\n connectionSignal,\n registry.resetAbort.signal,\n record,\n )\n },\n subscribe: (\n type: string,\n handler?: (arg: never) => void,\n options?: MessageBusAbortOptions,\n ): MessageBusStateSource<unknown> | Observable<unknown> | undefined => {\n throwIfDisconnected()\n if (isState(type)) {\n const source = stateSource(type)\n if (!handler) return source\n unsubscribeOnAbort(\n source.subscribe(handler as (value: unknown) => void),\n scopeSignal(options?.signal, registry.resetAbort.signal, connectionSignal),\n )\n return undefined\n }\n if (!handler) return eventStream(type)\n respond(\n registry,\n type,\n (message) =>\n (handler as (message: MessageBusMessage<unknown, unknown>) => void)(\n migrateEventMessage(\n message,\n (value) => compatibility.toApplicationEventPayload(type, value),\n (value) => compatibility.toInstalledEventReply(type, value),\n ),\n ),\n options,\n appId,\n connectionSignal,\n )\n return undefined\n },\n disconnect: () => {\n if (connectionSignal.aborted) return\n connectionAbort.abort()\n for (const subject of record.stateSubjects.values()) subject.complete()\n record.stateSubjects.clear()\n record.rejected$.complete()\n registry.connections.delete(record)\n },\n }\n\n if (appId === registry.appId) {\n Object.assign(connection, {\n connections: createConnectionsSource(registry, record, compatibility, connectionAborted$),\n })\n }\n\n const instance = connection as unknown as InternalMessageBus\n instance[MESSAGE_BUS_REGISTRY_KEY] = registry\n instance[MESSAGE_BUS_PROTOCOL_KEY] = MESSAGE_BUS_PROTOCOL\n\n // Announce last: a host subscriber may write to the new client synchronously.\n registry.connections.add(record)\n registry.connected$.next(record)\n return instance as unknown as MessageBusConnection\n}\n\nfunction reset(registry: MessageBusRegistry): void {\n registry.resetAbort.abort()\n for (const connection of registry.connections) {\n for (const subject of connection.stateSubjects.values()) subject.complete()\n connection.stateSubjects.clear()\n connection.rejections.clear()\n }\n for (const subject of registry.eventSubjects.values()) subject.complete()\n\n registry.topics.clear()\n registry.eventSubjects.clear()\n registry.responderCounts.clear()\n registry.resetAbort = new AbortController()\n registry.generation += 1\n mergeTopicManifest(registry, DASHBOARD_TOPIC_MANIFEST)\n}\n\ndeclare const __SANITY_APP_ID__: string | undefined\n\nfunction throwMissingAppId(): never {\n throw new MessageBusError(\n 'MISSING_APP_ID',\n 'Cannot initialize the message bus without an app ID. Build the application with the Sanity CLI or pass an app ID when connecting.',\n )\n}\n\nconst resolveAppId = (appId?: string): string | undefined =>\n appId ?? (typeof __SANITY_APP_ID__ === 'string' ? __SANITY_APP_ID__ : undefined)\n\nfunction getInstalledMessageBus(): MessageBus | undefined {\n const bus = (globalThis as {[MESSAGE_BUS_KEY]?: unknown})[MESSAGE_BUS_KEY]\n return typeof bus === 'object' && bus !== null && MESSAGE_BUS_REGISTRY_KEY in bus\n ? (bus as unknown as MessageBus)\n : undefined\n}\n\n/**\n * Returns whether a message bus registry is installed.\n * @internal\n */\nexport function isMessageBusInstalled(): boolean {\n return getInstalledMessageBus() !== undefined\n}\n\n/**\n * Options for connecting to an installed message bus.\n * @public\n */\nexport interface ConnectMessageBusOptions {\n /** The application ID. Defaults to the ID embedded by the Sanity CLI. */\n appId?: string\n /** The federation module id stamped on emitted messages. Defaults to `appId`. */\n moduleId?: string\n}\n\n/**\n * Connects to the installed message bus, or returns `undefined` when no compatible connection exists.\n * @public\n */\nexport function connectMessageBus(\n options: ConnectMessageBusOptions = {},\n): MessageBusConnection | undefined {\n const installedMessageBus = getInstalledMessageBus()\n if (!installedMessageBus) return undefined\n\n const appId = resolveAppId(options.appId)\n if (!appId) {\n console.warn(\n '[sanity-sdk:message-bus] cannot connect without an app ID; build with the Sanity CLI or pass appId',\n )\n return undefined\n }\n\n const connection = connectApplicationToMessageBus(installedMessageBus, {\n appId,\n moduleId: options.moduleId,\n })\n return MESSAGE_BUS_REGISTRY_KEY in connection ? connection : undefined\n}\n\n/**\n * Resets the installed message bus for test isolation.\n * @internal\n */\nexport function resetMessageBus(): void {\n const installedMessageBus = getInstalledMessageBus()\n if (!installedMessageBus) return\n reset((installedMessageBus as InternalMessageBus)[MESSAGE_BUS_REGISTRY_KEY])\n}\n\n/**\n * Options for installing the shared message bus. The host is the whole app, so it carries\n * no `moduleId`.\n * @internal\n */\nexport interface InstallMessageBusOptions {\n /** The application ID. Defaults to the ID embedded by the Sanity CLI. */\n appId?: string\n}\n\n/**\n * Installs the shared message bus and returns the host's connection, or connects to its\n * existing installation. Only the host calls this; another app's ID is rejected because the\n * connection it would get has no `connections`.\n * @internal\n */\nexport function installMessageBus(options: InstallMessageBusOptions = {}): MessageBusHost<Topics> {\n const appId = resolveAppId(options.appId) ?? throwMissingAppId()\n const installedMessageBus = getInstalledMessageBus()\n if (installedMessageBus) {\n const installedAppId = (installedMessageBus as InternalMessageBus)[MESSAGE_BUS_REGISTRY_KEY]\n .appId\n if (installedAppId !== appId) {\n throw new MessageBusError(\n 'OWNERSHIP_MISMATCH',\n `Cannot install the message bus as \"${appId}\": it is already installed by \"${installedAppId}\". Applications connect with connectMessageBus().`,\n )\n }\n return connectApplicationToMessageBus(installedMessageBus, {\n appId,\n }) as MessageBusHost<Topics>\n }\n\n const globals = globalThis as {[MESSAGE_BUS_KEY]?: MessageBus}\n if (globals[MESSAGE_BUS_KEY]) {\n // A foreign value already occupies the bus symbol (e.g. an older Workbench bus); overwriting it disconnects that bus.\n console.error(\n '[sanity-sdk:message-bus] overwriting an incompatible message bus already installed',\n )\n }\n const host = createIsolatedMessageBus(appId)\n globals[MESSAGE_BUS_KEY] = host\n return host\n}\n\n/**\n * Declares state topics that are not in the bundled manifest, with the seed a new\n * connection's box starts from.\n * @internal\n */\nexport function registerStateTopics(\n target: MessageBus,\n topics: Partial<{[K in StateTopic]: ValueOf<K> | undefined}>,\n): void {\n const registry = (target as InternalMessageBus)[MESSAGE_BUS_REGISTRY_KEY]\n const manifest: TopicManifest = Object.fromEntries(\n Object.entries(topics).map(([name, seed]) => [name, {kind: 'state', seed}]),\n )\n mergeTopicManifest(registry, manifest)\n}\n","import {createActionBinder} from '../../store/createActionBinder'\nimport {type SanityInstance} from '../../store/createSanityInstance'\nimport {defineStore} from '../../store/defineStore'\nimport {connectMessageBus, isMessageBusInstalled, type MessageBusConnection} from './bus'\n\ninterface DashboardMessageBusState {\n connection?: MessageBusConnection\n}\n\nconst bindActionByInstance = createActionBinder<{name: string}, [moduleId?: string]>(\n (instance) => ({name: instance.instanceId}),\n)\n\nconst dashboardMessageBusStore = defineStore<DashboardMessageBusState>({\n name: 'dashboardMessageBus',\n getInitialState: () => ({}),\n // Runs when the instance is disposed, tearing down this window's connection.\n initialize:\n ({state}) =>\n () =>\n state.get().connection?.disconnect(),\n})\n\n/**\n * Returns the message bus connection for this instance, connecting on first call.\n *\n * @remarks\n * One connection is made per {@link SanityInstance}, carrying that window's\n * module identity. The first caller wins; later callers get the cached\n * connection. When no host bus is installed it returns `undefined` without\n * caching, so a later call retries once a host installs the bus.\n * @internal\n */\nexport const getDashboardMessageBus = bindActionByInstance(\n dashboardMessageBusStore,\n ({state}, moduleId?: string): MessageBusConnection | undefined => {\n const current = state.get().connection\n if (current) return current\n if (!isMessageBusInstalled()) return undefined\n const connection = connectMessageBus({moduleId})\n if (!connection) return undefined\n state.set('connect', {connection})\n return connection\n },\n)\n\n/**\n * Returns the instance's message bus connection, or throws when no dashboard host has\n * installed a bus. `action` names what the caller was about to do, e.g. `read topic \"x\"`.\n * @internal\n */\nexport function requireDashboardMessageBus(\n instance: SanityInstance,\n action: string,\n): MessageBusConnection {\n const messageBus = getDashboardMessageBus(instance)\n if (!messageBus) {\n throw new Error(`Cannot ${action} without an installed dashboard message bus`)\n }\n return messageBus\n}\n\n/**\n * Returns whether a dashboard host has installed a message bus.\n *\n * @remarks\n * Answers \"does a host own this session\", not \"has this instance connected yet\". Callers\n * such as the login redirect run before {@link getDashboardMessageBus} has been called for\n * the instance, so this must not depend on the connection state.\n * @internal\n */\nexport function isDashboardEnvironment(): boolean {\n return isMessageBusInstalled()\n}\n","import {createActionBinder} from '../../store/createActionBinder'\nimport {type SanityInstance} from '../../store/createSanityInstance'\nimport {createStateSourceAction} from '../../store/createStateSourceAction'\nimport {defineStore} from '../../store/defineStore'\nimport {setCleanupTimeout} from '../../utils/setCleanupTimeout'\nimport {type MessageBus, type MessageBusStateSource} from './bus'\nimport {requireDashboardMessageBus} from './store'\nimport {type StateTopic, type ValueOf} from './topics'\n\n/**\n * An error raised when a dashboard state topic reports failure.\n * @public\n */\nexport class TopicError extends Error {\n /** The topic that failed. */\n readonly topic: StateTopic\n\n /** Creates an error for a failed topic. */\n constructor(topic: StateTopic) {\n super(`Topic \"${topic}\" failed`)\n this.name = 'TopicError'\n this.topic = topic\n }\n}\n\n/**\n * The value of a state topic, with `TopicResult` wrappers unwrapped to their success value.\n * @public\n */\nexport type TopicData<K extends StateTopic> =\n ValueOf<K> extends infer Value\n ? Value extends {ok: true; value: infer Success}\n ? Success\n : Value extends {ok: false}\n ? never\n : Value\n : never\n\n// Mirrors the `TopicResult` shape declared in the topic manifest; no plain-value topic has an `ok` field.\nfunction isTopicResult(value: unknown): value is {ok: boolean; value?: unknown} {\n return (\n typeof value === 'object' && value !== null && 'ok' in value && typeof value.ok === 'boolean'\n )\n}\n\nfunction unwrapTopicResult(topic: StateTopic, value: unknown): unknown {\n if (!isTopicResult(value)) return value\n if (!value.ok) throw new TopicError(topic)\n return value.value\n}\n\ninterface TopicEntry {\n /** Why the first-value query failed. Dropped with the entry when its last subscriber leaves. */\n error?: unknown\n /** The in-flight first-value query, shared by every suspended render of the topic. */\n pending?: Promise<void>\n subscribers: number\n}\n\ninterface DashboardTopicsState {\n topics: {[topic: string]: TopicEntry}\n}\n\n// Matches the query store: a suspended component re-renders after its promise settles before\n// the temporary subscriber is released, so it still sees the recorded failure.\nconst TOPIC_STATE_CLEAR_DELAY = 1000\n\nconst bindActionByInstance = createActionBinder<{name: string}, [topic: StateTopic]>(\n (instance) => ({name: instance.instanceId}),\n)\n\n// The bus is the source of truth for topic values (it already keeps one replaying source per\n// topic). This store only tracks what the bus cannot: read failures and who is still reading.\nconst dashboardTopicsStore = defineStore<DashboardTopicsState>({\n name: 'dashboardTopics',\n getInitialState: () => ({topics: {}}),\n})\n\nconst getMessageBus = (instance: SanityInstance, topic: StateTopic): MessageBus =>\n requireDashboardMessageBus(instance, `read topic \"${topic}\"`)\n\nfunction getSource(instance: SanityInstance, topic: StateTopic): MessageBusStateSource<unknown> {\n return getMessageBus(instance, topic).subscribe(topic)\n}\n\n// An entry lives as long as it has subscribers. Dropping it with the last one is what gives a\n// later mount a fresh deadline instead of a stale failure.\nconst updateEntry =\n (topic: string, update: (entry: TopicEntry) => TopicEntry) =>\n (state: DashboardTopicsState): DashboardTopicsState => {\n const {[topic]: entry = {subscribers: 0}, ...rest} = state.topics\n const next = update(entry)\n return {topics: next.subscribers > 0 ? {...rest, [topic]: next} : rest}\n }\n\nconst addSubscriber = (topic: string) =>\n updateEntry(topic, (entry) => ({...entry, subscribers: entry.subscribers + 1}))\n\nconst removeSubscriber = (topic: string) =>\n updateEntry(topic, (entry) => ({...entry, subscribers: entry.subscribers - 1}))\n\n/**\n * A state source for a dashboard state topic, with `TopicResult` wrappers unwrapped.\n *\n * @remarks\n * Returns `undefined` until the topic publishes; use {@link resolveTopic} to wait for that\n * first value. While the topic is still unpublished, reading throws the failure that\n * {@link resolveTopic} recorded. A published value takes precedence over a recorded failure,\n * and a {@link TopicError} is thrown when the topic itself reports failure. A thrown failure\n * ends the subscription, so a reader recovers by subscribing again.\n * @internal\n */\nexport const getTopicState = bindActionByInstance(\n dashboardTopicsStore,\n createStateSourceAction({\n selector: ({state, instance}, topic: StateTopic) => {\n const current = getSource(instance, topic).getCurrent()\n // A recorded failure only describes the wait for a first value; a published value wins.\n const entry = state.topics[topic]\n if (current === undefined && entry?.error) throw entry.error\n return unwrapTopicResult(topic, current)\n },\n onSubscribe: ({state, instance}, topic: StateTopic) => {\n state.set('addSubscriber', addSubscriber(topic))\n // Each publish writes a fresh state object so the selector re-runs against the bus.\n const subscription = getSource(instance, topic).subscribe(() =>\n state.set(\n 'publish',\n updateEntry(topic, (entry) => ({...entry})),\n ),\n )\n return () => {\n subscription.unsubscribe()\n setCleanupTimeout(\n () => state.set('removeSubscriber', removeSubscriber(topic)),\n TOPIC_STATE_CLEAR_DELAY,\n )\n }\n },\n }),\n)\n\n/**\n * Resolves once a dashboard state topic has published, or records the failure for\n * {@link getTopicState} to throw. Repeated calls for a pending topic share one query.\n *\n * @remarks\n * Holds a temporary subscriber while the query is in flight so a failure recorded after the\n * reading component has gone is dropped with it, rather than thrown at the next mount.\n * @internal\n */\nexport const resolveTopic = bindActionByInstance(\n dashboardTopicsStore,\n ({state, instance}, topic: StateTopic): Promise<void> => {\n const existing = state.get().topics[topic]?.pending\n if (existing) return existing\n\n const release = () =>\n setCleanupTimeout(\n () => state.set('removeSubscriber', removeSubscriber(topic)),\n TOPIC_STATE_CLEAR_DELAY,\n )\n const pending = getMessageBus(instance, topic)\n .query(topic)\n .then(\n () =>\n state.set(\n 'resolved',\n updateEntry(topic, ({pending: _pending, ...entry}) => entry),\n ),\n (error: unknown) =>\n state.set(\n 'failed',\n updateEntry(topic, ({pending: _pending, ...entry}) => ({...entry, error})),\n ),\n )\n pending.then(release, release)\n state.set(\n 'query',\n updateEntry(topic, (entry) => ({...entry, pending, subscribers: entry.subscribers + 1})),\n )\n return pending\n },\n)\n"],"mappings":"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"}
1
+ {"version":3,"file":"topicStore-BTwUymdG.js","names":["bindActionByInstance"],"sources":["../src/dashboard/messageBus/topics.ts","../src/dashboard/messageBus/bus.ts","../src/dashboard/messageBus/store.ts","../src/dashboard/messageBus/topicStore.ts"],"sourcesContent":["import {type CurrentUser} from '@sanity/types'\n\nimport {type AccessResourceType} from '../../access/checkPermissions'\nimport {type Application, type ApplicationInclude} from '../../applications/applications'\nimport {type Installation, type InstallationInclude} from '../../installations/installations'\nimport {type OrganizationBase} from '../../organization/organization'\n\n/**\n * Identifies a module federation expose and the manifest that serves it.\n * @public\n */\nexport interface RemoteModuleRef {\n readonly entry: string\n readonly moduleId: string\n readonly version: string\n}\n\n/**\n * Application types that can receive configuration modules.\n *\n * The dashboard supplies this value over the message bus, so any string is accepted; known\n * types are listed for autocomplete. `& {}` keeps the literal from collapsing into `string`.\n * @public\n */\nexport type ApplicationConfigAppType = 'media-library' | (string & {})\n\n/**\n * Identifies a configuration module for an application or application type.\n * @public\n */\nexport interface ApplicationConfig extends RemoteModuleRef {\n readonly appId?: Application['id']\n readonly appType: ApplicationConfigAppType\n}\n\n/**\n * The capabilities a host can provide.\n *\n * Reuses the Studio's rendering-context identifiers so an application can hide its own\n * implementation of anything the host provides.\n * @public\n */\nexport const capabilities = ['globalUserMenu', 'favorites', 'history'] as const\n\n/**\n * A capability a host can provide.\n * @public\n */\nexport type Capability = (typeof capabilities)[number]\n\n/**\n * The capabilities the host provides. A missing key means the host does not provide it.\n * @public\n */\nexport type CapabilityRecord = Partial<Record<Capability, boolean>>\n\n/**\n * A document a user viewed, edited, created, or deleted in an application.\n * @public\n */\nexport interface DocumentActivity {\n readonly kind: 'document'\n readonly eventType: 'viewed' | 'edited' | 'created' | 'deleted'\n readonly document: {\n readonly id: string\n readonly type: string\n readonly resource: {\n readonly id: string\n readonly type: 'dataset' | 'media-library' | 'canvas'\n /** Schema collection the document belongs to; the workspace name for studios. */\n readonly schemaName?: string\n }\n }\n}\n\n/**\n * Activity an application reports to the host. Discriminated on `kind`.\n * @public\n */\nexport type ApplicationActivity = DocumentActivity\n\n/**\n * A document a user favorited, addressed like {@link DocumentActivity}'s document.\n * @public\n */\nexport type FavoriteDocument = DocumentActivity['document']\n\n/**\n * A label rendered for an application interface; `null` clears it.\n * @internal\n */\nexport type ApplicationStatus = {label: string | null}\n\n/**\n * Updates the status rendered for an application interface.\n * @internal\n */\nexport type ApplicationStatusUpdate = {\n /** The application interface name. */\n name: string\n /** The status to render. */\n value: ApplicationStatus\n}\n\n/**\n * What an application is currently showing: the Comlink agent resource context, made generic.\n * @public\n */\nexport interface ApplicationContext {\n /** The resource the application is working in. Types follow `@sanity/client`'s resource config. */\n readonly resource: {\n readonly id: string\n readonly type: 'dataset' | 'media-library' | 'canvas'\n }\n /** The open document, or `null` when none is open. */\n readonly document: {readonly id: string} | null\n}\n\n/**\n * An application served by a CLI dev server. The workbench publishes it in the same shape as a\n * deployed application; `local` marks it and names the dev server it is served from.\n * @public\n */\nexport type LocalApplication = Application<ApplicationInclude> & {\n readonly local: {readonly host: string; readonly port: number}\n}\n\n/**\n * Declares a topic that stores and replays its current value.\n * @public\n */\nexport type StateTopicDef<T> = {kind: 'state'; value: T}\n\n/**\n * Declares a topic that delivers events and an optional reply.\n * @public\n */\nexport type EventTopicDef<P, R = never> = {\n kind: 'event'\n payload: P\n reply?: R\n}\n\n/**\n * Represents a successful topic value or a failed topic operation.\n * @public\n */\nexport type TopicResult<T> = {ok: true; value: T} | {ok: false}\n\n/**\n * Identifies a dashboard application and a route within it.\n * @public\n */\nexport type NavigationTarget = {\n /** The application ID, or `null` for dashboard-level routes. */\n appId: Application['id'] | null\n /** The route path, including its query string and fragment. */\n path: string\n}\n\n/**\n * Describes the current dashboard location and an active navigation.\n * @public\n */\nexport type NavigationLocation = NavigationTarget & {\n /** The active navigation, or `null` when navigation is idle. */\n transition: {\n /** Whether the navigation pushes or replaces browser history. */\n navigationType: 'push' | 'replace'\n /** The requested destination. */\n to: NavigationTarget\n } | null\n}\n\n/**\n * Declares the topics provided by the dashboard.\n * @public\n */\nexport interface DashboardTopics {\n /**\n * Asks the dashboard to show its access request prompt for a resource the user can't access,\n * scoped like the Access API's `/access/{resourceType}/{resourceId}/requests`. Replies once the\n * prompt is shown. The prompt handles the Access API outcome (SSO enforcement, pending or\n * declined requests, request limits), so the reply doesn't report it.\n *\n * `ok: false` reasons:\n * - `unsupported`: the dashboard doesn't show access requests for the sending application\n * - `already-has-access`: the user already has access to the resource, so there is nothing to request\n */\n 'access.request': EventTopicDef<\n {resourceType: Extract<AccessResourceType, 'organization' | 'project'>; resourceId: string},\n {ok: true} | {ok: false; reason: 'unsupported' | 'already-has-access'}\n >\n /** Reports user activity in an application, e.g. for the recents feed. Fire-and-forget. */\n 'applications.activity': EventTopicDef<ApplicationActivity>\n /**\n * The base path for an application.\n *\n * An unknown application returns `{ok: false}`.\n */\n 'applications.base-path': StateTopicDef<TopicResult<string>>\n /** The capabilities the host provides. A missing key means the host does not provide it. */\n 'applications.capabilities': StateTopicDef<CapabilityRecord>\n /** The available application configuration modules. */\n 'applications.config': StateTopicDef<ApplicationConfig[] | null>\n /** The foreground application's context, or `null` when it has published none. */\n 'applications.context': StateTopicDef<\n (ApplicationContext & {readonly appId: Application['id']}) | null\n >\n /** Publishes the sending application's context. `null` clears it. Fire-and-forget. */\n 'applications.context.update': EventTopicDef<ApplicationContext | null>\n /** The foreground application ID, or `null` on dashboard-level routes. */\n 'applications.foreground': StateTopicDef<Application['id'] | null>\n /**\n * The dashboard applications and installations available to the current user. Each member\n * publishes its own raw serialised record, so the kinds are distinguished by shape: an\n * {@link Application} has a `type` of `'studio' | 'coreApp'`, a {@link LocalApplication} adds\n * `local`, and an {@link Installation} has no `type` and carries `applicationId` with a nested\n * `application` object.\n */\n 'applications.list': StateTopicDef<TopicResult<\n (Application<ApplicationInclude> | LocalApplication | Installation<InstallationInclude>)[]\n > | null>\n 'applications.status.update': EventTopicDef<ApplicationStatusUpdate>\n /**\n * The session token for the reading connection, or `null` while signed out. The host\n * writes it to each connection separately, so one application never sees another's token.\n */\n 'auth.token': StateTopicDef<string | null>\n /** Requests a dashboard session token. */\n 'auth.token.refresh': EventTopicDef<void, string>\n /** The signed-in user's favorited documents, published while the host provides `favorites`. */\n 'favorites.documents': StateTopicDef<FavoriteDocument[]>\n /**\n * Favorites or unfavorites a document. The host replies once `favorites.documents` reflects\n * the change, so a read after the reply sees it. A favorite is identified by its published\n * document ID, type, resource and `schemaName` (workspace).\n */\n 'favorites.update': EventTopicDef<{document: FavoriteDocument; favorited: boolean}, void>\n /** The current dashboard location and active navigation. */\n 'navigation.location': StateTopicDef<NavigationLocation | null>\n /**\n * Requests navigation and replies when the location commits.\n *\n * `ok: false` reasons:\n * - `not-navigable`: the URL cannot be handled by a dashboard application\n * - `interrupted`: another navigation superseded the request\n * - `failed`: the router rejected or did not commit the request\n */\n 'navigation.location.update': EventTopicDef<\n {\n url: string\n history?: 'push' | 'replace'\n },\n {ok: true} | {ok: false; reason: 'not-navigable' | 'interrupted' | 'failed'}\n >\n /** The current organization, or `null` without an active organization. */\n 'organizations.current': StateTopicDef<Pick<OrganizationBase, 'id' | 'name' | 'slug'> | null>\n /** The open panel, its application, display mode, and optional width in pixels. */\n 'panels.mode': StateTopicDef<TopicResult<\n | {appId: string; name: string; mode: 'aside'; size?: number}\n | {appId: string; name: string; mode: 'full'}\n | null\n > | null>\n /** Opens, updates, resizes, or closes an application's panel. */\n 'panels.mode.set': EventTopicDef<\n {name: string; mode: 'aside' | 'full'} | {name: string; size: number} | null\n >\n /** The resolved dashboard color scheme. */\n 'preferences.color-scheme': StateTopicDef<'light' | 'dark'>\n /** Whether the dashboard dock is pinned open. */\n 'preferences.dock-locked': StateTopicDef<boolean>\n /** The signed-in user, or `null` while signed out. */\n 'users.current': StateTopicDef<CurrentUser | null>\n}\n\n/**\n * Declares every topic available through the message bus.\n * @public\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type -- Declaration merging extends the SDK manifest.\nexport interface Topics extends DashboardTopics {}\n\n/**\n * Hides provisional topics from the public message bus methods.\n * @internal\n */\nexport type MessageBusTopics = Omit<Topics, 'applications.status.update'>\n\n/**\n * Every declared topic name.\n * @public\n */\nexport type TopicName<TTopics = Topics> = keyof TTopics\n\ntype StateTopicsOf<T> = {\n [K in keyof T]: T[K] extends {kind: 'state'} ? K : never\n}[keyof T]\n\n/**\n * Names of state topics exposed by message bus methods.\n * @public\n */\nexport type StateTopic<TTopics = MessageBusTopics> = StateTopicsOf<TTopics>\n\ntype TopicOwnership = {readonly type: 'same_app'} | {readonly type: 'any_app'}\n\n// State is only ever written by the host, one connection at a time, so it carries no ownership.\ntype TopicManifestEntry<T> = T extends {kind: 'state'; value: infer V}\n ? {readonly kind: 'state'; readonly seed: V | undefined}\n : {readonly kind: 'event'; readonly ownership: TopicOwnership}\n\n// The value a connection's copy of the topic starts with; `undefined` means unpublished.\nconst stateTopic = <const V>(seed: V) => ({kind: 'state', seed}) as const\n\n// `same_app` restricts responding to the application that installed the bus.\nconst dashboardEvent = {kind: 'event', ownership: {type: 'same_app'}} as const\n\ntype DashboardTopicManifest = {\n readonly [K in keyof DashboardTopics]: TopicManifestEntry<DashboardTopics[K]>\n}\n\n/**\n * Defines the runtime kind, ownership, and initial value of dashboard topics.\n * @internal\n */\nexport const DASHBOARD_TOPIC_MANIFEST: DashboardTopicManifest = {\n 'access.request': dashboardEvent,\n 'applications.activity': dashboardEvent,\n 'applications.base-path': stateTopic(undefined),\n 'applications.capabilities': stateTopic(undefined),\n 'applications.config': stateTopic(undefined),\n 'applications.context': stateTopic(undefined),\n 'applications.context.update': dashboardEvent,\n 'applications.foreground': stateTopic(undefined),\n 'applications.list': stateTopic(undefined),\n 'applications.status.update': dashboardEvent,\n 'auth.token': stateTopic(undefined),\n 'auth.token.refresh': dashboardEvent,\n 'favorites.documents': stateTopic(undefined),\n 'favorites.update': dashboardEvent,\n 'navigation.location': stateTopic(undefined),\n 'navigation.location.update': dashboardEvent,\n 'organizations.current': stateTopic(undefined),\n 'panels.mode': stateTopic({ok: true, value: null}),\n 'panels.mode.set': dashboardEvent,\n 'preferences.color-scheme': stateTopic(undefined),\n 'preferences.dock-locked': stateTopic(undefined),\n 'users.current': stateTopic(undefined),\n}\n\n/**\n * Defines the runtime manifest accepted from any message bus version.\n * @internal\n */\nexport type TopicManifest = Readonly<\n Record<\n string,\n | {readonly kind: 'state'; readonly seed: unknown}\n | {readonly kind: 'event'; readonly ownership: TopicOwnership}\n >\n>\n\n/**\n * Names of event topics exposed by message bus methods.\n * @public\n */\nexport type EventTopic<TTopics = MessageBusTopics> = {\n [K in keyof TTopics]: TTopics[K] extends {kind: 'event'} ? K : never\n}[keyof TTopics]\n\n/**\n * The value type of a state topic.\n * @public\n */\nexport type ValueOf<K extends StateTopic<TTopics>, TTopics = Topics> =\n TTopics[K] extends StateTopicDef<infer T> ? T : never\n\n/**\n * The payload type of an event topic.\n * @public\n */\nexport type PayloadOf<K extends EventTopic<TTopics>, TTopics = Topics> =\n TTopics[K] extends EventTopicDef<infer P, infer _R> ? P : never\n\n/**\n * The reply type of an event topic (`never` if it declares none).\n * @public\n */\nexport type ReplyOf<K extends EventTopic<TTopics>, TTopics = Topics> =\n TTopics[K] extends EventTopicDef<infer _P, infer R> ? R : never\n\n/**\n * Converts a topic value between 2 adjacent versions.\n * @internal\n */\nexport interface TopicMigration {\n /** The older version. */\n readonly from: number\n /** The newer version. */\n readonly to: number\n /** Converts an older state value or event payload to the newer version. */\n up(older: unknown): unknown\n /** Converts a newer state value or event payload to the older version. */\n down(newer: unknown): unknown\n /** Converts event replies between these versions. */\n readonly reply?: {\n /** Converts an older reply to the newer version. */\n up(older: unknown): unknown\n /** Converts a newer reply to the older version. */\n down(newer: unknown): unknown\n }\n}\n\n/**\n * Defines the bundled migration chain for each topic.\n * @internal\n */\nexport const topicMigrations: Partial<Record<TopicName, readonly TopicMigration[]>> = {}\n","/* eslint-disable no-console -- The bus can initialize before the SDK logger exists. */\nimport {\n BehaviorSubject,\n concat,\n defer,\n filter,\n firstValueFrom,\n from,\n map,\n type Observable,\n ReplaySubject,\n Subject,\n type Subscription,\n takeUntil,\n} from 'rxjs'\n\nimport {type Application} from '../../applications/applications'\nimport {\n DASHBOARD_TOPIC_MANIFEST,\n type EventTopic,\n type MessageBusTopics,\n type PayloadOf,\n type ReplyOf,\n type StateTopic,\n type TopicManifest,\n type TopicMigration,\n topicMigrations,\n type TopicName,\n type Topics,\n type ValueOf,\n} from './topics'\n\n/**\n * A message bus protocol error code.\n * @public\n */\nexport type MessageBusErrorCode =\n | 'NO_RESPONDER'\n | 'TIMEOUT'\n | 'ABORTED'\n | 'HANDLER_THREW'\n | 'PROTOCOL_MISMATCH'\n | 'OWNERSHIP_MISMATCH'\n | 'MISSING_APP_ID'\n | 'REFUSED'\n\n/**\n * An error raised by the message bus protocol.\n * @public\n */\nexport class MessageBusError extends Error {\n /** The machine-readable protocol error code. */\n readonly code: MessageBusErrorCode\n\n /** Creates a message bus protocol error. */\n constructor(code: MessageBusErrorCode, message?: string, options?: {cause?: unknown}) {\n super(message ?? code, options)\n this.name = 'MessageBusError'\n this.code = code\n }\n}\n\n/**\n * Metadata that travels with each message bus event.\n * @public\n */\nexport interface MessageBusMeta {\n /** The application that produced the message. */\n appId: Application['id']\n /**\n * The federation module id of the connection that sent the message (e.g.\n * `favorites/views/list/panel`). Absent from older copies and when the host\n * supplied none, so readers normalise with `message.meta.moduleId ?? message.meta.appId`.\n */\n moduleId?: string\n /** The Unix timestamp in milliseconds when the message was produced. */\n timestamp: number\n}\n\n/**\n * A message delivered to an event topic responder.\n * @public\n */\nexport interface MessageBusMessage<T, R = never, K extends PropertyKey = TopicName> {\n /** The topic that carries the message. */\n type: K\n /** The event payload. */\n payload: T\n /** The message provenance. */\n meta: MessageBusMeta\n /** Replies to an awaiting sender. */\n reply(value: R): void\n /**\n * Refuses the message: the sender's awaited `emit()` rejects with a\n * {@link MessageBusError} whose `code` is `'REFUSED'`. Ignored, with a warning,\n * once the reply has already settled.\n */\n reject(message?: string): void\n /** Aborts when the sender stops waiting for a reply. */\n readonly signal: AbortSignal\n}\n\n/**\n * An observable state topic with access to its current and first values.\n * @public\n */\nexport interface MessageBusStateSource<T> extends Observable<T> {\n /** Returns the current value, or `undefined` before the first value is published. */\n getCurrent(): T | undefined\n /** Resolves with the first published value. */\n readonly firstValue: Promise<T>\n}\n\n/**\n * Options for aborting a message bus operation.\n * @public\n */\nexport interface MessageBusAbortOptions {\n /** Aborts the operation when signaled. */\n signal?: AbortSignal\n}\n\n/**\n * Options for reading a state topic.\n * @public\n */\nexport interface MessageBusQueryOptions extends MessageBusAbortOptions {\n /** Wait timeout in milliseconds. Defaults to 5 seconds; `null` disables it. */\n timeout?: number | null\n}\n\n/**\n * Options for emitting an event topic.\n * @public\n */\nexport interface MessageBusEmitOptions extends MessageBusAbortOptions {\n /** Reply timeout in milliseconds. Defaults to 5 seconds; `null` disables it. */\n timeout?: number | null\n}\n\n/**\n * A lazily awaited event reply.\n * @public\n */\nexport interface MessageBusEmitResult<R> extends PromiseLike<R> {\n /** Handles a rejected event reply. */\n catch<T = never>(onRejected?: (reason: unknown) => T | PromiseLike<T>): Promise<R | T>\n /** Runs after the event reply settles. */\n finally(onFinally?: () => void): Promise<R>\n}\n\n/**\n * Sends events and reads typed state and event topics.\n *\n * @remarks\n * State topics are written by the host to each connection separately through\n * {@link MessageBusClient.emit}; a connection only ever reads its own values.\n * @public\n */\nexport interface MessageBus<TTopics = MessageBusTopics> {\n /** Emits an event topic and provides its reply when awaited. */\n emit<K extends EventTopic<TTopics>>(\n type: K,\n ...rest: PayloadOf<K, TTopics> extends void\n ? [payload?: void, options?: MessageBusEmitOptions]\n : [payload: PayloadOf<K, TTopics>, options?: MessageBusEmitOptions]\n ): MessageBusEmitResult<ReplyOf<K, TTopics>>\n /** Reads the current or next value of a state topic. */\n query<K extends StateTopic<TTopics>>(\n type: K,\n options?: MessageBusQueryOptions,\n ): Promise<ValueOf<K, TTopics>>\n /** Runs a responder for each event until its signal aborts. */\n subscribe<K extends EventTopic<TTopics>>(\n type: K,\n handler: (message: MessageBusMessage<PayloadOf<K, TTopics>, ReplyOf<K, TTopics>, K>) => void,\n options?: MessageBusAbortOptions,\n ): void\n /** Runs a handler for each state value until its signal aborts. */\n subscribe<K extends StateTopic<TTopics>>(\n type: K,\n handler: (value: ValueOf<K, TTopics>) => void,\n options?: MessageBusAbortOptions,\n ): void\n /** Returns a state topic as a `MessageBusStateSource`. */\n subscribe<K extends StateTopic<TTopics>>(type: K): MessageBusStateSource<ValueOf<K, TTopics>>\n /** Returns an event topic as an observable of its payloads. */\n subscribe<K extends EventTopic<TTopics>>(type: K): Observable<PayloadOf<K, TTopics>>\n}\n\n/**\n * A message bus connection that can be torn down independently of its siblings.\n * @public\n */\nexport interface MessageBusConnection<TTopics = MessageBusTopics> extends MessageBus<TTopics> {\n /** The application this connection belongs to. */\n readonly appId: Application['id']\n /**\n * Tears down this connection: pending requests reject `ABORTED`, subscriptions\n * complete, and later `emit`, `query`, and `subscribe` calls fail with `ABORTED`.\n * Siblings and the shared bus are untouched.\n */\n disconnect(): void\n}\n\n/**\n * The host's handle to one connection: who it is, when it closes, and where to write its state.\n * @public\n */\nexport interface MessageBusClient {\n /** The application the connection belongs to. */\n readonly appId: Application['id']\n /** The federation module id of the connection. Equals `appId` when the host supplied none. */\n readonly moduleId: string\n /** Aborts when the connection disconnects. Drop per-client state here. */\n readonly closed: AbortSignal\n /** Writes a state value to this connection only. Ignored once the connection has closed. */\n emit<K extends StateTopic>(type: K, value: ValueOf<K>): void\n /**\n * Rejects a state topic for this connection: its pending and later {@link MessageBus.query}\n * calls reject with a {@link MessageBusError} whose `code` is `'REFUSED'`, until the host next\n * writes a value with {@link MessageBusClient.emit}, which clears the rejection.\n *\n * @remarks\n * A rejected promise cannot be un-rejected and an errored subject cannot resume, so the\n * rejection is tracked per connection alongside the topic's subject rather than by erroring\n * it: `query` consults it before awaiting, and {@link MessageBus.subscribe} and the topic's\n * {@link MessageBusStateSource} stay silent (they never error) until a value is written.\n * Ignored once the connection has closed, the same as {@link MessageBusClient.emit}.\n */\n reject<K extends StateTopic>(type: K, message?: string): void\n}\n\n/**\n * The host's connection to the bus.\n *\n * @remarks\n * State reaches an application only through {@link MessageBusClient.emit}. There is no\n * broadcast: sending the same value to everyone is a loop over `connections`, and giving a\n * new connection the current values is done when it appears there, as an SSE server writes\n * a snapshot to each new client.\n * @public\n */\nexport interface MessageBusHost<TTopics = MessageBusTopics> extends MessageBusConnection<TTopics> {\n /**\n * Every open connection other than this one first, then each new one as it joins. The same\n * {@link MessageBusClient} object is handed out for a connection across subscriptions, so it\n * can be kept in a `Set`. A connection stays listed until it calls `disconnect()`; the SDK\n * does that when the owning `SanityInstance` is disposed.\n */\n readonly connections: Observable<MessageBusClient>\n}\n\nconst MESSAGE_BUS_KEY = Symbol.for('sanity.os.bus')\nconst MESSAGE_BUS_PROTOCOL_KEY = Symbol.for('sanity.os.protocol')\nconst MESSAGE_BUS_REGISTRY_KEY = Symbol.for('sanity.os.registry')\nconst MESSAGE_BUS_PENDING_REPLY_KEY = Symbol.for('sanity.os.request')\n\nconst MESSAGE_BUS_PROTOCOL = 1\n\nconst DEFAULT_TIMEOUT_MS = 5000\n\n// Distinguishes an unpublished topic from a published `undefined` value.\nconst NO_VALUE = Symbol.for('sanity.os.no-value')\n\ntype ReplyOutcome =\n | {readonly ok: true; readonly value: unknown}\n | {readonly ok: false; readonly error: unknown}\n\n// Defers Promise creation until the reply is awaited to preserve fire-and-forget emission.\ninterface PendingReply {\n readonly responderAbort: AbortController\n readonly responderSignal: AbortSignal\n settled: boolean\n outcomeBeforeAwait?: ReplyOutcome\n settlePromise?: (outcome: ReplyOutcome) => void\n replyPromise?: Promise<unknown>\n}\n\n// One connection: its identity, its own copy of every state topic, and its lifetime.\ninterface ConnectionRecord {\n readonly appId: string\n readonly moduleId: string\n readonly stateSubjects: Map<string, BehaviorSubject<unknown>>\n // Topics the host has rejected for this connection, with the reason, until the next write.\n readonly rejections: Map<string, string | undefined>\n // Fires the rejected topic name so pending queries on that topic can reject at once.\n readonly rejected$: Subject<string>\n readonly abort: AbortController\n}\n\ninterface MessageBusRegistry {\n readonly appId: string\n readonly topics: Map<string, TopicManifest[string]>\n readonly connections: Set<ConnectionRecord>\n readonly connected$: Subject<ConnectionRecord>\n readonly eventSubjects: Map<string, Subject<MessageBusMessage<unknown, unknown>>>\n readonly responderCounts: Map<string, number>\n readonly migrations: ReadonlyMap<string, readonly TopicMigration[]>\n resetAbort: AbortController\n generation: number\n}\n\ntype InternalMessageBus = MessageBus & {\n [MESSAGE_BUS_REGISTRY_KEY]: MessageBusRegistry\n [MESSAGE_BUS_PROTOCOL_KEY]: number\n}\n\n// A connection's box for a state topic, created on first use from the topic's manifest seed.\nfunction resolveStateSubject(\n registry: MessageBusRegistry,\n connection: ConnectionRecord,\n type: string,\n): BehaviorSubject<unknown> {\n // Callers gate on the connection's lifetime; this keeps a slipped call from recreating a\n // box on a closed connection that nothing would ever complete.\n if (connection.abort.signal.aborted) throw new MessageBusError('ABORTED')\n let subject = connection.stateSubjects.get(type)\n if (!subject) {\n const topic = registry.topics.get(type)\n if (!topic) {\n console.warn(`[sanity-sdk:message-bus] state topic \"${type}\" is not declared`)\n }\n const seed = topic?.kind === 'state' ? topic.seed : undefined\n subject = new BehaviorSubject<unknown>(seed === undefined ? NO_VALUE : seed)\n connection.stateSubjects.set(type, subject)\n }\n return subject\n}\n\nfunction toStateSource(\n values: Observable<unknown>,\n getCurrent: () => unknown,\n): MessageBusStateSource<unknown> {\n const source = values as MessageBusStateSource<unknown>\n source.getCurrent = getCurrent\n // Unread sources must not create promises that reject when reset completes them.\n let firstValue: Promise<unknown> | undefined\n Object.defineProperty(source, 'firstValue', {\n get: () => (firstValue ??= firstValueFrom(values)),\n })\n return source\n}\n\nfunction assertCompatibleTopicManifest(\n registry: MessageBusRegistry,\n manifest: TopicManifest,\n): void {\n for (const [type, entry] of Object.entries(manifest)) {\n const existing = registry.topics.get(type)\n if (existing && existing.kind !== entry.kind) {\n throw new MessageBusError(\n 'PROTOCOL_MISMATCH',\n `topic \"${type}\" is declared \"${entry.kind}\" but the installed bus knows it as \"${existing.kind}\"`,\n )\n }\n if (\n existing?.kind === 'event' &&\n entry.kind === 'event' &&\n existing.ownership.type !== entry.ownership.type\n ) {\n throw new MessageBusError('OWNERSHIP_MISMATCH', `topic \"${type}\" has conflicting ownership`)\n }\n }\n}\n\nfunction mergeTopicManifest(registry: MessageBusRegistry, manifest: TopicManifest): void {\n assertCompatibleTopicManifest(registry, manifest)\n for (const [type, entry] of Object.entries(manifest)) registry.topics.set(type, entry)\n}\n\nfunction resolveEventSubject(\n registry: MessageBusRegistry,\n type: string,\n): Subject<MessageBusMessage<unknown, unknown>> {\n let subject = registry.eventSubjects.get(type)\n if (!subject) {\n subject = new Subject<MessageBusMessage<unknown, unknown>>()\n registry.eventSubjects.set(type, subject)\n }\n return subject\n}\n\nfunction settleReply(reply: PendingReply, outcome: ReplyOutcome): void {\n if (reply.settled) return\n reply.settled = true\n if (reply.settlePromise) reply.settlePromise(outcome)\n else reply.outcomeBeforeAwait = outcome\n}\n\nfunction createEventMessage(\n appId: string,\n moduleId: string,\n type: string,\n payload: unknown,\n pendingReply: PendingReply,\n): MessageBusMessage<unknown, unknown> {\n const message: MessageBusMessage<unknown, unknown> & {\n [MESSAGE_BUS_PENDING_REPLY_KEY]?: PendingReply\n } = {\n type: type as TopicName,\n payload,\n meta: {appId, moduleId, timestamp: Date.now()},\n reply: (value) => {\n if (pendingReply.settled) {\n console.warn(\n `[sanity-sdk:message-bus] reply ignored for \"${type}\": no waiting caller or already replied`,\n )\n return\n }\n settleReply(pendingReply, {ok: true, value})\n },\n reject: (reason) => {\n if (pendingReply.settled) {\n console.warn(\n `[sanity-sdk:message-bus] reject ignored for \"${type}\": no waiting caller or already replied`,\n )\n return\n }\n settleReply(pendingReply, {ok: false, error: new MessageBusError('REFUSED', reason)})\n },\n get signal() {\n return pendingReply.responderSignal\n },\n }\n\n message[MESSAGE_BUS_PENDING_REPLY_KEY] = pendingReply\n return message\n}\n\nfunction createReplyPromise(\n pendingReply: PendingReply,\n options: MessageBusEmitOptions & {hadResponderAtEmission: boolean},\n): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n const settlePromise = (outcome: ReplyOutcome) =>\n outcome.ok ? resolve(outcome.value) : reject(outcome.error)\n\n if (pendingReply.outcomeBeforeAwait) {\n pendingReply.responderAbort.abort()\n settlePromise(pendingReply.outcomeBeforeAwait)\n return\n }\n if (!options.hadResponderAtEmission) {\n reject(new MessageBusError('NO_RESPONDER'))\n return\n }\n\n const timeoutMs = options.timeout === undefined ? DEFAULT_TIMEOUT_MS : options.timeout\n let timer: ReturnType<typeof setTimeout> | undefined\n const onAbort = () =>\n settleReply(pendingReply, {ok: false, error: new MessageBusError('ABORTED')})\n\n pendingReply.settlePromise = (outcome) => {\n if (timer !== undefined) clearTimeout(timer)\n options.signal?.removeEventListener('abort', onAbort)\n pendingReply.responderAbort.abort()\n settlePromise(outcome)\n }\n\n if (timeoutMs !== null) {\n timer = setTimeout(\n () => settleReply(pendingReply, {ok: false, error: new MessageBusError('TIMEOUT')}),\n timeoutMs,\n )\n }\n if (options.signal) {\n if (options.signal.aborted) onAbort()\n else options.signal.addEventListener('abort', onAbort, {once: true})\n }\n })\n}\n\nfunction createLazyReply<R>(awaitReply: () => Promise<R>): MessageBusEmitResult<R> {\n return {\n // oxlint-disable-next-line unicorn/no-thenable -- awaiting arms reply failure handling; fire-and-forget does not.\n then: (onFulfilled, onRejected) => awaitReply().then(onFulfilled, onRejected),\n catch: (onRejected) => awaitReply().then(undefined, onRejected),\n finally: (onFinally) => awaitReply().finally(onFinally),\n }\n}\n\nfunction emitEvent(\n registry: MessageBusRegistry,\n type: string,\n payload: unknown,\n options: MessageBusEmitOptions | undefined,\n appId: string,\n moduleId: string,\n): MessageBusEmitResult<unknown> {\n const hadResponderAtEmission = (registry.responderCounts.get(type) ?? 0) > 0\n const responderAbort = new AbortController()\n const pendingReply: PendingReply = {\n responderAbort,\n responderSignal: scopeSignal(options?.signal, responderAbort.signal),\n settled: false,\n }\n\n resolveEventSubject(registry, type).next(\n createEventMessage(appId, moduleId, type, payload, pendingReply),\n )\n\n const awaitReply = () =>\n (pendingReply.replyPromise ??= createReplyPromise(pendingReply, {\n ...options,\n hadResponderAtEmission,\n }))\n return createLazyReply(awaitReply)\n}\n\nconst isStateTopic = (registry: MessageBusRegistry, type: string): boolean =>\n registry.topics.get(type)?.kind === 'state'\n\nconst canRespond = (\n registry: MessageBusRegistry,\n ownership: {readonly type: 'same_app' | 'any_app'},\n appId: string,\n): boolean => ownership.type === 'any_app' || appId === registry.appId\n\nfunction emit(\n registry: MessageBusRegistry,\n type: string,\n payload: unknown,\n options: MessageBusEmitOptions | undefined,\n appId: string,\n moduleId: string,\n connectionSignal: AbortSignal,\n): MessageBusEmitResult<unknown> {\n if (isStateTopic(registry, type)) {\n throw new MessageBusError(\n 'OWNERSHIP_MISMATCH',\n `Cannot emit state topic \"${type}\" from app \"${appId}\". The host writes state to each connection through its client handle. Read it with query() or subscribe().`,\n )\n }\n return emitEvent(\n registry,\n type,\n payload,\n {\n ...options,\n signal: scopeSignal(options?.signal, registry.resetAbort.signal, connectionSignal),\n },\n appId,\n moduleId,\n )\n}\n\nfunction emitState(\n registry: MessageBusRegistry,\n connection: ConnectionRecord,\n type: string,\n value: unknown,\n): void {\n // A written value clears any rejection so later queries resolve normally.\n connection.rejections.delete(type)\n const subject = resolveStateSubject(registry, connection, type)\n if (!Object.is(subject.getValue(), value)) subject.next(value)\n}\n\nfunction query(\n source: MessageBusStateSource<unknown>,\n type: string,\n options: MessageBusQueryOptions | undefined,\n connectionSignal: AbortSignal,\n resetSignal: AbortSignal,\n connection: ConnectionRecord,\n): Promise<unknown> {\n const rejection = () => new MessageBusError('REFUSED', connection.rejections.get(type))\n // A rejection outranks any earlier value: it is cleared only when the host writes a new one.\n if (connection.rejections.has(type)) return Promise.reject(rejection())\n const current = source.getCurrent()\n if (current !== undefined) return Promise.resolve(current)\n\n const signal = scopeSignal(options?.signal, resetSignal, connectionSignal)\n if (signal.aborted) return Promise.reject(new MessageBusError('ABORTED'))\n const timeoutMs = options?.timeout === undefined ? DEFAULT_TIMEOUT_MS : options.timeout\n return new Promise((resolve, reject) => {\n const timer =\n timeoutMs === null\n ? undefined\n : setTimeout(() => {\n clear()\n signal?.removeEventListener('abort', onAbort)\n reject(new MessageBusError('TIMEOUT', `query(\"${type}\") timed out`))\n }, timeoutMs)\n // A rejection that arrives while waiting rejects the pending query at once.\n const rejectedSub = connection.rejected$.subscribe((rejectedType) => {\n if (rejectedType !== type) return\n clear()\n signal?.removeEventListener('abort', onAbort)\n reject(rejection())\n })\n const clear = () => {\n if (timer !== undefined) clearTimeout(timer)\n rejectedSub.unsubscribe()\n }\n const onAbort = () => {\n clear()\n reject(new MessageBusError('ABORTED'))\n }\n signal?.addEventListener('abort', onAbort, {once: true})\n void source.firstValue.then(\n (value) => {\n clear()\n signal?.removeEventListener('abort', onAbort)\n resolve(value)\n },\n (error) => {\n clear()\n signal?.removeEventListener('abort', onAbort)\n reject(error)\n },\n )\n })\n}\n\nfunction unsubscribeOnAbort(subscription: Subscription, signal: AbortSignal | undefined): void {\n if (!signal) return\n if (signal.aborted) {\n subscription.unsubscribe()\n return\n }\n signal.addEventListener('abort', () => subscription.unsubscribe(), {\n once: true,\n })\n}\n\nfunction scopeSignal(signal: AbortSignal | undefined, ...lifecycles: AbortSignal[]): AbortSignal {\n return signal ? AbortSignal.any([signal, ...lifecycles]) : AbortSignal.any(lifecycles)\n}\n\nfunction invokeResponder(\n handler: (message: MessageBusMessage<unknown, unknown>) => unknown,\n message: MessageBusMessage<unknown, unknown> & {\n [MESSAGE_BUS_PENDING_REPLY_KEY]?: PendingReply\n },\n): void {\n const pendingReply = message[MESSAGE_BUS_PENDING_REPLY_KEY]\n const fail = (error: unknown) => {\n if (pendingReply) {\n settleReply(pendingReply, {\n ok: false,\n error: new MessageBusError('HANDLER_THREW', undefined, {\n cause: error,\n }),\n })\n }\n }\n try {\n const result = handler(message)\n if (result instanceof Promise) result.catch(fail)\n } catch (error) {\n fail(error)\n }\n}\n\nfunction respond(\n registry: MessageBusRegistry,\n type: string,\n handler: (message: MessageBusMessage<unknown, unknown>) => unknown,\n options: MessageBusAbortOptions | undefined,\n appId: string,\n connectionSignal: AbortSignal,\n): void {\n const topic = registry.topics.get(type)\n if (topic?.kind === 'event' && !canRespond(registry, topic.ownership, appId)) {\n throw new MessageBusError(\n 'OWNERSHIP_MISMATCH',\n `Cannot register a handler for event topic \"${type}\" from app \"${appId}\". Only the app that owns this topic can respond to it. Other apps can send it with emit().`,\n )\n }\n\n registry.responderCounts.set(type, (registry.responderCounts.get(type) ?? 0) + 1)\n const subscription = resolveEventSubject(registry, type).subscribe((message) =>\n invokeResponder(handler, message),\n )\n subscription.add(() => {\n const count = registry.responderCounts.get(type) ?? 0\n if (count > 0) registry.responderCounts.set(type, count - 1)\n })\n unsubscribeOnAbort(\n subscription,\n scopeSignal(options?.signal, registry.resetAbort.signal, connectionSignal),\n )\n}\n\nconst bundledMigrations = () => new Map(Object.entries(topicMigrations))\n\n/**\n * Creates an isolated message bus without installing it globally and returns the host's\n * connection to it.\n * @internal\n */\nexport function createIsolatedMessageBus(\n appId: string,\n config: {\n migrations?: ReadonlyMap<string, readonly TopicMigration[]>\n } = {},\n): MessageBusHost<Topics> {\n if (!appId) throwMissingAppId()\n\n const registry: MessageBusRegistry = {\n appId,\n topics: new Map(),\n connections: new Set(),\n connected$: new Subject(),\n eventSubjects: new Map(),\n responderCounts: new Map(),\n migrations: config.migrations ?? bundledMigrations(),\n resetAbort: new AbortController(),\n generation: 0,\n }\n\n mergeTopicManifest(registry, DASHBOARD_TOPIC_MANIFEST)\n\n return createConnection(registry, {\n appId,\n migrations: config.migrations,\n }) as MessageBusHost<Topics>\n}\n\ntype TopicVersionAdapter = {\n toInstalled: (type: string, value: unknown) => unknown\n toApplication: (type: string, value: unknown) => unknown\n}\n\ntype TopicCompatibility = {\n toInstalledEmission: TopicVersionAdapter['toInstalled']\n toApplicationStateValue: TopicVersionAdapter['toApplication']\n toApplicationEventPayload: TopicVersionAdapter['toApplication']\n toInstalledEventReply: TopicVersionAdapter['toInstalled']\n toApplicationEventReply: TopicVersionAdapter['toApplication']\n}\n\ntype TopicMigrationTransform = Pick<TopicMigration, 'up' | 'down'>\n\nconst identityMigration: TopicMigrationTransform = {\n up: (value) => value,\n down: (value) => value,\n}\n\nfunction effectiveTopicVersions(\n migrations: ReadonlyMap<string, readonly TopicMigration[]>,\n): Map<string, number> {\n const versions = new Map<string, number>()\n for (const [topic, steps] of migrations) {\n let version = 1\n for (const step of steps) if (step.to > version) version = step.to\n versions.set(topic, version)\n }\n return versions\n}\n\nfunction migrationTransformAt(\n steps: readonly TopicMigration[] | undefined,\n version: number,\n select: (step: TopicMigration) => TopicMigrationTransform | undefined,\n): TopicMigrationTransform {\n const step = steps?.find((candidate) => candidate.from === version)\n return (step ? select(step) : undefined) ?? identityMigration\n}\n\nfunction migrateVersionedValue(\n steps: readonly TopicMigration[] | undefined,\n value: unknown,\n from: number,\n to: number,\n select: (step: TopicMigration) => TopicMigrationTransform | undefined,\n): unknown {\n if (from === to) return value\n\n let current = value\n if (from < to) {\n for (let version = from; version < to; version++) {\n current = migrationTransformAt(steps, version, select).up(current)\n }\n } else {\n for (let version = from; version > to; version--) {\n current = migrationTransformAt(steps, version - 1, select).down(current)\n }\n }\n return current\n}\n\nfunction createTopicCompatibility(\n installedMigrations: ReadonlyMap<string, readonly TopicMigration[]>,\n applicationMigrations: ReadonlyMap<string, readonly TopicMigration[]>,\n): TopicCompatibility {\n const installedVersions = effectiveTopicVersions(installedMigrations)\n const applicationVersions = effectiveTopicVersions(applicationMigrations)\n const applicationVersion = (type: string) => applicationVersions.get(type) ?? 1\n const installedVersion = (type: string) => installedVersions.get(type) ?? 1\n const migrationChainFor = (type: string) =>\n (applicationVersion(type) > installedVersion(type)\n ? applicationMigrations\n : installedMigrations\n ).get(type)\n const createVersionAdapter = (\n select: (step: TopicMigration) => TopicMigrationTransform | undefined,\n ): TopicVersionAdapter => ({\n toInstalled: (type, value) =>\n migrateVersionedValue(\n migrationChainFor(type),\n value,\n applicationVersion(type),\n installedVersion(type),\n select,\n ),\n toApplication: (type, value) =>\n migrateVersionedValue(\n migrationChainFor(type),\n value,\n installedVersion(type),\n applicationVersion(type),\n select,\n ),\n })\n\n const stateValueAndEventPayload = createVersionAdapter((step) => step)\n const eventReply = createVersionAdapter((step) => step.reply)\n return {\n toInstalledEmission: stateValueAndEventPayload.toInstalled,\n toApplicationStateValue: stateValueAndEventPayload.toApplication,\n toApplicationEventPayload: stateValueAndEventPayload.toApplication,\n toInstalledEventReply: eventReply.toInstalled,\n toApplicationEventReply: eventReply.toApplication,\n }\n}\n\n// Cache projections by input reference to keep state snapshots stable.\nfunction projectCurrent(input: () => unknown, project: (value: unknown) => unknown): () => unknown {\n let lastInput: unknown\n let lastOutput: unknown\n let cached = false\n return () => {\n const current = input()\n if (current === undefined) return undefined\n if (!cached || current !== lastInput) {\n lastInput = current\n lastOutput = project(current)\n cached = true\n }\n return lastOutput\n }\n}\n\n// The connection's view of one of its state boxes, in the connection's topic version.\nfunction createStateSource(\n subject: BehaviorSubject<unknown>,\n project: (value: unknown) => unknown,\n completeOn: Observable<unknown>,\n): MessageBusStateSource<unknown> {\n const getCurrent = () => {\n const current = subject.getValue()\n return current === NO_VALUE ? undefined : current\n }\n return toStateSource(\n subject.pipe(\n filter((value) => value !== NO_VALUE),\n map(project),\n takeUntil(completeOn),\n ),\n projectCurrent(getCurrent, project),\n )\n}\n\nfunction migrateEventMessage(\n message: MessageBusMessage<unknown, unknown>,\n payload: (value: unknown) => unknown,\n reply: (value: unknown) => unknown,\n): MessageBusMessage<unknown, unknown> {\n return {\n type: message.type,\n payload: payload(message.payload),\n meta: message.meta,\n reply: (value) => message.reply(reply(value)),\n reject: (reason) => message.reject(reason),\n get signal() {\n return message.signal\n },\n }\n}\n\nfunction migrateEventReply(\n result: MessageBusEmitResult<unknown>,\n project: (value: unknown) => unknown,\n): MessageBusEmitResult<unknown> {\n let projected: Promise<unknown> | undefined\n return createLazyReply(() => (projected ??= Promise.resolve(result).then(project)))\n}\n\nfunction createRejectedConnection(\n appId: string,\n connectionError: () => unknown,\n): MessageBusConnection {\n const throwConnectionError = (): never => {\n throw connectionError()\n }\n return {\n appId,\n emit: throwConnectionError,\n // `query` is typed as a Promise, so it must reject rather than throw synchronously.\n query: () => Promise.reject(connectionError()),\n subscribe: throwConnectionError,\n disconnect: () => {},\n } as unknown as MessageBusConnection\n}\n\n/**\n * Options for connecting an application to an isolated message bus.\n * @internal\n */\nexport interface ConnectApplicationToMessageBusOptions {\n /** The application ID stamped on emitted messages. */\n appId: string\n /** The federation module id stamped on emitted messages. Defaults to `appId`. */\n moduleId?: string\n /** The topic migrations supported by the application. */\n migrations?: ReadonlyMap<string, readonly TopicMigration[]>\n}\n\n/**\n * Connects an application to an isolated message bus.\n * @internal\n */\nexport function connectApplicationToMessageBus(\n installedMessageBus: MessageBus,\n config: ConnectApplicationToMessageBusOptions,\n): MessageBusConnection<Topics> {\n if (!config.appId) throwMissingAppId()\n\n const {appId} = config\n const installedProtocol = (installedMessageBus as Partial<InternalMessageBus>)[\n MESSAGE_BUS_PROTOCOL_KEY\n ]\n if (installedProtocol !== MESSAGE_BUS_PROTOCOL) {\n console.error(\n `[sanity-sdk:message-bus] protocol mismatch for \"${appId}\": installed ${String(installedProtocol)}, this copy speaks ${MESSAGE_BUS_PROTOCOL}`,\n )\n return createRejectedConnection(\n appId,\n () =>\n new MessageBusError(\n 'PROTOCOL_MISMATCH',\n `installed message bus speaks protocol ${String(installedProtocol)}, this copy speaks ${MESSAGE_BUS_PROTOCOL}`,\n ),\n )\n }\n\n const registry = (installedMessageBus as Partial<InternalMessageBus>)[MESSAGE_BUS_REGISTRY_KEY]\n if (!registry) {\n console.error(`[sanity-sdk:message-bus] incompatible message bus for \"${appId}\"`)\n return createRejectedConnection(\n appId,\n () =>\n new MessageBusError(\n 'PROTOCOL_MISMATCH',\n 'installed message bus does not expose a compatible registry',\n ),\n )\n }\n\n try {\n mergeTopicManifest(registry, DASHBOARD_TOPIC_MANIFEST)\n } catch (error) {\n console.error(`[sanity-sdk:message-bus] topic manifest conflict for \"${appId}\"`, {error})\n return createRejectedConnection(appId, () => error)\n }\n\n return createConnection(registry, config) as MessageBusConnection<Topics>\n}\n\n// Hands the host the same client object for a connection across `connections` subscriptions.\nfunction createClient(\n registry: MessageBusRegistry,\n record: ConnectionRecord,\n compatibility: TopicCompatibility,\n): MessageBusClient {\n return {\n appId: record.appId,\n moduleId: record.moduleId,\n closed: record.abort.signal,\n emit: (type: string, value: unknown) => {\n if (record.abort.signal.aborted) {\n console.warn(\n `[sanity-sdk:message-bus] \"${type}\" not written: connection \"${record.moduleId}\" has closed`,\n )\n return\n }\n emitState(registry, record, type, compatibility.toInstalledEmission(type, value))\n },\n reject: (type: string, message?: string) => {\n if (record.abort.signal.aborted) return\n record.rejections.set(type, message)\n record.rejected$.next(type)\n },\n } as MessageBusClient\n}\n\n// Open connections other than the host's own first, then each new one; one client object per\n// connection so the host can keep them in a Set. Completes when the host connection disconnects.\nfunction createConnectionsSource(\n registry: MessageBusRegistry,\n self: ConnectionRecord,\n compatibility: TopicCompatibility,\n completeOn: Observable<unknown>,\n): Observable<MessageBusClient> {\n const clients = new WeakMap<ConnectionRecord, MessageBusClient>()\n const clientFor = (record: ConnectionRecord) => {\n let client = clients.get(record)\n if (!client) {\n client = createClient(registry, record, compatibility)\n clients.set(record, client)\n }\n return client\n }\n return defer(() => concat(from([...registry.connections]), registry.connected$)).pipe(\n filter((record) => record !== self),\n map(clientFor),\n takeUntil(completeOn),\n )\n}\n\nfunction createConnection(\n registry: MessageBusRegistry,\n config: ConnectApplicationToMessageBusOptions,\n): MessageBusConnection {\n const {appId} = config\n const moduleId = config.moduleId ?? appId\n // Each connection owns its lifetime; disconnect() aborts it without touching siblings.\n const connectionAbort = new AbortController()\n const connectionSignal = connectionAbort.signal\n // ReplaySubject, not fromEvent: `abort` fires once, so a cold listener attached by a\n // stream subscribed after disconnect() would never see it and never complete. Replaying\n // the notification lets those late subscribers complete immediately.\n const connectionAborted$ = new ReplaySubject<void>(1)\n connectionSignal.addEventListener('abort', () => connectionAborted$.next(), {once: true})\n\n const record: ConnectionRecord = {\n appId,\n moduleId,\n stateSubjects: new Map(),\n rejections: new Map(),\n rejected$: new Subject<string>(),\n abort: connectionAbort,\n }\n\n const compatibility = createTopicCompatibility(\n registry.migrations,\n config.migrations ?? bundledMigrations(),\n )\n const isState = (type: string) => isStateTopic(registry, type)\n\n // Reuse topic streams because React external-store snapshots require stable references.\n const applicationStreams = new Map<string, MessageBusStateSource<unknown> | Observable<unknown>>()\n let streamGeneration = registry.generation\n const cachedStream = <T extends MessageBusStateSource<unknown> | Observable<unknown>>(\n type: string,\n create: () => T,\n ): T => {\n if (streamGeneration !== registry.generation) {\n applicationStreams.clear()\n streamGeneration = registry.generation\n }\n let stream = applicationStreams.get(type)\n if (!stream) {\n stream = create()\n applicationStreams.set(type, stream)\n }\n return stream as T\n }\n const stateSource = (type: string) =>\n cachedStream(type, () =>\n createStateSource(\n resolveStateSubject(registry, record, type),\n (value) => compatibility.toApplicationStateValue(type, value),\n connectionAborted$,\n ),\n )\n const eventStream = (type: string) =>\n cachedStream(type, () =>\n resolveEventSubject(registry, type).pipe(\n map((message) => compatibility.toApplicationEventPayload(type, message.payload)),\n takeUntil(connectionAborted$),\n ),\n )\n\n // A disconnected connection must not reach siblings, so its operations fail before touching\n // the shared registry rather than only tearing down pending requests and subscriptions.\n const throwIfDisconnected = () => {\n if (connectionSignal.aborted) throw new MessageBusError('ABORTED')\n }\n\n const connection = {\n appId,\n emit: (type: string, payload: unknown, options?: MessageBusEmitOptions) => {\n throwIfDisconnected()\n return migrateEventReply(\n emit(\n registry,\n type,\n compatibility.toInstalledEmission(type, payload),\n options,\n appId,\n moduleId,\n connectionSignal,\n ),\n (value) => compatibility.toApplicationEventReply(type, value),\n )\n },\n query: (type: string, options?: MessageBusQueryOptions) => {\n if (connectionSignal.aborted) return Promise.reject(new MessageBusError('ABORTED'))\n return query(\n stateSource(type),\n type,\n options,\n connectionSignal,\n registry.resetAbort.signal,\n record,\n )\n },\n subscribe: (\n type: string,\n handler?: (arg: never) => void,\n options?: MessageBusAbortOptions,\n ): MessageBusStateSource<unknown> | Observable<unknown> | undefined => {\n throwIfDisconnected()\n if (isState(type)) {\n const source = stateSource(type)\n if (!handler) return source\n unsubscribeOnAbort(\n source.subscribe(handler as (value: unknown) => void),\n scopeSignal(options?.signal, registry.resetAbort.signal, connectionSignal),\n )\n return undefined\n }\n if (!handler) return eventStream(type)\n respond(\n registry,\n type,\n (message) =>\n (handler as (message: MessageBusMessage<unknown, unknown>) => void)(\n migrateEventMessage(\n message,\n (value) => compatibility.toApplicationEventPayload(type, value),\n (value) => compatibility.toInstalledEventReply(type, value),\n ),\n ),\n options,\n appId,\n connectionSignal,\n )\n return undefined\n },\n disconnect: () => {\n if (connectionSignal.aborted) return\n connectionAbort.abort()\n for (const subject of record.stateSubjects.values()) subject.complete()\n record.stateSubjects.clear()\n record.rejected$.complete()\n registry.connections.delete(record)\n },\n }\n\n if (appId === registry.appId) {\n Object.assign(connection, {\n connections: createConnectionsSource(registry, record, compatibility, connectionAborted$),\n })\n }\n\n const instance = connection as unknown as InternalMessageBus\n instance[MESSAGE_BUS_REGISTRY_KEY] = registry\n instance[MESSAGE_BUS_PROTOCOL_KEY] = MESSAGE_BUS_PROTOCOL\n\n // Announce last: a host subscriber may write to the new client synchronously.\n registry.connections.add(record)\n registry.connected$.next(record)\n return instance as unknown as MessageBusConnection\n}\n\nfunction reset(registry: MessageBusRegistry): void {\n registry.resetAbort.abort()\n for (const connection of registry.connections) {\n for (const subject of connection.stateSubjects.values()) subject.complete()\n connection.stateSubjects.clear()\n connection.rejections.clear()\n }\n for (const subject of registry.eventSubjects.values()) subject.complete()\n\n registry.topics.clear()\n registry.eventSubjects.clear()\n registry.responderCounts.clear()\n registry.resetAbort = new AbortController()\n registry.generation += 1\n mergeTopicManifest(registry, DASHBOARD_TOPIC_MANIFEST)\n}\n\ndeclare const __SANITY_APP_ID__: string | undefined\n\nfunction throwMissingAppId(): never {\n throw new MessageBusError(\n 'MISSING_APP_ID',\n 'Cannot initialize the message bus without an app ID. Build the application with the Sanity CLI or pass an app ID when connecting.',\n )\n}\n\nconst resolveAppId = (appId?: string): string | undefined =>\n appId ?? (typeof __SANITY_APP_ID__ === 'string' ? __SANITY_APP_ID__ : undefined)\n\nfunction getInstalledMessageBus(): MessageBus | undefined {\n const bus = (globalThis as {[MESSAGE_BUS_KEY]?: unknown})[MESSAGE_BUS_KEY]\n return typeof bus === 'object' && bus !== null && MESSAGE_BUS_REGISTRY_KEY in bus\n ? (bus as unknown as MessageBus)\n : undefined\n}\n\n/**\n * Returns whether a message bus registry is installed.\n * @internal\n */\nexport function isMessageBusInstalled(): boolean {\n return getInstalledMessageBus() !== undefined\n}\n\n/**\n * Options for connecting to an installed message bus.\n * @public\n */\nexport interface ConnectMessageBusOptions {\n /** The application ID. Defaults to the ID embedded by the Sanity CLI. */\n appId?: string\n /** The federation module id stamped on emitted messages. Defaults to `appId`. */\n moduleId?: string\n}\n\n/**\n * Connects to the installed message bus, or returns `undefined` when no compatible connection exists.\n * @public\n */\nexport function connectMessageBus(\n options: ConnectMessageBusOptions = {},\n): MessageBusConnection | undefined {\n const installedMessageBus = getInstalledMessageBus()\n if (!installedMessageBus) return undefined\n\n const appId = resolveAppId(options.appId)\n if (!appId) {\n console.warn(\n '[sanity-sdk:message-bus] cannot connect without an app ID; build with the Sanity CLI or pass appId',\n )\n return undefined\n }\n\n const connection = connectApplicationToMessageBus(installedMessageBus, {\n appId,\n moduleId: options.moduleId,\n })\n return MESSAGE_BUS_REGISTRY_KEY in connection ? connection : undefined\n}\n\n/**\n * Resets the installed message bus for test isolation.\n * @internal\n */\nexport function resetMessageBus(): void {\n const installedMessageBus = getInstalledMessageBus()\n if (!installedMessageBus) return\n reset((installedMessageBus as InternalMessageBus)[MESSAGE_BUS_REGISTRY_KEY])\n}\n\n/**\n * Options for installing the shared message bus. The host is the whole app, so it carries\n * no `moduleId`.\n * @internal\n */\nexport interface InstallMessageBusOptions {\n /** The application ID. Defaults to the ID embedded by the Sanity CLI. */\n appId?: string\n}\n\n/**\n * Installs the shared message bus and returns the host's connection, or connects to its\n * existing installation. Only the host calls this; another app's ID is rejected because the\n * connection it would get has no `connections`.\n * @internal\n */\nexport function installMessageBus(options: InstallMessageBusOptions = {}): MessageBusHost<Topics> {\n const appId = resolveAppId(options.appId) ?? throwMissingAppId()\n const installedMessageBus = getInstalledMessageBus()\n if (installedMessageBus) {\n const installedAppId = (installedMessageBus as InternalMessageBus)[MESSAGE_BUS_REGISTRY_KEY]\n .appId\n if (installedAppId !== appId) {\n throw new MessageBusError(\n 'OWNERSHIP_MISMATCH',\n `Cannot install the message bus as \"${appId}\": it is already installed by \"${installedAppId}\". Applications connect with connectMessageBus().`,\n )\n }\n return connectApplicationToMessageBus(installedMessageBus, {\n appId,\n }) as MessageBusHost<Topics>\n }\n\n const globals = globalThis as {[MESSAGE_BUS_KEY]?: MessageBus}\n if (globals[MESSAGE_BUS_KEY]) {\n // A foreign value already occupies the bus symbol (e.g. an older Workbench bus); overwriting it disconnects that bus.\n console.error(\n '[sanity-sdk:message-bus] overwriting an incompatible message bus already installed',\n )\n }\n const host = createIsolatedMessageBus(appId)\n globals[MESSAGE_BUS_KEY] = host\n return host\n}\n\n/**\n * Declares state topics that are not in the bundled manifest, with the seed a new\n * connection's box starts from.\n * @internal\n */\nexport function registerStateTopics(\n target: MessageBus,\n topics: Partial<{[K in StateTopic]: ValueOf<K> | undefined}>,\n): void {\n const registry = (target as InternalMessageBus)[MESSAGE_BUS_REGISTRY_KEY]\n const manifest: TopicManifest = Object.fromEntries(\n Object.entries(topics).map(([name, seed]) => [name, {kind: 'state', seed}]),\n )\n mergeTopicManifest(registry, manifest)\n}\n","import {createActionBinder} from '../../store/createActionBinder'\nimport {type SanityInstance} from '../../store/createSanityInstance'\nimport {defineStore} from '../../store/defineStore'\nimport {connectMessageBus, isMessageBusInstalled, type MessageBusConnection} from './bus'\n\ninterface DashboardMessageBusState {\n connection?: MessageBusConnection\n}\n\nconst bindActionByInstance = createActionBinder<{name: string}, [moduleId?: string]>(\n (instance) => ({name: instance.instanceId}),\n)\n\nconst dashboardMessageBusStore = defineStore<DashboardMessageBusState>({\n name: 'dashboardMessageBus',\n getInitialState: () => ({}),\n // Runs when the instance is disposed, tearing down this window's connection.\n initialize:\n ({state}) =>\n () =>\n state.get().connection?.disconnect(),\n})\n\n/**\n * Returns the message bus connection for this instance, connecting on first call.\n *\n * @remarks\n * One connection is made per {@link SanityInstance}, carrying that window's\n * module identity. The first caller wins; later callers get the cached\n * connection. When no host bus is installed it returns `undefined` without\n * caching, so a later call retries once a host installs the bus.\n * @internal\n */\nexport const getDashboardMessageBus = bindActionByInstance(\n dashboardMessageBusStore,\n ({state}, moduleId?: string): MessageBusConnection | undefined => {\n const current = state.get().connection\n if (current) return current\n if (!isMessageBusInstalled()) return undefined\n const connection = connectMessageBus({moduleId})\n if (!connection) return undefined\n state.set('connect', {connection})\n return connection\n },\n)\n\n/**\n * Returns the instance's message bus connection, or throws when no dashboard host has\n * installed a bus. `action` names what the caller was about to do, e.g. `read topic \"x\"`.\n * @internal\n */\nexport function requireDashboardMessageBus(\n instance: SanityInstance,\n action: string,\n): MessageBusConnection {\n const messageBus = getDashboardMessageBus(instance)\n if (!messageBus) {\n throw new Error(`Cannot ${action} without an installed dashboard message bus`)\n }\n return messageBus\n}\n\n/**\n * Returns whether a dashboard host has installed a message bus.\n *\n * @remarks\n * Answers \"does a host own this session\", not \"has this instance connected yet\". Callers\n * such as the login redirect run before {@link getDashboardMessageBus} has been called for\n * the instance, so this must not depend on the connection state.\n * @internal\n */\nexport function isDashboardEnvironment(): boolean {\n return isMessageBusInstalled()\n}\n","import {createActionBinder} from '../../store/createActionBinder'\nimport {type SanityInstance} from '../../store/createSanityInstance'\nimport {createStateSourceAction} from '../../store/createStateSourceAction'\nimport {defineStore} from '../../store/defineStore'\nimport {setCleanupTimeout} from '../../utils/setCleanupTimeout'\nimport {type MessageBus, type MessageBusStateSource} from './bus'\nimport {requireDashboardMessageBus} from './store'\nimport {type StateTopic, type ValueOf} from './topics'\n\n/**\n * An error raised when a dashboard state topic reports failure.\n * @public\n */\nexport class TopicError extends Error {\n /** The topic that failed. */\n readonly topic: StateTopic\n\n /** Creates an error for a failed topic. */\n constructor(topic: StateTopic) {\n super(`Topic \"${topic}\" failed`)\n this.name = 'TopicError'\n this.topic = topic\n }\n}\n\n/**\n * The value of a state topic, with `TopicResult` wrappers unwrapped to their success value.\n * @public\n */\nexport type TopicData<K extends StateTopic> =\n ValueOf<K> extends infer Value\n ? Value extends {ok: true; value: infer Success}\n ? Success\n : Value extends {ok: false}\n ? never\n : Value\n : never\n\n// Mirrors the `TopicResult` shape declared in the topic manifest; no plain-value topic has an `ok` field.\nfunction isTopicResult(value: unknown): value is {ok: boolean; value?: unknown} {\n return (\n typeof value === 'object' && value !== null && 'ok' in value && typeof value.ok === 'boolean'\n )\n}\n\nfunction unwrapTopicResult(topic: StateTopic, value: unknown): unknown {\n if (!isTopicResult(value)) return value\n if (!value.ok) throw new TopicError(topic)\n return value.value\n}\n\ninterface TopicEntry {\n /** Why the first-value query failed. Dropped with the entry when its last subscriber leaves. */\n error?: unknown\n /** The in-flight first-value query, shared by every suspended render of the topic. */\n pending?: Promise<void>\n subscribers: number\n}\n\ninterface DashboardTopicsState {\n topics: {[topic: string]: TopicEntry}\n}\n\n// Matches the query store: a suspended component re-renders after its promise settles before\n// the temporary subscriber is released, so it still sees the recorded failure.\nconst TOPIC_STATE_CLEAR_DELAY = 1000\n\nconst bindActionByInstance = createActionBinder<{name: string}, [topic: StateTopic]>(\n (instance) => ({name: instance.instanceId}),\n)\n\n// The bus is the source of truth for topic values (it already keeps one replaying source per\n// topic). This store only tracks what the bus cannot: read failures and who is still reading.\nconst dashboardTopicsStore = defineStore<DashboardTopicsState>({\n name: 'dashboardTopics',\n getInitialState: () => ({topics: {}}),\n})\n\nconst getMessageBus = (instance: SanityInstance, topic: StateTopic): MessageBus =>\n requireDashboardMessageBus(instance, `read topic \"${topic}\"`)\n\nfunction getSource(instance: SanityInstance, topic: StateTopic): MessageBusStateSource<unknown> {\n return getMessageBus(instance, topic).subscribe(topic)\n}\n\n// An entry lives as long as it has subscribers. Dropping it with the last one is what gives a\n// later mount a fresh deadline instead of a stale failure.\nconst updateEntry =\n (topic: string, update: (entry: TopicEntry) => TopicEntry) =>\n (state: DashboardTopicsState): DashboardTopicsState => {\n const {[topic]: entry = {subscribers: 0}, ...rest} = state.topics\n const next = update(entry)\n return {topics: next.subscribers > 0 ? {...rest, [topic]: next} : rest}\n }\n\nconst addSubscriber = (topic: string) =>\n updateEntry(topic, (entry) => ({...entry, subscribers: entry.subscribers + 1}))\n\nconst removeSubscriber = (topic: string) =>\n updateEntry(topic, (entry) => ({...entry, subscribers: entry.subscribers - 1}))\n\n/**\n * A state source for a dashboard state topic, with `TopicResult` wrappers unwrapped.\n *\n * @remarks\n * Returns `undefined` until the topic publishes; use {@link resolveTopic} to wait for that\n * first value. While the topic is still unpublished, reading throws the failure that\n * {@link resolveTopic} recorded. A published value takes precedence over a recorded failure,\n * and a {@link TopicError} is thrown when the topic itself reports failure. A thrown failure\n * ends the subscription, so a reader recovers by subscribing again.\n * @internal\n */\nexport const getTopicState = bindActionByInstance(\n dashboardTopicsStore,\n createStateSourceAction({\n selector: ({state, instance}, topic: StateTopic) => {\n const current = getSource(instance, topic).getCurrent()\n // A recorded failure only describes the wait for a first value; a published value wins.\n const entry = state.topics[topic]\n if (current === undefined && entry?.error) throw entry.error\n return unwrapTopicResult(topic, current)\n },\n onSubscribe: ({state, instance}, topic: StateTopic) => {\n state.set('addSubscriber', addSubscriber(topic))\n // Each publish writes a fresh state object so the selector re-runs against the bus.\n const subscription = getSource(instance, topic).subscribe(() =>\n state.set(\n 'publish',\n updateEntry(topic, (entry) => ({...entry})),\n ),\n )\n return () => {\n subscription.unsubscribe()\n setCleanupTimeout(\n () => state.set('removeSubscriber', removeSubscriber(topic)),\n TOPIC_STATE_CLEAR_DELAY,\n )\n }\n },\n }),\n)\n\n/**\n * Resolves once a dashboard state topic has published, or records the failure for\n * {@link getTopicState} to throw. Repeated calls for a pending topic share one query.\n *\n * @remarks\n * Holds a temporary subscriber while the query is in flight so a failure recorded after the\n * reading component has gone is dropped with it, rather than thrown at the next mount.\n * @internal\n */\nexport const resolveTopic = bindActionByInstance(\n dashboardTopicsStore,\n ({state, instance}, topic: StateTopic): Promise<void> => {\n const existing = state.get().topics[topic]?.pending\n if (existing) return existing\n\n const release = () =>\n setCleanupTimeout(\n () => state.set('removeSubscriber', removeSubscriber(topic)),\n TOPIC_STATE_CLEAR_DELAY,\n )\n const pending = getMessageBus(instance, topic)\n .query(topic)\n .then(\n () =>\n state.set(\n 'resolved',\n updateEntry(topic, ({pending: _pending, ...entry}) => entry),\n ),\n (error: unknown) =>\n state.set(\n 'failed',\n updateEntry(topic, ({pending: _pending, ...entry}) => ({...entry, error})),\n ),\n )\n pending.then(release, release)\n state.set(\n 'query',\n updateEntry(topic, (entry) => ({...entry, pending, subscribers: entry.subscribers + 1})),\n )\n return pending\n },\n)\n"],"mappings":"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"}
@@ -179,6 +179,7 @@ interface DashboardTopics {
179
179
  *
180
180
  * `ok: false` reasons:
181
181
  * - `unsupported`: the dashboard doesn't show access requests for the sending application
182
+ * - `already-has-access`: the user already has access to the resource, so there is nothing to request
182
183
  */
183
184
  'access.request': EventTopicDef<{
184
185
  resourceType: Extract<AccessResourceType, 'organization' | 'project'>;
@@ -187,7 +188,7 @@ interface DashboardTopics {
187
188
  ok: true;
188
189
  } | {
189
190
  ok: false;
190
- reason: 'unsupported';
191
+ reason: 'unsupported' | 'already-has-access';
191
192
  }>;
192
193
  /** Reports user activity in an application, e.g. for the recents feed. Fire-and-forget. */
193
194
  'applications.activity': EventTopicDef<ApplicationActivity>;
@@ -600,4 +601,4 @@ declare const getTopicState: BoundStoreAction<DashboardTopicsState, [topic: Stat
600
601
  */
601
602
  declare const resolveTopic: BoundStoreAction<DashboardTopicsState, [topic: StateTopic], Promise<void>>;
602
603
  export { CapabilityRecord as A, PayloadOf as B, ApplicationActivity as C, ApplicationStatus as D, ApplicationContext as E, FavoriteDocument as F, TopicName as G, ReplyOf as H, LocalApplication as I, ValueOf as J, TopicResult as K, MessageBusTopics as L, DocumentActivity as M, EventTopic as N, ApplicationStatusUpdate as O, EventTopicDef as P, NavigationLocation as R, resetMessageBus as S, ApplicationConfigAppType as T, StateTopic as U, RemoteModuleRef as V, StateTopicDef as W, capabilities as Y, MessageBusMeta as _, ConnectMessageBusOptions as a, connectMessageBus as b, MessageBusAbortOptions as c, MessageBusEmitOptions as d, MessageBusEmitResult as f, MessageBusMessage as g, MessageBusHost as h, resolveTopic as i, DashboardTopics as j, Capability as k, MessageBusClient as l, MessageBusErrorCode as m, TopicError as n, InstallMessageBusOptions as o, MessageBusError as p, Topics as q, getTopicState as r, MessageBus as s, TopicData as t, MessageBusConnection as u, MessageBusQueryOptions as v, ApplicationConfig as w, installMessageBus as x, MessageBusStateSource as y, NavigationTarget as z };
603
- //# sourceMappingURL=topicStore-CkO7ocKC.d.ts.map
604
+ //# sourceMappingURL=topicStore-CpQbrxAA.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topicStore-CpQbrxAA.d.ts","names":["_$_______exports0.StateSource","_$______store_createActionBinder0.BoundStoreAction"],"sources":["../src/dashboard/messageBus/topics.ts","../src/dashboard/messageBus/bus.ts","../src/dashboard/messageBus/topicStore.ts"],"mappings":"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"}
@@ -1434,7 +1434,7 @@ const _resolveQuery = bindActionByResource(queryStore, ({ state, instance }, { s
1434
1434
  }, QUERY_STATE_CLEAR_DELAY);
1435
1435
  };
1436
1436
  return promise.then(releaseSubscriber, releaseSubscriber), promise;
1437
- }), CORE_SDK_VERSION = getEnv("PKG_VERSION") || "3.7.0-development";
1437
+ }), CORE_SDK_VERSION = getEnv("PKG_VERSION") || "3.8.0-next.20260930225713-development";
1438
1438
  export { toCommentsKeyParts as A, setPendingTransaction as B, commentsStore as C, parseCommentsOptionsKey as D, getCommentsState as E, receiveComment as F, provisionAddonDataset as G, toCommentFieldPath as H, removeCommentById as I, defineMutation as J, COMMENTS_API_VERSION as K, restoreComments as L, applyCommentUpdate as M, clearPendingTransaction as N, resolveCommentThreads as O, getCommentsKey as P, rollbackCommentUpdate as R, toFavoriteDocument as S, getCommentsOptionsKey as T, assertDatasetResource as U, normalizeComment as V, getAddonDatasetState as W, randomUuid as X, randomId as Y, updateLastLoadMoreRequest as _, resolveQuery as a, favorites as b, getAllReleasesState as c, getUsersKey as d, initializeRequest as f, setUsersError as g, setUsersData as h, parseQueryKey as i, addComment as j, resolveComments as k, addSubscription as l, removeSubscription as m, getQueryKey as n, getPerspectiveState as o, parseUsersKey as p, defineFetcher as q, getQueryState as r, getActiveReleasesState as s, CORE_SDK_VERSION as t, cancelRequest as u, PROJECT_API_VERSION as v, getCommentThreadsState as w, setFavorite as x, USERS_STATE_CLEAR_DELAY as y, setCommentCreateError as z };
1439
1439
 
1440
- //# sourceMappingURL=version-CB-4z0ps.js.map
1440
+ //# sourceMappingURL=version-iOE6pYps.js.map