@sanity/sdk-react 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/_exports/dashboard-internal.d.ts +2 -0
  2. package/dist/_exports/dashboard-internal.js +2 -0
  3. package/dist/_exports/dashboard.d.ts +498 -7
  4. package/dist/_exports/dashboard.d.ts.map +1 -1
  5. package/dist/_exports/dashboard.js +623 -2
  6. package/dist/_exports/dashboard.js.map +1 -1
  7. package/dist/index.d.ts +90 -87
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +10 -115
  10. package/dist/index.js.map +1 -1
  11. package/dist/useStudioWorkspacesByProjectIdDataset-BZ_Ud887.js +314 -0
  12. package/dist/useStudioWorkspacesByProjectIdDataset-BZ_Ud887.js.map +1 -0
  13. package/package.json +34 -32
  14. package/src/_exports/dashboard-internal.ts +1 -0
  15. package/src/_exports/dashboard.test-d.ts +64 -0
  16. package/src/_exports/dashboard.ts +71 -0
  17. package/src/_exports/index.ts +1 -1
  18. package/src/components/auth/AuthBoundary.test.tsx +34 -0
  19. package/src/components/auth/AuthBoundary.tsx +1 -2
  20. package/src/context/DashboardTokenRefresh.test.tsx +124 -22
  21. package/src/context/DashboardTokenRefresh.tsx +70 -20
  22. package/src/dashboard/createRemoteInstance.test.ts +168 -0
  23. package/src/dashboard/createRemoteInstance.ts +123 -0
  24. package/src/dashboard/module.ts +39 -0
  25. package/src/dashboard/remoteClientState.ts +27 -0
  26. package/src/dashboard/urlFor.test.ts +246 -0
  27. package/src/dashboard/urlFor.ts +462 -0
  28. package/src/hooks/applications/useApplication.test-d.ts +1 -1
  29. package/src/hooks/dashboard/useApplication.test.tsx +59 -0
  30. package/src/hooks/dashboard/useApplication.ts +22 -0
  31. package/src/hooks/dashboard/useApplicationConfig.test.tsx +106 -0
  32. package/src/hooks/dashboard/useApplicationConfig.ts +45 -0
  33. package/src/hooks/dashboard/useApplicationConfigs.test.tsx +89 -0
  34. package/src/hooks/dashboard/useApplicationConfigs.ts +26 -0
  35. package/src/hooks/dashboard/useApplicationForegroundId.test.tsx +54 -0
  36. package/src/hooks/dashboard/useApplicationForegroundId.ts +22 -0
  37. package/src/hooks/dashboard/useApplications.test.tsx +249 -0
  38. package/src/hooks/dashboard/useApplications.ts +128 -0
  39. package/src/hooks/dashboard/useEmit.test.tsx +124 -0
  40. package/src/hooks/dashboard/useEmit.ts +78 -0
  41. package/src/hooks/dashboard/useNavigate.ts +1 -3
  42. package/src/hooks/dashboard/useRemoteClient.test.tsx +88 -0
  43. package/src/hooks/dashboard/useRemoteClient.ts +10 -0
  44. package/src/hooks/dashboard/useTopic.test.tsx +200 -0
  45. package/src/hooks/dashboard/useTopic.ts +29 -0
  46. package/src/hooks/dashboard/useUpdateFavorite.test.tsx +8 -1
  47. package/src/hooks/document/useApplyDocumentActions.ts +3 -4
  48. package/src/hooks/document/useCreateDocument.ts +4 -4
  49. package/src/hooks/document/useDocument.ts +11 -6
  50. package/src/hooks/document/useEditDocument.ts +5 -5
  51. package/src/hooks/projection/useDocumentProjection.ts +6 -7
  52. package/src/hooks/query/useQuery.ts +3 -4
  53. package/src/utils/resolveOrgResources.test.ts +2 -2
  54. package/src/utils/resolveOrgResources.ts +2 -2
  55. package/dist/useStudioWorkspacesByProjectIdDataset-I4S3CuR5.js +0 -177
  56. package/dist/useStudioWorkspacesByProjectIdDataset-I4S3CuR5.js.map +0 -1
  57. package/src/context/dashboardToken.ts +0 -63
