@sanity/sdk-react 3.4.0 → 3.5.0-next.20260922190712
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_exports/dashboard.d.ts +40 -12
- package/dist/_exports/dashboard.d.ts.map +1 -1
- package/dist/_exports/dashboard.js +49 -12
- package/dist/_exports/dashboard.js.map +1 -1
- package/dist/index.d.ts +141 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +85 -5
- package/dist/index.js.map +1 -1
- package/dist/{useStudioWorkspacesByProjectIdDataset-Bjwi4cLk.js → useStudioWorkspacesByProjectIdDataset-DL6_9jgU.js} +2 -2
- package/dist/{useStudioWorkspacesByProjectIdDataset-Bjwi4cLk.js.map → useStudioWorkspacesByProjectIdDataset-DL6_9jgU.js.map} +1 -1
- package/package.json +2 -2
- package/src/_exports/dashboard.ts +2 -0
- package/src/_exports/sdk-react.ts +3 -0
- package/src/hooks/auth/useHandleOAuthCallback.test.tsx +16 -0
- package/src/hooks/auth/useHandleOAuthCallback.tsx +46 -0
- package/src/hooks/auth/useOAuthAuthorize.test.tsx +16 -0
- package/src/hooks/auth/useOAuthAuthorize.tsx +28 -0
- package/src/hooks/auth/useOAuthTokens.test.tsx +267 -0
- package/src/hooks/auth/useOAuthTokens.tsx +104 -0
- package/src/hooks/dashboard/useApplications.test.tsx +165 -7
- package/src/hooks/dashboard/useApplications.ts +112 -28
- package/src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.test.tsx +23 -1
- package/src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.ts +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","names":["createInstance","createMFInstance","ModuleFederationRuntimePlugin","UserOptions","createLogger","MakeOptional","T","Omit","K","Partial","Pick","FederationRemote","name","entry","MANIFEST_FILE","withManifest","remote","endsWith","base","URL","href","CreateRemoteInstanceOptions","RemoteInstance","registerRemotes","remotes","loadRemote","id","Promise","preloadRemote","exposes","createRemoteInstance","options","logger","instance","plugins","log","debug","map","force","remoteModule","error","Error","cause","nameOrAlias","resourceCategory","onDebug","message","context","Record","logEvent","eventName","args","origin","_origin","data","internal","beforeInit","beforeRegisterRemote","beforePreloadRemote","beforeRequest","afterResolve","onLoad","loadShare","beforeLoadShare","DashboardUrl","url","options","origin","toURL","URL","toString","relativeUrlBase","EditIntentParameters","id","type","mode","Record","CreateIntentParameters","template","ReleaseIntentParameters","StudioWorkspaceUrl","intent","parameters","StudioIntentUrl","path","task","taskId","StudioUrl","workspace","perspective","comment","commentId","CoreApplicationUrl","MediaLibraryUrl","asset","assetId","collection","collectionId","CanvasUrl","document","documentId","IntentArguments","BuilderRegistry","Readonly","namespace","UrlBuilder","BuilderMethods","Builders","InstanceType","Name","splitPath","flatMap","part","split","filter","Boolean","intentParametersOf","searchParameters","URLSearchParams","set","undefined","parseIntentParameters","replaceAll","serializeIntentParameters","Array","from","name","value","encodeURIComponent","join","appendSegments","segments","nextUrl","length","pathname","replace","map","constructor","append","create","edit","update","transitionTo","Builder","relativeUrl","href","#relativeUrl","search","hash","#create","CoreApplicationUrlBuilder","MediaLibraryUrlBuilder","CanvasUrlBuilder","StudioUrlBuilder","args","searchParams","parametersIndex","Urls","studios","appId","applications","mediaLibrary","canvas","home","extend","builders","Partial","createRootBuilder","createUrls","methods","Object","fromEntries","entries","urls","addedBuilders","AddedBuilders","namespaces","Set","values","Error","has","add","assign","urlFor","ApplicationContext","useEffect","useEmit","useApplicationContext","context","$","_c","emit","t0","t1","Events","SDK_CHANNEL_NAME","SDK_NODE_NAME","isDashboardEnvironment","FrameMessage","ApplicationContext","useCallback","useEffect","useMemo","useRef","useWindowConnection","useApplicationContext","AgentResourceContextOptions","projectId","dataset","documentId","useAgentResourceContext","options","useBusAgentResourceContext","useComlinkAgentResourceContext","t0","$","_c","t1","resource","id","type","document","context","Symbol","for","name","connectTo","sendMessage","lastContextRef","console","warn","contextKey","current","message","data","t2","error","updateContext","t3","ApplicationBase","getApplicationOrigin","RemoteModuleRef","ValueOf","useMemo","useTopic","DashboardTopicApplication","Extract","NonNullable","ok","DashboardApplicationInterface","ViewInterface","Exclude","type","DashboardView","Omit","Type","application","module","surface","DashboardWebWorker","DashboardApplication","views","webWorkers","loadableInterfaces","activeDeployment","config","mfManifest","undefined","interfaces","toApplication","_activeDeployment","_config","applicationBase","entry","length","extension","moduleId","id","version","push","view","useApplications","$","_c","applications","t0","map","DashboardApplication","useApplications","useApplication","applicationId","find","t0","id","TopicData","useTopic","useApplicationBasePath","ApplicationConfig","useTopic","NO_CONFIGS","Object","freeze","useApplicationConfigs","Application","ApplicationConfig","ApplicationConfigAppType","useApplicationConfigs","ApplicationConfigSelector","appId","appType","useApplicationConfig","selector","$","_c","configs","t0","find","config","undefined","Application","useTopic","useApplicationForegroundId","useTopic","useAuthToken","TopicData","useTopic","useCapabilities","CurrentUser","useTopic","useCurrentUser","Bridge","PathChangeMessage","SDK_CHANNEL_NAME","SDK_NODE_NAME","isDashboardEnvironment","requireDashboardMessageBus","NavigationLocation","TopicError","useCallback","useEffect","useEffectEvent","useRef","filter","map","pairwise","useWindowConnection","useSanityInstance","UpdateURLMessage","Listeners","History","DashboardNavigation","NavigateToDashboardPath","options","path","type","useNavigate","navigateFn","useBusNavigate","useComlinkNavigate","$","_c","t0","name","connectTo","onMessage","data","sendMessage","t1","t2","url","URL","window","location","origin","href","joinPath","base","root","replace","instance","bus","navigate","ownRequest","sub","subscribe","pipe","pair","prev","curr","transition","appId","to","curr_0","suppress","current","_temp","change","unsubscribe","t3","t4","t5","undefined","own","clearIfStale","query","then","ok","emit","value","history","reply","error","console","warn","prev_0","curr_1","navigationType","Bridge","SDK_CHANNEL_NAME","SDK_NODE_NAME","DocumentHandle","isDashboardEnvironment","useCallback","urlFor","useWindowConnection","useEmit","DashboardResource","useStudioWorkspacesByProjectIdDataset","NavigateToStudioResult","navigateToStudioDocument","useComlinkStudioNavigator","documentHandle","$","_c","t0","Symbol","for","name","connectTo","sendMessage","t1","documentId","documentType","workspace","message","type","data","resourceId","id","resourceType","path","useBusStudioNavigator","navigate","url","studios","userApplicationId","intent","then","_temp","_temp2","error","console","warn","reply","ok","useNavigateToStudioDocument","preferredStudioUrl","workspacesByProjectIdAndDataset","sendToWorkspace","projectId","dataset","resolveWorkspace","WorkspacesByProjectIdDataset","ReturnType","key","const","allWorkspaces","find","w","workspaces","length","SanityInstance","StateSource","of","createRemoteInstance","RemoteInstance","remoteClientStates","WeakMap","getRemoteClientState","instance","isDisposed","Error","current","get","client","name","instanceId","state","getCurrent","observable","subscribe","set","onDispose","delete","RemoteInstance","getRemoteClientState","createStateSourceHook","useRemoteClient","SDK_CHANNEL_NAME","SDK_NODE_NAME","isDashboardEnvironment","useEffect","useState","useWindowConnection","ContextResource","type","title","manifest","activeDeployment","ContextResponse","context","resource","resolveAppTitle","useWindowTitle","viewTitle","useComlinkWindowTitle","appTitle","setAppTitle","fetch","name","connectTo","controller","AbortController","fetchAppTitle","signal","AbortSignal","data","undefined","document","err","Error","console","error","abort","previous"],"sources":["../../src/dashboard/createRemoteInstance.ts","../../src/dashboard/urlFor.ts","../../src/hooks/dashboard/useApplicationContext.ts","../../src/hooks/dashboard/useAgentResourceContext.ts","../../src/hooks/dashboard/useApplications.ts","../../src/hooks/dashboard/useApplication.ts","../../src/hooks/dashboard/useApplicationBasePath.ts","../../src/hooks/dashboard/useApplicationConfigs.ts","../../src/hooks/dashboard/useApplicationConfig.ts","../../src/hooks/dashboard/useApplicationForegroundId.ts","../../src/hooks/dashboard/useAuthToken.ts","../../src/hooks/dashboard/useCapabilities.ts","../../src/hooks/dashboard/useCurrentUser.ts","../../src/hooks/dashboard/useNavigate.ts","../../src/hooks/dashboard/useNavigateToStudioDocument.ts","../../src/dashboard/remoteClientState.ts","../../src/hooks/dashboard/useRemoteClient.ts","../../src/hooks/dashboard/useWindowTitle.ts"],"sourcesContent":["import {createInstance as createMFInstance} from '@module-federation/runtime'\nimport {\n type ModuleFederationRuntimePlugin,\n type UserOptions,\n} from '@module-federation/runtime/types'\nimport {createLogger} from '@sanity/sdk/_internal'\n\ntype MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\n\n/**\n * @public\n */\nexport type FederationRemote = {\n name: string\n entry: string\n}\n\nconst MANIFEST_FILE = 'mf-manifest.json'\n\nfunction withManifest<T extends {entry: string}>(remote: T): T {\n if (remote.entry.endsWith(`/${MANIFEST_FILE}`)) return remote\n const base = remote.entry.endsWith('/') ? remote.entry : `${remote.entry}/`\n return {...remote, entry: new URL(MANIFEST_FILE, base).href}\n}\n\n/**\n * @public\n */\nexport type CreateRemoteInstanceOptions = MakeOptional<\n Pick<UserOptions, 'name' | 'remotes' | 'shared' | 'plugins'>,\n 'remotes' | 'plugins'\n>\n\n/**\n * @public\n */\nexport interface RemoteInstance {\n registerRemotes(remotes: FederationRemote[]): void\n /** Loads and evaluates a remote expose. */\n loadRemote<T>(id: string): Promise<T | null>\n /** Preloads assets without evaluating them; omit exposes to warm the whole remote. */\n preloadRemote(name: string, exposes?: string[]): Promise<void>\n}\n\n/**\n * Creates a client for registering, loading, and preloading federated modules.\n * @public\n */\nexport function createRemoteInstance(options: CreateRemoteInstanceOptions): RemoteInstance {\n const logger = createLogger(options.name)\n const instance = createMFInstance({\n ...options,\n plugins: [log(logger.debug), ...(options.plugins ?? [])],\n remotes:\n options.remotes?.map((remote) => ('entry' in remote ? withManifest(remote) : remote)) ?? [],\n })\n\n return {\n registerRemotes: (remotes) =>\n instance.registerRemotes(remotes.map(withManifest), {force: false}),\n loadRemote: async <T>(id: string) => {\n let remoteModule: T | null\n\n try {\n remoteModule = await instance.loadRemote<T>(id)\n } catch (error) {\n throw new Error(`Failed to load remote module \"${id}\"`, {\n cause: error,\n })\n }\n\n return remoteModule\n },\n // Include async chunks so lazy code is warm before the remote loads.\n preloadRemote: (name, exposes) =>\n instance.preloadRemote([{nameOrAlias: name, exposes, resourceCategory: 'all'}]),\n }\n}\n\nfunction log(\n onDebug: (message: string, context?: Record<string, unknown>) => void,\n): ModuleFederationRuntimePlugin {\n const logEvent = (eventName: string, args: object) => {\n // `origin` logs the whole instance, which is noisy\n const {origin: _origin, ...data} = args as Record<string, unknown>\n\n onDebug(`[Lifecycle] ${eventName}`, {...data, internal: true})\n }\n\n return {\n name: 'sanity-logger',\n beforeInit(args) {\n logEvent('beforeInit', args)\n return args\n },\n beforeRegisterRemote(args) {\n logEvent('beforeRegisterRemote', args)\n return args\n },\n beforePreloadRemote(args) {\n logEvent('beforePreloadRemote', args)\n },\n beforeRequest(args) {\n logEvent('beforeRequest', args)\n return args\n },\n afterResolve(args) {\n logEvent('afterResolve', args)\n return args\n },\n onLoad(args) {\n logEvent('onLoad', args)\n return args\n },\n loadShare(args) {\n logEvent('loadShare', args)\n },\n beforeLoadShare(args) {\n logEvent('beforeLoadShare', args)\n return args\n },\n }\n}\n","/** @public */\nexport interface DashboardUrl {\n url(options?: {origin?: string}): string\n toURL(options: {origin: string}): URL\n toString(): string\n}\n\n// URL requires an origin; public output always strips this parsing base.\nconst relativeUrlBase = 'https://dashboard.invalid'\n\n/** @public */\nexport interface EditIntentParameters {\n id: string\n /** The document schema type, for example `post`. */\n type?: string\n /** Requests a Studio edit mode, such as `structure` or `presentation`. */\n mode?: 'structure' | 'presentation' | (string & Record<never, never>)\n}\n\n/** @public */\nexport interface CreateIntentParameters {\n template: string\n type: string\n}\n\n/** @public */\nexport interface ReleaseIntentParameters {\n id: string\n}\n\n/** @public */\nexport interface StudioWorkspaceUrl extends DashboardUrl {\n intent(intent: 'edit', parameters: EditIntentParameters): StudioIntentUrl\n intent(intent: 'create', parameters: CreateIntentParameters): StudioIntentUrl\n intent(intent: 'release', parameters: ReleaseIntentParameters): StudioIntentUrl\n path(...path: string[]): DashboardUrl\n task(taskId: string): DashboardUrl\n}\n\n/** @public */\nexport interface StudioUrl extends StudioWorkspaceUrl {\n workspace(workspace: string): StudioWorkspaceUrl\n}\n\n/** @public */\nexport interface StudioIntentUrl extends DashboardUrl {\n perspective(perspective: string): StudioIntentUrl\n comment(commentId: string): StudioIntentUrl\n task(taskId: string): DashboardUrl\n}\n\n/** @public */\nexport interface CoreApplicationUrl extends DashboardUrl {\n path(...path: string[]): DashboardUrl\n}\n\n/** @public */\nexport interface MediaLibraryUrl extends DashboardUrl {\n asset(assetId: string): DashboardUrl\n collection(collectionId: string): DashboardUrl\n}\n\n/** @public */\nexport interface CanvasUrl extends DashboardUrl {\n document(documentId: string): DashboardUrl\n}\n\ntype IntentArguments =\n | [intent: 'edit', parameters: EditIntentParameters]\n | [intent: 'create', parameters: CreateIntentParameters]\n | [intent: 'release', parameters: ReleaseIntentParameters]\n\ntype BuilderRegistry = Readonly<\n Record<\n string,\n {\n readonly namespace: string\n new (url: URL): UrlBuilder\n }\n >\n>\n\ntype BuilderMethods<Builders extends BuilderRegistry> = {\n readonly [Name in keyof Builders]: () => InstanceType<Builders[Name]>\n}\n\nconst splitPath = (path: readonly string[]) =>\n path.flatMap((part) => part.split('/')).filter(Boolean)\n\nconst intentParametersOf = (...[intent, parameters]: IntentArguments) => {\n const searchParameters = new URLSearchParams()\n\n switch (intent) {\n case 'edit': {\n const {id, type, mode} = parameters\n searchParameters.set('id', id)\n if (type !== undefined) searchParameters.set('type', type)\n if (mode !== undefined) searchParameters.set('mode', mode)\n break\n }\n case 'create': {\n searchParameters.set('template', parameters.template)\n searchParameters.set('type', parameters.type)\n break\n }\n case 'release': {\n searchParameters.set('id', parameters.id)\n break\n }\n }\n\n return searchParameters\n}\n\nconst parseIntentParameters = (parameters: string) =>\n new URLSearchParams(parameters.replaceAll(';', '&'))\n\nconst serializeIntentParameters = (parameters: URLSearchParams) =>\n Array.from(\n parameters,\n ([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`,\n ).join(';')\n\nconst appendSegments = (url: URL, segments: readonly string[]): URL => {\n const nextUrl = new URL(url)\n if (segments.length === 0) return nextUrl\n\n nextUrl.pathname = `${nextUrl.pathname.replace(/\\/$/, '')}/${segments\n .map(encodeURIComponent)\n .join('/')}`\n return nextUrl\n}\n\n/**\n * Base class for immutable URL grammars.\n *\n * @public\n */\nexport class UrlBuilder implements DashboardUrl {\n readonly #url: URL\n\n /**\n * Creates an immutable URL builder at the supplied URL.\n *\n * @example\n * ```ts\n * const builder = new UrlBuilder(\n * new URL('/applications/my-app', 'https://dashboard.sanity.io'),\n * )\n *\n * builder.url() // '/applications/my-app'\n * ```\n */\n constructor(url: URL) {\n this.#url = new URL(url)\n }\n\n /**\n * Returns a builder with each value appended as one encoded path segment.\n *\n * Route literals and identifiers can be passed together. A slash inside a value stays within\n * that segment instead of creating another route level.\n *\n * @example\n * ```ts\n * class DocumentUrlBuilder extends UrlBuilder {\n * document(documentId: string) {\n * return this.append('documents', documentId)\n * }\n * }\n *\n * const builder = new DocumentUrlBuilder(new URL('https://dashboard.sanity.io'))\n * builder.document('drafts/document-1').url()\n * // '/documents/drafts%2Fdocument-1'\n * ```\n */\n protected append(...segments: string[]): this {\n return this.#create(appendSegments(this.#url, segments))\n }\n\n /**\n * Returns a builder with changes made through the platform `URL` API.\n *\n * This supports URL details outside the path grammar, including search parameters and hashes.\n *\n * @example\n * ```ts\n * class DocumentUrlBuilder extends UrlBuilder {\n * perspective(name: string) {\n * return this.edit((url) => url.searchParams.set('perspective', name))\n * }\n *\n * panel(panelId: string) {\n * return this.edit((url) => {\n * url.hash = `panel/${panelId}`\n * })\n * }\n * }\n * ```\n */\n protected edit(update: (url: URL) => void): this {\n const url = new URL(this.#url)\n update(url)\n return this.#create(url)\n }\n\n /**\n * Returns another builder type rooted at the appended path segments.\n *\n * Use this inside a custom builder method only when its route enters a child grammar with\n * different methods; use `append` when chaining stays on the current grammar.\n *\n * @example\n * ```ts\n * class DocumentUrlBuilder extends UrlBuilder {\n * perspective(name: string) {\n * return this.edit((url) => url.searchParams.set('perspective', name))\n * }\n * }\n *\n * class ApplicationUrlBuilder extends UrlBuilder {\n * document(documentId: string) {\n * return this.transitionTo(DocumentUrlBuilder, 'documents', documentId)\n * }\n * }\n *\n * const builder = new ApplicationUrlBuilder(new URL('https://dashboard.sanity.io'))\n * builder.document('document-1').perspective('published').url()\n * // '/documents/document-1?perspective=published'\n * ```\n */\n protected transitionTo<Builder extends UrlBuilder>(\n Builder: new (url: URL) => Builder,\n ...segments: string[]\n ): Builder {\n return new Builder(appendSegments(this.#url, segments))\n }\n\n /**\n * Returns the URL as a relative string by default.\n *\n * Supplying an origin returns an absolute URL string for the same dashboard route.\n *\n * @example\n * ```ts\n * const builder = new UrlBuilder(\n * new URL('/applications/my-app', 'https://dashboard.sanity.io'),\n * )\n *\n * builder.url() // '/applications/my-app'\n * builder.url({origin: 'https://dashboard.sanity.io'})\n * // 'https://dashboard.sanity.io/applications/my-app'\n * ```\n */\n url(options?: {origin?: string}): string {\n const relativeUrl = this.#relativeUrl()\n return options?.origin === undefined ? relativeUrl : new URL(relativeUrl, options.origin).href\n }\n\n /**\n * Returns the URL as a platform `URL` object using the supplied origin.\n *\n * @example\n * ```ts\n * const builder = new UrlBuilder(\n * new URL('/applications/my-app', 'https://dashboard.sanity.io'),\n * )\n *\n * builder.toURL({origin: 'https://dashboard.sanity.io'}).pathname\n * // '/applications/my-app'\n * ```\n */\n toURL({origin}: {origin: string}): URL {\n return new URL(this.#relativeUrl(), origin)\n }\n\n /**\n * Returns the same relative URL as {@link UrlBuilder.url}.\n *\n * @example\n * ```ts\n * const builder = new UrlBuilder(\n * new URL('/applications/my-app', 'https://dashboard.sanity.io'),\n * )\n *\n * `${builder}` // '/applications/my-app'\n * ```\n */\n toString(): string {\n return this.url()\n }\n\n #relativeUrl(): string {\n return `${this.#url.pathname}${this.#url.search}${this.#url.hash}`\n }\n\n #create(url: URL): this {\n const Builder = this.constructor as new (url: URL) => this\n return new Builder(url)\n }\n}\n\nclass CoreApplicationUrlBuilder extends UrlBuilder implements CoreApplicationUrl {\n static readonly namespace = 'applications'\n\n path(...path: string[]): this {\n return this.append(...splitPath(path))\n }\n}\n\nclass MediaLibraryUrlBuilder extends UrlBuilder implements MediaLibraryUrl {\n static readonly namespace = 'media'\n\n asset(assetId: string): DashboardUrl {\n return this.append('assets', assetId)\n }\n\n collection(collectionId: string): DashboardUrl {\n return this.append('collections', collectionId)\n }\n}\n\nclass CanvasUrlBuilder extends UrlBuilder implements CanvasUrl {\n static readonly namespace = 'canvas'\n\n document(documentId: string): DashboardUrl {\n return this.append('doc', documentId)\n }\n}\n\nclass StudioUrlBuilder\n extends UrlBuilder\n implements StudioUrl, StudioWorkspaceUrl, StudioIntentUrl\n{\n static readonly namespace = 'studios'\n\n workspace(workspace: string): StudioWorkspaceUrl {\n return this.append(workspace)\n }\n\n intent(intent: 'edit', parameters: EditIntentParameters): StudioIntentUrl\n intent(intent: 'create', parameters: CreateIntentParameters): StudioIntentUrl\n intent(intent: 'release', parameters: ReleaseIntentParameters): StudioIntentUrl\n intent(...args: IntentArguments): StudioIntentUrl {\n return this.append('intent', args[0]).edit((url) => {\n url.pathname = `${url.pathname}/${serializeIntentParameters(intentParametersOf(...args))}/`\n })\n }\n\n perspective(perspective: string): StudioIntentUrl {\n return this.edit((url) => url.searchParams.set('perspective', perspective))\n }\n\n comment(commentId: string): StudioIntentUrl {\n return this.edit((url) => {\n const segments = url.pathname.split('/')\n const parametersIndex = segments.length - 2\n const parameters = parseIntentParameters(segments[parametersIndex]!)\n parameters.set('inspect', 'sanity/comments')\n parameters.set('comment', commentId)\n segments[parametersIndex] = serializeIntentParameters(parameters)\n url.pathname = segments.join('/')\n })\n }\n\n task(taskId: string): DashboardUrl {\n return this.edit((url) => url.searchParams.set('selectedTask', taskId))\n }\n\n path(...path: string[]): DashboardUrl {\n return this.append(...splitPath(path))\n }\n}\n\n/** @public */\nexport interface Urls {\n studios(): DashboardUrl\n studios(appId: string): StudioUrl\n applications(): DashboardUrl\n applications(appId: string): CoreApplicationUrl\n mediaLibrary(): MediaLibraryUrl\n canvas(): CanvasUrl\n home(): DashboardUrl\n extend<\n const Builders extends Readonly<\n Record<\n string,\n {\n readonly namespace: string\n new (url: URL): UrlBuilder\n }\n >\n >,\n >(\n builders: Builders & Partial<Record<keyof this, never>>,\n ): this & {readonly [Name in keyof Builders]: () => InstanceType<Builders[Name]>}\n}\n\nconst createRootBuilder = <Builder extends UrlBuilder>(\n Builder: (new (url: URL) => Builder) & {readonly namespace: string},\n ...segments: string[]\n): Builder =>\n new Builder(appendSegments(new URL(relativeUrlBase), [Builder.namespace, ...segments]))\n\nconst createUrls = <const Builders extends BuilderRegistry>(\n builders: Builders,\n): Urls & BuilderMethods<Builders> => {\n function studios(): DashboardUrl\n function studios(appId: string): StudioUrl\n function studios(appId?: string): DashboardUrl | StudioUrl {\n return appId === undefined\n ? new UrlBuilder(new URL('/studios/', relativeUrlBase))\n : createRootBuilder(StudioUrlBuilder, appId)\n }\n\n function applications(): DashboardUrl\n function applications(appId: string): CoreApplicationUrl\n function applications(appId?: string): DashboardUrl | CoreApplicationUrl {\n return appId === undefined\n ? new UrlBuilder(new URL('/applications/', relativeUrlBase))\n : createRootBuilder(CoreApplicationUrlBuilder, appId)\n }\n\n const methods = Object.fromEntries(\n Object.entries(builders).map(([name, Builder]) => [name, () => createRootBuilder(Builder)]),\n )\n\n const urls = {\n studios,\n applications,\n mediaLibrary: () => createRootBuilder(MediaLibraryUrlBuilder),\n canvas: () => createRootBuilder(CanvasUrlBuilder),\n home: () => new UrlBuilder(new URL(relativeUrlBase)),\n extend<const AddedBuilders extends BuilderRegistry>(addedBuilders: AddedBuilders) {\n const namespaces = new Set([\n '',\n StudioUrlBuilder.namespace,\n CoreApplicationUrlBuilder.namespace,\n MediaLibraryUrlBuilder.namespace,\n CanvasUrlBuilder.namespace,\n ...Object.values(builders).map((Builder) => Builder.namespace),\n ])\n\n for (const [name, Builder] of Object.entries(addedBuilders)) {\n if (name in urls || name in methods) {\n throw new Error(`URL builder \"${name}\" already exists`)\n }\n if (namespaces.has(Builder.namespace)) {\n throw new Error(`URL namespace \"${Builder.namespace}\" already exists`)\n }\n namespaces.add(Builder.namespace)\n }\n\n return createUrls({...builders, ...addedBuilders})\n },\n }\n\n return Object.assign(urls, methods) as Urls & BuilderMethods<Builders>\n}\n\n/** @public */\nexport const urlFor: Urls = createUrls({})\n","import {type ApplicationContext} from '@sanity/sdk/dashboard'\nimport {useEffect} from 'react'\n\nimport {useEmit} from './useEmit'\n\n/**\n * Publishes what this application is currently showing to the Dashboard message bus, so the host\n * can hold the foreground application's context. Pass `null` when nothing is open.\n *\n * The context is published on mount and whenever it changes, and cleared with `null` on unmount.\n * A change publishes `null` before the new context, as the component has stopped showing the old\n * one. Pass a memoised value: the hook compares by reference, so a new object on every render\n * republishes on every render.\n *\n * The host reads the sending application from `message.meta.appId`, so the payload carries no\n * application ID.\n *\n * @param context - The application context to publish, or `null` to clear it\n *\n * @example\n * ```tsx\n * import {useApplicationContext} from '@sanity/sdk-react/dashboard'\n * import {useMemo} from 'react'\n *\n * function DocumentView({documentId}: {documentId: string}) {\n * const context = useMemo(\n * () => ({resource: {id: 'my-project.production', type: 'dataset' as const}, document: {id: documentId}}),\n * [documentId],\n * )\n * useApplicationContext(context)\n * return <div>Editing {documentId}</div>\n * }\n * ```\n *\n * @public\n * @category Dashboard\n */\nexport function useApplicationContext(context: ApplicationContext | null): void {\n const emit = useEmit('applications.context.update')\n\n useEffect(() => {\n emit(context)\n return () => {\n emit(null)\n }\n }, [emit, context])\n}\n","/* eslint-disable react-compiler/react-compiler -- the transport branch in `useAgentResourceContext` is a deliberate rules-of-hooks exception; the compiler refuses files that disable it */\nimport {type Events, SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\nimport {isDashboardEnvironment} from '@sanity/sdk/_internal'\nimport {type FrameMessage} from '@sanity/sdk/comlink'\nimport {type ApplicationContext} from '@sanity/sdk/dashboard'\nimport {useCallback, useEffect, useMemo, useRef} from 'react'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\nimport {useApplicationContext} from './useApplicationContext'\n\n/**\n * @public\n */\nexport interface AgentResourceContextOptions {\n /**\n * The project ID of the current context\n */\n projectId: string\n /**\n * The dataset of the current context\n */\n dataset: string\n /**\n * Optional document ID if the user is viewing/editing a specific document\n */\n documentId?: string\n}\n\n/**\n * @public\n * Hook for reporting the resource the user is currently interacting with (for example, which\n * document they're editing) so the Agent can understand their context. The hook reports on mount\n * and again whenever the context changes.\n *\n * The two Dashboard runtimes differ in transport, but the signature is the same:\n * - In an iframe (Comlink), it emits the `dashboard/v1/events/agent/resource/update` event.\n * - In a federated app (message bus), it publishes {@link useApplicationContext}'s\n * `applications.context.update` topic. Reach for `useApplicationContext` directly in new code.\n *\n * @category Agent\n * @param options - The resource context options containing projectId, dataset, and optional documentId\n *\n * @example\n * ```tsx\n * import {useAgentResourceContext} from '@sanity/sdk-react/dashboard'\n *\n * function MyComponent() {\n * const documentId = 'my-document-id'\n *\n * // Automatically updates the Agent's context whenever the document changes\n * useAgentResourceContext({\n * projectId: 'my-project',\n * dataset: 'production',\n * documentId,\n * })\n *\n * return <div>Editing document: {documentId}</div>\n * }\n * ```\n */\nexport function useAgentResourceContext(options: AgentResourceContextOptions): void {\n // The branch is stable: the transport is fixed for the page lifetime, so one set of hooks\n // always runs and the other never does.\n // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n if (isDashboardEnvironment()) return useBusAgentResourceContext(options)\n // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n return useComlinkAgentResourceContext(options)\n}\n\nfunction useBusAgentResourceContext({projectId, dataset, documentId}: AgentResourceContextOptions) {\n const context = useMemo<ApplicationContext | null>(\n () =>\n projectId && dataset\n ? {\n resource: {id: `${projectId}.${dataset}`, type: 'dataset'},\n document: documentId ? {id: documentId} : null,\n }\n : null,\n [projectId, dataset, documentId],\n )\n useApplicationContext(context)\n}\n\nfunction useComlinkAgentResourceContext(options: AgentResourceContextOptions): void {\n const {projectId, dataset, documentId} = options\n const {sendMessage} = useWindowConnection<Events.AgentResourceUpdateMessage, FrameMessage>({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n })\n\n // Track the last sent context to avoid duplicate updates\n const lastContextRef = useRef<string | null>(null)\n\n const updateContext = useCallback(() => {\n // Validate required fields\n if (!projectId || !dataset) {\n // eslint-disable-next-line no-console\n console.warn('[useAgentResourceContext] projectId and dataset are required', {\n projectId,\n dataset,\n })\n return\n }\n\n // Create a stable key for the current context\n const contextKey = `${projectId}:${dataset}:${documentId || ''}`\n\n // Skip if context hasn't changed\n if (lastContextRef.current === contextKey) {\n return\n }\n\n try {\n const message: Events.AgentResourceUpdateMessage = {\n type: 'dashboard/v1/events/agent/resource/update',\n data: {\n projectId,\n dataset,\n documentId,\n },\n }\n\n sendMessage(message.type, message.data)\n lastContextRef.current = contextKey\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[useAgentResourceContext] Failed to update context:', error)\n }\n }, [projectId, dataset, documentId, sendMessage])\n\n // Update context whenever it changes\n useEffect(() => {\n updateContext()\n }, [updateContext])\n}\n","import {type ApplicationBase} from '@sanity/sdk'\nimport {getApplicationOrigin} from '@sanity/sdk/_internal'\nimport {type RemoteModuleRef, type ValueOf} from '@sanity/sdk/dashboard'\nimport {useMemo} from 'react'\n\nimport {useTopic} from './useTopic'\n\ntype DashboardTopicApplication = Extract<\n NonNullable<ValueOf<'applications.list'>>,\n {ok: true}\n>['value'][number]\ntype DashboardApplicationInterface = NonNullable<\n NonNullable<DashboardTopicApplication['activeDeployment']>['interfaces']\n>[number]\ntype ViewInterface = Exclude<DashboardApplicationInterface, {type: 'worker'}>\n\n/**\n * A dashboard view exposed by an application.\n * @public\n */\nexport type DashboardView = {\n [Type in ViewInterface['type']]: Omit<Extract<ViewInterface, {type: Type}>, 'type'> & {\n readonly application: ApplicationBase\n readonly module: RemoteModuleRef\n readonly surface: Type extends 'app' ? 'window' : Type\n }\n}[ViewInterface['type']]\n\n/**\n * A web worker exposed by an application.\n * @public\n */\nexport type DashboardWebWorker = Extract<DashboardApplicationInterface, {type: 'worker'}> & {\n readonly application: ApplicationBase\n readonly module: RemoteModuleRef\n}\n\n/**\n * The minimal Brett application fields with its loadable views and web workers.\n * @public\n */\nexport type DashboardApplication = DashboardTopicApplication & {\n readonly views: DashboardView[]\n readonly webWorkers: DashboardWebWorker[]\n}\n\n// Only a federated deployment (one with a module federation manifest) exposes loadable modules.\nconst loadableInterfaces = ({\n activeDeployment,\n config,\n}: DashboardTopicApplication): readonly DashboardApplicationInterface[] =>\n config?.mfManifest === undefined ? [] : (activeDeployment?.interfaces ?? [])\n\nconst toApplication = (application: DashboardTopicApplication): DashboardApplication => {\n const {activeDeployment: _activeDeployment, config: _config, ...applicationBase} = application\n const interfaces = loadableInterfaces(application)\n // Nothing to load without interfaces or an origin to load them from.\n const entry = interfaces.length === 0 ? null : getApplicationOrigin(applicationBase)\n if (entry === null) return {...application, views: [], webWorkers: []}\n\n const views: DashboardView[] = []\n const webWorkers: DashboardWebWorker[] = []\n\n for (const extension of interfaces) {\n const module: RemoteModuleRef = {\n entry,\n moduleId: `${applicationBase.id}/${extension.moduleId}`,\n version: extension.version,\n }\n\n if (extension.type === 'worker') {\n webWorkers.push({...extension, application: applicationBase, module})\n continue\n }\n\n const {type, ...view} = extension\n // TS cannot correlate `surface` with the narrowed `type` across the mapped union; the\n // cast is checked by the `DashboardView` mapping above and the surface assertions in the tests.\n views.push({\n ...view,\n application: applicationBase,\n module,\n surface: type === 'app' ? 'window' : type,\n } as DashboardView)\n }\n\n return {...application, views, webWorkers}\n}\n\n/**\n * Returns the applications available in the dashboard.\n *\n * Suspends until the dashboard publishes its application list; a cleared list is empty. Throws a\n * `TopicError` to the nearest error boundary when the dashboard fails to load applications.\n *\n * @example\n * ```tsx\n * function Applications() {\n * const applications = useApplications()\n * return applications.map((application) => <div key={application.id}>{application.title}</div>)\n * }\n * ```\n *\n * @public\n */\nexport function useApplications(): DashboardApplication[] {\n const applications = useTopic('applications.list')\n return useMemo(() => applications?.map(toApplication) ?? [], [applications])\n}\n","import {type DashboardApplication, useApplications} from './useApplications'\n\n/**\n * Returns a dashboard application by id, or `null` when it is unavailable.\n *\n * Suspends until the dashboard publishes its application list.\n *\n * @example\n * ```tsx\n * function SelectedApplication({id}: {id: string}) {\n * const application = useApplication(id)\n * return application ? <h1>{application.title}</h1> : <p>Application unavailable</p>\n * }\n * ```\n *\n * @public\n */\nexport function useApplication(\n applicationId: DashboardApplication['id'],\n): DashboardApplication | null {\n return useApplications().find(({id}) => id === applicationId) ?? null\n}\n","import {type TopicData} from '@sanity/sdk/dashboard'\n\nimport {useTopic} from './useTopic'\n\n/**\n * Returns the base path for an application.\n *\n * Suspends until Dashboard publishes the path and throws a `TopicError` when the application is\n * unknown.\n *\n * @example\n * ```tsx\n * function ApplicationBasePath() {\n * const basePath = useApplicationBasePath()\n * return <span>{basePath}</span>\n * }\n * ```\n *\n * @public\n */\nexport function useApplicationBasePath(): TopicData<'applications.base-path'> {\n return useTopic('applications.base-path')\n}\n","import {type ApplicationConfig} from '@sanity/sdk/dashboard'\n\nimport {useTopic} from './useTopic'\n\n// Stable, frozen reference for the cleared/absent case so the return value only changes with the\n// topic and callers cannot mutate the shared empty list.\nconst NO_CONFIGS: readonly ApplicationConfig[] = Object.freeze([])\n\n/**\n * Returns the application configuration modules available in the dashboard.\n *\n * Suspends until the dashboard publishes its application configs; a cleared list is empty.\n *\n * @example\n * ```tsx\n * function Applications() {\n * const configs = useApplicationConfigs()\n * return configs.map((config) => <div key={config.moduleId}>{config.appType}</div>)\n * }\n * ```\n *\n * @public\n */\nexport function useApplicationConfigs(): readonly ApplicationConfig[] {\n return useTopic('applications.config') ?? NO_CONFIGS\n}\n","import {type Application} from '@sanity/sdk'\nimport {type ApplicationConfig, type ApplicationConfigAppType} from '@sanity/sdk/dashboard'\n\nimport {useApplicationConfigs} from './useApplicationConfigs'\n\n/**\n * Selects an application configuration by application id or application type.\n * @public\n */\nexport type ApplicationConfigSelector =\n | {appId: Application['id']; appType?: never}\n | {appId?: never; appType: ApplicationConfigAppType}\n\n/**\n * Returns an application configuration by application id or application type, or `null` when\n * none matches.\n *\n * An `appType` query matches the type-level config only (the one without an `appId`), so it is\n * independent of the order the dashboard published configs in. An `appId` query matches that\n * application's config.\n *\n * Suspends until the dashboard publishes its application configs.\n *\n * @example\n * ```tsx\n * function MediaLibraryConfig() {\n * const config = useApplicationConfig({appType: 'media-library'})\n * return config ? <span>{config.moduleId}</span> : <p>Not installed</p>\n * }\n * ```\n *\n * @public\n */\nexport function useApplicationConfig(\n selector: ApplicationConfigSelector,\n): ApplicationConfig | null {\n const configs = useApplicationConfigs()\n return (\n configs.find((config) =>\n selector.appId === undefined\n ? config.appType === selector.appType && config.appId === undefined\n : config.appId === selector.appId,\n ) ?? null\n )\n}\n","import {type Application} from '@sanity/sdk'\n\nimport {useTopic} from './useTopic'\n\n/**\n * Returns the id of the application in the foreground, or `null` when Dashboard has none.\n *\n * Suspends until Dashboard publishes the foreground application.\n *\n * @example\n * ```tsx\n * function ForegroundApplication() {\n * const foregroundId = useApplicationForegroundId()\n * return <span>{foregroundId ?? 'No foreground application'}</span>\n * }\n * ```\n *\n * @public\n */\nexport function useApplicationForegroundId(): Application['id'] | null {\n return useTopic('applications.foreground')\n}\n","import {useTopic} from './useTopic'\n\n/**\n * Returns the session token for the reading connection, or `null` while signed out.\n *\n * Suspends until Dashboard publishes the token.\n *\n * @example\n * ```tsx\n * function AuthToken() {\n * const token = useAuthToken()\n * return <span>{token ?? 'Signed out'}</span>\n * }\n * ```\n *\n * @public\n */\nexport function useAuthToken(): string | null {\n return useTopic('auth.token')\n}\n","import {type TopicData} from '@sanity/sdk/dashboard'\n\nimport {useTopic} from './useTopic'\n\n/**\n * Returns the capabilities the host provides.\n *\n * A capability is something the host provides; an application reads this record to hide its own\n * implementation of anything the host provides, or to skip publishing to a capability the host\n * does not have. A missing key means the host does not provide that capability.\n *\n * Suspends until the host publishes the record.\n *\n * @example\n * ```tsx\n * function UserMenu() {\n * const {globalUserMenu} = useCapabilities()\n * if (globalUserMenu) return null\n * return <LocalUserMenu />\n * }\n * ```\n *\n * @public\n */\nexport function useCapabilities(): TopicData<'applications.capabilities'> {\n return useTopic('applications.capabilities')\n}\n","import {type CurrentUser} from '@sanity/types'\n\nimport {useTopic} from './useTopic'\n\n/**\n * Returns the signed-in user, or `null` while signed out.\n *\n * Suspends until Dashboard publishes the current user.\n *\n * @example\n * ```tsx\n * function CurrentUser() {\n * const user = useCurrentUser()\n * return <span>{user?.name ?? 'Signed out'}</span>\n * }\n * ```\n *\n * @public\n */\nexport function useCurrentUser(): CurrentUser | null {\n return useTopic('users.current')\n}\n","/* eslint-disable react-compiler/react-compiler -- the transport branch in `useNavigate` is a deliberate rules-of-hooks exception; the compiler refuses files that disable it */\nimport {\n type Bridge,\n type PathChangeMessage,\n SDK_CHANNEL_NAME,\n SDK_NODE_NAME,\n} from '@sanity/message-protocol'\nimport {isDashboardEnvironment, requireDashboardMessageBus} from '@sanity/sdk/_internal'\nimport {type NavigationLocation, TopicError} from '@sanity/sdk/dashboard'\nimport {useCallback, useEffect, useEffectEvent, useRef} from 'react'\nimport {filter, map, pairwise} from 'rxjs'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\nimport {useSanityInstance} from '../context/useSanityInstance'\n\ntype UpdateURLMessage = Bridge.Listeners.History.UpdateURLMessage\n\n/**\n * A navigation the Dashboard asks the app to perform, or that the app reports to the Dashboard.\n * `path` is relative to the app's own route, with no leading slash (`'documents/abc'`), and may\n * carry a query string and hash.\n * @public\n */\nexport type DashboardNavigation = PathChangeMessage['data']\n\n/**\n * Reports an in-app navigation to the Dashboard so the browser URL and the Dashboard's own\n * router follow the app. `type` defaults to `'push'` and only applies in the federated runtime;\n * see {@link useNavigate}.\n * @public\n */\nexport type NavigateToDashboardPath = (options: {path: string; type?: 'push' | 'replace'}) => void\n\n/**\n * @public\n *\n * A helper hook designed to be injected into routing components for apps within the Dashboard.\n * It keeps the app's router and the Dashboard in two-way sync: `navigateFn` receives the\n * navigations the Dashboard asks the app to perform, and the returned function reports the app's\n * own in-app navigations back to the Dashboard so the browser URL and the Dashboard's router\n * follow along.\n *\n * A navigation the app reports is not echoed back through `navigateFn`. The returned function is\n * referentially stable.\n *\n * The two Dashboard runtimes differ:\n * - In an iframe (Comlink), inbound `type` may be `'push'`, `'replace'` or `'pop'`. Reporting is\n * optional because the bridge already forwards the iframe's own `pushState`; a reported `type`\n * is ignored and the host applies the URL as a `replace`.\n * - In a federated app (message bus), inbound `type` is `'push'` or `'replace'`; `'pop'` is never\n * sent. Reporting is required because the app's router does not reach the host, and `type` is\n * honoured. Until the Dashboard publishes the app's base path, reports are dropped with a\n * console warning.\n *\n * @param navigateFn - Function to handle navigation; should accept:\n * - `path`: a string, which will be a relative path (for example, 'my-route')\n * - `type`: 'push', 'replace', or 'pop', which will be the type of navigation to perform\n * @returns A function the app calls to report its own in-app navigations to the Dashboard.\n *\n * @example\n * ```tsx\n * import {useNavigate} from '@sanity/sdk-react/dashboard'\n * import {useEffect} from 'react'\n * import {useLocation, useNavigate as useRouterNavigate} from 'react-router'\n *\n * function DashboardNavigationSync() {\n * const routerNavigate = useRouterNavigate()\n * const {pathname, search, hash} = useLocation()\n * const navigate = useNavigate(({path, type}) => {\n * routerNavigate(path, {replace: type === 'replace'})\n * })\n * useEffect(() => {\n * navigate({path: `${pathname.slice(1)}${search}${hash}`})\n * }, [navigate, pathname, search, hash])\n * return null\n * }\n * ```\n */\nexport function useNavigate(\n navigateFn: (options: DashboardNavigation) => void,\n): NavigateToDashboardPath {\n // The branch is stable: the transport is fixed for the page lifetime, so one set of hooks\n // always runs and the other never does.\n // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n if (isDashboardEnvironment()) return useBusNavigate(navigateFn)\n // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n return useComlinkNavigate(navigateFn)\n}\n\nfunction useComlinkNavigate(\n navigateFn: (options: DashboardNavigation) => void,\n): NavigateToDashboardPath {\n const {sendMessage} = useWindowConnection<\n UpdateURLMessage | PathChangeMessage,\n PathChangeMessage\n >({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n onMessage: {\n 'dashboard/v1/history/change-path': (data: PathChangeMessage['data']) => {\n navigateFn(data)\n },\n },\n })\n\n return useCallback<NavigateToDashboardPath>(\n ({path}) =>\n sendMessage('dashboard/v1/bridge/listeners/history/update-url', {\n url: new URL(path, window.location.origin).href,\n }),\n [sendMessage],\n )\n}\n\nfunction joinPath(base: string, path: string): string {\n const root = base.replace(/\\/$/, '')\n return path ? `${root}/${path}` : root\n}\n\nfunction useBusNavigate(\n navigateFn: (options: DashboardNavigation) => void,\n): NavigateToDashboardPath {\n const instance = useSanityInstance()\n const bus = requireDashboardMessageBus(instance, 'navigate')\n const navigate = useEffectEvent(navigateFn)\n // The path of this app's latest outbound request, used to suppress the echo of our own commit.\n const ownRequest = useRef<string | null>(null)\n\n useEffect(() => {\n // bus.subscribe, not useTopic: the rule needs the previous emission, and React coalesces\n // store notifications that land in one task, so a snapshot + ref could miss the\n // `{to, transition}` -> `{to, transition: null}` pair. The observable delivers every emission.\n const sub = bus\n .subscribe('navigation.location')\n .pipe(\n pairwise(),\n filter(\n (\n pair,\n ): pair is [\n NavigationLocation & {transition: NonNullable<NavigationLocation['transition']>},\n NavigationLocation & {transition: null},\n ] => {\n const [prev, curr] = pair\n return (\n curr?.transition === null &&\n curr.appId === bus.appId &&\n prev?.transition?.to.appId === bus.appId &&\n prev.transition.to.path === curr.path\n )\n },\n ),\n filter(([, curr]) => {\n // Loop suppression: any own-app host-mediated commit consumes the pending request, so\n // a request that lands elsewhere (or never lands) can't strand its path and drop a\n // later host navigation. Only the commit for our exact path is our own echo to drop.\n const suppress = curr.path === ownRequest.current\n ownRequest.current = null\n return !suppress\n }),\n map(([prev, curr]) => ({path: curr.path, type: prev.transition.navigationType})),\n )\n .subscribe((change) => navigate(change))\n return () => sub.unsubscribe()\n }, [bus])\n\n return useCallback<NavigateToDashboardPath>(\n ({path, type = 'push'}) => {\n // The host commits paths without a leading slash; store the same form so the echo matches.\n const own = path.replace(/^\\//, '')\n ownRequest.current = own\n // A request that never landed must drop its suppression, or a later host navigation to the\n // same path would be dropped. Best-effort: never throws.\n const clearIfStale = () => {\n if (ownRequest.current === own) ownRequest.current = null\n }\n // The base path is read per call, not in render, so a host that has not published it only\n // loses outbound reporting; the inbound subscription above still installs.\n bus\n .query('applications.base-path')\n .then((base) => {\n if (!base.ok) throw new TopicError('applications.base-path')\n return bus.emit('navigation.location.update', {\n url: joinPath(base.value, own),\n history: type,\n })\n })\n .then(\n (reply) => {\n if (!reply.ok) clearIfStale()\n },\n (error) => {\n // eslint-disable-next-line no-console\n console.warn('Failed to report navigation to the Dashboard', error)\n clearIfStale()\n },\n )\n },\n [bus],\n )\n}\n","/* eslint-disable react-compiler/react-compiler -- the transport branch in `useNavigateToStudioDocument` is a deliberate rules-of-hooks exception; the compiler refuses files that disable it */\nimport {type Bridge, SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\nimport {type DocumentHandle} from '@sanity/sdk'\nimport {isDashboardEnvironment} from '@sanity/sdk/_internal'\nimport {useCallback} from 'react'\n\nimport {urlFor} from '../../dashboard/urlFor'\nimport {useWindowConnection} from '../comlink/useWindowConnection'\nimport {useEmit} from './useEmit'\nimport {\n type DashboardResource,\n useStudioWorkspacesByProjectIdDataset,\n} from './useStudioWorkspacesByProjectIdDataset'\n\n/**\n * @public\n * @category Types\n */\nexport interface NavigateToStudioResult {\n navigateToStudioDocument: () => void\n}\n\nfunction useComlinkStudioNavigator(\n documentHandle: DocumentHandle,\n): (workspace: DashboardResource) => void {\n const {sendMessage} = useWindowConnection<Bridge.Navigation.NavigateToResourceMessage, never>({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n })\n\n return useCallback(\n (workspace: DashboardResource) => {\n const message: Bridge.Navigation.NavigateToResourceMessage = {\n type: 'dashboard/v1/bridge/navigate-to-resource',\n data: {\n resourceId: workspace.id,\n resourceType: 'studio',\n path: `/intent/edit/id=${documentHandle.documentId};type=${documentHandle.documentType}`,\n },\n }\n sendMessage(message.type, message.data)\n },\n [documentHandle, sendMessage],\n )\n}\n\nfunction useBusStudioNavigator(\n documentHandle: DocumentHandle,\n): (workspace: DashboardResource) => void {\n const navigate = useEmit('navigation.location.update')\n\n return useCallback(\n (workspace: DashboardResource) => {\n const url = urlFor\n .studios(workspace.userApplicationId)\n .workspace(workspace.name)\n .intent('edit', {id: documentHandle.documentId, type: documentHandle.documentType})\n .url()\n navigate({url}).then(\n (reply) => {\n if (reply.ok === false) {\n // eslint-disable-next-line no-console\n console.warn('Failed to navigate to studio document', reply)\n }\n },\n // The reply can reject (no responder, timeout, aborted); navigation is best-effort, so\n // warn instead of throwing.\n (error) => {\n // eslint-disable-next-line no-console\n console.warn('Failed to navigate to studio document', error)\n },\n )\n },\n [documentHandle, navigate],\n )\n}\n\n/**\n * @public\n *\n * Hook that provides a function to navigate to a given document in its parent Studio.\n *\n * Uses the `projectId` and `dataset` properties of the {@link DocumentHandle} you provide to resolve the correct Studio.\n * This will only work if you have deployed a studio with a workspace with this `projectId` / `dataset` combination.\n *\n * Works in both Dashboard runtimes: it sends the navigation over the Comlink connection in the\n * iframe runtime and over the message bus in the federated runtime.\n *\n * @remarks If you write your own Document Handle to pass to this hook (as opposed to a Document Handle generated by another hook),\n * it must include values for `documentId`, `documentType`, `projectId`, and `dataset`.\n *\n * @category Documents\n * @param documentHandle - The document handle for the document to navigate to\n * @param preferredStudioUrl - The preferred studio url to navigate to if you have multiple\n * studios with the same projectId and dataset\n * @returns An object containing:\n * - `navigateToStudioDocument` - Function that when called will navigate to the studio document\n *\n * @example\n * ```tsx\n * import {type DocumentHandle} from '@sanity/sdk-react'\n * import {useNavigateToStudioDocument} from '@sanity/sdk-react/dashboard'\n * import {Button} from '@sanity/ui'\n * import {Suspense} from 'react'\n *\n * function NavigateButton({documentHandle}: {documentHandle: DocumentHandle}) {\n * const {navigateToStudioDocument} = useNavigateToStudioDocument(documentHandle)\n * return (\n * <Button\n * onClick={navigateToStudioDocument}\n * text=\"Navigate to Studio Document\"\n * />\n * )\n * }\n *\n * // Wrap the component with Suspense since the hook may suspend\n * function MyDocumentAction({documentHandle}: {documentHandle: DocumentHandle}) {\n * return (\n * <Suspense fallback={<Button text=\"Loading...\" disabled />}>\n * <NavigateButton documentHandle={documentHandle} />\n * </Suspense>\n * )\n * }\n * ```\n */\nexport function useNavigateToStudioDocument(\n documentHandle: DocumentHandle,\n preferredStudioUrl?: string,\n): NavigateToStudioResult {\n const {workspacesByProjectIdAndDataset} = useStudioWorkspacesByProjectIdDataset()\n // The branch is stable: the transport is fixed for the page lifetime, so one navigator hook\n // always runs and the other never does.\n const sendToWorkspace = isDashboardEnvironment()\n ? // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n useBusStudioNavigator(documentHandle)\n : // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n useComlinkStudioNavigator(documentHandle)\n\n const navigateToStudioDocument = useCallback(() => {\n const {projectId, dataset} = documentHandle\n\n if (!projectId || !dataset) {\n // eslint-disable-next-line no-console\n console.warn('Project ID and dataset are required to navigate to a studio document')\n return\n }\n\n const workspace = resolveWorkspace(\n documentHandle,\n workspacesByProjectIdAndDataset,\n preferredStudioUrl,\n )\n\n if (!workspace) {\n // eslint-disable-next-line no-console\n console.warn(\n `No workspace found for document with projectId: ${projectId} and dataset: ${dataset}${preferredStudioUrl ? ` or with preferred studio url: ${preferredStudioUrl}` : ''}`,\n )\n return\n }\n\n sendToWorkspace(workspace)\n }, [documentHandle, workspacesByProjectIdAndDataset, preferredStudioUrl, sendToWorkspace])\n\n return {\n navigateToStudioDocument,\n }\n}\n\ntype WorkspacesByProjectIdDataset = ReturnType<\n typeof useStudioWorkspacesByProjectIdDataset\n>['workspacesByProjectIdAndDataset']\n\nfunction resolveWorkspace(\n documentHandle: DocumentHandle,\n workspacesByProjectIdAndDataset: WorkspacesByProjectIdDataset,\n preferredStudioUrl?: string,\n): DashboardResource | undefined {\n const {projectId, dataset} = documentHandle\n const key = `${projectId}:${dataset}` as const\n\n if (preferredStudioUrl) {\n // Include workspaces without projectId/dataset in case their manifest has not loaded yet.\n const allWorkspaces = [\n ...(workspacesByProjectIdAndDataset[key] || []),\n ...(workspacesByProjectIdAndDataset['NO_PROJECT_ID:NO_DATASET'] || []),\n ]\n return allWorkspaces.find((w) => w.url === preferredStudioUrl)\n }\n\n const workspaces = workspacesByProjectIdAndDataset[key]\n if (workspaces && workspaces.length > 1) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Multiple workspaces found for document and no preferred studio url',\n documentHandle,\n )\n // eslint-disable-next-line no-console\n console.warn('Using the first one', workspaces[0])\n }\n return workspaces?.[0]\n}\n","import {type SanityInstance, type StateSource} from '@sanity/sdk'\nimport {of} from 'rxjs'\n\nimport {createRemoteInstance, type RemoteInstance} from './createRemoteInstance'\n\nconst remoteClientStates = new WeakMap<SanityInstance, StateSource<RemoteInstance>>()\n\n/** Returns the instance's remote client state, creating it on first use. @internal */\nexport function getRemoteClientState(instance: SanityInstance): StateSource<RemoteInstance> {\n if (instance.isDisposed()) {\n throw new Error('Cannot create a remote client for a disposed Sanity instance')\n }\n\n const current = remoteClientStates.get(instance)\n if (current) return current\n\n const client = createRemoteInstance({name: `sanity-remote-${instance.instanceId}`})\n const state = {\n getCurrent: () => client,\n observable: of(client),\n subscribe: () => () => {},\n }\n\n remoteClientStates.set(instance, state)\n instance.onDispose(() => remoteClientStates.delete(instance))\n return state\n}\n","import {type RemoteInstance} from '../../dashboard/createRemoteInstance'\nimport {getRemoteClientState} from '../../dashboard/remoteClientState'\nimport {createStateSourceHook} from '../helpers/createStateSourceHook'\n\n/**\n * Returns a remote client shared by hooks using the same Sanity instance.\n * The first call creates the client; later calls reuse it.\n * @public\n */\nexport const useRemoteClient: () => RemoteInstance = createStateSourceHook(getRemoteClientState)\n","/* eslint-disable react-compiler/react-compiler -- the transport branch in `useWindowTitle` is a deliberate rules-of-hooks exception; the compiler refuses files that disable it */\nimport {SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\nimport {isDashboardEnvironment} from '@sanity/sdk/_internal'\nimport {useEffect, useState} from 'react'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\n\ninterface ContextResource {\n type: string\n title?: string\n manifest?: {\n title?: string\n } | null\n activeDeployment?: {\n manifest?: {\n title?: string\n } | null\n } | null\n}\n\ninterface ContextResponse {\n context: {\n resource: ContextResource\n }\n}\n\nfunction resolveAppTitle(resource: ContextResource): string | undefined {\n return resource.manifest?.title || resource.activeDeployment?.manifest?.title || resource.title\n}\n\n/**\n * Sets the browser's document title, automatically including the app's name\n * from the manifest.\n *\n * This follows the same convention as Sanity Studio workspaces, where the\n * workspace name is always present in the title:\n *\n * - With a view title: `<viewTitle> | <appTitle>`\n * - Without a view title: `<appTitle>`\n *\n * The Sanity dashboard appends `| Sanity` to produce the final browser tab title.\n *\n * Works in both Dashboard runtimes: it manages the document title over the Comlink connection, and\n * no-ops under the message bus, where the Dashboard host owns the title.\n *\n * @param viewTitle - An optional view-specific title to prepend to the app title.\n *\n * @example\n * ```tsx\n * import {useWindowTitle} from '@sanity/sdk-react/dashboard'\n *\n * function MoviesList() {\n * useWindowTitle('Movies')\n * return <div>...</div>\n * }\n *\n * // Browser tab: \"Movies | My App | Sanity\"\n * ```\n *\n * @example\n * ```tsx\n * // Call without arguments to show just the app title\n * function AppRoot() {\n * useWindowTitle()\n * return <Outlet />\n * }\n *\n * // Browser tab: \"My App | Sanity\"\n * ```\n *\n * @public\n */\nexport function useWindowTitle(viewTitle?: string): void {\n // The document title is set by the Dashboard host under the message bus, so this hook only\n // manages it in the Comlink runtime. The branch is stable: the transport is fixed for the page\n // lifetime, so the Comlink hooks below always run or never run.\n if (isDashboardEnvironment()) return\n // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n useComlinkWindowTitle(viewTitle)\n}\n\nfunction useComlinkWindowTitle(viewTitle?: string): void {\n const [appTitle, setAppTitle] = useState<string | null>(null)\n\n const {fetch} = useWindowConnection({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n })\n\n useEffect(() => {\n if (!fetch) return\n\n const controller = new AbortController()\n\n async function fetchAppTitle(signal: AbortSignal) {\n try {\n const data = await fetch<ContextResponse>('dashboard/v1/context', undefined, {signal})\n // Local development resources do not have a registered title or deployment manifest.\n // In that case, use the title rendered into the iframe HTML by the Sanity CLI.\n const title = resolveAppTitle(data.context.resource) || document.title\n if (title) {\n setAppTitle(title)\n }\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') return\n // eslint-disable-next-line no-console\n console.error('Failed to fetch app title from dashboard context:', err)\n }\n }\n\n fetchAppTitle(controller.signal)\n\n return () => {\n controller.abort()\n }\n }, [fetch])\n\n useEffect(() => {\n if (!appTitle) return\n\n const previous = document.title\n document.title = viewTitle ? `${viewTitle} | ${appTitle}` : appTitle\n\n return () => {\n document.title = previous\n }\n }, [viewTitle, appTitle])\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dashboard.js","names":["createInstance","createMFInstance","ModuleFederationRuntimePlugin","UserOptions","createLogger","MakeOptional","T","Omit","K","Partial","Pick","FederationRemote","name","entry","MANIFEST_FILE","withManifest","remote","endsWith","base","URL","href","CreateRemoteInstanceOptions","RemoteInstance","registerRemotes","remotes","loadRemote","id","Promise","preloadRemote","exposes","createRemoteInstance","options","logger","instance","plugins","log","debug","map","force","remoteModule","error","Error","cause","nameOrAlias","resourceCategory","onDebug","message","context","Record","logEvent","eventName","args","origin","_origin","data","internal","beforeInit","beforeRegisterRemote","beforePreloadRemote","beforeRequest","afterResolve","onLoad","loadShare","beforeLoadShare","DashboardUrl","url","options","origin","toURL","URL","toString","relativeUrlBase","EditIntentParameters","id","type","mode","Record","CreateIntentParameters","template","ReleaseIntentParameters","StudioWorkspaceUrl","intent","parameters","StudioIntentUrl","path","task","taskId","StudioUrl","workspace","perspective","comment","commentId","CoreApplicationUrl","MediaLibraryUrl","asset","assetId","collection","collectionId","CanvasUrl","document","documentId","IntentArguments","BuilderRegistry","Readonly","namespace","UrlBuilder","BuilderMethods","Builders","InstanceType","Name","splitPath","flatMap","part","split","filter","Boolean","intentParametersOf","searchParameters","URLSearchParams","set","undefined","parseIntentParameters","replaceAll","serializeIntentParameters","Array","from","name","value","encodeURIComponent","join","appendSegments","segments","nextUrl","length","pathname","replace","map","constructor","append","create","edit","update","transitionTo","Builder","relativeUrl","href","#relativeUrl","search","hash","#create","CoreApplicationUrlBuilder","MediaLibraryUrlBuilder","CanvasUrlBuilder","StudioUrlBuilder","args","searchParams","parametersIndex","Urls","studios","appId","applications","mediaLibrary","canvas","home","extend","builders","Partial","createRootBuilder","createUrls","methods","Object","fromEntries","entries","urls","addedBuilders","AddedBuilders","namespaces","Set","values","Error","has","add","assign","urlFor","ApplicationContext","useEffect","useEmit","useApplicationContext","context","$","_c","emit","t0","t1","Events","SDK_CHANNEL_NAME","SDK_NODE_NAME","isDashboardEnvironment","FrameMessage","ApplicationContext","useCallback","useEffect","useMemo","useRef","useWindowConnection","useApplicationContext","AgentResourceContextOptions","projectId","dataset","documentId","useAgentResourceContext","options","useBusAgentResourceContext","useComlinkAgentResourceContext","t0","$","_c","t1","resource","id","type","document","context","Symbol","for","name","connectTo","sendMessage","lastContextRef","console","warn","contextKey","current","message","data","t2","error","updateContext","t3","ApplicationBase","Installation","InstallationInclude","getApplicationOrigin","RemoteModuleRef","ValueOf","useMemo","useTopic","DashboardTopicEntry","Extract","NonNullable","ok","DashboardTopicApplication","type","DashboardTopicInstallation","Exclude","DashboardApplicationInterface","ViewInterface","DashboardApplicationBase","Omit","isLocal","DashboardView","Type","application","module","surface","DashboardWebWorker","DashboardInstallation","installation","DashboardApplication","Pick","views","webWorkers","loadableInterfaces","activeDeployment","config","mfManifest","undefined","interfaces","loadModules","base","entry","length","extension","moduleId","id","version","push","view","toApplication","_activeDeployment","_config","applicationBase","toInstallation","title","name","reference","icon","isSingleton","visibility","slug","externalUrl","organizationId","createdAt","updatedAt","useApplications","$","_c","applications","t0","map","_temp","DashboardApplication","useApplications","useApplication","applicationId","find","t0","id","TopicData","useTopic","useApplicationBasePath","ApplicationConfig","useTopic","NO_CONFIGS","Object","freeze","useApplicationConfigs","Application","ApplicationConfig","ApplicationConfigAppType","useApplicationConfigs","ApplicationConfigSelector","appId","appType","useApplicationConfig","selector","$","_c","configs","t0","find","config","undefined","Application","useTopic","useApplicationForegroundId","useTopic","useAuthToken","TopicData","useTopic","useCapabilities","CurrentUser","useTopic","useCurrentUser","Bridge","PathChangeMessage","SDK_CHANNEL_NAME","SDK_NODE_NAME","isDashboardEnvironment","requireDashboardMessageBus","NavigationLocation","TopicError","useCallback","useEffect","useEffectEvent","useRef","filter","map","pairwise","useWindowConnection","useSanityInstance","UpdateURLMessage","Listeners","History","DashboardNavigation","NavigateToDashboardPath","options","path","type","useNavigate","navigateFn","useBusNavigate","useComlinkNavigate","$","_c","t0","name","connectTo","onMessage","data","sendMessage","t1","t2","url","URL","window","location","origin","href","joinPath","base","root","replace","instance","bus","navigate","ownRequest","sub","subscribe","pipe","pair","prev","curr","transition","appId","to","curr_0","suppress","current","_temp","change","unsubscribe","t3","t4","t5","undefined","own","clearIfStale","query","then","ok","emit","value","history","reply","error","console","warn","prev_0","curr_1","navigationType","Bridge","SDK_CHANNEL_NAME","SDK_NODE_NAME","DocumentHandle","isDashboardEnvironment","useCallback","urlFor","useWindowConnection","useEmit","DashboardResource","useStudioWorkspacesByProjectIdDataset","NavigateToStudioResult","navigateToStudioDocument","useComlinkStudioNavigator","documentHandle","$","_c","t0","Symbol","for","name","connectTo","sendMessage","t1","documentId","documentType","workspace","message","type","data","resourceId","id","resourceType","path","useBusStudioNavigator","navigate","url","studios","userApplicationId","intent","then","_temp","_temp2","error","console","warn","reply","ok","useNavigateToStudioDocument","preferredStudioUrl","workspacesByProjectIdAndDataset","sendToWorkspace","projectId","dataset","resolveWorkspace","WorkspacesByProjectIdDataset","ReturnType","key","const","allWorkspaces","find","w","workspaces","length","SanityInstance","StateSource","of","createRemoteInstance","RemoteInstance","remoteClientStates","WeakMap","getRemoteClientState","instance","isDisposed","Error","current","get","client","name","instanceId","state","getCurrent","observable","subscribe","set","onDispose","delete","RemoteInstance","getRemoteClientState","createStateSourceHook","useRemoteClient","SDK_CHANNEL_NAME","SDK_NODE_NAME","isDashboardEnvironment","useEffect","useState","useWindowConnection","ContextResource","type","title","manifest","activeDeployment","ContextResponse","context","resource","resolveAppTitle","useWindowTitle","viewTitle","useComlinkWindowTitle","appTitle","setAppTitle","fetch","name","connectTo","controller","AbortController","fetchAppTitle","signal","AbortSignal","data","undefined","document","err","Error","console","error","abort","previous"],"sources":["../../src/dashboard/createRemoteInstance.ts","../../src/dashboard/urlFor.ts","../../src/hooks/dashboard/useApplicationContext.ts","../../src/hooks/dashboard/useAgentResourceContext.ts","../../src/hooks/dashboard/useApplications.ts","../../src/hooks/dashboard/useApplication.ts","../../src/hooks/dashboard/useApplicationBasePath.ts","../../src/hooks/dashboard/useApplicationConfigs.ts","../../src/hooks/dashboard/useApplicationConfig.ts","../../src/hooks/dashboard/useApplicationForegroundId.ts","../../src/hooks/dashboard/useAuthToken.ts","../../src/hooks/dashboard/useCapabilities.ts","../../src/hooks/dashboard/useCurrentUser.ts","../../src/hooks/dashboard/useNavigate.ts","../../src/hooks/dashboard/useNavigateToStudioDocument.ts","../../src/dashboard/remoteClientState.ts","../../src/hooks/dashboard/useRemoteClient.ts","../../src/hooks/dashboard/useWindowTitle.ts"],"sourcesContent":["import {createInstance as createMFInstance} from '@module-federation/runtime'\nimport {\n type ModuleFederationRuntimePlugin,\n type UserOptions,\n} from '@module-federation/runtime/types'\nimport {createLogger} from '@sanity/sdk/_internal'\n\ntype MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\n\n/**\n * @public\n */\nexport type FederationRemote = {\n name: string\n entry: string\n}\n\nconst MANIFEST_FILE = 'mf-manifest.json'\n\nfunction withManifest<T extends {entry: string}>(remote: T): T {\n if (remote.entry.endsWith(`/${MANIFEST_FILE}`)) return remote\n const base = remote.entry.endsWith('/') ? remote.entry : `${remote.entry}/`\n return {...remote, entry: new URL(MANIFEST_FILE, base).href}\n}\n\n/**\n * @public\n */\nexport type CreateRemoteInstanceOptions = MakeOptional<\n Pick<UserOptions, 'name' | 'remotes' | 'shared' | 'plugins'>,\n 'remotes' | 'plugins'\n>\n\n/**\n * @public\n */\nexport interface RemoteInstance {\n registerRemotes(remotes: FederationRemote[]): void\n /** Loads and evaluates a remote expose. */\n loadRemote<T>(id: string): Promise<T | null>\n /** Preloads assets without evaluating them; omit exposes to warm the whole remote. */\n preloadRemote(name: string, exposes?: string[]): Promise<void>\n}\n\n/**\n * Creates a client for registering, loading, and preloading federated modules.\n * @public\n */\nexport function createRemoteInstance(options: CreateRemoteInstanceOptions): RemoteInstance {\n const logger = createLogger(options.name)\n const instance = createMFInstance({\n ...options,\n plugins: [log(logger.debug), ...(options.plugins ?? [])],\n remotes:\n options.remotes?.map((remote) => ('entry' in remote ? withManifest(remote) : remote)) ?? [],\n })\n\n return {\n registerRemotes: (remotes) =>\n instance.registerRemotes(remotes.map(withManifest), {force: false}),\n loadRemote: async <T>(id: string) => {\n let remoteModule: T | null\n\n try {\n remoteModule = await instance.loadRemote<T>(id)\n } catch (error) {\n throw new Error(`Failed to load remote module \"${id}\"`, {\n cause: error,\n })\n }\n\n return remoteModule\n },\n // Include async chunks so lazy code is warm before the remote loads.\n preloadRemote: (name, exposes) =>\n instance.preloadRemote([{nameOrAlias: name, exposes, resourceCategory: 'all'}]),\n }\n}\n\nfunction log(\n onDebug: (message: string, context?: Record<string, unknown>) => void,\n): ModuleFederationRuntimePlugin {\n const logEvent = (eventName: string, args: object) => {\n // `origin` logs the whole instance, which is noisy\n const {origin: _origin, ...data} = args as Record<string, unknown>\n\n onDebug(`[Lifecycle] ${eventName}`, {...data, internal: true})\n }\n\n return {\n name: 'sanity-logger',\n beforeInit(args) {\n logEvent('beforeInit', args)\n return args\n },\n beforeRegisterRemote(args) {\n logEvent('beforeRegisterRemote', args)\n return args\n },\n beforePreloadRemote(args) {\n logEvent('beforePreloadRemote', args)\n },\n beforeRequest(args) {\n logEvent('beforeRequest', args)\n return args\n },\n afterResolve(args) {\n logEvent('afterResolve', args)\n return args\n },\n onLoad(args) {\n logEvent('onLoad', args)\n return args\n },\n loadShare(args) {\n logEvent('loadShare', args)\n },\n beforeLoadShare(args) {\n logEvent('beforeLoadShare', args)\n return args\n },\n }\n}\n","/** @public */\nexport interface DashboardUrl {\n url(options?: {origin?: string}): string\n toURL(options: {origin: string}): URL\n toString(): string\n}\n\n// URL requires an origin; public output always strips this parsing base.\nconst relativeUrlBase = 'https://dashboard.invalid'\n\n/** @public */\nexport interface EditIntentParameters {\n id: string\n /** The document schema type, for example `post`. */\n type?: string\n /** Requests a Studio edit mode, such as `structure` or `presentation`. */\n mode?: 'structure' | 'presentation' | (string & Record<never, never>)\n}\n\n/** @public */\nexport interface CreateIntentParameters {\n template: string\n type: string\n}\n\n/** @public */\nexport interface ReleaseIntentParameters {\n id: string\n}\n\n/** @public */\nexport interface StudioWorkspaceUrl extends DashboardUrl {\n intent(intent: 'edit', parameters: EditIntentParameters): StudioIntentUrl\n intent(intent: 'create', parameters: CreateIntentParameters): StudioIntentUrl\n intent(intent: 'release', parameters: ReleaseIntentParameters): StudioIntentUrl\n path(...path: string[]): DashboardUrl\n task(taskId: string): DashboardUrl\n}\n\n/** @public */\nexport interface StudioUrl extends StudioWorkspaceUrl {\n workspace(workspace: string): StudioWorkspaceUrl\n}\n\n/** @public */\nexport interface StudioIntentUrl extends DashboardUrl {\n perspective(perspective: string): StudioIntentUrl\n comment(commentId: string): StudioIntentUrl\n task(taskId: string): DashboardUrl\n}\n\n/** @public */\nexport interface CoreApplicationUrl extends DashboardUrl {\n path(...path: string[]): DashboardUrl\n}\n\n/** @public */\nexport interface MediaLibraryUrl extends DashboardUrl {\n asset(assetId: string): DashboardUrl\n collection(collectionId: string): DashboardUrl\n}\n\n/** @public */\nexport interface CanvasUrl extends DashboardUrl {\n document(documentId: string): DashboardUrl\n}\n\ntype IntentArguments =\n | [intent: 'edit', parameters: EditIntentParameters]\n | [intent: 'create', parameters: CreateIntentParameters]\n | [intent: 'release', parameters: ReleaseIntentParameters]\n\ntype BuilderRegistry = Readonly<\n Record<\n string,\n {\n readonly namespace: string\n new (url: URL): UrlBuilder\n }\n >\n>\n\ntype BuilderMethods<Builders extends BuilderRegistry> = {\n readonly [Name in keyof Builders]: () => InstanceType<Builders[Name]>\n}\n\nconst splitPath = (path: readonly string[]) =>\n path.flatMap((part) => part.split('/')).filter(Boolean)\n\nconst intentParametersOf = (...[intent, parameters]: IntentArguments) => {\n const searchParameters = new URLSearchParams()\n\n switch (intent) {\n case 'edit': {\n const {id, type, mode} = parameters\n searchParameters.set('id', id)\n if (type !== undefined) searchParameters.set('type', type)\n if (mode !== undefined) searchParameters.set('mode', mode)\n break\n }\n case 'create': {\n searchParameters.set('template', parameters.template)\n searchParameters.set('type', parameters.type)\n break\n }\n case 'release': {\n searchParameters.set('id', parameters.id)\n break\n }\n }\n\n return searchParameters\n}\n\nconst parseIntentParameters = (parameters: string) =>\n new URLSearchParams(parameters.replaceAll(';', '&'))\n\nconst serializeIntentParameters = (parameters: URLSearchParams) =>\n Array.from(\n parameters,\n ([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`,\n ).join(';')\n\nconst appendSegments = (url: URL, segments: readonly string[]): URL => {\n const nextUrl = new URL(url)\n if (segments.length === 0) return nextUrl\n\n nextUrl.pathname = `${nextUrl.pathname.replace(/\\/$/, '')}/${segments\n .map(encodeURIComponent)\n .join('/')}`\n return nextUrl\n}\n\n/**\n * Base class for immutable URL grammars.\n *\n * @public\n */\nexport class UrlBuilder implements DashboardUrl {\n readonly #url: URL\n\n /**\n * Creates an immutable URL builder at the supplied URL.\n *\n * @example\n * ```ts\n * const builder = new UrlBuilder(\n * new URL('/applications/my-app', 'https://dashboard.sanity.io'),\n * )\n *\n * builder.url() // '/applications/my-app'\n * ```\n */\n constructor(url: URL) {\n this.#url = new URL(url)\n }\n\n /**\n * Returns a builder with each value appended as one encoded path segment.\n *\n * Route literals and identifiers can be passed together. A slash inside a value stays within\n * that segment instead of creating another route level.\n *\n * @example\n * ```ts\n * class DocumentUrlBuilder extends UrlBuilder {\n * document(documentId: string) {\n * return this.append('documents', documentId)\n * }\n * }\n *\n * const builder = new DocumentUrlBuilder(new URL('https://dashboard.sanity.io'))\n * builder.document('drafts/document-1').url()\n * // '/documents/drafts%2Fdocument-1'\n * ```\n */\n protected append(...segments: string[]): this {\n return this.#create(appendSegments(this.#url, segments))\n }\n\n /**\n * Returns a builder with changes made through the platform `URL` API.\n *\n * This supports URL details outside the path grammar, including search parameters and hashes.\n *\n * @example\n * ```ts\n * class DocumentUrlBuilder extends UrlBuilder {\n * perspective(name: string) {\n * return this.edit((url) => url.searchParams.set('perspective', name))\n * }\n *\n * panel(panelId: string) {\n * return this.edit((url) => {\n * url.hash = `panel/${panelId}`\n * })\n * }\n * }\n * ```\n */\n protected edit(update: (url: URL) => void): this {\n const url = new URL(this.#url)\n update(url)\n return this.#create(url)\n }\n\n /**\n * Returns another builder type rooted at the appended path segments.\n *\n * Use this inside a custom builder method only when its route enters a child grammar with\n * different methods; use `append` when chaining stays on the current grammar.\n *\n * @example\n * ```ts\n * class DocumentUrlBuilder extends UrlBuilder {\n * perspective(name: string) {\n * return this.edit((url) => url.searchParams.set('perspective', name))\n * }\n * }\n *\n * class ApplicationUrlBuilder extends UrlBuilder {\n * document(documentId: string) {\n * return this.transitionTo(DocumentUrlBuilder, 'documents', documentId)\n * }\n * }\n *\n * const builder = new ApplicationUrlBuilder(new URL('https://dashboard.sanity.io'))\n * builder.document('document-1').perspective('published').url()\n * // '/documents/document-1?perspective=published'\n * ```\n */\n protected transitionTo<Builder extends UrlBuilder>(\n Builder: new (url: URL) => Builder,\n ...segments: string[]\n ): Builder {\n return new Builder(appendSegments(this.#url, segments))\n }\n\n /**\n * Returns the URL as a relative string by default.\n *\n * Supplying an origin returns an absolute URL string for the same dashboard route.\n *\n * @example\n * ```ts\n * const builder = new UrlBuilder(\n * new URL('/applications/my-app', 'https://dashboard.sanity.io'),\n * )\n *\n * builder.url() // '/applications/my-app'\n * builder.url({origin: 'https://dashboard.sanity.io'})\n * // 'https://dashboard.sanity.io/applications/my-app'\n * ```\n */\n url(options?: {origin?: string}): string {\n const relativeUrl = this.#relativeUrl()\n return options?.origin === undefined ? relativeUrl : new URL(relativeUrl, options.origin).href\n }\n\n /**\n * Returns the URL as a platform `URL` object using the supplied origin.\n *\n * @example\n * ```ts\n * const builder = new UrlBuilder(\n * new URL('/applications/my-app', 'https://dashboard.sanity.io'),\n * )\n *\n * builder.toURL({origin: 'https://dashboard.sanity.io'}).pathname\n * // '/applications/my-app'\n * ```\n */\n toURL({origin}: {origin: string}): URL {\n return new URL(this.#relativeUrl(), origin)\n }\n\n /**\n * Returns the same relative URL as {@link UrlBuilder.url}.\n *\n * @example\n * ```ts\n * const builder = new UrlBuilder(\n * new URL('/applications/my-app', 'https://dashboard.sanity.io'),\n * )\n *\n * `${builder}` // '/applications/my-app'\n * ```\n */\n toString(): string {\n return this.url()\n }\n\n #relativeUrl(): string {\n return `${this.#url.pathname}${this.#url.search}${this.#url.hash}`\n }\n\n #create(url: URL): this {\n const Builder = this.constructor as new (url: URL) => this\n return new Builder(url)\n }\n}\n\nclass CoreApplicationUrlBuilder extends UrlBuilder implements CoreApplicationUrl {\n static readonly namespace = 'applications'\n\n path(...path: string[]): this {\n return this.append(...splitPath(path))\n }\n}\n\nclass MediaLibraryUrlBuilder extends UrlBuilder implements MediaLibraryUrl {\n static readonly namespace = 'media'\n\n asset(assetId: string): DashboardUrl {\n return this.append('assets', assetId)\n }\n\n collection(collectionId: string): DashboardUrl {\n return this.append('collections', collectionId)\n }\n}\n\nclass CanvasUrlBuilder extends UrlBuilder implements CanvasUrl {\n static readonly namespace = 'canvas'\n\n document(documentId: string): DashboardUrl {\n return this.append('doc', documentId)\n }\n}\n\nclass StudioUrlBuilder\n extends UrlBuilder\n implements StudioUrl, StudioWorkspaceUrl, StudioIntentUrl\n{\n static readonly namespace = 'studios'\n\n workspace(workspace: string): StudioWorkspaceUrl {\n return this.append(workspace)\n }\n\n intent(intent: 'edit', parameters: EditIntentParameters): StudioIntentUrl\n intent(intent: 'create', parameters: CreateIntentParameters): StudioIntentUrl\n intent(intent: 'release', parameters: ReleaseIntentParameters): StudioIntentUrl\n intent(...args: IntentArguments): StudioIntentUrl {\n return this.append('intent', args[0]).edit((url) => {\n url.pathname = `${url.pathname}/${serializeIntentParameters(intentParametersOf(...args))}/`\n })\n }\n\n perspective(perspective: string): StudioIntentUrl {\n return this.edit((url) => url.searchParams.set('perspective', perspective))\n }\n\n comment(commentId: string): StudioIntentUrl {\n return this.edit((url) => {\n const segments = url.pathname.split('/')\n const parametersIndex = segments.length - 2\n const parameters = parseIntentParameters(segments[parametersIndex]!)\n parameters.set('inspect', 'sanity/comments')\n parameters.set('comment', commentId)\n segments[parametersIndex] = serializeIntentParameters(parameters)\n url.pathname = segments.join('/')\n })\n }\n\n task(taskId: string): DashboardUrl {\n return this.edit((url) => url.searchParams.set('selectedTask', taskId))\n }\n\n path(...path: string[]): DashboardUrl {\n return this.append(...splitPath(path))\n }\n}\n\n/** @public */\nexport interface Urls {\n studios(): DashboardUrl\n studios(appId: string): StudioUrl\n applications(): DashboardUrl\n applications(appId: string): CoreApplicationUrl\n mediaLibrary(): MediaLibraryUrl\n canvas(): CanvasUrl\n home(): DashboardUrl\n extend<\n const Builders extends Readonly<\n Record<\n string,\n {\n readonly namespace: string\n new (url: URL): UrlBuilder\n }\n >\n >,\n >(\n builders: Builders & Partial<Record<keyof this, never>>,\n ): this & {readonly [Name in keyof Builders]: () => InstanceType<Builders[Name]>}\n}\n\nconst createRootBuilder = <Builder extends UrlBuilder>(\n Builder: (new (url: URL) => Builder) & {readonly namespace: string},\n ...segments: string[]\n): Builder =>\n new Builder(appendSegments(new URL(relativeUrlBase), [Builder.namespace, ...segments]))\n\nconst createUrls = <const Builders extends BuilderRegistry>(\n builders: Builders,\n): Urls & BuilderMethods<Builders> => {\n function studios(): DashboardUrl\n function studios(appId: string): StudioUrl\n function studios(appId?: string): DashboardUrl | StudioUrl {\n return appId === undefined\n ? new UrlBuilder(new URL('/studios/', relativeUrlBase))\n : createRootBuilder(StudioUrlBuilder, appId)\n }\n\n function applications(): DashboardUrl\n function applications(appId: string): CoreApplicationUrl\n function applications(appId?: string): DashboardUrl | CoreApplicationUrl {\n return appId === undefined\n ? new UrlBuilder(new URL('/applications/', relativeUrlBase))\n : createRootBuilder(CoreApplicationUrlBuilder, appId)\n }\n\n const methods = Object.fromEntries(\n Object.entries(builders).map(([name, Builder]) => [name, () => createRootBuilder(Builder)]),\n )\n\n const urls = {\n studios,\n applications,\n mediaLibrary: () => createRootBuilder(MediaLibraryUrlBuilder),\n canvas: () => createRootBuilder(CanvasUrlBuilder),\n home: () => new UrlBuilder(new URL(relativeUrlBase)),\n extend<const AddedBuilders extends BuilderRegistry>(addedBuilders: AddedBuilders) {\n const namespaces = new Set([\n '',\n StudioUrlBuilder.namespace,\n CoreApplicationUrlBuilder.namespace,\n MediaLibraryUrlBuilder.namespace,\n CanvasUrlBuilder.namespace,\n ...Object.values(builders).map((Builder) => Builder.namespace),\n ])\n\n for (const [name, Builder] of Object.entries(addedBuilders)) {\n if (name in urls || name in methods) {\n throw new Error(`URL builder \"${name}\" already exists`)\n }\n if (namespaces.has(Builder.namespace)) {\n throw new Error(`URL namespace \"${Builder.namespace}\" already exists`)\n }\n namespaces.add(Builder.namespace)\n }\n\n return createUrls({...builders, ...addedBuilders})\n },\n }\n\n return Object.assign(urls, methods) as Urls & BuilderMethods<Builders>\n}\n\n/** @public */\nexport const urlFor: Urls = createUrls({})\n","import {type ApplicationContext} from '@sanity/sdk/dashboard'\nimport {useEffect} from 'react'\n\nimport {useEmit} from './useEmit'\n\n/**\n * Publishes what this application is currently showing to the Dashboard message bus, so the host\n * can hold the foreground application's context. Pass `null` when nothing is open.\n *\n * The context is published on mount and whenever it changes, and cleared with `null` on unmount.\n * A change publishes `null` before the new context, as the component has stopped showing the old\n * one. Pass a memoised value: the hook compares by reference, so a new object on every render\n * republishes on every render.\n *\n * The host reads the sending application from `message.meta.appId`, so the payload carries no\n * application ID.\n *\n * @param context - The application context to publish, or `null` to clear it\n *\n * @example\n * ```tsx\n * import {useApplicationContext} from '@sanity/sdk-react/dashboard'\n * import {useMemo} from 'react'\n *\n * function DocumentView({documentId}: {documentId: string}) {\n * const context = useMemo(\n * () => ({resource: {id: 'my-project.production', type: 'dataset' as const}, document: {id: documentId}}),\n * [documentId],\n * )\n * useApplicationContext(context)\n * return <div>Editing {documentId}</div>\n * }\n * ```\n *\n * @public\n * @category Dashboard\n */\nexport function useApplicationContext(context: ApplicationContext | null): void {\n const emit = useEmit('applications.context.update')\n\n useEffect(() => {\n emit(context)\n return () => {\n emit(null)\n }\n }, [emit, context])\n}\n","/* eslint-disable react-compiler/react-compiler -- the transport branch in `useAgentResourceContext` is a deliberate rules-of-hooks exception; the compiler refuses files that disable it */\nimport {type Events, SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\nimport {isDashboardEnvironment} from '@sanity/sdk/_internal'\nimport {type FrameMessage} from '@sanity/sdk/comlink'\nimport {type ApplicationContext} from '@sanity/sdk/dashboard'\nimport {useCallback, useEffect, useMemo, useRef} from 'react'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\nimport {useApplicationContext} from './useApplicationContext'\n\n/**\n * @public\n */\nexport interface AgentResourceContextOptions {\n /**\n * The project ID of the current context\n */\n projectId: string\n /**\n * The dataset of the current context\n */\n dataset: string\n /**\n * Optional document ID if the user is viewing/editing a specific document\n */\n documentId?: string\n}\n\n/**\n * @public\n * Hook for reporting the resource the user is currently interacting with (for example, which\n * document they're editing) so the Agent can understand their context. The hook reports on mount\n * and again whenever the context changes.\n *\n * The two Dashboard runtimes differ in transport, but the signature is the same:\n * - In an iframe (Comlink), it emits the `dashboard/v1/events/agent/resource/update` event.\n * - In a federated app (message bus), it publishes {@link useApplicationContext}'s\n * `applications.context.update` topic. Reach for `useApplicationContext` directly in new code.\n *\n * @category Agent\n * @param options - The resource context options containing projectId, dataset, and optional documentId\n *\n * @example\n * ```tsx\n * import {useAgentResourceContext} from '@sanity/sdk-react/dashboard'\n *\n * function MyComponent() {\n * const documentId = 'my-document-id'\n *\n * // Automatically updates the Agent's context whenever the document changes\n * useAgentResourceContext({\n * projectId: 'my-project',\n * dataset: 'production',\n * documentId,\n * })\n *\n * return <div>Editing document: {documentId}</div>\n * }\n * ```\n */\nexport function useAgentResourceContext(options: AgentResourceContextOptions): void {\n // The branch is stable: the transport is fixed for the page lifetime, so one set of hooks\n // always runs and the other never does.\n // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n if (isDashboardEnvironment()) return useBusAgentResourceContext(options)\n // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n return useComlinkAgentResourceContext(options)\n}\n\nfunction useBusAgentResourceContext({projectId, dataset, documentId}: AgentResourceContextOptions) {\n const context = useMemo<ApplicationContext | null>(\n () =>\n projectId && dataset\n ? {\n resource: {id: `${projectId}.${dataset}`, type: 'dataset'},\n document: documentId ? {id: documentId} : null,\n }\n : null,\n [projectId, dataset, documentId],\n )\n useApplicationContext(context)\n}\n\nfunction useComlinkAgentResourceContext(options: AgentResourceContextOptions): void {\n const {projectId, dataset, documentId} = options\n const {sendMessage} = useWindowConnection<Events.AgentResourceUpdateMessage, FrameMessage>({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n })\n\n // Track the last sent context to avoid duplicate updates\n const lastContextRef = useRef<string | null>(null)\n\n const updateContext = useCallback(() => {\n // Validate required fields\n if (!projectId || !dataset) {\n // eslint-disable-next-line no-console\n console.warn('[useAgentResourceContext] projectId and dataset are required', {\n projectId,\n dataset,\n })\n return\n }\n\n // Create a stable key for the current context\n const contextKey = `${projectId}:${dataset}:${documentId || ''}`\n\n // Skip if context hasn't changed\n if (lastContextRef.current === contextKey) {\n return\n }\n\n try {\n const message: Events.AgentResourceUpdateMessage = {\n type: 'dashboard/v1/events/agent/resource/update',\n data: {\n projectId,\n dataset,\n documentId,\n },\n }\n\n sendMessage(message.type, message.data)\n lastContextRef.current = contextKey\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[useAgentResourceContext] Failed to update context:', error)\n }\n }, [projectId, dataset, documentId, sendMessage])\n\n // Update context whenever it changes\n useEffect(() => {\n updateContext()\n }, [updateContext])\n}\n","import {type ApplicationBase, type Installation, type InstallationInclude} from '@sanity/sdk'\nimport {getApplicationOrigin} from '@sanity/sdk/_internal'\nimport {type RemoteModuleRef, type ValueOf} from '@sanity/sdk/dashboard'\nimport {useMemo} from 'react'\n\nimport {useTopic} from './useTopic'\n\ntype DashboardTopicEntry = Extract<\n NonNullable<ValueOf<'applications.list'>>,\n {ok: true}\n>['value'][number]\n// The published union distinguishes an `Application` (has `type`) from an `Installation` (has not).\ntype DashboardTopicApplication = Extract<DashboardTopicEntry, {type: unknown}>\ntype DashboardTopicInstallation = Exclude<DashboardTopicEntry, {type: unknown}>\ntype DashboardApplicationInterface = NonNullable<\n NonNullable<DashboardTopicApplication['activeDeployment']>['interfaces']\n>[number]\ntype ViewInterface = Exclude<DashboardApplicationInterface, {type: 'worker'}>\n\n/**\n * The shared identity fields attached to each {@link DashboardView.application} and\n * {@link DashboardWebWorker.application}, and the input to module loading. Widens\n * {@link ApplicationBase} so `type` also covers installations.\n * @public\n */\nexport type DashboardApplicationBase = Omit<ApplicationBase, 'type'> & {\n readonly type: ApplicationBase['type'] | 'installation'\n /** Served by a CLI dev server rather than a deployment. */\n readonly isLocal: boolean\n}\n\n/**\n * A dashboard view exposed by an application or installation.\n * @public\n */\nexport type DashboardView = {\n [Type in ViewInterface['type']]: Omit<Extract<ViewInterface, {type: Type}>, 'type'> & {\n readonly application: DashboardApplicationBase\n readonly module: RemoteModuleRef\n readonly surface: Type extends 'app' ? 'window' : Type\n }\n}[ViewInterface['type']]\n\n/**\n * A web worker exposed by an application or installation.\n * @public\n */\nexport type DashboardWebWorker = Extract<DashboardApplicationInterface, {type: 'worker'}> & {\n readonly application: DashboardApplicationBase\n readonly module: RemoteModuleRef\n}\n\n/**\n * An installation shaped into the shared application base, with its raw record kept under\n * `installation` for consumers that need the full record.\n * @public\n */\nexport type DashboardInstallation = Omit<DashboardApplicationBase, 'type'> & {\n readonly type: 'installation'\n readonly installation: Installation<InstallationInclude>\n}\n\n/**\n * A dashboard entry — an application or an installation — in one consistent shape, with its\n * loadable views and web workers. `type` distinguishes the kinds and `isLocal` marks dev-server\n * applications; render a list without branching.\n * @public\n */\nexport type DashboardApplication = (DashboardTopicApplication | DashboardInstallation) &\n Pick<DashboardApplicationBase, 'isLocal'> & {\n readonly views: DashboardView[]\n readonly webWorkers: DashboardWebWorker[]\n }\n\n// Only a federated deployment (one with a module federation manifest) exposes loadable modules.\n// A dev server is always federated, whether or not the workbench synthesised a manifest for it.\nconst loadableInterfaces = (\n {activeDeployment, config}: DashboardTopicApplication,\n isLocal: boolean,\n): readonly DashboardApplicationInterface[] =>\n isLocal || config?.mfManifest !== undefined ? (activeDeployment?.interfaces ?? []) : []\n\n// Turns an interface list into views and web workers loaded from `base`'s origin. Shared by\n// applications and installations so the surface mapping lives in one place.\nconst loadModules = (\n base: DashboardApplicationBase,\n interfaces: readonly DashboardApplicationInterface[],\n): {views: DashboardView[]; webWorkers: DashboardWebWorker[]} => {\n // Nothing to load without interfaces or an origin to load them from.\n const entry = interfaces.length === 0 ? null : getApplicationOrigin(base)\n if (entry === null) return {views: [], webWorkers: []}\n\n const views: DashboardView[] = []\n const webWorkers: DashboardWebWorker[] = []\n\n for (const extension of interfaces) {\n const module: RemoteModuleRef = {\n entry,\n moduleId: `${base.id}/${extension.moduleId}`,\n version: extension.version,\n }\n\n if (extension.type === 'worker') {\n webWorkers.push({...extension, application: base, module})\n continue\n }\n\n const {type, ...view} = extension\n // TS cannot correlate `surface` with the narrowed `type` across the mapped union; the\n // cast is checked by the `DashboardView` mapping above and the surface assertions in the tests.\n views.push({\n ...view,\n application: base,\n module,\n surface: type === 'app' ? 'window' : type,\n } as DashboardView)\n }\n\n return {views, webWorkers}\n}\n\nconst toApplication = (application: DashboardTopicApplication): DashboardApplication => {\n const {activeDeployment: _activeDeployment, config: _config, ...applicationBase} = application\n const isLocal = 'local' in application\n return {\n ...application,\n isLocal,\n ...loadModules({...applicationBase, isLocal}, loadableInterfaces(application, isLocal)),\n }\n}\n\nconst toInstallation = (installation: DashboardTopicInstallation): DashboardApplication => {\n const {application} = installation\n // `id` is the installation record's id, not its `applicationId`, so two installs of one app never\n // collide. `organizationId` is the publisher's: the singleton bundle is hosted under the org that\n // published it, and `isSingleton: true` with `externalUrl: null` routes the origin through\n // `getApplicationOrigin`'s singleton branch (`https://<slug>-apps-<publisherOrgId>.sanity.run`).\n // The installing org stays on the raw record. The workbench always serves installations\n // federated, so their interfaces are never gated.\n const base: DashboardApplicationBase = {\n id: installation.id,\n type: 'installation',\n isLocal: false,\n title: application.title,\n name: application.name,\n reference: application.reference,\n icon: application.icon,\n isSingleton: true,\n visibility: 'default',\n slug: application.slug,\n externalUrl: null,\n organizationId: application.organizationId,\n createdAt: installation.createdAt,\n updatedAt: installation.updatedAt,\n }\n // The raw record rides only the entry, not every view's `application`.\n return {\n ...base,\n type: 'installation',\n installation,\n ...loadModules(base, installation.interfaces ?? []),\n }\n}\n\n/**\n * Returns the applications and installations available in the dashboard, in one consistent shape.\n *\n * Suspends until the dashboard publishes its list; a cleared list is empty. Throws a `TopicError`\n * to the nearest error boundary when the dashboard fails to load. Every entry exposes the same base\n * fields plus `views` and `webWorkers`; `type` distinguishes applications (`'studio' | 'coreApp'`)\n * from installations (`'installation'`), which also carry the raw record under `installation`.\n * `isLocal` marks applications served by a CLI dev server.\n *\n * @example\n * ```tsx\n * function Applications() {\n * const applications = useApplications()\n * return applications.map((application) => <div key={application.id}>{application.title}</div>)\n * }\n * ```\n *\n * @public\n */\nexport function useApplications(): DashboardApplication[] {\n const applications = useTopic('applications.list')\n return useMemo(\n () =>\n applications?.map((entry) =>\n 'type' in entry ? toApplication(entry) : toInstallation(entry),\n ) ?? [],\n [applications],\n )\n}\n","import {type DashboardApplication, useApplications} from './useApplications'\n\n/**\n * Returns a dashboard application by id, or `null` when it is unavailable.\n *\n * Suspends until the dashboard publishes its application list.\n *\n * @example\n * ```tsx\n * function SelectedApplication({id}: {id: string}) {\n * const application = useApplication(id)\n * return application ? <h1>{application.title}</h1> : <p>Application unavailable</p>\n * }\n * ```\n *\n * @public\n */\nexport function useApplication(\n applicationId: DashboardApplication['id'],\n): DashboardApplication | null {\n return useApplications().find(({id}) => id === applicationId) ?? null\n}\n","import {type TopicData} from '@sanity/sdk/dashboard'\n\nimport {useTopic} from './useTopic'\n\n/**\n * Returns the base path for an application.\n *\n * Suspends until Dashboard publishes the path and throws a `TopicError` when the application is\n * unknown.\n *\n * @example\n * ```tsx\n * function ApplicationBasePath() {\n * const basePath = useApplicationBasePath()\n * return <span>{basePath}</span>\n * }\n * ```\n *\n * @public\n */\nexport function useApplicationBasePath(): TopicData<'applications.base-path'> {\n return useTopic('applications.base-path')\n}\n","import {type ApplicationConfig} from '@sanity/sdk/dashboard'\n\nimport {useTopic} from './useTopic'\n\n// Stable, frozen reference for the cleared/absent case so the return value only changes with the\n// topic and callers cannot mutate the shared empty list.\nconst NO_CONFIGS: readonly ApplicationConfig[] = Object.freeze([])\n\n/**\n * Returns the application configuration modules available in the dashboard.\n *\n * Suspends until the dashboard publishes its application configs; a cleared list is empty.\n *\n * @example\n * ```tsx\n * function Applications() {\n * const configs = useApplicationConfigs()\n * return configs.map((config) => <div key={config.moduleId}>{config.appType}</div>)\n * }\n * ```\n *\n * @public\n */\nexport function useApplicationConfigs(): readonly ApplicationConfig[] {\n return useTopic('applications.config') ?? NO_CONFIGS\n}\n","import {type Application} from '@sanity/sdk'\nimport {type ApplicationConfig, type ApplicationConfigAppType} from '@sanity/sdk/dashboard'\n\nimport {useApplicationConfigs} from './useApplicationConfigs'\n\n/**\n * Selects an application configuration by application id or application type.\n * @public\n */\nexport type ApplicationConfigSelector =\n | {appId: Application['id']; appType?: never}\n | {appId?: never; appType: ApplicationConfigAppType}\n\n/**\n * Returns an application configuration by application id or application type, or `null` when\n * none matches.\n *\n * An `appType` query matches the type-level config only (the one without an `appId`), so it is\n * independent of the order the dashboard published configs in. An `appId` query matches that\n * application's config.\n *\n * Suspends until the dashboard publishes its application configs.\n *\n * @example\n * ```tsx\n * function MediaLibraryConfig() {\n * const config = useApplicationConfig({appType: 'media-library'})\n * return config ? <span>{config.moduleId}</span> : <p>Not installed</p>\n * }\n * ```\n *\n * @public\n */\nexport function useApplicationConfig(\n selector: ApplicationConfigSelector,\n): ApplicationConfig | null {\n const configs = useApplicationConfigs()\n return (\n configs.find((config) =>\n selector.appId === undefined\n ? config.appType === selector.appType && config.appId === undefined\n : config.appId === selector.appId,\n ) ?? null\n )\n}\n","import {type Application} from '@sanity/sdk'\n\nimport {useTopic} from './useTopic'\n\n/**\n * Returns the id of the application in the foreground, or `null` when Dashboard has none.\n *\n * Suspends until Dashboard publishes the foreground application.\n *\n * @example\n * ```tsx\n * function ForegroundApplication() {\n * const foregroundId = useApplicationForegroundId()\n * return <span>{foregroundId ?? 'No foreground application'}</span>\n * }\n * ```\n *\n * @public\n */\nexport function useApplicationForegroundId(): Application['id'] | null {\n return useTopic('applications.foreground')\n}\n","import {useTopic} from './useTopic'\n\n/**\n * Returns the session token for the reading connection, or `null` while signed out.\n *\n * Suspends until Dashboard publishes the token.\n *\n * @example\n * ```tsx\n * function AuthToken() {\n * const token = useAuthToken()\n * return <span>{token ?? 'Signed out'}</span>\n * }\n * ```\n *\n * @public\n */\nexport function useAuthToken(): string | null {\n return useTopic('auth.token')\n}\n","import {type TopicData} from '@sanity/sdk/dashboard'\n\nimport {useTopic} from './useTopic'\n\n/**\n * Returns the capabilities the host provides.\n *\n * A capability is something the host provides; an application reads this record to hide its own\n * implementation of anything the host provides, or to skip publishing to a capability the host\n * does not have. A missing key means the host does not provide that capability.\n *\n * Suspends until the host publishes the record.\n *\n * @example\n * ```tsx\n * function UserMenu() {\n * const {globalUserMenu} = useCapabilities()\n * if (globalUserMenu) return null\n * return <LocalUserMenu />\n * }\n * ```\n *\n * @public\n */\nexport function useCapabilities(): TopicData<'applications.capabilities'> {\n return useTopic('applications.capabilities')\n}\n","import {type CurrentUser} from '@sanity/types'\n\nimport {useTopic} from './useTopic'\n\n/**\n * Returns the signed-in user, or `null` while signed out.\n *\n * Suspends until Dashboard publishes the current user.\n *\n * @example\n * ```tsx\n * function CurrentUser() {\n * const user = useCurrentUser()\n * return <span>{user?.name ?? 'Signed out'}</span>\n * }\n * ```\n *\n * @public\n */\nexport function useCurrentUser(): CurrentUser | null {\n return useTopic('users.current')\n}\n","/* eslint-disable react-compiler/react-compiler -- the transport branch in `useNavigate` is a deliberate rules-of-hooks exception; the compiler refuses files that disable it */\nimport {\n type Bridge,\n type PathChangeMessage,\n SDK_CHANNEL_NAME,\n SDK_NODE_NAME,\n} from '@sanity/message-protocol'\nimport {isDashboardEnvironment, requireDashboardMessageBus} from '@sanity/sdk/_internal'\nimport {type NavigationLocation, TopicError} from '@sanity/sdk/dashboard'\nimport {useCallback, useEffect, useEffectEvent, useRef} from 'react'\nimport {filter, map, pairwise} from 'rxjs'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\nimport {useSanityInstance} from '../context/useSanityInstance'\n\ntype UpdateURLMessage = Bridge.Listeners.History.UpdateURLMessage\n\n/**\n * A navigation the Dashboard asks the app to perform, or that the app reports to the Dashboard.\n * `path` is relative to the app's own route, with no leading slash (`'documents/abc'`), and may\n * carry a query string and hash.\n * @public\n */\nexport type DashboardNavigation = PathChangeMessage['data']\n\n/**\n * Reports an in-app navigation to the Dashboard so the browser URL and the Dashboard's own\n * router follow the app. `type` defaults to `'push'` and only applies in the federated runtime;\n * see {@link useNavigate}.\n * @public\n */\nexport type NavigateToDashboardPath = (options: {path: string; type?: 'push' | 'replace'}) => void\n\n/**\n * @public\n *\n * A helper hook designed to be injected into routing components for apps within the Dashboard.\n * It keeps the app's router and the Dashboard in two-way sync: `navigateFn` receives the\n * navigations the Dashboard asks the app to perform, and the returned function reports the app's\n * own in-app navigations back to the Dashboard so the browser URL and the Dashboard's router\n * follow along.\n *\n * A navigation the app reports is not echoed back through `navigateFn`. The returned function is\n * referentially stable.\n *\n * The two Dashboard runtimes differ:\n * - In an iframe (Comlink), inbound `type` may be `'push'`, `'replace'` or `'pop'`. Reporting is\n * optional because the bridge already forwards the iframe's own `pushState`; a reported `type`\n * is ignored and the host applies the URL as a `replace`.\n * - In a federated app (message bus), inbound `type` is `'push'` or `'replace'`; `'pop'` is never\n * sent. Reporting is required because the app's router does not reach the host, and `type` is\n * honoured. Until the Dashboard publishes the app's base path, reports are dropped with a\n * console warning.\n *\n * @param navigateFn - Function to handle navigation; should accept:\n * - `path`: a string, which will be a relative path (for example, 'my-route')\n * - `type`: 'push', 'replace', or 'pop', which will be the type of navigation to perform\n * @returns A function the app calls to report its own in-app navigations to the Dashboard.\n *\n * @example\n * ```tsx\n * import {useNavigate} from '@sanity/sdk-react/dashboard'\n * import {useEffect} from 'react'\n * import {useLocation, useNavigate as useRouterNavigate} from 'react-router'\n *\n * function DashboardNavigationSync() {\n * const routerNavigate = useRouterNavigate()\n * const {pathname, search, hash} = useLocation()\n * const navigate = useNavigate(({path, type}) => {\n * routerNavigate(path, {replace: type === 'replace'})\n * })\n * useEffect(() => {\n * navigate({path: `${pathname.slice(1)}${search}${hash}`})\n * }, [navigate, pathname, search, hash])\n * return null\n * }\n * ```\n */\nexport function useNavigate(\n navigateFn: (options: DashboardNavigation) => void,\n): NavigateToDashboardPath {\n // The branch is stable: the transport is fixed for the page lifetime, so one set of hooks\n // always runs and the other never does.\n // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n if (isDashboardEnvironment()) return useBusNavigate(navigateFn)\n // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n return useComlinkNavigate(navigateFn)\n}\n\nfunction useComlinkNavigate(\n navigateFn: (options: DashboardNavigation) => void,\n): NavigateToDashboardPath {\n const {sendMessage} = useWindowConnection<\n UpdateURLMessage | PathChangeMessage,\n PathChangeMessage\n >({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n onMessage: {\n 'dashboard/v1/history/change-path': (data: PathChangeMessage['data']) => {\n navigateFn(data)\n },\n },\n })\n\n return useCallback<NavigateToDashboardPath>(\n ({path}) =>\n sendMessage('dashboard/v1/bridge/listeners/history/update-url', {\n url: new URL(path, window.location.origin).href,\n }),\n [sendMessage],\n )\n}\n\nfunction joinPath(base: string, path: string): string {\n const root = base.replace(/\\/$/, '')\n return path ? `${root}/${path}` : root\n}\n\nfunction useBusNavigate(\n navigateFn: (options: DashboardNavigation) => void,\n): NavigateToDashboardPath {\n const instance = useSanityInstance()\n const bus = requireDashboardMessageBus(instance, 'navigate')\n const navigate = useEffectEvent(navigateFn)\n // The path of this app's latest outbound request, used to suppress the echo of our own commit.\n const ownRequest = useRef<string | null>(null)\n\n useEffect(() => {\n // bus.subscribe, not useTopic: the rule needs the previous emission, and React coalesces\n // store notifications that land in one task, so a snapshot + ref could miss the\n // `{to, transition}` -> `{to, transition: null}` pair. The observable delivers every emission.\n const sub = bus\n .subscribe('navigation.location')\n .pipe(\n pairwise(),\n filter(\n (\n pair,\n ): pair is [\n NavigationLocation & {transition: NonNullable<NavigationLocation['transition']>},\n NavigationLocation & {transition: null},\n ] => {\n const [prev, curr] = pair\n return (\n curr?.transition === null &&\n curr.appId === bus.appId &&\n prev?.transition?.to.appId === bus.appId &&\n prev.transition.to.path === curr.path\n )\n },\n ),\n filter(([, curr]) => {\n // Loop suppression: any own-app host-mediated commit consumes the pending request, so\n // a request that lands elsewhere (or never lands) can't strand its path and drop a\n // later host navigation. Only the commit for our exact path is our own echo to drop.\n const suppress = curr.path === ownRequest.current\n ownRequest.current = null\n return !suppress\n }),\n map(([prev, curr]) => ({path: curr.path, type: prev.transition.navigationType})),\n )\n .subscribe((change) => navigate(change))\n return () => sub.unsubscribe()\n }, [bus])\n\n return useCallback<NavigateToDashboardPath>(\n ({path, type = 'push'}) => {\n // The host commits paths without a leading slash; store the same form so the echo matches.\n const own = path.replace(/^\\//, '')\n ownRequest.current = own\n // A request that never landed must drop its suppression, or a later host navigation to the\n // same path would be dropped. Best-effort: never throws.\n const clearIfStale = () => {\n if (ownRequest.current === own) ownRequest.current = null\n }\n // The base path is read per call, not in render, so a host that has not published it only\n // loses outbound reporting; the inbound subscription above still installs.\n bus\n .query('applications.base-path')\n .then((base) => {\n if (!base.ok) throw new TopicError('applications.base-path')\n return bus.emit('navigation.location.update', {\n url: joinPath(base.value, own),\n history: type,\n })\n })\n .then(\n (reply) => {\n if (!reply.ok) clearIfStale()\n },\n (error) => {\n // eslint-disable-next-line no-console\n console.warn('Failed to report navigation to the Dashboard', error)\n clearIfStale()\n },\n )\n },\n [bus],\n )\n}\n","/* eslint-disable react-compiler/react-compiler -- the transport branch in `useNavigateToStudioDocument` is a deliberate rules-of-hooks exception; the compiler refuses files that disable it */\nimport {type Bridge, SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\nimport {type DocumentHandle} from '@sanity/sdk'\nimport {isDashboardEnvironment} from '@sanity/sdk/_internal'\nimport {useCallback} from 'react'\n\nimport {urlFor} from '../../dashboard/urlFor'\nimport {useWindowConnection} from '../comlink/useWindowConnection'\nimport {useEmit} from './useEmit'\nimport {\n type DashboardResource,\n useStudioWorkspacesByProjectIdDataset,\n} from './useStudioWorkspacesByProjectIdDataset'\n\n/**\n * @public\n * @category Types\n */\nexport interface NavigateToStudioResult {\n navigateToStudioDocument: () => void\n}\n\nfunction useComlinkStudioNavigator(\n documentHandle: DocumentHandle,\n): (workspace: DashboardResource) => void {\n const {sendMessage} = useWindowConnection<Bridge.Navigation.NavigateToResourceMessage, never>({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n })\n\n return useCallback(\n (workspace: DashboardResource) => {\n const message: Bridge.Navigation.NavigateToResourceMessage = {\n type: 'dashboard/v1/bridge/navigate-to-resource',\n data: {\n resourceId: workspace.id,\n resourceType: 'studio',\n path: `/intent/edit/id=${documentHandle.documentId};type=${documentHandle.documentType}`,\n },\n }\n sendMessage(message.type, message.data)\n },\n [documentHandle, sendMessage],\n )\n}\n\nfunction useBusStudioNavigator(\n documentHandle: DocumentHandle,\n): (workspace: DashboardResource) => void {\n const navigate = useEmit('navigation.location.update')\n\n return useCallback(\n (workspace: DashboardResource) => {\n const url = urlFor\n .studios(workspace.userApplicationId)\n .workspace(workspace.name)\n .intent('edit', {id: documentHandle.documentId, type: documentHandle.documentType})\n .url()\n navigate({url}).then(\n (reply) => {\n if (reply.ok === false) {\n // eslint-disable-next-line no-console\n console.warn('Failed to navigate to studio document', reply)\n }\n },\n // The reply can reject (no responder, timeout, aborted); navigation is best-effort, so\n // warn instead of throwing.\n (error) => {\n // eslint-disable-next-line no-console\n console.warn('Failed to navigate to studio document', error)\n },\n )\n },\n [documentHandle, navigate],\n )\n}\n\n/**\n * @public\n *\n * Hook that provides a function to navigate to a given document in its parent Studio.\n *\n * Uses the `projectId` and `dataset` properties of the {@link DocumentHandle} you provide to resolve the correct Studio.\n * This will only work if you have deployed a studio with a workspace with this `projectId` / `dataset` combination.\n *\n * Works in both Dashboard runtimes: it sends the navigation over the Comlink connection in the\n * iframe runtime and over the message bus in the federated runtime.\n *\n * @remarks If you write your own Document Handle to pass to this hook (as opposed to a Document Handle generated by another hook),\n * it must include values for `documentId`, `documentType`, `projectId`, and `dataset`.\n *\n * @category Documents\n * @param documentHandle - The document handle for the document to navigate to\n * @param preferredStudioUrl - The preferred studio url to navigate to if you have multiple\n * studios with the same projectId and dataset\n * @returns An object containing:\n * - `navigateToStudioDocument` - Function that when called will navigate to the studio document\n *\n * @example\n * ```tsx\n * import {type DocumentHandle} from '@sanity/sdk-react'\n * import {useNavigateToStudioDocument} from '@sanity/sdk-react/dashboard'\n * import {Button} from '@sanity/ui'\n * import {Suspense} from 'react'\n *\n * function NavigateButton({documentHandle}: {documentHandle: DocumentHandle}) {\n * const {navigateToStudioDocument} = useNavigateToStudioDocument(documentHandle)\n * return (\n * <Button\n * onClick={navigateToStudioDocument}\n * text=\"Navigate to Studio Document\"\n * />\n * )\n * }\n *\n * // Wrap the component with Suspense since the hook may suspend\n * function MyDocumentAction({documentHandle}: {documentHandle: DocumentHandle}) {\n * return (\n * <Suspense fallback={<Button text=\"Loading...\" disabled />}>\n * <NavigateButton documentHandle={documentHandle} />\n * </Suspense>\n * )\n * }\n * ```\n */\nexport function useNavigateToStudioDocument(\n documentHandle: DocumentHandle,\n preferredStudioUrl?: string,\n): NavigateToStudioResult {\n const {workspacesByProjectIdAndDataset} = useStudioWorkspacesByProjectIdDataset()\n // The branch is stable: the transport is fixed for the page lifetime, so one navigator hook\n // always runs and the other never does.\n const sendToWorkspace = isDashboardEnvironment()\n ? // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n useBusStudioNavigator(documentHandle)\n : // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n useComlinkStudioNavigator(documentHandle)\n\n const navigateToStudioDocument = useCallback(() => {\n const {projectId, dataset} = documentHandle\n\n if (!projectId || !dataset) {\n // eslint-disable-next-line no-console\n console.warn('Project ID and dataset are required to navigate to a studio document')\n return\n }\n\n const workspace = resolveWorkspace(\n documentHandle,\n workspacesByProjectIdAndDataset,\n preferredStudioUrl,\n )\n\n if (!workspace) {\n // eslint-disable-next-line no-console\n console.warn(\n `No workspace found for document with projectId: ${projectId} and dataset: ${dataset}${preferredStudioUrl ? ` or with preferred studio url: ${preferredStudioUrl}` : ''}`,\n )\n return\n }\n\n sendToWorkspace(workspace)\n }, [documentHandle, workspacesByProjectIdAndDataset, preferredStudioUrl, sendToWorkspace])\n\n return {\n navigateToStudioDocument,\n }\n}\n\ntype WorkspacesByProjectIdDataset = ReturnType<\n typeof useStudioWorkspacesByProjectIdDataset\n>['workspacesByProjectIdAndDataset']\n\nfunction resolveWorkspace(\n documentHandle: DocumentHandle,\n workspacesByProjectIdAndDataset: WorkspacesByProjectIdDataset,\n preferredStudioUrl?: string,\n): DashboardResource | undefined {\n const {projectId, dataset} = documentHandle\n const key = `${projectId}:${dataset}` as const\n\n if (preferredStudioUrl) {\n // Include workspaces without projectId/dataset in case their manifest has not loaded yet.\n const allWorkspaces = [\n ...(workspacesByProjectIdAndDataset[key] || []),\n ...(workspacesByProjectIdAndDataset['NO_PROJECT_ID:NO_DATASET'] || []),\n ]\n return allWorkspaces.find((w) => w.url === preferredStudioUrl)\n }\n\n const workspaces = workspacesByProjectIdAndDataset[key]\n if (workspaces && workspaces.length > 1) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Multiple workspaces found for document and no preferred studio url',\n documentHandle,\n )\n // eslint-disable-next-line no-console\n console.warn('Using the first one', workspaces[0])\n }\n return workspaces?.[0]\n}\n","import {type SanityInstance, type StateSource} from '@sanity/sdk'\nimport {of} from 'rxjs'\n\nimport {createRemoteInstance, type RemoteInstance} from './createRemoteInstance'\n\nconst remoteClientStates = new WeakMap<SanityInstance, StateSource<RemoteInstance>>()\n\n/** Returns the instance's remote client state, creating it on first use. @internal */\nexport function getRemoteClientState(instance: SanityInstance): StateSource<RemoteInstance> {\n if (instance.isDisposed()) {\n throw new Error('Cannot create a remote client for a disposed Sanity instance')\n }\n\n const current = remoteClientStates.get(instance)\n if (current) return current\n\n const client = createRemoteInstance({name: `sanity-remote-${instance.instanceId}`})\n const state = {\n getCurrent: () => client,\n observable: of(client),\n subscribe: () => () => {},\n }\n\n remoteClientStates.set(instance, state)\n instance.onDispose(() => remoteClientStates.delete(instance))\n return state\n}\n","import {type RemoteInstance} from '../../dashboard/createRemoteInstance'\nimport {getRemoteClientState} from '../../dashboard/remoteClientState'\nimport {createStateSourceHook} from '../helpers/createStateSourceHook'\n\n/**\n * Returns a remote client shared by hooks using the same Sanity instance.\n * The first call creates the client; later calls reuse it.\n * @public\n */\nexport const useRemoteClient: () => RemoteInstance = createStateSourceHook(getRemoteClientState)\n","/* eslint-disable react-compiler/react-compiler -- the transport branch in `useWindowTitle` is a deliberate rules-of-hooks exception; the compiler refuses files that disable it */\nimport {SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\nimport {isDashboardEnvironment} from '@sanity/sdk/_internal'\nimport {useEffect, useState} from 'react'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\n\ninterface ContextResource {\n type: string\n title?: string\n manifest?: {\n title?: string\n } | null\n activeDeployment?: {\n manifest?: {\n title?: string\n } | null\n } | null\n}\n\ninterface ContextResponse {\n context: {\n resource: ContextResource\n }\n}\n\nfunction resolveAppTitle(resource: ContextResource): string | undefined {\n return resource.manifest?.title || resource.activeDeployment?.manifest?.title || resource.title\n}\n\n/**\n * Sets the browser's document title, automatically including the app's name\n * from the manifest.\n *\n * This follows the same convention as Sanity Studio workspaces, where the\n * workspace name is always present in the title:\n *\n * - With a view title: `<viewTitle> | <appTitle>`\n * - Without a view title: `<appTitle>`\n *\n * The Sanity dashboard appends `| Sanity` to produce the final browser tab title.\n *\n * Works in both Dashboard runtimes: it manages the document title over the Comlink connection, and\n * no-ops under the message bus, where the Dashboard host owns the title.\n *\n * @param viewTitle - An optional view-specific title to prepend to the app title.\n *\n * @example\n * ```tsx\n * import {useWindowTitle} from '@sanity/sdk-react/dashboard'\n *\n * function MoviesList() {\n * useWindowTitle('Movies')\n * return <div>...</div>\n * }\n *\n * // Browser tab: \"Movies | My App | Sanity\"\n * ```\n *\n * @example\n * ```tsx\n * // Call without arguments to show just the app title\n * function AppRoot() {\n * useWindowTitle()\n * return <Outlet />\n * }\n *\n * // Browser tab: \"My App | Sanity\"\n * ```\n *\n * @public\n */\nexport function useWindowTitle(viewTitle?: string): void {\n // The document title is set by the Dashboard host under the message bus, so this hook only\n // manages it in the Comlink runtime. The branch is stable: the transport is fixed for the page\n // lifetime, so the Comlink hooks below always run or never run.\n if (isDashboardEnvironment()) return\n // eslint-disable-next-line react-hooks/rules-of-hooks -- transport is fixed for the page lifetime\n useComlinkWindowTitle(viewTitle)\n}\n\nfunction useComlinkWindowTitle(viewTitle?: string): void {\n const [appTitle, setAppTitle] = useState<string | null>(null)\n\n const {fetch} = useWindowConnection({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n })\n\n useEffect(() => {\n if (!fetch) return\n\n const controller = new AbortController()\n\n async function fetchAppTitle(signal: AbortSignal) {\n try {\n const data = await fetch<ContextResponse>('dashboard/v1/context', undefined, {signal})\n // Local development resources do not have a registered title or deployment manifest.\n // In that case, use the title rendered into the iframe HTML by the Sanity CLI.\n const title = resolveAppTitle(data.context.resource) || document.title\n if (title) {\n setAppTitle(title)\n }\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') return\n // eslint-disable-next-line no-console\n console.error('Failed to fetch app title from dashboard context:', err)\n }\n }\n\n fetchAppTitle(controller.signal)\n\n return () => {\n controller.abort()\n }\n }, [fetch])\n\n useEffect(() => {\n if (!appTitle) return\n\n const previous = document.title\n document.title = viewTitle ? `${viewTitle} | ${appTitle}` : appTitle\n\n return () => {\n document.title = previous\n }\n }, [viewTitle, appTitle])\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAMc,gBAAgB;AAEtB,SAASC,aAAwCC,QAAc;CAC7D,IAAIA,OAAOH,MAAMI,SAAS,IAAIH,eAAe,GAAG,OAAOE;CACvD,IAAME,OAAOF,OAAOH,MAAMI,SAAS,GAAG,IAAID,OAAOH,QAAQ,GAAGG,OAAOH,MAAK;CACxE,OAAO;EAAC,GAAGG;EAAQH,OAAO,IAAIM,IAAIL,eAAeI,IAAI,CAAC,CAACE;CAAI;AAC7D;;;;;AAyBA,SAAgBU,qBAAqBC,SAAsD;CACzF,IAAMC,SAAS5B,aAAa2B,QAAQnB,IAAI,GAClCqB,WAAWhC,eAAiB;EAChC,GAAG8B;EACHG,SAAS,CAACC,IAAIH,OAAOI,KAAK,GAAG,GAAIL,QAAQG,WAAW,CAAA,CAAG;EACvDV,SACEO,QAAQP,SAASa,KAAKrB,WAAY,WAAWA,SAASD,aAAaC,MAAM,IAAIA,MAAO,KAAK,CAAA;CAC7F,CAAC;CAED,OAAO;EACLO,kBAAkBC,YAChBS,SAASV,gBAAgBC,QAAQa,IAAItB,YAAY,GAAG,EAACuB,OAAO,GAAK,CAAC;EACpEb,YAAY,OAAUC,OAAe;GACnC,IAAIa;GAEJ,IAAI;IACFA,eAAe,MAAMN,SAASR,WAAcC,EAAE;GAChD,SAASc,OAAO;IACd,MAAUC,MAAM,iCAAiCf,GAAE,IAAK,EACtDgB,OAAOF,MACT,CAAC;GACH;GAEA,OAAOD;EACT;EAEAX,gBAAgBhB,MAAMiB,YACpBI,SAASL,cAAc,CAAC;GAACe,aAAa/B;GAAMiB;GAASe,kBAAkB;EAAK,CAAC,CAAC;CAClF;AACF;AAEA,SAAST,IACPU,SAC+B;CAC/B,IAAMI,YAAYC,WAAmBC,SAAiB;EAEpD,IAAM,EAACC,QAAQC,SAAS,GAAGC,SAAQH;EAEnCN,QAAQ,eAAeK,aAAa;GAAC,GAAGI;GAAMC,UAAU;EAAI,CAAC;CAC/D;CAEA,OAAO;EACL3C,MAAM;EACN4C,WAAWL,MAAM;GAEf,OADAF,SAAS,cAAcE,IAAI,GACpBA;EACT;EACAM,qBAAqBN,MAAM;GAEzB,OADAF,SAAS,wBAAwBE,IAAI,GAC9BA;EACT;EACAO,oBAAoBP,MAAM;GACxBF,SAAS,uBAAuBE,IAAI;EACtC;EACAQ,cAAcR,MAAM;GAElB,OADAF,SAAS,iBAAiBE,IAAI,GACvBA;EACT;EACAS,aAAaT,MAAM;GAEjB,OADAF,SAAS,gBAAgBE,IAAI,GACtBA;EACT;EACAU,OAAOV,MAAM;GAEX,OADAF,SAAS,UAAUE,IAAI,GAChBA;EACT;EACAW,UAAUX,MAAM;GACdF,SAAS,aAAaE,IAAI;EAC5B;EACAY,gBAAgBZ,MAAM;GAEpB,OADAF,SAAS,mBAAmBE,IAAI,GACzBA;EACT;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AClHA,MAAMoB,kBAAkB,6BA8ElBuC,aAAa1B,SACjBA,KAAK2B,SAASC,SAASA,KAAKC,MAAM,GAAG,CAAC,CAAC,CAACC,OAAOC,OAAO,GAElDC,sBAAsB,GAAG,CAACnC,QAAQC,gBAAiC;CACvE,IAAMmC,mBAAmB,IAAIC,gBAAgB;CAE7C,QAAQrC,QAAR;EACE,KAAK,QAAQ;GACX,IAAM,EAACR,IAAIC,MAAMC,SAAQO;GAGzB,AAFAmC,iBAAiBE,IAAI,MAAM9C,EAAE,GACzBC,SAAS8C,KAAAA,KAAWH,iBAAiBE,IAAI,QAAQ7C,IAAI,GACrDC,SAAS6C,KAAAA,KAAWH,iBAAiBE,IAAI,QAAQ5C,IAAI;GACzD;EACF;EACA,KAAK;GAEH0C,AADAA,iBAAiBE,IAAI,YAAYrC,WAAWJ,QAAQ,GACpDuC,iBAAiBE,IAAI,QAAQrC,WAAWR,IAAI;GAC5C;EAEF,KAAK,WACH2C,iBAAiBE,IAAI,MAAMrC,WAAWT,EAAE;CAG5C;CAEA,OAAO4C;AACT,GAEMI,yBAAyBvC,eAC7B,IAAIoC,gBAAgBpC,WAAWwC,WAAW,KAAK,GAAG,CAAC,GAE/CC,6BAA6BzC,eACjC0C,MAAMC,KACJ3C,aACC,CAAC4C,MAAMC,WAAW,GAAGC,mBAAmBF,IAAI,EAAC,GAAIE,mBAAmBD,KAAK,GAC5E,CAAC,CAACE,KAAK,GAAG,GAENC,kBAAkBjE,KAAUkE,aAAqC;CACrE,IAAMC,UAAU,IAAI/D,IAAIJ,GAAG;CAM3B,OALIkE,SAASE,WAAW,MAExBD,QAAQE,WAAW,GAAGF,QAAQE,SAASC,QAAQ,OAAO,EAAE,EAAC,GAAIJ,SAC1DK,IAAIR,kBAAkB,CAAC,CACvBC,KAAK,GAAG,MAJuBG;AAMpC;6FAOa3B,aAAb,MAAgD;;;;;;;;;;;;;CAe9CgC,YAAYxE,KAAU;EACpB,sGAAA,uBAAA,MAAA,MAAY,IAAII,IAAIJ,GAAG,CAAA;CACzB;;;;;;;;;;;;;;;;;;;;CAqBA,OAAiB,GAAGkE,UAA0B;EAC5C,OAAA,kBAAA,mBAAO,MAAA,OAAA,CAAA,CAAA,KAAA,MAAaD,eAAAA,uBAAAA,MAAe,IAAA,GAAWC,QAAQ,CAAC;CACzD;;;;;;;;;;;;;;;;;;;;;CAsBA,KAAeU,QAAkC;EAC/C,IAAM5E,MAAM,IAAII,IAAAA,uBAAAA,MAAI,IAAA,CAAS;EAE7B,OADAwE,OAAO5E,GAAG,GACV,kBAAA,mBAAO,MAAA,OAAA,CAAA,CAAA,KAAA,MAAaA,GAAG;CACzB;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BA,aACE8E,SACA,GAAGZ,UACM;EACT,OAAO,IAAIY,QAAQb,eAAAA,uBAAAA,MAAe,IAAA,GAAWC,QAAQ,CAAC;CACxD;;;;;;;;;;;;;;;;;CAkBAlE,IAAIC,SAAqC;EACvC,IAAM8E,cAAAA,kBAAAA,mBAAc,MAAA,YAAA,CAAA,CAAA,KAAA,IAAkB;EACtC,OAAO9E,SAASC,WAAWqD,KAAAA,IAAYwB,cAAc,IAAI3E,IAAI2E,aAAa9E,QAAQC,MAAM,CAAC,CAAC8E;CAC5F;;;;;;;;;;;;;;CAeA7E,MAAM,EAACD,UAAgC;EACrC,OAAO,IAAIE,IAAAA,kBAAAA,mBAAI,MAAA,YAAA,CAAA,CAAA,KAAA,IAAkB,GAAGF,MAAM;CAC5C;;;;;;;;;;;;;CAcAG,WAAmB;EACjB,OAAO,KAAKL,IAAI;CAClB;AAUF;AARE,SAAA,eAAuB;CACrB,OAAO,GAAA,uBAAA,MAAG,IAAA,CAAA,CAAUqE,WAAAA,uBAAAA,MAAW,IAAA,CAAA,CAAUa,SAAAA,uBAAAA,MAAS,IAAA,CAAA,CAAUC;AAC9D;AAEA,SAAA,QAAQnF,KAAgB;CACtB,IAAM8E,UAAU,KAAKN;CACrB,OAAO,IAAIM,QAAQ9E,GAAG;AACxB;AAGF,IAAMqF,4BAAN,cAAwC7C,WAAyC;CAG/ErB,KAAK,GAAGA,MAAsB;EAC5B,OAAO,KAAKsD,OAAO,GAAG5B,UAAU1B,IAAI,CAAC;CACvC;AACF;AALkBoB,gBAAAA,2BAAAA,aAAY,cAAA;AAO9B,IAAM+C,yBAAN,cAAqC9C,WAAsC;CAGzEX,MAAMC,SAA+B;EACnC,OAAO,KAAK2C,OAAO,UAAU3C,OAAO;CACtC;CAEAC,WAAWC,cAAoC;EAC7C,OAAO,KAAKyC,OAAO,eAAezC,YAAY;CAChD;AACF;AATkBO,gBAAAA,wBAAAA,aAAY,OAAA;AAW9B,IAAMgD,mBAAN,cAA+B/C,WAAgC;CAG7DN,SAASC,YAAkC;EACzC,OAAO,KAAKsC,OAAO,OAAOtC,UAAU;CACtC;AACF;AALkBI,gBAAAA,kBAAAA,aAAY,QAAA;AAO9B,IAAMiD,mBAAN,cACUhD,WAEV;CAGEjB,UAAUA,WAAuC;EAC/C,OAAO,KAAKkD,OAAOlD,SAAS;CAC9B;CAKAP,OAAO,GAAGyE,MAAwC;EAChD,OAAO,KAAKhB,OAAO,UAAUgB,KAAK,EAAE,CAAC,CAACd,MAAM3E,QAAQ;GAClDA,IAAIqE,WAAW,GAAGrE,IAAIqE,SAAQ,GAAIX,0BAA0BP,mBAAmB,GAAGsC,IAAI,CAAC,EAAC;EAC1F,CAAC;CACH;CAEAjE,YAAYA,aAAsC;EAChD,OAAO,KAAKmD,MAAM3E,QAAQA,IAAI0F,aAAapC,IAAI,eAAe9B,WAAW,CAAC;CAC5E;CAEAC,QAAQC,WAAoC;EAC1C,OAAO,KAAKiD,MAAM3E,QAAQ;GACxB,IAAMkE,WAAWlE,IAAIqE,SAASrB,MAAM,GAAG,GACjC2C,kBAAkBzB,SAASE,SAAS,GACpCnD,aAAauC,sBAAsBU,SAASyB,gBAAiB;GAInE3F,AAHAiB,WAAWqC,IAAI,WAAW,iBAAiB,GAC3CrC,WAAWqC,IAAI,WAAW5B,SAAS,GACnCwC,SAASyB,mBAAmBjC,0BAA0BzC,UAAU,GAChEjB,IAAIqE,WAAWH,SAASF,KAAK,GAAG;EAClC,CAAC;CACH;CAEA5C,KAAKC,QAA8B;EACjC,OAAO,KAAKsD,MAAM3E,QAAQA,IAAI0F,aAAapC,IAAI,gBAAgBjC,MAAM,CAAC;CACxE;CAEAF,KAAK,GAAGA,MAA8B;EACpC,OAAO,KAAKsD,OAAO,GAAG5B,UAAU1B,IAAI,CAAC;CACvC;AACF;AAtCkBoB,gBAAAA,kBAAAA,aAAY,SAAA;AAgE9B,MAAM+D,qBACJxB,SACA,GAAGZ,aAEH,IAAIY,QAAQb,eAAe,IAAI7D,IAAIE,eAAe,GAAG,CAACwE,QAAQvC,WAAW,GAAG2B,QAAQ,CAAC,CAAC,GAElFqC,cACJH,aACoC;CAGpC,SAASP,QAAQC,OAA0C;EACzD,OAAOA,UAAUvC,KAAAA,IACb,IAAIf,WAAW,IAAIpC,IAAI,aAAaE,eAAe,CAAC,IACpDgG,kBAAkBd,kBAAkBM,KAAK;CAC/C;CAIA,SAASC,aAAaD,OAAmD;EACvE,OAAOA,UAAUvC,KAAAA,IACb,IAAIf,WAAW,IAAIpC,IAAI,kBAAkBE,eAAe,CAAC,IACzDgG,kBAAkBjB,2BAA2BS,KAAK;CACxD;CAEA,IAAMU,UAAUC,OAAOC,YACrBD,OAAOE,QAAQP,QAAQ,CAAC,CAAC7B,KAAK,CAACV,MAAMiB,aAAa,CAACjB,YAAYyC,kBAAkBxB,OAAO,CAAC,CAAC,CAC5F,GAEM8B,OAAO;EACXf;EACAE;EACAC,oBAAoBM,kBAAkBhB,sBAAsB;EAC5DW,cAAcK,kBAAkBf,gBAAgB;EAChDW,YAAY,IAAI1D,WAAW,IAAIpC,IAAIE,eAAe,CAAC;EACnD6F,OAAoDU,eAA8B;GAChF,IAAME,6BAAa,IAAIC,IAAI;IACzB;IACAxB,iBAAiBjD;IACjB8C,0BAA0B9C;IAC1B+C,uBAAuB/C;IACvBgD,iBAAiBhD;IACjB,GAAGkE,OAAOQ,OAAOb,QAAQ,CAAC,CAAC7B,KAAKO,YAAYA,QAAQvC,SAAS;GAAC,CAC/D;GAED,KAAK,IAAM,CAACsB,MAAMiB,YAAY2B,OAAOE,QAAQE,aAAa,GAAG;IAC3D,IAAIhD,QAAQ+C,QAAQ/C,QAAQ2C,SAC1B,MAAUU,MAAM,gBAAgBrD,KAAI,iBAAkB;IAExD,IAAIkD,WAAWI,IAAIrC,QAAQvC,SAAS,GAClC,MAAU2E,MAAM,kBAAkBpC,QAAQvC,UAAS,iBAAkB;IAEvEwE,WAAWK,IAAItC,QAAQvC,SAAS;GAClC;GAEA,OAAOgE,WAAW;IAAC,GAAGH;IAAU,GAAGS;GAAa,CAAC;EACnD;CACF;CAEA,OAAOJ,OAAOY,OAAOT,MAAMJ,OAAO;AACpC,GAGac,SAAef,WAAW,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxazC,SAAOmB,sBAAAC,SAAA;CAAA,IAAAC,IAAAC,EAAA,CAAA,GACLC,OAAaL,QAAQ,6BAA6B,GAACM,IAAAC;CAEnDR,AAFmDI,EAAA,OAAAD,WAAAC,EAAA,OAAAE,QAEzCC,YACRD,KAAKH,OAAO,SACL;EACLG,KAAK,IAAI;CAAC,IAEXE,KAAA,CAACF,MAAMH,OAAO,GAACC,EAAA,KAAAD,SAAAC,EAAA,KAAAE,MAAAF,EAAA,KAAAG,IAAAH,EAAA,KAAAI,OAAAD,KAAAH,EAAA,IAAAI,KAAAJ,EAAA,KALlBJ,UAAUO,IAKPC,EAAe;AAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACerB,SAAgBiB,wBAAwBC,SAA4C;CAMlF,OAFId,uBAAuB,IAAUe,2BAA2BD,OAAO,IAEhEE,+BAA+BF,OAAO;AAC/C;AAEA,SAAAC,2BAAAE,IAAA;CAAA,IAAAC,IAAAC,EAAA,CAAA,GAAoC,EAAAT,WAAAC,SAAAC,eAAAK,IAA6DG;CAW/FZ,AAX+FU,EAAA,OAAAP,WAAAO,EAAA,OAAAN,cAAAM,EAAA,OAAAR,aAG3FU,KAAAV,aAAAC,UAAA;EAAAU,UAEgB;GAAAC,IAAK,GAAGZ,UAAS,GAAIC;GAASY,MAAQ;EAAS;EAACC,UAChDZ,aAAA,EAAAU,IAAkBV,WAAiB,IAAnC;CAET,IALP,MAKQM,EAAA,KAAAP,SAAAO,EAAA,KAAAN,YAAAM,EAAA,KAAAR,WAAAQ,EAAA,KAAAE,MAAAA,KAAAF,EAAA,IAGZV,sBAAsBiB,EAAO;AAAC;AAGhC,SAAAT,+BAAAF,SAAA;CAAA,IAAAI,IAAAC,EAAA,CAAA,GACE,EAAAT,WAAAC,SAAAC,eAAyCE,SAAOG;CAAA,AAAAC,EAAA,OAAAQ,OAAAC,IAAA,2BAAA,KAC2CV,KAAA;EAAAW,MACnF7B;EAAa8B,WACR/B;CACb,GAACoB,EAAA,KAAAD,MAAAA,KAAAC,EAAA;CAHD,IAAA,EAAAY,gBAAsBvB,oBAAqEU,EAG1F,GAGDc,iBAAuBzB,OAAsB,IAAI,GAACc;CAAA,AAAAF,EAAA,OAAAP,WAAAO,EAAA,OAAAN,cAAAM,EAAA,OAAAR,aAAAQ,EAAA,OAAAY,eAEhBV,WAAA;EAEhC,IAAI,CAACV,aAAD,CAAeC,SAAO;GAExBqB,QAAOC,KAAM,gEAAgE;IAAAvB;IAAAC;GAG7E,CAAC;GAAC;EAAA;EAKJ,IAAAuB,aAAmB,GAAGxB,UAAS,GAAIC,QAAO,GAAIC,cAAA;EAG1CmB,mBAAcI,YAAaD,YAI/B,IAAA;GACE,IAAAE,UAAmD;IAAAb,MAC3C;IAA2Cc,MAC3C;KAAA3B;KAAAC;KAAAC;IAIN;GACF;GAGAmB,AADAD,YAAYM,QAAOb,MAAOa,QAAOC,IAAK,GACtCN,eAAcI,UAAWD;EAAH,SAAAI,IAAA;GAGtBN,QAAOO,MAAO,uDAAuDA,EAAK;EAAC;CAC5E,GACFrB,EAAA,KAAAP,SAAAO,EAAA,KAAAN,YAAAM,EAAA,KAAAR,WAAAQ,EAAA,KAAAY,aAAAZ,EAAA,KAAAE,MAAAA,KAAAF,EAAA;CAnCD,IAAAsB,gBAAsBpB,IAmC2BkB,IAAAG;CAGjDrC,AAHiDc,EAAA,OAAAsB,iBAK/BF,KAAApB,EAAA,IAAAuB,KAAAvB,EAAA,OAFRoB,WAAA;EACRE,cAAc;CAAC,GACdC,KAAA,CAACD,aAAa,GAACtB,EAAA,KAAAsB,eAAAtB,EAAA,KAAAoB,IAAApB,EAAA,KAAAuB,KAFlBrC,UAAUkC,IAEPG,EAAe;AAAC;ACzDrB,MAAMkC,sBACJ,EAACC,kBAAkBC,UACnBf,YAEAA,WAAWe,QAAQC,eAAeC,KAAAA,IAAaH,kBAAkBI,cAAc,CAAA,IAAM,CAAA,GAIjFC,eACJC,MACAF,eAC+D;CAE/D,IAAMG,QAAQH,WAAWI,WAAW,IAAI,OAAOvC,qBAAqBqC,IAAI;CACxE,IAAIC,UAAU,MAAM,OAAO;EAACV,OAAO,CAAA;EAAIC,YAAY,CAAA;CAAE;CAErD,IAAMD,QAAyB,CAAA,GACzBC,aAAmC,CAAA;CAEzC,KAAK,IAAMW,aAAaL,YAAY;EAClC,IAAMd,SAA0B;GAC9BiB;GACAG,UAAU,GAAGJ,KAAKK,GAAE,GAAIF,UAAUC;GAClCE,SAASH,UAAUG;EACrB;EAEA,IAAIH,UAAU9B,SAAS,UAAU;GAC/BmB,WAAWe,KAAK;IAAC,GAAGJ;IAAWpB,aAAaiB;IAAMhB;GAAM,CAAC;GACzD;EACF;EAEA,IAAM,EAACX,MAAM,GAAGmC,SAAQL;EAGxBZ,MAAMgB,KAAK;GACT,GAAGC;GACHzB,aAAaiB;GACbhB;GACAC,SAASZ,SAAS,QAAQ,WAAWA;EACvC,CAAkB;CACpB;CAEA,OAAO;EAACkB;EAAOC;CAAU;AAC3B,GAEMiB,iBAAiB1B,gBAAiE;CACtF,IAAM,EAACW,kBAAkBgB,mBAAmBf,QAAQgB,SAAS,GAAGC,oBAAmB7B,aAC7EH,UAAU,WAAWG;CAC3B,OAAO;EACL,GAAGA;EACHH;EACA,GAAGmB,YAAY;GAAC,GAAGa;GAAiBhC;EAAO,GAAGa,mBAAmBV,aAAaH,OAAO,CAAC;CACxF;AACF,GAEMiC,kBAAkBzB,iBAAmE;CACzF,IAAM,EAACL,gBAAeK,cAOhBY,OAAiC;EACrCK,IAAIjB,aAAaiB;EACjBhC,MAAM;EACNO,SAAS;EACTkC,OAAO/B,YAAY+B;EACnBC,MAAMhC,YAAYgC;EAClBC,WAAWjC,YAAYiC;EACvBC,MAAMlC,YAAYkC;EAClBC,aAAa;EACbC,YAAY;EACZC,MAAMrC,YAAYqC;EAClBC,aAAa;EACbC,gBAAgBvC,YAAYuC;EAC5BC,WAAWnC,aAAamC;EACxBC,WAAWpC,aAAaoC;CAC1B;CAEA,OAAO;EACL,GAAGxB;EACH3B,MAAM;EACNe;EACA,GAAGW,YAAYC,MAAMZ,aAAaU,cAAc,CAAA,CAAE;CACpD;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,SAAO2B,kBAAA;CAAA,IAAAC,IAAAC,EAAA,CAAA,GACLC,eAAqB7D,SAAS,mBAAmB,GAAC8D;CAKvC,OALuCH,EAAA,OAAAE,eAKvCC,KAAAH,EAAA,MAFPG,KAAAD,cAAYE,IAAMC,OAEZ,KAFN,CAAA,GAEOL,EAAA,KAAAE,cAAAF,EAAA,KAAAG,KAFPA;AAEO;AANN,SAAAE,QAAA9B,OAAA;CAAA,OAKC,UAAUA,QAAQQ,cAAcR,KAA6B,IAApBY,eAAeZ,KAAK;AAAC;;;;;;;;;;;;;;;;AC3KtE,SAAOiC,eAAAC,eAAA;CAAA,OAGEF,gBAAgB,CAAC,CAAAG,MAAMC,OAAA;EAAC,IAAA,EAAAC,OAAAD;EAAI,OAAKC,OAAOH;CAAa,CAAQ,KAA7D;AAA8D;;;;;;;;;;;;;;;;;ACAvE,SAAOM,yBAAA;CAAA,OACED,SAAS,wBAAwB;AAAC;ACf3C,MAAMI,aAA2CC,OAAOC,OAAO,CAAA,CAAE;;;;;;;;;;;;;;;;AAiBjE,SAAOC,wBAAA;CAAA,OACEJ,SAAS,qBAAmC,KAA5CC;AAA6C;;;;;;;;;;;;;;;;;;;;;ACStD,SAAOW,qBAAAC,UAAA;CAAA,IAAAC,IAAAC,EAAA,CAAA,GAGLC,UAAgBR,sBAAsB,GAACS;CAM5B,OAN4BH,EAAA,OAAAE,WAAAF,EAAA,OAAAD,YAErCI,KAAAD,QAAOE,MAAMC,WACXN,SAAQH,UAAWU,KAAAA,IACfD,OAAMR,YAAaE,SAAQF,WAAYQ,OAAMT,UAAWU,KAAAA,IACxDD,OAAMT,UAAWG,SAAQH,KACvB,KAJR,MAISI,EAAA,KAAAE,SAAAF,EAAA,KAAAD,UAAAC,EAAA,KAAAG,MAAAA,KAAAH,EAAA,IAJTG;AAIS;;;;;;;;;;;;;;;;ACvBb,SAAOM,6BAAA;CAAA,OACED,SAAS,yBAAyB;AAAC;;;;;;;;;;;;;;;;ACH5C,SAAOG,eAAA;CAAA,OACED,SAAS,YAAY;AAAC;;;;;;;;;;;;;;;;;;;;;ACM/B,SAAOI,kBAAA;CAAA,OACED,SAAS,2BAA2B;AAAC;;;;;;;;;;;;;;;;ACN9C,SAAOI,iBAAA;CAAA,OACED,SAAS,eAAe;AAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC0DlC,SAAgB2B,YACdC,YACyB;CAMzB,OAFItB,uBAAuB,IAAUuB,eAAeD,UAAU,IAEvDE,mBAAmBF,UAAU;AACtC;AAEA,SAAAE,mBAAAF,YAAA;CAAA,IAAAG,IAAAC,EAAA,CAAA,GAAAC;CAAA,AAAAF,EAAA,OAAAH,aAcGK,KAAAF,EAAA,MARCE,KAAA;EAAAC,MACM7B;EAAa8B,WACR/B;EAAgBgC,WAChB,EAAA,qCAC2BC,SAAA;GAClCT,WAAWS,IAAI;EAAC,EAEpB;CACF,GAACN,EAAA,KAAAH,YAAAG,EAAA,KAAAE;CAXD,IAAA,EAAAK,gBAAsBrB,oBAGpBgB,EAQD,GAACM;CAMI,OANJR,EAAA,OAAAO,cAMIC,KAAAR,EAAA,MAHJQ,MAAAC,OAAA;EAAC,IAAA,EAAAf,SAAAe;EAAM,OACLF,YAAY,oDAAoD,EAAAG,KACzD,IAAIC,IAAIjB,MAAMkB,OAAMC,SAASC,MAAO,CAAC,CAAAC,KAC5C,CAAC;CAAC,GAAAf,EAAA,KAAAO,aAAAP,EAAA,KAAAQ,KAJCA;AAMN;AAGH,SAASQ,SAASC,MAAcvB,MAAsB;CACpD,IAAMwB,OAAOD,KAAKE,QAAQ,OAAO,EAAE;CACnC,OAAOzB,OAAO,GAAGwB,KAAI,GAAIxB,SAASwB;AACpC;AAEA,SAAApB,eAAAD,YAAA;CAAA,IAAAG,IAAAC,EAAA,CAAA,GAGEmB,WAAiBjC,kBAAkB,GAACe;CAAA,AAAAF,EAAA,OAAAoB,WACwBlB,KAAAF,EAAA,MAAhDE,KAAA1B,2BAA2B4C,UAAU,UAAU,GAACpB,EAAA,KAAAoB,UAAApB,EAAA,KAAAE;CAA5D,IAAAmB,MAAYnB,IACZoB,WAAiBzC,eAAegB,UAAU,GAE1C0B,aAAmBzC,OAAsB,IAAI,GAAC0B;CAAA,AAAAR,EAAA,OAAAqB,OAAArB,EAAA,OAAAsB,YAEpCd,WAAA;EAIR,IAAAgB,MAAYH,IAAGI,UACF,qBAAqB,CAAC,CAAAC,KAE/BzC,SAAS,GACTF,QACE4C,SAAA;GAME,IAAA,CAAAC,MAAAC,QAAqBF;GAAI,OAEvBE,MAAIC,eAAiB,QACrBD,KAAIE,UAAWV,IAAGU,SAClBH,MAAIE,YAAgBE,GAAMD,UAAKV,IAAGU,SAClCH,KAAIE,WAAWE,GAAGtC,SAAUmC,KAAInC;EAAK,CAG3C,GACAX,QAAO0B,OAAA;GAAC,IAAA,GAAAwB,UAAAxB,IAINyB,WAAiBL,OAAInC,SAAU6B,WAAUY;GACvB,OAAlBZ,WAAUY,UAAW,MACd,CAACD;EAAQ,CACjB,GACDlD,IAAIoD,OAA2E,CACjF,CAAC,CAAAX,WACUY,WAAYf,SAASe,MAAM,CAAC;EAAC,aAC7Bb,IAAGc,YAAa;CAAC,GAC/BtC,EAAA,KAAAqB,KAAArB,EAAA,KAAAsB,UAAAtB,EAAA,KAAAQ,MAAAA,KAAAR,EAAA;CAAA,IAAAS;CApCD7B,AAoCCoB,EAAA,OAAAqB,MAAOZ,KAAAT,EAAA,MAALS,KAAA,CAACY,GAAG,GAACrB,EAAA,KAAAqB,KAAArB,EAAA,KAAAS,KApCR7B,UAAU4B,IAoCPC,EAAK;CAAC,IAAA8B;CAiCN,OAjCMvC,EAAA,OAAAqB,MAiCNkB,KAAAvC,EAAA,MA9BDuC,MAAAC,OAAA;EAAC,IAAA,EAAA9C,MAAAC,MAAA8C,OAAAD,IAAO7C,OAAA8C,OAAAC,KAAAA,IAAA,SAAAD,IAENE,MAAYjD,KAAIyB,QAAS,OAAO,EAAE;EAClCI,WAAUY,UAAWQ;EAGrB,IAAAC,qBAAqB;GACnB,AAAIrB,WAAUY,YAAaQ,QAAKpB,WAAUY,UAAW;EAAI;EAI3Dd,IAAGwB,MACM,wBAAwB,CAAC,CAAAC,MAC1B7B,SAAA;GACJ,IAAI,CAACA,KAAI8B,IAAK,MAAM,IAAIrE,aAAW,wBAAwB;GAAC,OACrD2C,IAAG2B,KAAM,8BAA8B;IAAAtC,KACvCM,SAASC,KAAIgC,OAAQN,GAAG;IAACO,SACrBvD;GACX,CAAC;EAAC,CACH,CAAC,CAAAmD,MAEAK,UAAA;GACE,AAAKA,MAAKJ,MAAKH,aAAa;EAAC,IAE/BQ,UAAA;GAGER,AADAS,QAAOC,KAAM,gDAAgDF,KAAK,GAClER,aAAa;EAAC,CAElB;CAAC,GACJ5C,EAAA,KAAAqB,KAAArB,EAAA,KAAAuC,KA/BIA;AAiCN;AAhFH,SAAAH,QAAAlC,IAAA;CAyCa,IAAA,CAAAqD,QAAAC,UAAAtD;CAAY,OAAM;EAAAR,MAAOmC,OAAInC;EAAKC,MAAQiC,OAAIE,WAAW2B;CAAe;AAAC;AC1ItF,SAAAc,0BAAAC,gBAAA;CAAA,IAAAC,IAAAC,EAAA,CAAA,GAAAC;CAAA,AAAAF,EAAA,OAAAG,OAAAC,IAAA,2BAAA,KAGgGF,KAAA;EAAAG,MACtFlB;EAAamB,WACRpB;CACb,GAACc,EAAA,KAAAE,MAAAA,KAAAF,EAAA;CAHD,IAAA,EAAAO,gBAAsBf,oBAAwEU,EAG7F,GAACM;CAaC,OAbDR,EAAA,OAAAD,eAAAU,cAAAT,EAAA,OAAAD,eAAAW,gBAAAV,EAAA,OAAAO,eAGAC,MAAAG,cAAA;EACE,IAAAC,UAA6D;GAAAC,MACrD;GAA0CC,MAC1C;IAAAC,YACQJ,UAASK;IAAGC,cACV;IAAQC,MAChB,mBAAmBnB,eAAcU,WAAW,QAASV,eAAcW;GAC3E;EACF;EACAH,YAAYK,QAAOC,MAAOD,QAAOE,IAAK;CAAC,GACxCd,EAAA,KAAAD,eAAAU,YAAAT,EAAA,KAAAD,eAAAW,cAAAV,EAAA,KAAAO,aAAAP,EAAA,KAAAQ,MAAAA,KAAAR,EAAA,IAXIQ;AAaN;AAGH,SAAAW,sBAAApB,gBAAA;CAAA,IAAAC,IAAAC,EAAA,CAAA,GAGEmB,WAAiB3B,QAAQ,4BAA4B,GAACS;CAuBnD,OAvBmDF,EAAA,OAAAD,eAAAU,cAAAT,EAAA,OAAAD,eAAAW,gBAAAV,EAAA,OAAAoB,YAGpDlB,MAAAS,cAAA;EACE,IAAAU,MAAY9B,OAAM+B,QACPX,UAASY,iBAAkB,CAAC,CAAAZ,UAC1BA,UAASN,IAAK,CAAC,CAAAmB,OAClB,QAAQ;GAAAR,IAAKjB,eAAcU;GAAWI,MAAQd,eAAcW;EAAa,CAAC,CAAC,CAAAW,IAC9E;EACPD,SAAS,EAAAC,IAAI,CAAC,CAAC,CAAAI,KACbC,OAQAC,MAIF;CAAC,GACF3B,EAAA,KAAAD,eAAAU,YAAAT,EAAA,KAAAD,eAAAW,cAAAV,EAAA,KAAAoB,UAAApB,EAAA,KAAAE,MAAAA,KAAAF,EAAA,IArBIE;AAuBN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA5BH,SAAAyB,OAAAC,OAAA;CAuBUC,QAAOC,KAAM,yCAAyCF,KAAK;AAAC;AAvBtE,SAAAF,MAAAK,OAAA;CAcU,AAAIA,MAAKC,OAAQ,MAEfH,QAAOC,KAAM,yCAAyCC,KAAK;AAC5D;AA8DX,SAAgBE,4BACdlC,gBACAmC,oBACwB;CACxB,IAAM,EAACC,oCAAmCxC,sCAAsC,GAG1EyC,kBAAkB/C,uBAAuB,IAE3C8B,sBAAsBpB,cAAc,IAEpCD,0BAA0BC,cAAc;CA4B5C,OAAO,EACLF,0BA3B+BP,kBAAkB;EACjD,IAAM,EAAC+C,WAAWC,YAAWvC;EAE7B,IAAI,CAACsC,aAAa,CAACC,SAAS;GAE1BT,QAAQC,KAAK,sEAAsE;GACnF;EACF;EAEA,IAAMnB,YAAY4B,iBAChBxC,gBACAoC,iCACAD,kBACF;EAEA,IAAI,CAACvB,WAAW;GAEdkB,QAAQC,KACN,mDAAmDO,UAAS,gBAAiBC,UAAUJ,qBAAqB,kCAAkCA,uBAAuB,IACvK;GACA;EACF;EAEAE,gBAAgBzB,SAAS;CAC3B,GAAG;EAACZ;EAAgBoC;EAAiCD;EAAoBE;CAAe,CAGtFvC,EACF;AACF;AAMA,SAAS0C,iBACPxC,gBACAoC,iCACAD,oBAC+B;CAC/B,IAAM,EAACG,WAAWC,YAAWvC,gBACvB2C,MAAM,GAAGL,UAAS,GAAIC;CAE5B,IAAIJ,oBAMF,OAAOU,CAHL,GAAIT,gCAAgCO,QAAQ,CAAA,GAC5C,GAAIP,gCAAgC,+BAA+B,CAAA,CAE9DS,CAAa,CAACC,MAAMC,MAAMA,EAAEzB,QAAQa,kBAAkB;CAG/D,IAAMa,aAAaZ,gCAAgCO;CAUnD,OATIK,cAAcA,WAAWC,SAAS,MAEpCnB,QAAQC,KACN,sEACA/B,cACF,GAEA8B,QAAQC,KAAK,uBAAuBiB,WAAW,EAAE,IAE5CA,aAAa;AACtB;ACpMA,MAAMO,qCAAqB,IAAIC,QAAqD;;AAGpF,SAAgBC,qBAAqBC,UAAuD;CAC1F,IAAIA,SAASC,WAAW,GACtB,MAAUC,MAAM,8DAA8D;CAGhF,IAAMC,UAAUN,mBAAmBO,IAAIJ,QAAQ;CAC/C,IAAIG,SAAS,OAAOA;CAEpB,IAAME,SAASV,qBAAqB,EAACW,MAAM,iBAAiBN,SAASO,aAAY,CAAC,GAC5EC,QAAQ;EACZC,kBAAkBJ;EAClBK,YAAYhB,GAAGW,MAAM;EACrBM,uBAAuB,CAAC;CAC1B;CAIA,OAFAd,mBAAmBe,IAAIZ,UAAUQ,KAAK,GACtCR,SAASa,gBAAgBhB,mBAAmBiB,OAAOd,QAAQ,CAAC,GACrDQ;AACT;;;;;;ACjBA,MAAaU,kBAAwCD,sBAAsBD,oBAAoB;ACiB/F,SAASiB,gBAAgBD,UAA+C;CACtE,OAAOA,SAASJ,UAAUD,SAASK,SAASH,kBAAkBD,UAAUD,SAASK,SAASL;AAC5F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,SAAgBO,eAAeC,WAA0B;CAInDd,uBAAuB,KAE3Be,sBAAsBD,SAAS;AACjC;AAEA,SAASC,sBAAsBD,WAA0B;CACvD,IAAM,CAACE,UAAUC,eAAef,SAAwB,IAAI,GAEtD,EAACgB,UAASf,oBAAoB;EAClCgB,MAAMpB;EACNqB,WAAWtB;CACb,CAAC;CA8BDG,AA5BAA,gBAAgB;EACd,IAAI,CAACiB,OAAO;EAEZ,IAAMG,aAAa,IAAIC,gBAAgB;EAEvC,eAAeC,cAAcC,QAAqB;GAChD,IAAI;IAIF,IAAMlB,QAAQM,iBAAgBc,MAHXR,MAAuB,wBAAwBS,KAAAA,GAAW,EAACH,OAAM,CAAC,EAAA,CAGlDd,QAAQC,QAAQ,KAAKiB,SAAStB;IACjE,AAAIA,SACFW,YAAYX,KAAK;GAErB,SAASuB,KAAc;IACrB,IAAIA,eAAeC,SAASD,IAAIV,SAAS,cAAc;IAEvDY,QAAQC,MAAM,qDAAqDH,GAAG;GACxE;EACF;EAIA,OAFAN,cAAcF,WAAWG,MAAM,SAElB;GACXH,WAAWY,MAAM;EACnB;CACF,GAAG,CAACf,KAAK,CAAC,GAEVjB,gBAAgB;EACd,IAAI,CAACe,UAAU;EAEf,IAAMkB,WAAWN,SAAStB;EAG1B,OAFAsB,SAAStB,QAAQQ,YAAY,GAAGA,UAAS,KAAME,aAAaA,gBAE/C;GACXY,SAAStB,QAAQ4B;EACnB;CACF,GAAG,CAACpB,WAAWE,QAAQ,CAAC;AAC1B"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DatasetsResponse, DatasetsResponse as DatasetsResponse$1, SanityProjectMember } from "@sanity/client";
|
|
2
|
-
import { AccessResourceType, ActionsResult, Application, ApplicationInclude, ApplicationsOptions, ApplicationsResponse, AuthState, ClientOptions, Comment, CommentThread, CommentsOptions, CreateCommentOptions, CurrentUser, DatasetHandle, DocumentAction, DocumentEvent, DocumentHandle as DocumentHandle$1, DocumentOptions, DocumentPermissionsResult, DocumentPresence, DocumentResource, DocumentTypeHandle as DocumentTypeHandle$1, FavoriteStatusResponse, GetUserOptions, GetUsersOptions, Installation, InstallationInclude, InstallationsOptions, InstallationsResponse, JsonMatch, Organization, OrganizationOptions, Organizations, OrganizationsOptions, PresenceSelection, PreviewValue, Project, ProjectHandle, ProjectOptions, ProjectsOptions, QueryOptions, ReleaseAction, ReleaseDocument, RemoveCommentOptions, ReplyToCommentOptions, ResolveDocument, ResolveProjectionResult, ResolveQueryResult, SanityConfig, SanityInstance, SanityUser, SetCommentStatusOptions, TokenSource, UpdateCommentOptions, UserPresence } from "@sanity/sdk";
|
|
2
|
+
import { AccessResourceType, ActionsResult, Application, ApplicationInclude, ApplicationsOptions, ApplicationsResponse, AuthState, ClientOptions, Comment, CommentThread, CommentsOptions, CreateCommentOptions, CurrentUser, DatasetHandle, DocumentAction, DocumentEvent, DocumentHandle as DocumentHandle$1, DocumentOptions, DocumentPermissionsResult, DocumentPresence, DocumentResource, DocumentTypeHandle as DocumentTypeHandle$1, FavoriteStatusResponse, GetUserOptions, GetUsersOptions, Installation, InstallationInclude, InstallationsOptions, InstallationsResponse, JsonMatch, OAuthTokens, Organization, OrganizationOptions, Organizations, OrganizationsOptions, PresenceSelection, PreviewValue, Project, ProjectHandle, ProjectOptions, ProjectsOptions, QueryOptions, ReleaseAction, ReleaseDocument, RemoveCommentOptions, ReplyToCommentOptions, ResolveDocument, ResolveProjectionResult, ResolveQueryResult, SanityConfig, SanityInstance, SanityUser, SetCommentStatusOptions, TokenSource, UpdateCommentOptions, UserPresence } from "@sanity/sdk";
|
|
3
3
|
import "@sanity/sdk/_internal";
|
|
4
4
|
import React$1, { PropsWithChildren, ReactElement, ReactNode } from "react";
|
|
5
5
|
import { FallbackProps } from "react-error-boundary";
|
|
@@ -1095,6 +1095,48 @@ export declare const useCurrentUser: UseCurrentUser;
|
|
|
1095
1095
|
* @public
|
|
1096
1096
|
*/
|
|
1097
1097
|
export declare const useHandleAuthCallback: () => (locationHref?: string | undefined) => Promise<string | false>;
|
|
1098
|
+
/**
|
|
1099
|
+
* A React hook that returns a function for handling the OAuth redirect callback.
|
|
1100
|
+
*
|
|
1101
|
+
* @remarks
|
|
1102
|
+
* This is the OAuth counterpart to `useHandleAuthCallback`. The returned
|
|
1103
|
+
* function invokes core's `handleOAuthCallback`, which validates the `state`
|
|
1104
|
+
* parameter, surfaces `?error=` redirects, exchanges the authorization `code`
|
|
1105
|
+
* for tokens, persists them, and transitions the auth state to `LOGGED_IN` —
|
|
1106
|
+
* all in core. It resolves the callback URL cleaned of the OAuth params
|
|
1107
|
+
* (`code`, `state`, `error`, `error_description`) so the caller can strip them
|
|
1108
|
+
* with `history.replaceState`, or `false` when there was nothing to handle.
|
|
1109
|
+
*
|
|
1110
|
+
* Concurrent calls are single-flight in core, so React StrictMode's double
|
|
1111
|
+
* invocation will not trigger a second code exchange. Calling it again after
|
|
1112
|
+
* the exchange has completed, with the OAuth params still in the URL, fails
|
|
1113
|
+
* state validation and moves the auth state to `ERROR`, so apply the returned
|
|
1114
|
+
* URL before the component can remount.
|
|
1115
|
+
*
|
|
1116
|
+
* @example
|
|
1117
|
+
* ```tsx
|
|
1118
|
+
* function OAuthCallback() {
|
|
1119
|
+
* const handleCallback = useHandleOAuthCallback()
|
|
1120
|
+
*
|
|
1121
|
+
* useEffect(() => {
|
|
1122
|
+
* handleCallback(window.location.href)
|
|
1123
|
+
* .then((cleanedUrl) => {
|
|
1124
|
+
* if (cleanedUrl) {
|
|
1125
|
+
* // Remove the OAuth params from the URL without a reload
|
|
1126
|
+
* history.replaceState(null, '', cleanedUrl)
|
|
1127
|
+
* }
|
|
1128
|
+
* })
|
|
1129
|
+
* .catch(console.error)
|
|
1130
|
+
* }, [handleCallback])
|
|
1131
|
+
*
|
|
1132
|
+
* return <div>Completing sign-in…</div>
|
|
1133
|
+
* }
|
|
1134
|
+
* ```
|
|
1135
|
+
*
|
|
1136
|
+
* @returns A callback handler that processes the OAuth redirect
|
|
1137
|
+
* @public
|
|
1138
|
+
*/
|
|
1139
|
+
export declare const useHandleOAuthCallback: () => (locationHref?: string | undefined) => Promise<string | false>;
|
|
1098
1140
|
/**
|
|
1099
1141
|
* @internal
|
|
1100
1142
|
*/
|
|
@@ -1105,6 +1147,103 @@ export declare function useLoginUrl(): string;
|
|
|
1105
1147
|
* @returns A function to log out of the current session
|
|
1106
1148
|
*/
|
|
1107
1149
|
export declare const useLogOut: () => () => Promise<void>;
|
|
1150
|
+
/**
|
|
1151
|
+
* A React hook that returns a function for starting the OAuth authorization-code + PKCE flow.
|
|
1152
|
+
*
|
|
1153
|
+
* @remarks
|
|
1154
|
+
* The returned function invokes core's `startOAuthAuthorization`, which generates
|
|
1155
|
+
* the PKCE `code_verifier`, `code_challenge` and `state`, persists the verifier and
|
|
1156
|
+
* state to `sessionStorage`, and navigates the browser to the authorize endpoint.
|
|
1157
|
+
* `clientId`, `redirectUri` and `organizationId` are read from the instance's
|
|
1158
|
+
* `auth.oauth` config. The returned promise rejects if the instance has no `auth.oauth` config.
|
|
1159
|
+
*
|
|
1160
|
+
* Pair with {@link useHandleOAuthCallback} on the redirect URI to complete the flow.
|
|
1161
|
+
*
|
|
1162
|
+
* @example
|
|
1163
|
+
* ```tsx
|
|
1164
|
+
* function LoginButton() {
|
|
1165
|
+
* const authorize = useOAuthAuthorize()
|
|
1166
|
+
* return <button onClick={() => authorize().catch(console.error)}>Sign in</button>
|
|
1167
|
+
* }
|
|
1168
|
+
* ```
|
|
1169
|
+
*
|
|
1170
|
+
* @returns A function that starts the OAuth flow by navigating to the authorization URL
|
|
1171
|
+
* @public
|
|
1172
|
+
*/
|
|
1173
|
+
export declare const useOAuthAuthorize: () => () => Promise<void>;
|
|
1174
|
+
/**
|
|
1175
|
+
* The current OAuth token state, plus actions to refresh and revoke it.
|
|
1176
|
+
*
|
|
1177
|
+
* @public
|
|
1178
|
+
*/
|
|
1179
|
+
interface UseOAuthTokensResult {
|
|
1180
|
+
/**
|
|
1181
|
+
* The stored OAuth tokens, or `null` when not logged in via OAuth. The
|
|
1182
|
+
* refresh token is omitted — core retains it internally for `refresh`.
|
|
1183
|
+
*/
|
|
1184
|
+
tokens: Omit<OAuthTokens, 'refreshToken'> | null;
|
|
1185
|
+
/**
|
|
1186
|
+
* Returns whether the access token has expired, comparing the latest stored
|
|
1187
|
+
* `expiresAt` against the current time at the moment it is called. Both the
|
|
1188
|
+
* tokens and the clock are read at call time, so a reference captured in an
|
|
1189
|
+
* earlier render stays accurate after a `refresh`. Reading the clock does not
|
|
1190
|
+
* trigger a re-render, so call this in an event handler or effect rather than
|
|
1191
|
+
* during render. Returns `false` when there are no tokens.
|
|
1192
|
+
*/
|
|
1193
|
+
isExpired: () => boolean;
|
|
1194
|
+
/**
|
|
1195
|
+
* Refresh via the OAuth `refresh_token` grant. When there is no refresh token,
|
|
1196
|
+
* core clears the stored tokens, logs the user out, and this resolves `null`.
|
|
1197
|
+
* Rejects on transient failures (network, 5xx, 408, 429), leaving tokens
|
|
1198
|
+
* unchanged so the call can be retried. Also rejects when the server rejects
|
|
1199
|
+
* the refresh token itself (other 4xx); core clears the tokens and logs out
|
|
1200
|
+
* first, so check `tokens` before retrying.
|
|
1201
|
+
*/
|
|
1202
|
+
refresh: () => Promise<Omit<OAuthTokens, 'refreshToken'> | null>;
|
|
1203
|
+
/** Revoke the tokens at the OAuth server, clear them locally, and log out. */
|
|
1204
|
+
revoke: () => Promise<void>;
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* A React hook that exposes the stored OAuth token state along with `refresh`
|
|
1208
|
+
* and `revoke` actions.
|
|
1209
|
+
*
|
|
1210
|
+
* @remarks
|
|
1211
|
+
* The token view is a synchronous read over core's token state source, so the
|
|
1212
|
+
* hook re-renders whenever tokens change — including changes made in another
|
|
1213
|
+
* tab, which core propagates via `storage` events.
|
|
1214
|
+
*
|
|
1215
|
+
* @returns The current {@link UseOAuthTokensResult}
|
|
1216
|
+
*
|
|
1217
|
+
* @example
|
|
1218
|
+
* ```tsx
|
|
1219
|
+
* function TokenStatus() {
|
|
1220
|
+
* const {tokens, isExpired, refresh, revoke} = useOAuthTokens()
|
|
1221
|
+
*
|
|
1222
|
+
* if (!tokens) return <div>Not signed in</div>
|
|
1223
|
+
*
|
|
1224
|
+
* const handleRefresh = async () => {
|
|
1225
|
+
* if (!isExpired()) return
|
|
1226
|
+
* try {
|
|
1227
|
+
* await refresh()
|
|
1228
|
+
* } catch {
|
|
1229
|
+
* // Transient failure (tokens unchanged, retry later) or the refresh
|
|
1230
|
+
* // token was rejected (tokens now null, user is logged out).
|
|
1231
|
+
* }
|
|
1232
|
+
* }
|
|
1233
|
+
*
|
|
1234
|
+
* return (
|
|
1235
|
+
* <div>
|
|
1236
|
+
* <p>Expires at {tokens.expiresAt.toLocaleTimeString()}</p>
|
|
1237
|
+
* <button onClick={handleRefresh}>Refresh if expired</button>
|
|
1238
|
+
* <button onClick={() => revoke()}>Revoke tokens</button>
|
|
1239
|
+
* </div>
|
|
1240
|
+
* )
|
|
1241
|
+
* }
|
|
1242
|
+
* ```
|
|
1243
|
+
*
|
|
1244
|
+
* @public
|
|
1245
|
+
*/
|
|
1246
|
+
export declare function useOAuthTokens(): UseOAuthTokensResult;
|
|
1108
1247
|
/**
|
|
1109
1248
|
* Hook that verifies the current projects belongs to the organization ID specified in the dashboard context.
|
|
1110
1249
|
*
|
|
@@ -3522,5 +3661,5 @@ export declare function useUsers(options?: GetUsersOptions): UsersResult;
|
|
|
3522
3661
|
* @internal
|
|
3523
3662
|
*/
|
|
3524
3663
|
export declare const REACT_SDK_VERSION: {};
|
|
3525
|
-
export type { AuthBoundaryProps, Status as ComlinkStatus, CommentActions, CreateDocumentOverrides, DatasetsResponse, DocumentHandle, DocumentTypeHandle, DocumentsOptions, DocumentsResponse, FetcherHookResult, FrameConnection, FrameMessageHandler as MessageHandler, MutationHookResult, PaginatedDocumentsOptions, PaginatedDocumentsResponse, ResourceHandle, ResourceProviderProps, SDKProviderProps, SanityAppProps, SanityDocument, SanityInstanceProviderProps, SanityProjectMember, SortOrderingItem, StudioWorkspaceHandle, UseCommentThreadsResult, UseCommentsResult, UseFrameConnectionOptions, UsePresenceForDocumentOptions, UseReportPresenceOptions, UseWindowConnectionOptions, UserResult, UsersResult, WindowConnection, WindowMessageHandler, useDocumentPreviewOptions, useDocumentPreviewResults, useDocumentProjectionOptions, useDocumentProjectionResults };
|
|
3664
|
+
export type { AuthBoundaryProps, Status as ComlinkStatus, CommentActions, CreateDocumentOverrides, DatasetsResponse, DocumentHandle, DocumentTypeHandle, DocumentsOptions, DocumentsResponse, FetcherHookResult, FrameConnection, FrameMessageHandler as MessageHandler, MutationHookResult, PaginatedDocumentsOptions, PaginatedDocumentsResponse, ResourceHandle, ResourceProviderProps, SDKProviderProps, SanityAppProps, SanityDocument, SanityInstanceProviderProps, SanityProjectMember, SortOrderingItem, StudioWorkspaceHandle, UseCommentThreadsResult, UseCommentsResult, UseFrameConnectionOptions, UseOAuthTokensResult, UsePresenceForDocumentOptions, UseReportPresenceOptions, UseWindowConnectionOptions, UserResult, UsersResult, WindowConnection, WindowMessageHandler, useDocumentPreviewOptions, useDocumentPreviewResults, useDocumentProjectionOptions, useDocumentProjectionResults };
|
|
3526
3665
|
//# sourceMappingURL=index.d.ts.map
|