@sanity/sdk-react 2.8.0 → 3.0.0-rc.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 (87) hide show
  1. package/README.md +125 -63
  2. package/dist/index.d.ts +381 -571
  3. package/dist/index.js +435 -366
  4. package/dist/index.js.map +1 -1
  5. package/package.json +7 -9
  6. package/src/_exports/index.ts +4 -0
  7. package/src/_exports/sdk-react.ts +16 -0
  8. package/src/components/SDKProvider.test.tsx +23 -58
  9. package/src/components/SDKProvider.tsx +38 -30
  10. package/src/components/SanityApp.test.tsx +12 -68
  11. package/src/components/SanityApp.tsx +88 -65
  12. package/src/components/auth/AuthBoundary.test.tsx +8 -26
  13. package/src/components/auth/LoginError.tsx +5 -5
  14. package/src/config/handles.ts +53 -0
  15. package/src/context/ComlinkTokenRefresh.test.tsx +27 -10
  16. package/src/context/DefaultResourceContext.ts +10 -0
  17. package/src/context/PerspectiveContext.ts +12 -0
  18. package/src/context/ResourceProvider.test.tsx +99 -19
  19. package/src/context/ResourceProvider.tsx +103 -37
  20. package/src/context/ResourcesContext.tsx +7 -0
  21. package/src/context/SDKStudioContext.test.tsx +33 -28
  22. package/src/context/SDKStudioContext.ts +6 -0
  23. package/src/context/renderSanityApp.test.tsx +49 -151
  24. package/src/context/renderSanityApp.tsx +8 -12
  25. package/src/hooks/agent/agentActions.test.tsx +1 -1
  26. package/src/hooks/agent/agentActions.ts +56 -19
  27. package/src/hooks/auth/useDashboardOrganizationId.test.tsx +8 -2
  28. package/src/hooks/auth/useVerifyOrgProjects.test.tsx +32 -8
  29. package/src/hooks/client/useClient.test.tsx +4 -1
  30. package/src/hooks/client/useClient.ts +0 -1
  31. package/src/hooks/context/useDefaultResource.test.tsx +25 -0
  32. package/src/hooks/context/useDefaultResource.ts +30 -0
  33. package/src/hooks/context/useSanityInstance.test.tsx +2 -140
  34. package/src/hooks/context/useSanityInstance.ts +9 -53
  35. package/src/hooks/dashboard/useDispatchIntent.test.ts +24 -15
  36. package/src/hooks/dashboard/useDispatchIntent.ts +7 -7
  37. package/src/hooks/dashboard/useManageFavorite.test.tsx +34 -94
  38. package/src/hooks/dashboard/useManageFavorite.ts +16 -10
  39. package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +7 -5
  40. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +6 -2
  41. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.test.ts +2 -0
  42. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.ts +2 -1
  43. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.test.ts +17 -38
  44. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.ts +12 -19
  45. package/src/hooks/datasets/useDatasets.test.ts +8 -22
  46. package/src/hooks/datasets/useDatasets.ts +8 -16
  47. package/src/hooks/document/useApplyDocumentActions.test.ts +98 -52
  48. package/src/hooks/document/useApplyDocumentActions.ts +35 -37
  49. package/src/hooks/document/useDocument.test.tsx +8 -37
  50. package/src/hooks/document/useDocument.ts +78 -129
  51. package/src/hooks/document/useDocumentEvent.test.tsx +7 -19
  52. package/src/hooks/document/useDocumentEvent.ts +21 -19
  53. package/src/hooks/document/useDocumentPermissions.test.tsx +75 -84
  54. package/src/hooks/document/useDocumentPermissions.ts +41 -28
  55. package/src/hooks/document/useDocumentSyncStatus.test.ts +13 -3
  56. package/src/hooks/document/useDocumentSyncStatus.ts +19 -14
  57. package/src/hooks/document/useEditDocument.test.tsx +28 -70
  58. package/src/hooks/document/useEditDocument.ts +29 -149
  59. package/src/hooks/documents/useDocuments.test.tsx +44 -64
  60. package/src/hooks/documents/useDocuments.ts +19 -25
  61. package/src/hooks/helpers/createCallbackHook.test.tsx +19 -13
  62. package/src/hooks/helpers/createStateSourceHook.test.tsx +10 -10
  63. package/src/hooks/helpers/createStateSourceHook.tsx +2 -4
  64. package/src/hooks/helpers/useNormalizedResourceOptions.test.ts +65 -0
  65. package/src/hooks/helpers/useNormalizedResourceOptions.ts +127 -0
  66. package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +27 -34
  67. package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +19 -20
  68. package/src/hooks/presence/usePresence.test.tsx +71 -9
  69. package/src/hooks/presence/usePresence.ts +28 -3
  70. package/src/hooks/preview/useDocumentPreview.test.tsx +85 -193
  71. package/src/hooks/preview/useDocumentPreview.tsx +42 -62
  72. package/src/hooks/projection/useDocumentProjection.test.tsx +9 -37
  73. package/src/hooks/projection/useDocumentProjection.ts +9 -82
  74. package/src/hooks/projects/useProject.test.ts +1 -2
  75. package/src/hooks/projects/useProject.ts +7 -8
  76. package/src/hooks/query/useQuery.test.tsx +5 -6
  77. package/src/hooks/query/useQuery.ts +12 -91
  78. package/src/hooks/releases/useActiveReleases.test.tsx +2 -2
  79. package/src/hooks/releases/useActiveReleases.ts +25 -13
  80. package/src/hooks/releases/usePerspective.test.tsx +9 -17
  81. package/src/hooks/releases/usePerspective.ts +29 -18
  82. package/src/hooks/users/useUser.test.tsx +9 -3
  83. package/src/hooks/users/useUser.ts +1 -1
  84. package/src/hooks/users/useUsers.test.tsx +5 -2
  85. package/src/hooks/users/useUsers.ts +1 -1
  86. package/src/context/SourcesContext.tsx +0 -7
  87. package/src/hooks/helpers/useNormalizedSourceOptions.ts +0 -85