@@ -1,177 +0,0 @@
1
- import { c } from "react-compiler-runtime";
2
- import { getDashboardOrganizationId } from "@sanity/sdk";
3
- import { createContext, useContext, useEffect, useRef, useState, useSyncExternalStore } from "react";
4
- import { SDK_CHANNEL_NAME, SDK_NODE_NAME } from "@sanity/message-protocol";
5
- import { getNodeState } from "@sanity/sdk/comlink";
6
- import { filter, firstValueFrom } from "rxjs";
7
- const SanityInstanceContext = createContext(null), useSanityInstance = () => {
8
- let instance = useContext(SanityInstanceContext);
9
- if (!instance) throw Error("SanityInstance context not found. Please ensure that your component is wrapped in a ResourceProvider or a SanityApp component.");
10
- return instance;
11
- };
12
- function createStateSourceHook(options) {
13
- let getState = typeof options == "function" ? options : options.getState, suspense = "shouldSuspend" in options && "suspender" in options ? options : void 0;
14
- function useHook(...t0) {
15
- let $ = c(3), params = t0, instance = useSanityInstance();
16
- if (suspense?.suspender && suspense?.shouldSuspend?.(instance, ...params)) throw suspense.suspender(instance, ...params);
17
- let t1;
18
- $[0] !== instance || $[1] !== params ? (t1 = getState(instance, ...params), $[0] = instance, $[1] = params, $[2] = t1) : t1 = $[2];
19
- let state = t1;
20
- return useSyncExternalStore(state.subscribe, state.getCurrent);
21
- }
22
- return useHook;
23
- }
24
- const useNodeState = createStateSourceHook({
25
- getState: getNodeState,
26
- shouldSuspend: (instance, nodeInput) => getNodeState(instance, nodeInput).getCurrent() === void 0,
27
- suspender: (instance, nodeInput) => firstValueFrom(getNodeState(instance, nodeInput).observable.pipe(filter(Boolean)))
28
- });
29
- /**
30
- * @internal
31
- * Hook to wrap a Comlink node in a React hook.
32
- * Our store functionality takes care of the lifecycle of the node,
33
- * as well as sharing a single node between invocations if they share the same name.
34
- *
35
- * Generally not to be used directly, but to be used as a dependency of
36
- * Comlink-powered hooks like `useStudioWorkspacesByProjectIdDataset`.
37
- */
38
- function useWindowConnection(t0) {
39
- let $ = c(19), { name, connectTo, onMessage } = t0, t1;
40
- $[0] !== connectTo || $[1] !== name ? (t1 = {
41
- name,
42
- connectTo
43
- }, $[0] = connectTo, $[1] = name, $[2] = t1) : t1 = $[2];
44
- let { node } = useNodeState(t1), t2;
45
- $[3] === Symbol.for("react.memo_cache_sentinel") ? (t2 = [], $[3] = t2) : t2 = $[3];
46
- let messageUnsubscribers = useRef(t2), instance = useSanityInstance(), t3;
47
- $[4] !== node || $[5] !== onMessage ? (t3 = () => (onMessage && Object.entries(onMessage).forEach((t4) => {
48
- let [type, handler] = t4, messageUnsubscribe = node.on(type, handler);
49
- messageUnsubscribe && messageUnsubscribers.current.push(messageUnsubscribe);
50
- }), () => {
51
- messageUnsubscribers.current.forEach(_temp), messageUnsubscribers.current = [];
52
- }), $[4] = node, $[5] = onMessage, $[6] = t3) : t3 = $[6];
53
- let t4;
54
- $[7] !== instance || $[8] !== name || $[9] !== node || $[10] !== onMessage ? (t4 = [
55
- instance,
56
- name,
57
- onMessage,
58
- node
59
- ], $[7] = instance, $[8] = name, $[9] = node, $[10] = onMessage, $[11] = t4) : t4 = $[11], useEffect(t3, t4);
60
- let t5;
61
- $[12] === node ? t5 = $[13] : (t5 = (type_0, data) => {
62
- node.post(type_0, data);
63
- }, $[12] = node, $[13] = t5);
64
- let sendMessage = t5, t6;
65
- $[14] === node ? t6 = $[15] : (t6 = (type_1, data_0, fetchOptions) => node.fetch(type_1, data_0, fetchOptions ?? {}), $[14] = node, $[15] = t6);
66
- let fetch = t6, t7;
67
- return $[16] !== fetch || $[17] !== sendMessage ? (t7 = {
68
- sendMessage,
69
- fetch
70
- }, $[16] = fetch, $[17] = sendMessage, $[18] = t7) : t7 = $[18], t7;
71
- }
72
- function _temp(unsubscribe) {
73
- return unsubscribe();
74
- }
75
- /**
76
- * @public
77
- *
78
- * A React hook that retrieves the dashboard organization ID that is currently selected in the Sanity Dashboard.
79
- *
80
- * @example
81
- * ```tsx
82
- * function DashboardComponent() {
83
- * const orgId = useOrganizationId()
84
- *
85
- * if (!orgId) return null
86
- *
87
- * return <div>Organization ID: {String(orgId)}</div>
88
- * }
89
- * ```
90
- *
91
- * @category Dashboard
92
- * @returns The dashboard organization ID (string | undefined)
93
- */
94
- function useOrganizationId() {
95
- let $ = c(2), instance = useSanityInstance(), t0;
96
- $[0] === instance ? t0 = $[1] : (t0 = getDashboardOrganizationId(instance), $[0] = instance, $[1] = t0);
97
- let { subscribe, getCurrent } = t0;
98
- return useSyncExternalStore(subscribe, getCurrent);
99
- }
100
- /**
101
- * Hook that fetches studio workspaces and organizes them by projectId:dataset
102
- * @internal
103
- *
104
- * @example
105
- * ```tsx
106
- * import {useStudioWorkspacesByProjectIdDataset} from '@sanity/sdk-react'
107
- * import {Card, Code, Button} from '@sanity/ui'
108
- * import {Suspense} from 'react'
109
- *
110
- * function WorkspacesCard() {
111
- * const {workspacesByProjectIdAndDataset, error} = useStudioWorkspacesByProjectIdDataset()
112
- * if (error) {
113
- * return <div>Error: {error}</div>
114
- * }
115
- * return (
116
- * <Card padding={4} radius={2} shadow={1}>
117
- * <Code language="json">
118
- * {JSON.stringify(workspacesByProjectIdAndDataset, null, 2)}
119
- * </Code>
120
- * </Card>
121
- * )
122
- * }
123
- *
124
- * // Wrap the component with Suspense since the hook may suspend
125
- * function DashboardWorkspaces() {
126
- * return (
127
- * <Suspense fallback={<Button text="Loading..." disabled />}>
128
- * <WorkspacesCard />
129
- * </Suspense>
130
- * )
131
- * }
132
- * ```
133
- */
134
- function useStudioWorkspacesByProjectIdDataset() {
135
- let $ = c(8), t0;
136
- $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = {}, $[0] = t0) : t0 = $[0];
137
- let [workspacesByProjectIdAndDataset, setWorkspacesByProjectIdAndDataset] = useState(t0), [error, setError] = useState(null), t1;
138
- $[1] === Symbol.for("react.memo_cache_sentinel") ? (t1 = {
139
- name: SDK_NODE_NAME,
140
- connectTo: SDK_CHANNEL_NAME
141
- }, $[1] = t1) : t1 = $[1];
142
- let { fetch } = useWindowConnection(t1), t2, t3;
143
- $[2] === fetch ? (t2 = $[3], t3 = $[4]) : (t2 = () => {
144
- if (!fetch) return;
145
- let fetchWorkspaces = async function fetchWorkspaces(signal) {
146
- try {
147
- let data = await fetch("dashboard/v1/context", void 0, { signal }), workspaceMap = {}, noProjectIdAndDataset = [];
148
- data.context.availableResources.forEach((resource) => {
149
- if (resource.type !== "studio") return;
150
- if (!resource.projectId || !resource.dataset) {
151
- noProjectIdAndDataset.push(resource);
152
- return;
153
- }
154
- let key = `${resource.projectId}:${resource.dataset}`;
155
- workspaceMap[key] || (workspaceMap[key] = []), workspaceMap[key].push(resource);
156
- }), noProjectIdAndDataset.length > 0 && (workspaceMap["NO_PROJECT_ID:NO_DATASET"] = noProjectIdAndDataset), setWorkspacesByProjectIdAndDataset(workspaceMap), setError(null);
157
- } catch (t4) {
158
- let err = t4;
159
- if (err instanceof Error) {
160
- if (err.name === "AbortError") return;
161
- setError("Failed to fetch workspaces");
162
- }
163
- }
164
- }, controller = new AbortController();
165
- return fetchWorkspaces(controller.signal), () => {
166
- controller.abort();
167
- };
168
- }, t3 = [fetch], $[2] = fetch, $[3] = t2, $[4] = t3), useEffect(t2, t3);
169
- let t4;
170
- return $[5] !== error || $[6] !== workspacesByProjectIdAndDataset ? (t4 = {
171
- workspacesByProjectIdAndDataset,
172
- error
173
- }, $[5] = error, $[6] = workspacesByProjectIdAndDataset, $[7] = t4) : t4 = $[7], t4;
174
- }
175
- export { useSanityInstance as a, createStateSourceHook as i, useOrganizationId as n, SanityInstanceContext as o, useWindowConnection as r, useStudioWorkspacesByProjectIdDataset as t };
176
-
177
- //# sourceMappingURL=useStudioWorkspacesByProjectIdDataset-I4S3CuR5.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useStudioWorkspacesByProjectIdDataset-I4S3CuR5.js","names":["SanityInstance","createContext","SanityInstanceContext","SanityInstance","useContext","SanityInstanceContext","useSanityInstance","instance","Error","SanityConfig","SanityInstance","StateSource","useSyncExternalStore","useSanityInstance","StateSourceFactory","instance","params","TParams","TState","CreateStateSourceHookOptions","getState","shouldSuspend","suspender","Promise","getConfig","createStateSourceHook","options","suspense","undefined","useHook","t0","$","_c","t1","state","subscribe","getCurrent","MessageData","NodeInput","SanityInstance","StateSource","FrameMessage","getNodeState","NodeState","WindowMessage","useCallback","useEffect","useRef","filter","firstValueFrom","useSanityInstance","createStateSourceHook","WindowMessageHandler","event","TFrameMessage","UseWindowConnectionOptions","name","connectTo","onMessage","Record","TMessage","WindowConnection","sendMessage","type","TType","data","Extract","fetch","options","signal","AbortSignal","suppressWarnings","responseTimeout","Promise","TResponse","useNodeState","getState","instance","nodeInput","shouldSuspend","getCurrent","undefined","suspender","observable","pipe","Boolean","useWindowConnection","t0","$","_c","t1","node","t2","Symbol","for","messageUnsubscribers","t3","Object","entries","forEach","t4","handler","messageUnsubscribe","on","current","push","_temp","t5","type_0","post","t6","type_1","data_0","fetchOptions","t7","unsubscribe","getDashboardOrganizationId","useMemo","useSyncExternalStore","useSanityInstance","useOrganizationId","$","_c","instance","t0","subscribe","getCurrent","SDK_CHANNEL_NAME","SDK_NODE_NAME","useEffect","useState","useWindowConnection","DashboardResource","id","name","title","basePath","projectId","dataset","type","userApplicationId","url","WorkspacesByProjectIdDataset","key","StudioWorkspacesResult","workspacesByProjectIdAndDataset","error","useStudioWorkspacesByProjectIdDataset","$","_c","t0","Symbol","for","setWorkspacesByProjectIdAndDataset","setError","t1","connectTo","fetch","t2","t3","fetchWorkspaces","signal","data","undefined","workspaceMap","noProjectIdAndDataset","context","availableResources","forEach","resource","push","const","length","t4","err","Error","controller","AbortController","abort"],"sources":["../src/context/SanityInstanceContext.ts","../src/hooks/context/useSanityInstance.ts","../src/hooks/helpers/createStateSourceHook.tsx","../src/hooks/comlink/useWindowConnection.ts","../src/hooks/dashboard/useOrganizationId.tsx","../src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.ts"],"sourcesContent":["import {type SanityInstance} from '@sanity/sdk'\nimport {createContext} from 'react'\n\nexport const SanityInstanceContext = createContext<SanityInstance | null>(null)\n","import {type SanityInstance} from '@sanity/sdk'\nimport {useContext} from 'react'\n\nimport {SanityInstanceContext} from '../../context/SanityInstanceContext'\n\n/**\n * Retrieves the current Sanity instance from context\n *\n * @public\n *\n * @category Platform\n * @returns The current Sanity instance\n *\n * @remarks\n * This hook accesses the nearest Sanity instance from the React context.\n * The hook must be used within a component wrapped by a `ResourceProvider` or `SanityApp`.\n *\n * @example Get the current instance\n * ```tsx\n * const instance = useSanityInstance()\n * console.log(instance.config.projectId)\n * ```\n *\n * @throws Error if no SanityInstance is found in context\n */\nexport const useSanityInstance = (): SanityInstance => {\n const instance = useContext(SanityInstanceContext)\n\n if (!instance) {\n throw new Error(\n `SanityInstance context not found. Please ensure that your component is wrapped in a ResourceProvider or a SanityApp component.`,\n )\n }\n\n return instance\n}\n","import {type SanityConfig, type SanityInstance, type StateSource} from '@sanity/sdk'\nimport {useSyncExternalStore} from 'react'\n\nimport {useSanityInstance} from '../context/useSanityInstance'\n\ntype StateSourceFactory<TParams extends unknown[], TState> = (\n instance: SanityInstance,\n ...params: TParams\n) => StateSource<TState>\n\ninterface CreateStateSourceHookOptions<TParams extends unknown[], TState> {\n getState: StateSourceFactory<TParams, TState>\n shouldSuspend?: (instance: SanityInstance, ...params: TParams) => boolean\n suspender?: (instance: SanityInstance, ...params: TParams) => Promise<unknown>\n getConfig?: (...params: TParams) => SanityConfig | undefined\n}\n\nexport function createStateSourceHook<TParams extends unknown[], TState>(\n options: StateSourceFactory<TParams, TState> | CreateStateSourceHookOptions<TParams, TState>,\n): (...params: TParams) => TState {\n const getState = typeof options === 'function' ? options : options.getState\n const suspense = 'shouldSuspend' in options && 'suspender' in options ? options : undefined\n\n function useHook(...params: TParams) {\n const instance = useSanityInstance()\n\n if (suspense?.suspender && suspense?.shouldSuspend?.(instance, ...params)) {\n throw suspense.suspender(instance, ...params)\n }\n\n const state = getState(instance, ...params)\n return useSyncExternalStore(state.subscribe, state.getCurrent)\n }\n\n return useHook\n}\n","import {type MessageData, type NodeInput} from '@sanity/comlink'\nimport {type SanityInstance, type StateSource} from '@sanity/sdk'\nimport {\n type FrameMessage,\n getNodeState,\n type NodeState,\n type WindowMessage,\n} from '@sanity/sdk/comlink'\nimport {useCallback, useEffect, useRef} from 'react'\nimport {filter, firstValueFrom} from 'rxjs'\n\nimport {useSanityInstance} from '../context/useSanityInstance'\nimport {createStateSourceHook} from '../helpers/createStateSourceHook'\n\n/**\n * @internal\n */\nexport type WindowMessageHandler<TFrameMessage extends FrameMessage> = (\n event: TFrameMessage['data'],\n) => TFrameMessage['response']\n\n/**\n * @internal\n */\nexport interface UseWindowConnectionOptions<TMessage extends FrameMessage> {\n name: string\n connectTo: string\n onMessage?: Record<TMessage['type'], WindowMessageHandler<TMessage>>\n}\n\n/**\n * @internal\n */\nexport interface WindowConnection<TMessage extends WindowMessage> {\n sendMessage: <TType extends TMessage['type']>(\n type: TType,\n data?: Extract<TMessage, {type: TType}>['data'],\n ) => void\n fetch: <TResponse>(\n type: string,\n data?: MessageData,\n options?: {\n signal?: AbortSignal\n suppressWarnings?: boolean\n responseTimeout?: number\n },\n ) => Promise<TResponse>\n}\n\nconst useNodeState = createStateSourceHook({\n getState: getNodeState as (\n instance: SanityInstance,\n nodeInput: NodeInput,\n ) => StateSource<NodeState>,\n shouldSuspend: (instance: SanityInstance, nodeInput: NodeInput) =>\n getNodeState(instance, nodeInput).getCurrent() === undefined,\n suspender: (instance: SanityInstance, nodeInput: NodeInput) => {\n return firstValueFrom(getNodeState(instance, nodeInput).observable.pipe(filter(Boolean)))\n },\n})\n\n/**\n * @internal\n * Hook to wrap a Comlink node in a React hook.\n * Our store functionality takes care of the lifecycle of the node,\n * as well as sharing a single node between invocations if they share the same name.\n *\n * Generally not to be used directly, but to be used as a dependency of\n * Comlink-powered hooks like `useStudioWorkspacesByProjectIdDataset`.\n */\nexport function useWindowConnection<\n TWindowMessage extends WindowMessage,\n TFrameMessage extends FrameMessage,\n>({\n name,\n connectTo,\n onMessage,\n}: UseWindowConnectionOptions<TFrameMessage>): WindowConnection<TWindowMessage> {\n const {node} = useNodeState({name, connectTo})\n const messageUnsubscribers = useRef<(() => void)[]>([])\n const instance = useSanityInstance()\n\n useEffect(() => {\n if (onMessage) {\n Object.entries(onMessage).forEach(([type, handler]) => {\n const messageUnsubscribe = node.on(type, handler as WindowMessageHandler<TFrameMessage>)\n if (messageUnsubscribe) {\n messageUnsubscribers.current.push(messageUnsubscribe)\n }\n })\n }\n\n return () => {\n messageUnsubscribers.current.forEach((unsubscribe) => unsubscribe())\n messageUnsubscribers.current = []\n }\n }, [instance, name, onMessage, node])\n\n const sendMessage = useCallback(\n (type: TWindowMessage['type'], data?: Extract<TWindowMessage, {type: typeof type}>['data']) => {\n node.post(type, data)\n },\n [node],\n )\n\n const fetch = useCallback(\n <TResponse>(\n type: string,\n data?: MessageData,\n fetchOptions?: {\n responseTimeout?: number\n signal?: AbortSignal\n suppressWarnings?: boolean\n },\n ): Promise<TResponse> => {\n return node.fetch(type, data, fetchOptions ?? {}) as Promise<TResponse>\n },\n [node],\n )\n return {\n sendMessage,\n fetch,\n }\n}\n","import {getDashboardOrganizationId} from '@sanity/sdk'\nimport {useMemo, useSyncExternalStore} from 'react'\n\nimport {useSanityInstance} from '../context/useSanityInstance'\n\n/**\n * @public\n *\n * A React hook that retrieves the dashboard organization ID that is currently selected in the Sanity Dashboard.\n *\n * @example\n * ```tsx\n * function DashboardComponent() {\n * const orgId = useOrganizationId()\n *\n * if (!orgId) return null\n *\n * return <div>Organization ID: {String(orgId)}</div>\n * }\n * ```\n *\n * @category Dashboard\n * @returns The dashboard organization ID (string | undefined)\n */\nexport function useOrganizationId(): string | undefined {\n const instance = useSanityInstance()\n const {subscribe, getCurrent} = useMemo(() => getDashboardOrganizationId(instance), [instance])\n\n return useSyncExternalStore(subscribe, getCurrent)\n}\n","import {SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\nimport {useEffect, useState} from 'react'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\n\nexport interface DashboardResource {\n id: string\n name: string\n title: string\n basePath: string\n projectId: string\n dataset: string\n type: string\n userApplicationId: string\n url: string\n}\n\ninterface WorkspacesByProjectIdDataset {\n [key: `${string}:${string}`]: DashboardResource[] // key format: `${projectId}:${dataset}`\n}\n\ninterface StudioWorkspacesResult {\n workspacesByProjectIdAndDataset: WorkspacesByProjectIdDataset\n error: string | null\n}\n\n/**\n * Hook that fetches studio workspaces and organizes them by projectId:dataset\n * @internal\n *\n * @example\n * ```tsx\n * import {useStudioWorkspacesByProjectIdDataset} from '@sanity/sdk-react'\n * import {Card, Code, Button} from '@sanity/ui'\n * import {Suspense} from 'react'\n *\n * function WorkspacesCard() {\n * const {workspacesByProjectIdAndDataset, error} = useStudioWorkspacesByProjectIdDataset()\n * if (error) {\n * return <div>Error: {error}</div>\n * }\n * return (\n * <Card padding={4} radius={2} shadow={1}>\n * <Code language=\"json\">\n * {JSON.stringify(workspacesByProjectIdAndDataset, null, 2)}\n * </Code>\n * </Card>\n * )\n * }\n *\n * // Wrap the component with Suspense since the hook may suspend\n * function DashboardWorkspaces() {\n * return (\n * <Suspense fallback={<Button text=\"Loading...\" disabled />}>\n * <WorkspacesCard />\n * </Suspense>\n * )\n * }\n * ```\n */\nexport function useStudioWorkspacesByProjectIdDataset(): StudioWorkspacesResult {\n const [workspacesByProjectIdAndDataset, setWorkspacesByProjectIdAndDataset] =\n useState<WorkspacesByProjectIdDataset>({})\n const [error, setError] = useState<string | null>(null)\n\n const {fetch} = useWindowConnection({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n })\n\n // Once computed, this should probably be in a store and poll for changes\n // However, our stores are currently being refactored\n useEffect(() => {\n if (!fetch) return\n\n async function fetchWorkspaces(signal: AbortSignal) {\n try {\n const data = await fetch<{\n context: {availableResources: Array<DashboardResource>}\n }>('dashboard/v1/context', undefined, {signal})\n\n const workspaceMap: WorkspacesByProjectIdDataset = {}\n const noProjectIdAndDataset: DashboardResource[] = []\n\n data.context.availableResources.forEach((resource) => {\n if (resource.type !== 'studio') return\n if (!resource.projectId || !resource.dataset) {\n noProjectIdAndDataset.push(resource)\n return\n }\n const key = `${resource.projectId}:${resource.dataset}` as const\n if (!workspaceMap[key]) {\n workspaceMap[key] = []\n }\n workspaceMap[key].push(resource)\n })\n\n if (noProjectIdAndDataset.length > 0) {\n workspaceMap['NO_PROJECT_ID:NO_DATASET'] = noProjectIdAndDataset\n }\n\n setWorkspacesByProjectIdAndDataset(workspaceMap)\n setError(null)\n } catch (err: unknown) {\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n return\n }\n setError('Failed to fetch workspaces')\n }\n }\n }\n\n const controller = new AbortController()\n fetchWorkspaces(controller.signal)\n\n return () => {\n controller.abort()\n }\n }, [fetch])\n\n return {\n workspacesByProjectIdAndDataset,\n error,\n }\n}\n"],"mappings":";;;;;;AAGA,MAAaE,wBAAwBD,cAAqC,IAAI,GCsBjEK,0BAAoB;CAC/B,IAAAC,WAAiBH,WAAWC,qBAAqB;CAEjD,IAAI,CAACE,UACH,MAAUC,MACR,gIACF;CACD,OAEMD;AAAQ;ACjBjB,SAAgBkB,sBACdC,SACgC;CAChC,IAAMN,WAAW,OAAOM,WAAY,aAAaA,UAAUA,QAAQN,UAC7DO,WAAW,mBAAmBD,WAAW,eAAeA,UAAUA,UAAUE,KAAAA;CAElF,SAAAC,QAAA,GAAAC,IAAA;EAAA,IAAAC,IAAAC,EAAA,CAAA,GAAiBhB,SAAAc,IACff,WAAiBF,kBAAkB;EAEnC,IAAIc,UAAQL,aAAeK,UAAQN,gBAAkBN,UAAQ,GAAKC,MAAM,GACtE,MAAMW,SAAQL,UAAWP,UAAQ,GAAKC,MAAM;EAC7C,IAAAiB;EAAA,AAAAF,EAAA,OAAAhB,YAAAgB,EAAA,OAAAf,UAEaiB,KAAAb,SAASL,UAAQ,GAAKC,MAAM,GAACe,EAAA,KAAAhB,UAAAgB,EAAA,KAAAf,QAAAe,EAAA,KAAAE,MAAAA,KAAAF,EAAA;EAA3C,IAAAG,QAAcD;EAA6B,OACpCrB,qBAAqBsB,MAAKC,WAAYD,MAAKE,UAAW;CAAC;CAGhE,OAAOP;AACT;ACcA,MAAM8C,eAAexB,sBAAsB;CACzCyB,UAAUlC;CAIVqC,gBAAgBF,UAA0BC,cACxCpC,aAAamC,UAAUC,SAAS,CAAC,CAACE,WAAW,MAAMC,KAAAA;CACrDC,YAAYL,UAA0BC,cAC7B7B,eAAeP,aAAamC,UAAUC,SAAS,CAAC,CAACK,WAAWC,KAAKpC,OAAOqC,OAAO,CAAC,CAAC;AAE5F,CAAC;;;;;;;;;;AAWD,SAAOC,oBAAAC,IAAA;CAAA,IAAAC,IAAAC,EAAA,EAAA,GAGL,EAAAjC,MAAAC,WAAAC,cAAA6B,IAI0CG;CAAA,AAAAF,EAAA,OAAA/B,aAAA+B,EAAA,OAAAhC,QACdkC,KAAA;EAAAlC;EAAAC;CAAgB,GAAC+B,EAAA,KAAA/B,WAAA+B,EAAA,KAAAhC,MAAAgC,EAAA,KAAAE,MAAAA,KAAAF,EAAA;CAA7C,IAAA,EAAAG,SAAehB,aAAae,EAAiB,GAACE;CAAA,AAAAJ,EAAA,OAAAK,OAAAC,IAAA,2BAAA,KACMF,KAAA,CAAA,GAAEJ,EAAA,KAAAI,MAAAA,KAAAJ,EAAA;CAAtD,IAAAO,uBAA6BhD,OAAuB6C,EAAE,GACtDf,WAAiB3B,kBAAkB,GAAC8C;CAAA,AAAAR,EAAA,OAAAG,QAAAH,EAAA,OAAA9B,aAE1BsC,YACJtC,aACFuC,OAAMC,QAASxC,SAAS,CAAC,CAAAyC,SAASC,OAAA;EAAC,IAAA,CAAArC,MAAAsC,WAAAD,IACjCE,qBAA2BX,KAAIY,GAAIxC,MAAMsC,OAA8C;EACvF,AAAIC,sBACFP,qBAAoBS,QAAQC,KAAMH,kBAAkB;CACrD,CACF,SAGI;EAELP,AADAA,qBAAoBS,QAAQL,QAASO,KAA8B,GACnEX,qBAAoBS,UAAW,CAAA;CAAH,IAE/BhB,EAAA,KAAAG,MAAAH,EAAA,KAAA9B,WAAA8B,EAAA,KAAAQ,MAAAA,KAAAR,EAAA;CAAA,IAAAY;CAdDtD,AAcC0C,EAAA,OAAAX,YAAAW,EAAA,OAAAhC,QAAAgC,EAAA,OAAAG,QAAAH,EAAA,QAAA9B,aAAE0C,KAAA;EAACvB;EAAUrB;EAAME;EAAWiC;CAAI,GAACH,EAAA,KAAAX,UAAAW,EAAA,KAAAhC,MAAAgC,EAAA,KAAAG,MAAAH,EAAA,MAAA9B,WAAA8B,EAAA,MAAAY,MAAAA,KAAAZ,EAAA,KAdpC1C,UAAUkD,IAcPI,EAAiC;CAAC,IAAAO;CAAA,AAAAnB,EAAA,QAAAG,OAKlCgB,KAAAnB,EAAA,OAFDmB,MAAAC,QAAA3C,SAAA;EACE0B,KAAIkB,KAAM9C,QAAME,IAAI;CAAC,GACtBuB,EAAA,MAAAG,MAAAH,EAAA,MAAAmB;CAHH,IAAA7C,cAAoB6C,IAKnBG;CAAA,AAAAtB,EAAA,QAAAG,OAaEmB,KAAAtB,EAAA,OAVDsB,MAAAC,QAAAC,QAAAC,iBASStB,KAAIxB,MAAOJ,QAAME,QAAMgD,gBAAA,CAAiB,CAAC,GACjDzB,EAAA,MAAAG,MAAAH,EAAA,MAAAsB;CAXH,IAAA3C,QAAc2C,IAabI;CAIA,OAJA1B,EAAA,QAAArB,SAAAqB,EAAA,QAAA1B,eACMoD,KAAA;EAAApD;EAAAK;CAGP,GAACqB,EAAA,MAAArB,OAAAqB,EAAA,MAAA1B,aAAA0B,EAAA,MAAA0B,MAAAA,KAAA1B,EAAA,KAHM0B;AAGN;AApDI,SAAAR,MAAAS,aAAA;CAAA,OAuBqDA,YAAY;AAAC;;;;;;;;;;;;;;;;;;;;ACrEzE,SAAOK,oBAAA;CAAA,IAAAC,IAAAC,EAAA,CAAA,GACLC,WAAiBJ,kBAAkB,GAACK;CAAA,AAAAH,EAAA,OAAAE,WAC8CC,KAAAH,EAAA,MAApCG,KAAAR,2BAA2BO,QAAQ,GAACF,EAAA,KAAAE,UAAAF,EAAA,KAAAG;CAAlF,IAAA,EAAAC,WAAAC,eAA8CF;CAAiD,OAExFN,qBAAqBO,WAAWC,UAAU;AAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACgCpD,SAAOqB,wCAAA;CAAA,IAAAC,IAAAC,EAAA,CAAA,GAAAC;CAAA,AAAAF,EAAA,OAAAG,OAAAC,IAAA,2BAAA,KAEoCF,KAAA,CAAC,GAACF,EAAA,KAAAE,MAAAA,KAAAF,EAAA;CAD3C,IAAA,CAAAH,iCAAAQ,sCACEvB,SAAuCoB,EAAE,GAC3C,CAAAJ,OAAAQ,YAA0BxB,SAAwB,IAAI,GAACyB;CAAA,AAAAP,EAAA,OAAAG,OAAAC,IAAA,2BAAA,KAEnBG,KAAA;EAAArB,MAC5BN;EAAa4B,WACR7B;CACb,GAACqB,EAAA,KAAAO,MAAAA,KAAAP,EAAA;CAHD,IAAA,EAAAS,UAAgB1B,oBAAoBwB,EAGnC,GAACG,IAAAC;CAIF9B,AAJEmB,EAAA,OAAAS,SAmDQC,KAAAV,EAAA,IAAAW,KAAAX,EAAA,OA/CAU,WAAA;EACR,IAAI,CAACD,OAAK;EAEV,IAAAG,kBAAA,eAAAA,gBAAAC,QAAA;GACE,IAAA;IACE,IAAAC,OAAa,MAAML,MAEhB,wBAAwBM,KAAAA,GAAW,EAAAF,OAAO,CAAC,GAE9CG,eAAmD,CAAC,GACpDC,wBAAmD,CAAA;IAoBnDX,AAlBAQ,KAAII,QAAQC,mBAAmBC,SAASC,aAAA;KACtC,IAAIA,SAAQ9B,SAAU,UAAQ;KAC9B,IAAI,CAAC8B,SAAQhC,aAAT,CAAwBgC,SAAQ/B,SAAQ;MAC1C2B,sBAAqBK,KAAMD,QAAQ;MAAC;KAAA;KAGtC,IAAA1B,MAAY,GAAG0B,SAAQhC,UAAU,GAAIgC,SAAQ/B;KAI7C0B,AAHKA,aAAarB,SAChBqB,aAAarB,OAAO,CAAA,IAEtBqB,aAAarB,IAAI,CAAA2B,KAAMD,QAAQ;IAAC,CACjC,GAEGJ,sBAAqBO,SAAU,MACjCR,aAAa,8BAA8BC,wBAG7CZ,mCAAmCW,YAAY,GAC/CV,SAAS,IAAI;GAAC,SAAAmB,IAAA;IACPC,IAAAA,MAAAA;IACP,IAAIA,eAAeC,OAAK;KACtB,IAAID,IAAGxC,SAAU,cAAY;KAG7BoB,SAAS,4BAA4B;IAAC;GACvC;EACF,GAGHsB,aAAmB,IAAIC,gBAAgB;EACL,OAAlCjB,gBAAgBgB,WAAUf,MAAO,SAE1B;GACLe,WAAUE,MAAO;EAAC;CACnB,GACAnB,KAAA,CAACF,KAAK,GAACT,EAAA,KAAAS,OAAAT,EAAA,KAAAU,IAAAV,EAAA,KAAAW,KA/CV9B,UAAU6B,IA+CPC,EAAO;CAAC,IAAAc;CAKV,OALUzB,EAAA,OAAAF,SAAAE,EAAA,OAAAH,mCAEJ4B,KAAA;EAAA5B;EAAAC;CAGP,GAACE,EAAA,KAAAF,OAAAE,EAAA,KAAAH,iCAAAG,EAAA,KAAAyB,MAAAA,KAAAzB,EAAA,IAHMyB;AAGN"}
@@ -1,63 +0,0 @@
1
- import {from, type Observable, of} from 'rxjs'
2
- import {catchError, switchMap} from 'rxjs/operators'
3
-
4
- // The dashboard host installs its shared message bus on this well-known global
5
- // symbol before it loads federated remotes. It must match the key used by
6
- // `@sanity/workbench` (`Symbol.for('sanity.os.bus')`).
7
- const OS_BUS_KEY = Symbol.for('sanity.os.bus')
8
-
9
- /**
10
- * Whether this app is running as a federated remote inside the dashboard.
11
- *
12
- * Federation shares the host's realm, so the installed bus is visible on
13
- * `globalThis`. This is `false` in a standalone app, where we must never import
14
- * `@sanity/workbench` (it would install a bus and add bundle weight for no
15
- * reason). Note: this is a different embedding model to the Core UI iframe,
16
- * which is detected separately via the dashboard context — that signal is not
17
- * set on the federation path.
18
- *
19
- * @internal
20
- */
21
- export function isDashboardEnvironment(): boolean {
22
- return typeof globalThis === 'object' && OS_BUS_KEY in globalThis
23
- }
24
-
25
- /**
26
- * Observes the session token issued by the dashboard "OS", tracking the OS auth
27
- * state over time.
28
- *
29
- * Returns `undefined` when the app is not embedded in the dashboard, so the
30
- * caller uses its normal auth flow. Inside the dashboard, subscribes to the
31
- * `auth.token` state topic, emitting the current token — or `null` when the OS
32
- * is signed out — and re-emitting as the OS auth state changes, so sign-in/out
33
- * propagates instead of being captured once. Any bus error is treated as "no
34
- * token" (`null`). The token is used in-memory only and never persisted.
35
- *
36
- * @internal
37
- */
38
- export function observeDashboardToken(): Observable<string | null> | undefined {
39
- if (!isDashboardEnvironment()) return undefined
40
-
41
- return from(import('@sanity/workbench')).pipe(
42
- switchMap(({os}) => os.subscribe('auth.token')),
43
- // Any failure (importing the host bundle, or the subscription) means "no OS token".
44
- catchError(() => of(null)),
45
- )
46
- }
47
-
48
- /**
49
- * Asks the dashboard "OS" to reissue the session token, e.g. after its current
50
- * one was rejected with a 401. Fire-and-forget: the reissued token arrives via
51
- * the `auth.token` subscription in {@link observeDashboardToken}. No-op outside
52
- * the dashboard.
53
- *
54
- * @internal
55
- */
56
- export function refreshDashboardToken(): void {
57
- if (!isDashboardEnvironment()) return
58
-
59
- void import('@sanity/workbench').then(
60
- ({os}) => os.emit('auth.token.refresh', undefined),
61
- () => {},
62
- )
63
- }