@sanity/sdk-react 3.0.0-rc.0 → 3.0.0-rc.2

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 (168) hide show
  1. package/README.md +103 -155
  2. package/dist/index.d.ts +2488 -2019
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +4435 -1529
  5. package/dist/index.js.map +1 -1
  6. package/package.json +55 -59
  7. package/src/_exports/index.ts +2 -4
  8. package/src/_exports/sdk-react.ts +30 -18
  9. package/src/components/SDKProvider.test.tsx +173 -32
  10. package/src/components/SDKProvider.tsx +87 -28
  11. package/src/components/SanityApp.test.tsx +68 -12
  12. package/src/components/SanityApp.tsx +93 -87
  13. package/src/components/auth/AuthBoundary.recovery.test.tsx +86 -0
  14. package/src/components/auth/AuthBoundary.test.tsx +7 -7
  15. package/src/components/auth/AuthBoundary.tsx +29 -6
  16. package/src/components/auth/DashboardAccessRequest.tsx +37 -0
  17. package/src/components/auth/LoginError.test.tsx +191 -5
  18. package/src/components/auth/LoginError.tsx +104 -61
  19. package/src/components/errors/ChunkLoadError.test.tsx +59 -0
  20. package/src/components/errors/ChunkLoadError.tsx +56 -0
  21. package/src/components/errors/CorsErrorComponent.tsx +2 -2
  22. package/src/components/errors/chunkReloadStorage.ts +57 -0
  23. package/src/config/handles.ts +29 -27
  24. package/src/constants.ts +5 -0
  25. package/src/context/ComlinkTokenRefresh.test.tsx +10 -27
  26. package/src/context/ComlinkTokenRefresh.tsx +3 -5
  27. package/src/context/DefaultResourceContext.ts +3 -3
  28. package/src/context/OrganizationResourcesProvider.test.tsx +189 -0
  29. package/src/context/OrganizationResourcesProvider.tsx +111 -0
  30. package/src/context/PerspectiveContext.ts +3 -3
  31. package/src/context/ProjectContext.ts +15 -0
  32. package/src/context/ResourceProvider.test.tsx +93 -90
  33. package/src/context/ResourceProvider.tsx +62 -93
  34. package/src/context/SDKStudioContext.test.tsx +28 -33
  35. package/src/context/SanityInstanceProvider.test.tsx +100 -0
  36. package/src/context/SanityInstanceProvider.tsx +71 -0
  37. package/src/context/WorkbenchTokenRefresh.test.tsx +106 -0
  38. package/src/context/WorkbenchTokenRefresh.tsx +61 -0
  39. package/src/context/renderSanityApp.test.tsx +151 -49
  40. package/src/context/renderSanityApp.tsx +12 -8
  41. package/src/context/workbenchToken.ts +63 -0
  42. package/src/hooks/access/useCheckPermissions.test-d.ts +15 -0
  43. package/src/hooks/access/useCheckPermissions.test.tsx +53 -0
  44. package/src/hooks/access/useCheckPermissions.ts +24 -0
  45. package/src/hooks/agent/agentActions.test.tsx +2 -2
  46. package/src/hooks/agent/agentActions.ts +52 -72
  47. package/src/hooks/agent/useAgentResourceContext.ts +1 -1
  48. package/src/hooks/applications/useApplication.test-d.ts +30 -0
  49. package/src/hooks/applications/useApplication.ts +22 -0
  50. package/src/hooks/applications/useApplications.test-d.ts +31 -0
  51. package/src/hooks/applications/useApplications.ts +25 -0
  52. package/src/hooks/applications/useDeleteApplication.test-d.ts +12 -0
  53. package/src/hooks/applications/useDeleteApplication.ts +11 -0
  54. package/src/hooks/applications/useUpdateApplication.test-d.ts +12 -0
  55. package/src/hooks/applications/useUpdateApplication.ts +11 -0
  56. package/src/hooks/auth/useDashboardOrganizationId.test.tsx +2 -8
  57. package/src/hooks/auth/useVerifyOrgProjects.test.tsx +8 -32
  58. package/src/hooks/auth/useVerifyOrgProjects.tsx +13 -6
  59. package/src/hooks/client/useClient.test.tsx +1 -4
  60. package/src/hooks/client/useClient.ts +1 -0
  61. package/src/hooks/comlink/useFrameConnection.test.tsx +3 -2
  62. package/src/hooks/comlink/useFrameConnection.ts +1 -1
  63. package/src/hooks/comlink/useWindowConnection.test.tsx +4 -3
  64. package/src/hooks/comlink/useWindowConnection.ts +2 -3
  65. package/src/hooks/comments/useCommentActions.test.tsx +150 -0
  66. package/src/hooks/comments/useCommentActions.ts +109 -0
  67. package/src/hooks/comments/useCommentList.ts +79 -0
  68. package/src/hooks/comments/useCommentThreads.test.tsx +107 -0
  69. package/src/hooks/comments/useCommentThreads.ts +73 -0
  70. package/src/hooks/comments/useComments.test.tsx +242 -0
  71. package/src/hooks/comments/useComments.ts +59 -0
  72. package/src/hooks/context/useResource.test.tsx +32 -0
  73. package/src/hooks/context/useResource.ts +24 -0
  74. package/src/hooks/context/useSanityInstance.test.tsx +18 -1
  75. package/src/hooks/context/useSanityInstance.ts +6 -6
  76. package/src/hooks/dashboard/useManageFavorite.test.tsx +110 -46
  77. package/src/hooks/dashboard/useManageFavorite.ts +11 -17
  78. package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +5 -7
  79. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +2 -6
  80. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.test.ts +0 -2
  81. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.ts +2 -2
  82. package/src/hooks/dashboard/useWindowTitle.test.ts +244 -0
  83. package/src/hooks/dashboard/useWindowTitle.ts +114 -0
  84. package/src/hooks/datasets/useDatasets.test.tsx +116 -0
  85. package/src/hooks/datasets/useDatasets.ts +33 -13
  86. package/src/hooks/document/useApplyDocumentActions.test.tsx +48 -0
  87. package/src/hooks/document/useApplyDocumentActions.ts +66 -48
  88. package/src/hooks/document/useCreateDocument.test.tsx +83 -0
  89. package/src/hooks/document/useCreateDocument.ts +117 -0
  90. package/src/hooks/document/useDocument.test.tsx +37 -8
  91. package/src/hooks/document/useDocument.ts +131 -66
  92. package/src/hooks/document/useDocumentEvent.test.tsx +16 -4
  93. package/src/hooks/document/useDocumentEvent.ts +16 -10
  94. package/src/hooks/document/useDocumentPermissions.test.tsx +8 -7
  95. package/src/hooks/document/useDocumentPermissions.ts +71 -39
  96. package/src/hooks/document/useDocumentSyncStatus.test.ts +1 -0
  97. package/src/hooks/document/useDocumentSyncStatus.ts +13 -9
  98. package/src/hooks/document/useEditDocument.test.tsx +21 -14
  99. package/src/hooks/document/useEditDocument.ts +163 -17
  100. package/src/hooks/documents/useDocuments.test.tsx +64 -44
  101. package/src/hooks/documents/useDocuments.ts +31 -20
  102. package/src/hooks/helpers/createCallbackHook.test.tsx +13 -19
  103. package/src/hooks/helpers/createFetcherHook.test.tsx +180 -0
  104. package/src/hooks/helpers/createFetcherHook.ts +69 -0
  105. package/src/hooks/helpers/createMutationHook.test.tsx +125 -0
  106. package/src/hooks/helpers/createMutationHook.tsx +93 -0
  107. package/src/hooks/helpers/createStateSourceHook.test.tsx +10 -10
  108. package/src/hooks/helpers/createStateSourceHook.tsx +2 -1
  109. package/src/hooks/{document/useApplyDocumentActions.test.ts → helpers/useApplyActions.test.tsx} +92 -53
  110. package/src/hooks/helpers/useApplyActions.ts +68 -0
  111. package/src/hooks/helpers/useNormalizedResourceOptions.test.tsx +226 -0
  112. package/src/hooks/helpers/useNormalizedResourceOptions.ts +79 -48
  113. package/src/hooks/helpers/useResolvedProjectId.test.tsx +59 -0
  114. package/src/hooks/helpers/useResolvedProjectId.ts +35 -0
  115. package/src/hooks/helpers/useTrackHookUsage.ts +37 -0
  116. package/src/hooks/installations/useInstallation.test-d.ts +19 -0
  117. package/src/hooks/installations/useInstallation.ts +22 -0
  118. package/src/hooks/installations/useInstallations.test-d.ts +26 -0
  119. package/src/hooks/installations/useInstallations.ts +25 -0
  120. package/src/hooks/organizations/useOrganization.test-d.ts +60 -0
  121. package/src/hooks/organizations/useOrganization.test.ts +63 -0
  122. package/src/hooks/organizations/useOrganization.ts +34 -0
  123. package/src/hooks/organizations/useOrganizations.test-d.ts +68 -0
  124. package/src/hooks/organizations/useOrganizations.test.ts +63 -0
  125. package/src/hooks/organizations/useOrganizations.ts +39 -0
  126. package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +34 -27
  127. package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +35 -22
  128. package/src/hooks/presence/usePresence.test.tsx +0 -15
  129. package/src/hooks/presence/usePresence.ts +22 -20
  130. package/src/hooks/presence/usePresenceForDocument.test.tsx +141 -0
  131. package/src/hooks/presence/usePresenceForDocument.ts +104 -0
  132. package/src/hooks/presence/useReportPresence.test.tsx +202 -0
  133. package/src/hooks/presence/useReportPresence.ts +170 -0
  134. package/src/hooks/preview/useDocumentPreview.test.tsx +0 -1
  135. package/src/hooks/preview/useDocumentPreview.tsx +12 -14
  136. package/src/hooks/projection/useDocumentProjection.test.tsx +78 -9
  137. package/src/hooks/projection/useDocumentProjection.ts +81 -2
  138. package/src/hooks/projects/useProject.test-d.ts +56 -0
  139. package/src/hooks/projects/useProject.test.tsx +120 -0
  140. package/src/hooks/projects/useProject.ts +42 -43
  141. package/src/hooks/projects/useProjects.test-d.ts +60 -0
  142. package/src/hooks/projects/useProjects.test.ts +45 -98
  143. package/src/hooks/projects/useProjects.ts +17 -32
  144. package/src/hooks/query/useQuery.test.tsx +6 -5
  145. package/src/hooks/query/useQuery.ts +94 -17
  146. package/src/hooks/releases/useActiveReleases.test.tsx +13 -7
  147. package/src/hooks/releases/useActiveReleases.ts +28 -24
  148. package/src/hooks/releases/useAllReleases.test.tsx +93 -0
  149. package/src/hooks/releases/useAllReleases.ts +62 -0
  150. package/src/hooks/releases/useApplyReleaseActions.test.tsx +66 -0
  151. package/src/hooks/releases/useApplyReleaseActions.ts +82 -0
  152. package/src/hooks/releases/usePerspective.test.tsx +18 -10
  153. package/src/hooks/releases/usePerspective.ts +13 -17
  154. package/src/hooks/users/useUser.test.tsx +3 -9
  155. package/src/hooks/users/useUser.ts +4 -8
  156. package/src/hooks/users/useUsers.test.tsx +103 -7
  157. package/src/hooks/users/useUsers.ts +38 -5
  158. package/src/utils/resolveOrgResources.test.ts +111 -0
  159. package/src/utils/resolveOrgResources.ts +69 -0
  160. package/src/hooks/context/useDefaultResource.test.tsx +0 -25
  161. package/src/hooks/context/useDefaultResource.ts +0 -30
  162. package/src/hooks/dashboard/useDispatchIntent.test.ts +0 -254
  163. package/src/hooks/dashboard/useDispatchIntent.ts +0 -157
  164. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.test.ts +0 -107
  165. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.ts +0 -39
  166. package/src/hooks/datasets/useDatasets.test.ts +0 -66
  167. package/src/hooks/helpers/useNormalizedResourceOptions.test.ts +0 -65
  168. package/src/hooks/projects/useProject.test.ts +0 -79