@@ -1,4 +1,4 @@
1
- import {type SanityConfig} from '@sanity/sdk'
1
+ import {type DocumentResource} from '@sanity/sdk'
2
2
  import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
3
3
 
4
4
  import {SanityApp} from '../components/SanityApp'
@@ -26,6 +26,7 @@ vi.mock('react-dom/client', () => ({
26
26
 
27
27
  describe('renderSanityApp', () => {
28
28
  let rootElement: HTMLElement | null
29
+ let namedResources: Record<string, DocumentResource>
29
30
 
30
31
  beforeEach(() => {
31
32
  vi.clearAllMocks()
@@ -34,6 +35,9 @@ describe('renderSanityApp', () => {
34
35
  mockCreateRoot.mockClear()
35
36
  rootElement = document.createElement('div')
36
37
  document.body.appendChild(rootElement)
38
+ namedResources = {
39
+ default: {projectId: 'test-project', dataset: 'production'},
40
+ }
37
41
  })
38
42
 
39
43
  afterEach(() => {
@@ -44,53 +48,44 @@ describe('renderSanityApp', () => {
44
48
  })
45
49
 
46
50
  it('throws error when rootElement is null', () => {
47
- const namedSources = {
48
- main: {projectId: 'test-project', dataset: 'production'},
49
- }
50
-
51
- expect(() => renderSanityApp(null, namedSources, {}, <div>Test</div>)).toThrowError(
51
+ expect(() => renderSanityApp(null, namedResources, {}, <div>Test</div>)).toThrowError(
52
52
  'Missing root element to mount application into',
53
53
  )
54
54
  })
55
55
 
56
56
  it('creates root with the provided element', () => {
57
- const namedSources = {
58
- main: {projectId: 'test-project', dataset: 'production'},
59
- }
60
-
61
- renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
57
+ renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
62
58
 
63
59
  expect(mockCreateRoot).toHaveBeenCalledWith(rootElement)
64
60
  expect(mockCreateRoot).toHaveBeenCalledTimes(1)
65
61
  })
66
62
 
67
- it('converts namedSources object to array of configs', () => {
68
- const namedSources = {
69
- main: {projectId: 'project-1', dataset: 'production'},
63
+ it('merges namedResources into a single config and resources map', () => {
64
+ namedResources = {
65
+ default: {projectId: 'project-1', dataset: 'production'},
70
66
  secondary: {projectId: 'project-2', dataset: 'staging'},
71
67
  }
72
68
 
73
- renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
69
+ renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
74
70
 
75
71
  expect(mockRender).toHaveBeenCalledTimes(1)
76
72
  const renderCall = mockRender.mock.calls[0][0]
77
73
  expect(renderCall).toBeDefined()
78
74
 
79
- // The renderCall is the SanityApp component directly when not using StrictMode
80
75
  expect(renderCall.type).toBe(SanityApp)
81
- expect(renderCall.props.config).toEqual([
82
- {projectId: 'project-1', dataset: 'production'},
83
- {projectId: 'project-2', dataset: 'staging'},
84
- ])
76
+ // config has no resource fields
77
+ expect(renderCall.props.config).toEqual(undefined)
78
+ // resources map is populated from named entries
79
+ expect(renderCall.props.resources).toEqual({
80
+ default: {projectId: 'project-1', dataset: 'production'},
81
+ secondary: {projectId: 'project-2', dataset: 'staging'},
82
+ })
85
83
  })
86
84
 
87
85
  it('renders without StrictMode when reactStrictMode is false', () => {
88
- const namedSources = {
89
- main: {projectId: 'test-project', dataset: 'production'},
90
- }
91
86
  const children = <div>Test Children</div>
92
87
 
93
- renderSanityApp(rootElement, namedSources, {reactStrictMode: false}, children)
88
+ renderSanityApp(rootElement, namedResources, {reactStrictMode: false}, children)
94
89
 
95
90
  expect(mockRender).toHaveBeenCalledTimes(1)
96
91
  const renderCall = mockRender.mock.calls[0][0]
@@ -101,12 +96,9 @@ describe('renderSanityApp', () => {
101
96
  })
102
97
 
103
98
  it('renders without StrictMode by default', () => {
104
- const namedSources = {
105
- main: {projectId: 'test-project', dataset: 'production'},
106
- }
107
99
  const children = <div>Test Children</div>
108
100
 
109
- renderSanityApp(rootElement, namedSources, {}, children)
101
+ renderSanityApp(rootElement, namedResources, {}, children)
110
102
 
111
103
  expect(mockRender).toHaveBeenCalledTimes(1)
112
104
  const renderCall = mockRender.mock.calls[0][0]
@@ -117,17 +109,14 @@ describe('renderSanityApp', () => {
117
109
  })
118
110
 
119
111
  it('renders with StrictMode when reactStrictMode is true', () => {
120
- const namedSources = {
121
- main: {projectId: 'test-project', dataset: 'production'},
122
- }
123
112
  const children = <div>Test Children</div>
124
113
 
125
- renderSanityApp(rootElement, namedSources, {reactStrictMode: true}, children)
114
+ renderSanityApp(rootElement, namedResources, {reactStrictMode: true}, children)
126
115
 
127
116
  expect(mockRender).toHaveBeenCalledTimes(1)
128
117
  const renderCall = mockRender.mock.calls[0][0]
129
118
 
130
- // Should have StrictMode wrapper (StrictMode is a Symbol in React 18)
119
+ // Should have StrictMode wrapper (StrictMode is a Symbol)
131
120
  expect(renderCall.type).toBeDefined()
132
121
  expect(renderCall.type.toString()).toContain('Symbol')
133
122
  const strictModeChild = renderCall.props.children
@@ -136,11 +125,7 @@ describe('renderSanityApp', () => {
136
125
  })
137
126
 
138
127
  it('passes loading fallback to SanityApp', () => {
139
- const namedSources = {
140
- main: {projectId: 'test-project', dataset: 'production'},
141
- }
142
-
143
- renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
128
+ renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
144
129
 
145
130
  expect(mockRender).toHaveBeenCalledTimes(1)
146
131
  const renderCall = mockRender.mock.calls[0][0]
@@ -151,103 +136,51 @@ describe('renderSanityApp', () => {
151
136
  })
152
137
 
153
138
  it('returns an unmount function', () => {
154
- const namedSources = {
155
- main: {projectId: 'test-project', dataset: 'production'},
156
- }
157
-
158
- const unmount = renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
139
+ const unmount = renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
159
140
 
160
141
  expect(typeof unmount).toBe('function')
161
142
  })
162
143
 
163
144
  it('calls root.unmount when unmount function is invoked', () => {
164
- const namedSources = {
165
- main: {projectId: 'test-project', dataset: 'production'},
166
- }
167
-
168
- const unmount = renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
145
+ const unmount = renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
169
146
 
170
147
  expect(mockUnmount).not.toHaveBeenCalled()
171
148
  unmount()
172
149
  expect(mockUnmount).toHaveBeenCalledTimes(1)
173
150
  })
174
151
 
175
- it('handles empty namedSources object', () => {
176
- const namedSources = {}
152
+ it('handles empty namedResources object', () => {
153
+ namedResources = {}
177
154
 
178
- renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
155
+ renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
179
156
 
180
157
  expect(mockRender).toHaveBeenCalledTimes(1)
181
158
  const renderCall = mockRender.mock.calls[0][0]
182
159
  const sanityAppElement = renderCall
183
160
 
184
161
  expect(sanityAppElement.type).toBe(SanityApp)
185
- expect(sanityAppElement.props.config).toEqual([])
162
+ expect(sanityAppElement.props.config).toEqual(undefined)
163
+ expect(sanityAppElement.props.resources).toEqual({})
186
164
  })
187
165
 
188
166
  it('handles single namedSource', () => {
189
- const namedSources = {
190
- main: {projectId: 'test-project', dataset: 'production'},
167
+ const namedResource = {
168
+ default: {projectId: 'test-project', dataset: 'production'},
191
169
  }
192
-
193
- renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
170
+ renderSanityApp(rootElement, namedResource, {}, <div>Test</div>)
194
171
 
195
172
  expect(mockRender).toHaveBeenCalledTimes(1)
196
173
  const renderCall = mockRender.mock.calls[0][0]
197
174
  const sanityAppElement = renderCall
198
175
 
199
176
  expect(sanityAppElement.type).toBe(SanityApp)
200
- expect(sanityAppElement.props.config).toEqual([
201
- {projectId: 'test-project', dataset: 'production'},
202
- ])
203
- })
204
-
205
- it('handles multiple namedSources', () => {
206
- const namedSources = {
207
- main: {projectId: 'project-1', dataset: 'production'},
208
- blog: {projectId: 'project-2', dataset: 'staging'},
209
- ecommerce: {projectId: 'project-3', dataset: 'development'},
210
- }
211
-
212
- renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
213
-
214
- expect(mockRender).toHaveBeenCalledTimes(1)
215
- const renderCall = mockRender.mock.calls[0][0]
216
- const sanityAppElement = renderCall
217
-
218
- expect(sanityAppElement.type).toBe(SanityApp)
219
- expect(sanityAppElement.props.config).toHaveLength(3)
220
- expect(sanityAppElement.props.config).toEqual([
221
- {projectId: 'project-1', dataset: 'production'},
222
- {projectId: 'project-2', dataset: 'staging'},
223
- {projectId: 'project-3', dataset: 'development'},
224
- ])
225
- })
226
-
227
- it('preserves order of namedSources in config array', () => {
228
- const namedSources = {
229
- z: {projectId: 'project-z', dataset: 'z-dataset'},
230
- a: {projectId: 'project-a', dataset: 'a-dataset'},
231
- m: {projectId: 'project-m', dataset: 'm-dataset'},
232
- }
233
-
234
- renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
235
-
236
- const renderCall = mockRender.mock.calls[0][0]
237
- const sanityAppElement = renderCall
238
-
239
- // Object.values preserves insertion order in modern JS
240
- expect(sanityAppElement.props.config).toEqual([
241
- {projectId: 'project-z', dataset: 'z-dataset'},
242
- {projectId: 'project-a', dataset: 'a-dataset'},
243
- {projectId: 'project-m', dataset: 'm-dataset'},
244
- ])
177
+ expect(sanityAppElement.props.config).toEqual(undefined)
178
+ expect(sanityAppElement.props.resources).toEqual({
179
+ default: {projectId: 'test-project', dataset: 'production'},
180
+ })
245
181
  })
246
182
 
247
183
  it('passes children to SanityApp', () => {
248
- const namedSources = {
249
- main: {projectId: 'test-project', dataset: 'production'},
250
- }
251
184
  const children = (
252
185
  <div>
253
186
  <h1>Test App</h1>
@@ -255,7 +188,7 @@ describe('renderSanityApp', () => {
255
188
  </div>
256
189
  )
257
190
 
258
- renderSanityApp(rootElement, namedSources, {}, children)
191
+ renderSanityApp(rootElement, namedResources, {}, children)
259
192
 
260
193
  const renderCall = mockRender.mock.calls[0][0]
261
194
  const sanityAppElement = renderCall
@@ -264,12 +197,8 @@ describe('renderSanityApp', () => {
264
197
  })
265
198
 
266
199
  it('works with different types of children', () => {
267
- const namedSources = {
268
- main: {projectId: 'test-project', dataset: 'production'},
269
- }
270
-
271
200
  // Test with string children
272
- renderSanityApp(rootElement, namedSources, {}, 'String child')
201
+ renderSanityApp(rootElement, namedResources, {}, 'String child')
273
202
 
274
203
  let renderCall = mockRender.mock.calls[0][0]
275
204
  let sanityAppElement = renderCall
@@ -278,7 +207,7 @@ describe('renderSanityApp', () => {
278
207
 
279
208
  // Test with null children
280
209
  mockRender.mockClear()
281
- renderSanityApp(rootElement, namedSources, {}, null)
210
+ renderSanityApp(rootElement, namedResources, {}, null)
282
211
 
283
212
  renderCall = mockRender.mock.calls[0][0]
284
213
  sanityAppElement = renderCall
@@ -288,7 +217,7 @@ describe('renderSanityApp', () => {
288
217
  // Test with array of children
289
218
  mockRender.mockClear()
290
219
  const arrayChildren = [<div key="1">Child 1</div>, <div key="2">Child 2</div>]
291
- renderSanityApp(rootElement, namedSources, {}, arrayChildren)
220
+ renderSanityApp(rootElement, namedResources, {}, arrayChildren)
292
221
 
293
222
  renderCall = mockRender.mock.calls[0][0]
294
223
  sanityAppElement = renderCall
@@ -296,50 +225,19 @@ describe('renderSanityApp', () => {
296
225
  expect(sanityAppElement.props.children).toEqual(arrayChildren)
297
226
  })
298
227
 
299
- it('integrates with StrictMode and passes all props correctly', () => {
300
- const namedSources = {
301
- main: {
302
- projectId: 'test-project',
303
- dataset: 'production',
304
- apiVersion: '2023-01-01',
305
- } as SanityConfig,
306
- secondary: {
307
- projectId: 'test-project-2',
308
- dataset: 'staging',
309
- } as SanityConfig,
310
- }
311
- const children = <div>App Content</div>
312
-
313
- renderSanityApp(rootElement, namedSources, {reactStrictMode: true}, children)
314
-
315
- const renderCall = mockRender.mock.calls[0][0]
316
-
317
- // Verify StrictMode wrapper (StrictMode is a Symbol in React 18)
318
- expect(renderCall.type).toBeDefined()
319
- expect(renderCall.type.toString()).toContain('Symbol')
320
-
321
- // Verify SanityApp is inside StrictMode
322
- const strictModeChild = renderCall.props.children
323
- expect(strictModeChild.type).toBe(SanityApp)
324
-
325
- // Verify all props are passed correctly
326
- expect(strictModeChild.props.config).toEqual([
327
- {projectId: 'test-project', dataset: 'production', apiVersion: '2023-01-01'},
328
- {projectId: 'test-project-2', dataset: 'staging'},
329
- ])
330
- expect(strictModeChild.props.fallback).toEqual(<div>Loading...</div>)
331
- expect(strictModeChild.props.children).toEqual(children)
332
- })
333
-
334
228
  it('can be called multiple times with different roots', () => {
335
229
  const rootElement2 = document.createElement('div')
336
230
  document.body.appendChild(rootElement2)
337
231
 
338
- const namedSources1 = {main: {projectId: 'project-1', dataset: 'production'}}
339
- const namedSources2 = {main: {projectId: 'project-2', dataset: 'staging'}}
232
+ const namedResources1 = {
233
+ default: {projectId: 'project-1', dataset: 'production'},
234
+ }
235
+ const namedResources2 = {
236
+ default: {projectId: 'project-2', dataset: 'staging'},
237
+ }
340
238
 
341
- const unmount1 = renderSanityApp(rootElement, namedSources1, {}, <div>App 1</div>)
342
- const unmount2 = renderSanityApp(rootElement2, namedSources2, {}, <div>App 2</div>)
239
+ const unmount1 = renderSanityApp(rootElement, namedResources1, {}, <div>App 1</div>)
240
+ const unmount2 = renderSanityApp(rootElement2, namedResources2, {}, <div>App 2</div>)
343
241
 
344
242
  expect(mockCreateRoot).toHaveBeenCalledTimes(2)
345
243
  expect(mockCreateRoot).toHaveBeenNthCalledWith(1, rootElement)
@@ -1,4 +1,4 @@
1
- import {type SanityConfig} from '@sanity/sdk'
1
+ import {type DocumentResource} from '@sanity/sdk'
2
2
  import {StrictMode} from 'react'
3
3
  import {createRoot} from 'react-dom/client'
4
4
 
@@ -8,16 +8,14 @@ interface RenderSanitySDKAppOptions {
8
8
  reactStrictMode?: boolean
9
9
  }
10
10
 
11
- /** In-flight CLI PR is using named sources since it's aspirational.
12
- * We can transform the shape in this function until it's finalized.
13
- */
14
- interface NamedSources {
15
- [key: string]: SanityConfig
11
+ interface NamedResources {
12
+ [key: string]: DocumentResource
16
13
  }
14
+
17
15
  /** @internal */
18
16
  export function renderSanityApp(
19
17
  rootElement: HTMLElement | null,
20
- namedSources: NamedSources,
18
+ namedResources: NamedResources,
21
19
  options: RenderSanitySDKAppOptions,
22
20
  children: React.ReactNode,
23
21
  ): () => void {
@@ -27,18 +25,16 @@ export function renderSanityApp(
27
25
  const {reactStrictMode = false} = options
28
26
 
29
27
  const root = createRoot(rootElement)
30
- const config = Object.values(namedSources)
31
-
32
28
  root.render(
33
29
  reactStrictMode ? (
34
30
  <StrictMode>
35
- {/* TODO: think about a loading component we want to be "universal" */}
36
- <SanityApp config={config} fallback={<div>Loading...</div>}>
31
+ {/* TODO: we should find some way to pass top-level config, like auth, from a flatfile */}
32
+ <SanityApp resources={namedResources} fallback={<div>Loading...</div>}>
37
33
  {children}
38
34
  </SanityApp>
39
35
  </StrictMode>
40
36
  ) : (
41
- <SanityApp config={config} fallback={<div>Loading...</div>}>
37
+ <SanityApp resources={namedResources} fallback={<div>Loading...</div>}>
42
38
  {children}
43
39
  </SanityApp>
44
40
  ),
@@ -26,7 +26,7 @@ vi.mock('@sanity/sdk', async (orig) => {
26
26
 
27
27
  describe('agent action hooks', () => {
28
28
  const wrapper = ({children}: {children: React.ReactNode}) => (
29
- <ResourceProvider projectId="p" dataset="d" fallback={null}>
29
+ <ResourceProvider resource={{projectId: 'p', dataset: 'd'}} fallback={null}>
30
30
  {children}
31
31
  </ResourceProvider>
32
32
  )
@@ -7,15 +7,23 @@ import {
7
7
  agentPrompt,
8
8
  type AgentPromptOptions,
9
9
  type AgentPromptResult,
10
+ type AgentResourceOptions,
10
11
  agentTransform,
11
12
  type AgentTransformOptions,
12
13
  agentTranslate,
13
14
  type AgentTranslateOptions,
14
15
  type SanityInstance,
15
16
  } from '@sanity/sdk'
17
+ import {useCallback, useContext} from 'react'
16
18
  import {firstValueFrom} from 'rxjs'
17
19
 
18
- import {createCallbackHook} from '../helpers/createCallbackHook'
20
+ import {ResourceContext} from '../../context/DefaultResourceContext'
21
+ import {ResourcesContext} from '../../context/ResourcesContext'
22
+ import {useSanityInstance} from '../context/useSanityInstance'
23
+ import {
24
+ normalizeResourceOptions,
25
+ type WithResourceNameSupport,
26
+ } from '../helpers/useNormalizedResourceOptions'
19
27
 
20
28
  interface Subscription {
21
29
  unsubscribe(): void
@@ -36,6 +44,26 @@ interface Subscribable<T> {
36
44
  ): Subscription
37
45
  }
38
46
 
47
+ function useAgentCallback<TOptions extends object, TReturn>(
48
+ action: (instance: SanityInstance, options: TOptions & AgentResourceOptions) => TReturn,
49
+ ): (options: TOptions & WithResourceNameSupport<AgentResourceOptions>) => TReturn {
50
+ const instance = useSanityInstance()
51
+ const resources = useContext(ResourcesContext)
52
+ const contextResource = useContext(ResourceContext)
53
+ return useCallback(
54
+ (options: TOptions & WithResourceNameSupport<AgentResourceOptions>) => {
55
+ const {resource} = normalizeResourceOptions(
56
+ {resource: options.resource, resourceName: options.resourceName},
57
+ resources,
58
+ contextResource,
59
+ )
60
+ const {resourceName: _, ...rest} = options
61
+ return action(instance, {...rest, resource} as TOptions & AgentResourceOptions)
62
+ },
63
+ [instance, resources, contextResource, action],
64
+ )
65
+ }
66
+
39
67
  /**
40
68
  * @alpha
41
69
  * Generates content for a document (or specific fields) via Sanity Agent Actions.
@@ -103,10 +131,11 @@ interface Subscribable<T> {
103
131
  *
104
132
  * @category Agent Actions
105
133
  */
106
- export const useAgentGenerate: () => (options: AgentGenerateOptions) => Subscribable<unknown> =
107
- createCallbackHook(agentGenerate) as unknown as () => (
108
- options: AgentGenerateOptions,
109
- ) => Subscribable<unknown>
134
+ export function useAgentGenerate(): (
135
+ options: AgentGenerateOptions & WithResourceNameSupport<AgentResourceOptions>,
136
+ ) => Subscribable<unknown> {
137
+ return useAgentCallback<AgentGenerateOptions, Subscribable<unknown>>(agentGenerate)
138
+ }
110
139
 
111
140
  /**
112
141
  * @alpha
@@ -179,10 +208,11 @@ export const useAgentGenerate: () => (options: AgentGenerateOptions) => Subscrib
179
208
  *
180
209
  * @category Agent Actions
181
210
  */
182
- export const useAgentTransform: () => (options: AgentTransformOptions) => Subscribable<unknown> =
183
- createCallbackHook(agentTransform) as unknown as () => (
184
- options: AgentTransformOptions,
185
- ) => Subscribable<unknown>
211
+ export function useAgentTransform(): (
212
+ options: AgentTransformOptions & WithResourceNameSupport<AgentResourceOptions>,
213
+ ) => Subscribable<unknown> {
214
+ return useAgentCallback<AgentTransformOptions, Subscribable<unknown>>(agentTransform)
215
+ }
186
216
 
187
217
  /**
188
218
  * @alpha
@@ -274,10 +304,11 @@ export const useAgentTransform: () => (options: AgentTransformOptions) => Subscr
274
304
  *
275
305
  * @category Agent Actions
276
306
  */
277
- export const useAgentTranslate: () => (options: AgentTranslateOptions) => Subscribable<unknown> =
278
- createCallbackHook(agentTranslate) as unknown as () => (
279
- options: AgentTranslateOptions,
280
- ) => Subscribable<unknown>
307
+ export function useAgentTranslate(): (
308
+ options: AgentTranslateOptions & WithResourceNameSupport<AgentResourceOptions>,
309
+ ) => Subscribable<unknown> {
310
+ return useAgentCallback<AgentTranslateOptions, Subscribable<unknown>>(agentTranslate)
311
+ }
281
312
 
282
313
  /**
283
314
  * @internal
@@ -285,7 +316,7 @@ export const useAgentTranslate: () => (options: AgentTranslateOptions) => Subscr
285
316
  */
286
317
  function promptAdapter(
287
318
  instance: SanityInstance,
288
- options: AgentPromptOptions,
319
+ options: AgentPromptOptions & AgentResourceOptions,
289
320
  ): Promise<AgentPromptResult> {
290
321
  return firstValueFrom(agentPrompt(instance, options))
291
322
  }
@@ -384,8 +415,11 @@ function promptAdapter(
384
415
  *
385
416
  * @category Agent Actions
386
417
  */
387
- export const useAgentPrompt: () => (options: AgentPromptOptions) => Promise<AgentPromptResult> =
388
- createCallbackHook(promptAdapter)
418
+ export function useAgentPrompt(): (
419
+ options: AgentPromptOptions & WithResourceNameSupport<AgentResourceOptions>,
420
+ ) => Promise<AgentPromptResult> {
421
+ return useAgentCallback<AgentPromptOptions, Promise<AgentPromptResult>>(promptAdapter)
422
+ }
389
423
 
390
424
  /**
391
425
  * @internal
@@ -393,7 +427,7 @@ export const useAgentPrompt: () => (options: AgentPromptOptions) => Promise<Agen
393
427
  */
394
428
  function patchAdapter(
395
429
  instance: SanityInstance,
396
- options: AgentPatchOptions,
430
+ options: AgentPatchOptions & AgentResourceOptions,
397
431
  ): Promise<AgentPatchResult> {
398
432
  return firstValueFrom(agentPatch(instance, options))
399
433
  }
@@ -547,5 +581,8 @@ function patchAdapter(
547
581
  *
548
582
  * @category Agent Actions
549
583
  */
550
- export const useAgentPatch: () => (options: AgentPatchOptions) => Promise<AgentPatchResult> =
551
- createCallbackHook(patchAdapter)
584
+ export function useAgentPatch(): (
585
+ options: AgentPatchOptions & WithResourceNameSupport<AgentResourceOptions>,
586
+ ) => Promise<AgentPatchResult> {
587
+ return useAgentCallback<AgentPatchOptions, Promise<AgentPatchResult>>(patchAdapter)
588
+ }
@@ -22,7 +22,10 @@ describe('useDashboardOrganizationId', () => {
22
22
 
23
23
  const {result} = renderHook(() => useDashboardOrganizationId(), {
24
24
  wrapper: ({children}) => (
25
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
25
+ <ResourceProvider
26
+ resource={{projectId: 'test-project', dataset: 'test-dataset'}}
27
+ fallback={null}
28
+ >
26
29
  {children}
27
30
  </ResourceProvider>
28
31
  ),
@@ -41,7 +44,10 @@ describe('useDashboardOrganizationId', () => {
41
44
 
42
45
  const {result} = renderHook(() => useDashboardOrganizationId(), {
43
46
  wrapper: ({children}) => (
44
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
47
+ <ResourceProvider
48
+ resource={{projectId: 'test-project', dataset: 'test-dataset'}}
49
+ fallback={null}
50
+ >
45
51
  {children}
46
52
  </ResourceProvider>
47
53
  ),
@@ -26,7 +26,10 @@ describe('useVerifyOrgProjects', () => {
26
26
  it('should return null and not observe state if disabled', () => {
27
27
  const {result} = renderHook(() => useVerifyOrgProjects(true, testProjectIds), {
28
28
  wrapper: ({children}) => (
29
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
29
+ <ResourceProvider
30
+ resource={{projectId: 'test-project', dataset: 'test-dataset'}}
31
+ fallback={null}
32
+ >
30
33
  {children}
31
34
  </ResourceProvider>
32
35
  ),
@@ -39,7 +42,10 @@ describe('useVerifyOrgProjects', () => {
39
42
  it('should return null and not observe state if projectIds is missing or empty', () => {
40
43
  const {result: resultUndefined} = renderHook(() => useVerifyOrgProjects(false, undefined), {
41
44
  wrapper: ({children}) => (
42
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
45
+ <ResourceProvider
46
+ resource={{projectId: 'test-project', dataset: 'test-dataset'}}
47
+ fallback={null}
48
+ >
43
49
  {children}
44
50
  </ResourceProvider>
45
51
  ),
@@ -49,7 +55,10 @@ describe('useVerifyOrgProjects', () => {
49
55
 
50
56
  const {result: resultEmpty} = renderHook(() => useVerifyOrgProjects(false, []), {
51
57
  wrapper: ({children}) => (
52
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
58
+ <ResourceProvider
59
+ resource={{projectId: 'test-project', dataset: 'test-dataset'}}
60
+ fallback={null}
61
+ >
53
62
  {children}
54
63
  </ResourceProvider>
55
64
  ),
@@ -64,7 +73,10 @@ describe('useVerifyOrgProjects', () => {
64
73
 
65
74
  const {result} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
66
75
  wrapper: ({children}) => (
67
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
76
+ <ResourceProvider
77
+ resource={{projectId: 'test-project', dataset: 'test-dataset'}}
78
+ fallback={null}
79
+ >
68
80
  {children}
69
81
  </ResourceProvider>
70
82
  ),
@@ -80,7 +92,10 @@ describe('useVerifyOrgProjects', () => {
80
92
 
81
93
  const {result} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
82
94
  wrapper: ({children}) => (
83
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
95
+ <ResourceProvider
96
+ resource={{projectId: 'test-project', dataset: 'test-dataset'}}
97
+ fallback={null}
98
+ >
84
99
  {children}
85
100
  </ResourceProvider>
86
101
  ),
@@ -102,7 +117,10 @@ describe('useVerifyOrgProjects', () => {
102
117
 
103
118
  const {result} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
104
119
  wrapper: ({children}) => (
105
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
120
+ <ResourceProvider
121
+ resource={{projectId: 'test-project', dataset: 'test-dataset'}}
122
+ fallback={null}
123
+ >
106
124
  {children}
107
125
  </ResourceProvider>
108
126
  ),
@@ -124,7 +142,10 @@ describe('useVerifyOrgProjects', () => {
124
142
 
125
143
  const {unmount} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
126
144
  wrapper: ({children}) => (
127
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
145
+ <ResourceProvider
146
+ resource={{projectId: 'test-project', dataset: 'test-dataset'}}
147
+ fallback={null}
148
+ >
128
149
  {children}
129
150
  </ResourceProvider>
130
151
  ),
@@ -152,7 +173,10 @@ describe('useVerifyOrgProjects', () => {
152
173
  {
153
174
  initialProps: {disabled: false, pIds: testProjectIds},
154
175
  wrapper: ({children}) => (
155
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
176
+ <ResourceProvider
177
+ resource={{projectId: 'test-project', dataset: 'test-dataset'}}
178
+ fallback={null}
179
+ >
156
180
  {children}
157
181
  </ResourceProvider>
158
182
  ),