@@ -1,19 +1,16 @@
1
1
  import {type Message} from '@sanity/comlink'
2
- import {
3
- type FavoriteStatusResponse,
4
- getFavoritesState,
5
- resolveFavoritesState,
6
- type SanityInstance,
7
- } from '@sanity/sdk'
2
+ import {type FavoriteStatusResponse, getFavoritesState, resolveFavoritesState} from '@sanity/sdk'
3
+ import {act, renderHook} from '@testing-library/react'
8
4
  import {BehaviorSubject} from 'rxjs'
9
- import {beforeEach, describe, expect, it, vi} from 'vitest'
5
+ import {beforeEach, describe, expect, it, type Mock, vi} from 'vitest'
10
6
 
11
- import {act, renderHook} from '../../../test/test-utils'
12
7
  import {ResourceProvider} from '../../context/ResourceProvider'
13
- import {useSanityInstance} from '../../hooks/context/useSanityInstance'
14
8
  import {useWindowConnection, type WindowConnection} from '../comlink/useWindowConnection'
15
9
  import {useManageFavorite} from './useManageFavorite'
16
10
 
11
+ type MockFetch = (type: string, data?: unknown, options?: unknown) => Promise<{success: boolean}>
12
+ type MockSendMessage = (type: string, data?: unknown) => void
13
+
17
14
  vi.mock(import('@sanity/sdk'), async (importOriginal) => {
18
15
  const actual = await importOriginal()
19
16
  return {
@@ -27,20 +24,15 @@ vi.mock('../comlink/useWindowConnection', () => ({
27
24
  useWindowConnection: vi.fn(),
28
25
  }))
29
26
 
30
- vi.mock('../../hooks/context/useSanityInstance', () => ({
31
- useSanityInstance: vi.fn(),
32
- }))
33
-
34
27
  describe('useManageFavorite', () => {
35
28
  let favoriteStatusSubject: BehaviorSubject<FavoriteStatusResponse>
36
- let mockFetch: ReturnType<typeof vi.fn>
37
- let mockSendMessage: ReturnType<typeof vi.fn>
29
+ let mockFetch: Mock<MockFetch>
30
+ let mockSendMessage: Mock<MockSendMessage>
38
31
 
39
32
  const mockDocumentHandle = {
40
33
  documentId: 'mock-id',
41
34
  documentType: 'mock-type',
42
35
  resourceType: 'studio' as const,
43
- resource: {projectId: 'test', dataset: 'test'},
44
36
  }
45
37
 
46
38
  beforeEach(() => {
@@ -67,32 +59,43 @@ describe('useManageFavorite', () => {
67
59
  })
68
60
 
69
61
  // Mock useWindowConnection
70
- mockFetch = vi.fn().mockResolvedValue({success: true})
71
- mockSendMessage = vi.fn()
72
- vi.mocked(useWindowConnection).mockImplementation(() => {
73
- return {
74
- fetch: (type: string, data?: unknown, options: unknown = {}) =>
75
- mockFetch(type, data, options),
76
- sendMessage: mockSendMessage,
77
- }
78
- })
62
+ mockFetch = vi.fn<MockFetch>().mockResolvedValue({success: true})
63
+ mockSendMessage = vi.fn<MockSendMessage>()
64
+ vi.mocked(useWindowConnection).mockImplementation(
65
+ () =>
66
+ ({
67
+ fetch: (type: string, data?: unknown, options: unknown = {}) =>
68
+ mockFetch(type, data, options),
69
+ sendMessage: mockSendMessage,
70
+ }) as unknown as WindowConnection<Message>,
71
+ )
79
72
  })
80
73
 
81
- vi.mocked(useSanityInstance).mockImplementation(() => ({}) as unknown as SanityInstance)
82
-
83
74
  afterEach(() => {
84
75
  favoriteStatusSubject.complete()
85
76
  vi.clearAllMocks()
86
77
  })
87
78
 
88
79
  it('should initialize with default states', () => {
89
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
80
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
81
+ wrapper: ({children}) => (
82
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
83
+ {children}
84
+ </ResourceProvider>
85
+ ),
86
+ })
90
87
 
91
88
  expect(result.current.isFavorited).toBe(false)
92
89
  })
93
90
 
94
91
  it('should handle favorite action and update state', async () => {
95
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
92
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
93
+ wrapper: ({children}) => (
94
+ <ResourceProvider projectId="test" dataset="test" fallback={null}>
95
+ {children}
96
+ </ResourceProvider>
97
+ ),
98
+ })
96
99
 
97
100
  expect(result.current.isFavorited).toBe(false)
98
101
 
@@ -121,7 +124,13 @@ describe('useManageFavorite', () => {
121
124
  })
122
125
 
123
126
  it('should handle unfavorite action and update state', async () => {
124
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
127
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
128
+ wrapper: ({children}) => (
129
+ <ResourceProvider projectId="test" dataset="test" fallback={null}>
130
+ {children}
131
+ </ResourceProvider>
132
+ ),
133
+ })
125
134
 
126
135
  // Set initial state to favorited
127
136
  await act(async () => {
@@ -156,7 +165,13 @@ describe('useManageFavorite', () => {
156
165
  it('should not update state if favorite action fails', async () => {
157
166
  mockFetch.mockResolvedValueOnce({success: false})
158
167
 
159
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
168
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
169
+ wrapper: ({children}) => (
170
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
171
+ {children}
172
+ </ResourceProvider>
173
+ ),
174
+ })
160
175
 
161
176
  expect(result.current.isFavorited).toBe(false)
162
177
 
@@ -176,7 +191,13 @@ describe('useManageFavorite', () => {
176
191
  throw new Error(errorMessage)
177
192
  })
178
193
 
179
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
194
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
195
+ wrapper: ({children}) => (
196
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
197
+ {children}
198
+ </ResourceProvider>
199
+ ),
200
+ })
180
201
 
181
202
  await act(async () => {
182
203
  await expect(result.current.favorite()).rejects.toThrow(errorMessage)
@@ -193,7 +214,7 @@ describe('useManageFavorite', () => {
193
214
  consoleErrorSpy.mockRestore()
194
215
  })
195
216
 
196
- it('should throw error when studio resource is missing resource', () => {
217
+ it('should throw error when studio resource is missing projectId or dataset', () => {
197
218
  const mockDocumentHandleWithoutProjectId = {
198
219
  documentId: 'mock-id',
199
220
  documentType: 'mock-type',
@@ -202,8 +223,10 @@ describe('useManageFavorite', () => {
202
223
 
203
224
  expect(() =>
204
225
  renderHook(() => useManageFavorite(mockDocumentHandleWithoutProjectId), {
205
- wrapper: ({children}: {children: React.ReactNode}) => (
206
- <ResourceProvider fallback={null}>{children}</ResourceProvider>
226
+ wrapper: ({children}) => (
227
+ <ResourceProvider projectId={undefined} dataset={undefined} fallback={null}>
228
+ {children}
229
+ </ResourceProvider>
207
230
  ),
208
231
  }),
209
232
  ).toThrow('projectId and dataset are required for studio resources')
@@ -215,12 +238,17 @@ describe('useManageFavorite', () => {
215
238
  documentType: 'mock-type',
216
239
  resourceType: 'media-library' as const,
217
240
  resourceId: undefined,
218
- resource: {mediaLibraryId: 'media-library-id'},
219
241
  }
220
242
 
221
- expect(() => renderHook(() => useManageFavorite(mockMediaDocumentHandle))).toThrow(
222
- 'resourceId is required for media-library and canvas resources',
223
- )
243
+ expect(() =>
244
+ renderHook(() => useManageFavorite(mockMediaDocumentHandle), {
245
+ wrapper: ({children}) => (
246
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
247
+ {children}
248
+ </ResourceProvider>
249
+ ),
250
+ }),
251
+ ).toThrow('resourceId is required for media-library and canvas resources')
224
252
  })
225
253
 
226
254
  it('should include schemaName in payload when provided', async () => {
@@ -228,7 +256,13 @@ describe('useManageFavorite', () => {
228
256
  ...mockDocumentHandle,
229
257
  schemaName: 'testSchema',
230
258
  }
231
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandleWithSchema))
259
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandleWithSchema), {
260
+ wrapper: ({children}) => (
261
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
262
+ {children}
263
+ </ResourceProvider>
264
+ ),
265
+ })
232
266
 
233
267
  await act(async () => {
234
268
  await result.current.favorite()
@@ -241,7 +275,7 @@ describe('useManageFavorite', () => {
241
275
  id: 'mock-id',
242
276
  type: 'mock-type',
243
277
  resource: {
244
- id: 'test.test',
278
+ id: 'test-project.test-dataset',
245
279
  type: 'studio',
246
280
  schemaName: 'testSchema',
247
281
  },
@@ -263,7 +297,13 @@ describe('useManageFavorite', () => {
263
297
  getCurrent: () => undefined,
264
298
  observable: favoriteStatusSubject.asObservable(),
265
299
  }))
266
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
300
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
301
+ wrapper: ({children}) => (
302
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
303
+ {children}
304
+ </ResourceProvider>
305
+ ),
306
+ })
267
307
  expect(result.current.isFavorited).toBe(false)
268
308
  })
269
309
 
@@ -272,7 +312,13 @@ describe('useManageFavorite', () => {
272
312
  fetch: undefined,
273
313
  sendMessage: mockSendMessage,
274
314
  } as unknown as WindowConnection<Message>)
275
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
315
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
316
+ wrapper: ({children}) => (
317
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
318
+ {children}
319
+ </ResourceProvider>
320
+ ),
321
+ })
276
322
  await act(async () => {
277
323
  await result.current.favorite()
278
324
  await result.current.unfavorite()
@@ -283,7 +329,13 @@ describe('useManageFavorite', () => {
283
329
  it('should do nothing if documentId is missing', async () => {
284
330
  const handle = {...mockDocumentHandle, documentId: undefined}
285
331
  // @ts-expect-error -- no access to ManageFavorite props type
286
- const {result} = renderHook(() => useManageFavorite(handle))
332
+ const {result} = renderHook(() => useManageFavorite(handle), {
333
+ wrapper: ({children}) => (
334
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
335
+ {children}
336
+ </ResourceProvider>
337
+ ),
338
+ })
287
339
  await act(async () => {
288
340
  await result.current.favorite()
289
341
  await result.current.unfavorite()
@@ -294,7 +346,13 @@ describe('useManageFavorite', () => {
294
346
  it('should do nothing if documentType is missing', async () => {
295
347
  const handle = {...mockDocumentHandle, documentType: undefined}
296
348
  // @ts-expect-error -- no access to ManageFavorite props type
297
- const {result} = renderHook(() => useManageFavorite(handle))
349
+ const {result} = renderHook(() => useManageFavorite(handle), {
350
+ wrapper: ({children}) => (
351
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
352
+ {children}
353
+ </ResourceProvider>
354
+ ),
355
+ })
298
356
  await act(async () => {
299
357
  await result.current.favorite()
300
358
  await result.current.unfavorite()
@@ -305,7 +363,13 @@ describe('useManageFavorite', () => {
305
363
  it('should do nothing if resourceType is missing', async () => {
306
364
  const handle = {...mockDocumentHandle, resourceType: undefined, resourceId: 'studio'}
307
365
  // @ts-expect-error -- no access to ManageFavorite props type
308
- const {result} = renderHook(() => useManageFavorite(handle))
366
+ const {result} = renderHook(() => useManageFavorite(handle), {
367
+ wrapper: ({children}) => (
368
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
369
+ {children}
370
+ </ResourceProvider>
371
+ ),
372
+ })
309
373
  await act(async () => {
310
374
  await result.current.favorite()
311
375
  await result.current.unfavorite()
@@ -7,16 +7,14 @@ import {
7
7
  type StudioResource,
8
8
  } from '@sanity/message-protocol'
9
9
  import {
10
+ type DocumentHandle,
10
11
  type FavoriteStatusResponse,
11
- type FrameMessage,
12
12
  getFavoritesState,
13
- isDatasetResource,
14
13
  resolveFavoritesState,
15
14
  } from '@sanity/sdk'
16
- import {useCallback, useContext, useMemo, useSyncExternalStore} from 'react'
15
+ import {type FrameMessage} from '@sanity/sdk/comlink'
16
+ import {useCallback, useMemo, useSyncExternalStore} from 'react'
17
17
 
18
- import {type DocumentHandle} from '../../config/handles'
19
- import {ResourceContext} from '../../context/DefaultResourceContext'
20
18
  import {useWindowConnection} from '../comlink/useWindowConnection'
21
19
  import {useSanityInstance} from '../context/useSanityInstance'
22
20
 
@@ -84,7 +82,8 @@ interface UseManageFavoriteProps extends DocumentHandle {
84
82
  export function useManageFavorite({
85
83
  documentId,
86
84
  documentType,
87
- resource: paramResource,
85
+ projectId: paramProjectId,
86
+ dataset: paramDataset,
88
87
  resourceId: paramResourceId,
89
88
  resourceType,
90
89
  schemaName,
@@ -94,11 +93,11 @@ export function useManageFavorite({
94
93
  connectTo: SDK_CHANNEL_NAME,
95
94
  })
96
95
  const instance = useSanityInstance()
97
- const contextResource = useContext(ResourceContext)
98
- const resource = paramResource ?? contextResource
99
- const datasetResource = resource && isDatasetResource(resource) ? resource : undefined
100
- const projectId = datasetResource?.projectId
101
- const dataset = datasetResource?.dataset
96
+ const {config} = instance
97
+ const instanceProjectId = config?.projectId
98
+ const instanceDataset = config?.dataset
99
+ const projectId = paramProjectId ?? instanceProjectId
100
+ const dataset = paramDataset ?? instanceDataset
102
101
 
103
102
  if (resourceType === 'studio' && (!projectId || !dataset)) {
104
103
  throw new Error('projectId and dataset are required for studio resources')
@@ -111,21 +110,16 @@ export function useManageFavorite({
111
110
  throw new Error('resourceId is required for media-library and canvas resources')
112
111
  }
113
112
 
114
- if (!resource) {
115
- throw new Error('resource is required')
116
- }
117
-
118
113
  // used for favoriteStore functions like getFavoritesState and resolveFavoritesState
119
114
  const context = useMemo(
120
115
  () => ({
121
116
  documentId,
122
117
  documentType,
123
- resource,
124
118
  resourceId,
125
119
  resourceType,
126
120
  schemaName,
127
121
  }),
128
- [documentId, documentType, resource, resourceId, resourceType, schemaName],
122
+ [documentId, documentType, resourceId, resourceType, schemaName],
129
123
  )
130
124
 
131
125
  // Get favorite status using StateSource
@@ -33,7 +33,8 @@ describe('useNavigateToStudioDocument', () => {
33
33
  const mockDocumentHandle: DocumentHandle = {
34
34
  documentId: 'doc123',
35
35
  documentType: 'article',
36
- resource: {projectId: 'project1', dataset: 'dataset1'},
36
+ projectId: 'project1',
37
+ dataset: 'dataset1',
37
38
  }
38
39
 
39
40
  const mockWorkspace = {
@@ -108,13 +109,13 @@ describe('useNavigateToStudioDocument', () => {
108
109
  consoleSpy.mockRestore()
109
110
  })
110
111
 
111
- it('warns and does not navigate when resource is not a dataset resource', () => {
112
+ it('warns and does not navigate when projectId or dataset is missing', () => {
112
113
  const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
113
114
 
114
115
  const incompleteDocumentHandle: DocumentHandle = {
115
116
  documentId: 'doc123',
116
117
  documentType: 'article',
117
- resource: {mediaLibraryId: 'ml123'},
118
+ // missing projectId and dataset
118
119
  }
119
120
 
120
121
  const {result} = renderHook(() => useNavigateToStudioDocument(incompleteDocumentHandle))
@@ -196,11 +197,8 @@ describe('useNavigateToStudioDocument', () => {
196
197
 
197
198
  result.current.navigateToStudioDocument()
198
199
 
199
- const resource = mockDocumentHandle.resource
200
- const projectId = 'projectId' in resource ? resource.projectId : undefined
201
- const dataset = 'dataset' in resource ? resource.dataset : undefined
202
200
  expect(consoleSpy).toHaveBeenCalledWith(
203
- `No workspace found for document with projectId: ${projectId} and dataset: ${dataset} or with preferred studio url: ${preferredUrl}`,
201
+ `No workspace found for document with projectId: ${mockDocumentHandle.projectId} and dataset: ${mockDocumentHandle.dataset} or with preferred studio url: ${preferredUrl}`,
204
202
  )
205
203
  expect(mockSendMessage).not.toHaveBeenCalled()
206
204
 
@@ -1,8 +1,7 @@
1
1
  import {type Bridge, SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'
2
- import {isDatasetResource} from '@sanity/sdk'
2
+ import {type DocumentHandle} from '@sanity/sdk'
3
3
  import {useCallback} from 'react'
4
4
 
5
- import {type DocumentHandle} from '../../config/handles'
6
5
  import {useWindowConnection} from '../comlink/useWindowConnection'
7
6
  import {
8
7
  type DashboardResource,
@@ -72,10 +71,7 @@ export function useNavigateToStudioDocument(
72
71
  })
73
72
 
74
73
  const navigateToStudioDocument = useCallback(() => {
75
- const {resource} = documentHandle
76
- const datasetResource = resource && isDatasetResource(resource) ? resource : undefined
77
- const projectId = datasetResource?.projectId
78
- const dataset = datasetResource?.dataset
74
+ const {projectId, dataset} = documentHandle
79
75
 
80
76
  if (!projectId || !dataset) {
81
77
  // eslint-disable-next-line no-console
@@ -15,7 +15,6 @@ describe('useRecordDocumentHistoryEvent', () => {
15
15
  documentType: 'mock-type',
16
16
  resourceType: 'studio' as const,
17
17
  resourceId: 'mock-resource-id',
18
- resource: {projectId: 'mock-project-id', dataset: 'mock-dataset'},
19
18
  }
20
19
 
21
20
  beforeEach(() => {
@@ -63,7 +62,6 @@ describe('useRecordDocumentHistoryEvent', () => {
63
62
  documentType: 'mock-type',
64
63
  resourceType: 'media-library' as const,
65
64
  resourceId: undefined,
66
- resource: {mediaLibraryId: 'mock-media-library-id'},
67
65
  }
68
66
 
69
67
  expect(() => renderHook(() => useRecordDocumentHistoryEvent(mockMediaDocumentHandle))).toThrow(
@@ -6,10 +6,10 @@ import {
6
6
  SDK_NODE_NAME,
7
7
  type StudioResource,
8
8
  } from '@sanity/message-protocol'
9
- import {type FrameMessage} from '@sanity/sdk'
9
+ import {type DocumentHandle} from '@sanity/sdk'
10
+ import {type FrameMessage} from '@sanity/sdk/comlink'
10
11
  import {useCallback} from 'react'
11
12
 
12
- import {type DocumentHandle} from '../../config/handles'
13
13
  import {useWindowConnection} from '../comlink/useWindowConnection'
14
14
 
15
15
  interface DocumentInteractionHistory {