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

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 (160) hide show
  1. package/README.md +103 -155
  2. package/dist/index.d.ts +1564 -682
  3. package/dist/index.js +1118 -612
  4. package/dist/index.js.map +1 -1
  5. package/package.json +51 -54
  6. package/src/_exports/index.ts +2 -4
  7. package/src/_exports/sdk-react.ts +26 -17
  8. package/src/components/SDKProvider.test.tsx +173 -32
  9. package/src/components/SDKProvider.tsx +87 -28
  10. package/src/components/SanityApp.test.tsx +68 -12
  11. package/src/components/SanityApp.tsx +94 -87
  12. package/src/components/auth/AuthBoundary.recovery.test.tsx +86 -0
  13. package/src/components/auth/AuthBoundary.test.tsx +26 -8
  14. package/src/components/auth/AuthBoundary.tsx +28 -6
  15. package/src/components/auth/DashboardAccessRequest.tsx +37 -0
  16. package/src/components/auth/LoginError.test.tsx +191 -5
  17. package/src/components/auth/LoginError.tsx +103 -59
  18. package/src/components/errors/ChunkLoadError.test.tsx +59 -0
  19. package/src/components/errors/ChunkLoadError.tsx +56 -0
  20. package/src/components/errors/CorsErrorComponent.tsx +2 -2
  21. package/src/components/errors/chunkReloadStorage.ts +57 -0
  22. package/src/config/handles.ts +29 -27
  23. package/src/constants.ts +5 -0
  24. package/src/context/ComlinkTokenRefresh.test.tsx +10 -27
  25. package/src/context/DefaultResourceContext.ts +3 -3
  26. package/src/context/OrganizationResourcesProvider.test.tsx +189 -0
  27. package/src/context/OrganizationResourcesProvider.tsx +111 -0
  28. package/src/context/PerspectiveContext.ts +3 -3
  29. package/src/context/ProjectContext.ts +15 -0
  30. package/src/context/ResourceProvider.test.tsx +72 -90
  31. package/src/context/ResourceProvider.tsx +59 -93
  32. package/src/context/SDKStudioContext.test.tsx +28 -33
  33. package/src/context/SanityInstanceProvider.test.tsx +100 -0
  34. package/src/context/SanityInstanceProvider.tsx +71 -0
  35. package/src/context/WorkbenchTokenRefresh.test.tsx +106 -0
  36. package/src/context/WorkbenchTokenRefresh.tsx +61 -0
  37. package/src/context/renderSanityApp.test.tsx +151 -49
  38. package/src/context/renderSanityApp.tsx +12 -8
  39. package/src/context/workbenchToken.ts +63 -0
  40. package/src/hooks/access/useCheckPermissions.test-d.ts +15 -0
  41. package/src/hooks/access/useCheckPermissions.test.tsx +53 -0
  42. package/src/hooks/access/useCheckPermissions.ts +24 -0
  43. package/src/hooks/agent/agentActions.test.tsx +1 -1
  44. package/src/hooks/agent/agentActions.ts +51 -71
  45. package/src/hooks/applications/useApplication.test-d.ts +30 -0
  46. package/src/hooks/applications/useApplication.ts +22 -0
  47. package/src/hooks/applications/useApplications.test-d.ts +31 -0
  48. package/src/hooks/applications/useApplications.ts +25 -0
  49. package/src/hooks/applications/useCreateUserApplication.test-d.ts +14 -0
  50. package/src/hooks/applications/useCreateUserApplication.ts +11 -0
  51. package/src/hooks/applications/useDeleteApplication.test-d.ts +12 -0
  52. package/src/hooks/applications/useDeleteApplication.ts +11 -0
  53. package/src/hooks/applications/useDeleteUserApplication.test-d.ts +14 -0
  54. package/src/hooks/applications/useDeleteUserApplication.ts +11 -0
  55. package/src/hooks/applications/useUpdateApplication.test-d.ts +12 -0
  56. package/src/hooks/applications/useUpdateApplication.ts +11 -0
  57. package/src/hooks/applications/useUpdateUserApplication.test-d.ts +14 -0
  58. package/src/hooks/applications/useUpdateUserApplication.ts +11 -0
  59. package/src/hooks/applications/useUserApplication.test-d.ts +11 -0
  60. package/src/hooks/applications/useUserApplication.ts +14 -0
  61. package/src/hooks/applications/useUserApplications.test-d.ts +11 -0
  62. package/src/hooks/applications/useUserApplications.ts +14 -0
  63. package/src/hooks/auth/useDashboardOrganizationId.test.tsx +2 -8
  64. package/src/hooks/auth/useVerifyOrgProjects.test.tsx +8 -32
  65. package/src/hooks/auth/useVerifyOrgProjects.tsx +13 -6
  66. package/src/hooks/client/useClient.test.tsx +1 -4
  67. package/src/hooks/client/useClient.ts +1 -0
  68. package/src/hooks/context/useResource.test.tsx +32 -0
  69. package/src/hooks/context/useResource.ts +24 -0
  70. package/src/hooks/context/useSanityInstance.test.tsx +72 -3
  71. package/src/hooks/context/useSanityInstance.ts +30 -8
  72. package/src/hooks/dashboard/useManageFavorite.test.tsx +110 -46
  73. package/src/hooks/dashboard/useManageFavorite.ts +10 -16
  74. package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +5 -7
  75. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +2 -6
  76. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.test.ts +0 -2
  77. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.ts +1 -2
  78. package/src/hooks/dashboard/useWindowTitle.test.ts +244 -0
  79. package/src/hooks/dashboard/useWindowTitle.ts +114 -0
  80. package/src/hooks/datasets/useDatasets.test.tsx +116 -0
  81. package/src/hooks/datasets/useDatasets.ts +33 -13
  82. package/src/hooks/document/useApplyDocumentActions.test.tsx +48 -0
  83. package/src/hooks/document/useApplyDocumentActions.ts +66 -48
  84. package/src/hooks/document/useCreateDocument.test.tsx +83 -0
  85. package/src/hooks/document/useCreateDocument.ts +117 -0
  86. package/src/hooks/document/useDocument.test.tsx +37 -8
  87. package/src/hooks/document/useDocument.ts +131 -66
  88. package/src/hooks/document/useDocumentEvent.test.tsx +16 -4
  89. package/src/hooks/document/useDocumentEvent.ts +16 -10
  90. package/src/hooks/document/useDocumentPermissions.test.tsx +8 -7
  91. package/src/hooks/document/useDocumentPermissions.ts +71 -39
  92. package/src/hooks/document/useDocumentSyncStatus.test.ts +1 -0
  93. package/src/hooks/document/useDocumentSyncStatus.ts +13 -9
  94. package/src/hooks/document/useEditDocument.test.tsx +21 -14
  95. package/src/hooks/document/useEditDocument.ts +163 -17
  96. package/src/hooks/documents/useDocuments.test.tsx +64 -44
  97. package/src/hooks/documents/useDocuments.ts +36 -20
  98. package/src/hooks/helpers/createCallbackHook.test.tsx +13 -19
  99. package/src/hooks/helpers/createFetcherHook.test.tsx +180 -0
  100. package/src/hooks/helpers/createFetcherHook.ts +69 -0
  101. package/src/hooks/helpers/createMutationHook.test.tsx +125 -0
  102. package/src/hooks/helpers/createMutationHook.tsx +93 -0
  103. package/src/hooks/helpers/createStateSourceHook.test.tsx +10 -10
  104. package/src/hooks/helpers/createStateSourceHook.tsx +2 -1
  105. package/src/hooks/{document/useApplyDocumentActions.test.ts → helpers/useApplyActions.test.tsx} +92 -53
  106. package/src/hooks/helpers/useApplyActions.ts +68 -0
  107. package/src/hooks/helpers/useNormalizedResourceOptions.test.tsx +253 -0
  108. package/src/hooks/helpers/useNormalizedResourceOptions.ts +92 -50
  109. package/src/hooks/helpers/useResolvedProjectId.test.tsx +59 -0
  110. package/src/hooks/helpers/useResolvedProjectId.ts +35 -0
  111. package/src/hooks/helpers/useTrackHookUsage.ts +37 -0
  112. package/src/hooks/installations/useInstallation.test-d.ts +19 -0
  113. package/src/hooks/installations/useInstallation.ts +22 -0
  114. package/src/hooks/installations/useInstallations.test-d.ts +26 -0
  115. package/src/hooks/installations/useInstallations.ts +25 -0
  116. package/src/hooks/organizations/useOrganization.test-d.ts +60 -0
  117. package/src/hooks/organizations/useOrganization.test.ts +63 -0
  118. package/src/hooks/organizations/useOrganization.ts +34 -0
  119. package/src/hooks/organizations/useOrganizations.test-d.ts +68 -0
  120. package/src/hooks/organizations/useOrganizations.test.ts +63 -0
  121. package/src/hooks/organizations/useOrganizations.ts +39 -0
  122. package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +34 -27
  123. package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +40 -22
  124. package/src/hooks/presence/usePresence.test.tsx +0 -15
  125. package/src/hooks/presence/usePresence.ts +9 -20
  126. package/src/hooks/preview/useDocumentPreview.test.tsx +0 -1
  127. package/src/hooks/preview/useDocumentPreview.tsx +10 -8
  128. package/src/hooks/projection/useDocumentProjection.test.tsx +78 -9
  129. package/src/hooks/projection/useDocumentProjection.ts +81 -2
  130. package/src/hooks/projects/useProject.test-d.ts +56 -0
  131. package/src/hooks/projects/useProject.test.tsx +120 -0
  132. package/src/hooks/projects/useProject.ts +42 -43
  133. package/src/hooks/projects/useProjects.test-d.ts +60 -0
  134. package/src/hooks/projects/useProjects.test.ts +45 -98
  135. package/src/hooks/projects/useProjects.ts +19 -26
  136. package/src/hooks/query/useQuery.test.tsx +6 -5
  137. package/src/hooks/query/useQuery.ts +91 -9
  138. package/src/hooks/releases/useActiveReleases.test.tsx +13 -7
  139. package/src/hooks/releases/useActiveReleases.ts +28 -24
  140. package/src/hooks/releases/useAllReleases.test.tsx +93 -0
  141. package/src/hooks/releases/useAllReleases.ts +62 -0
  142. package/src/hooks/releases/useApplyReleaseActions.test.tsx +66 -0
  143. package/src/hooks/releases/useApplyReleaseActions.ts +82 -0
  144. package/src/hooks/releases/usePerspective.test.tsx +18 -10
  145. package/src/hooks/releases/usePerspective.ts +13 -17
  146. package/src/hooks/users/useUser.test.tsx +3 -9
  147. package/src/hooks/users/useUser.ts +2 -0
  148. package/src/hooks/users/useUsers.test.tsx +103 -7
  149. package/src/hooks/users/useUsers.ts +37 -3
  150. package/src/utils/resolveOrgResources.test.ts +111 -0
  151. package/src/utils/resolveOrgResources.ts +69 -0
  152. package/src/hooks/context/useDefaultResource.test.tsx +0 -25
  153. package/src/hooks/context/useDefaultResource.ts +0 -30
  154. package/src/hooks/dashboard/useDispatchIntent.test.ts +0 -254
  155. package/src/hooks/dashboard/useDispatchIntent.ts +0 -157
  156. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.test.ts +0 -107
  157. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.ts +0 -39
  158. package/src/hooks/datasets/useDatasets.test.ts +0 -66
  159. package/src/hooks/helpers/useNormalizedResourceOptions.test.ts +0 -65
  160. package/src/hooks/projects/useProject.test.ts +0 -79
@@ -22,10 +22,7 @@ describe('useDashboardOrganizationId', () => {
22
22
 
23
23
  const {result} = renderHook(() => useDashboardOrganizationId(), {
24
24
  wrapper: ({children}) => (
25
- <ResourceProvider
26
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
27
- fallback={null}
28
- >
25
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
29
26
  {children}
30
27
  </ResourceProvider>
31
28
  ),
@@ -44,10 +41,7 @@ describe('useDashboardOrganizationId', () => {
44
41
 
45
42
  const {result} = renderHook(() => useDashboardOrganizationId(), {
46
43
  wrapper: ({children}) => (
47
- <ResourceProvider
48
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
49
- fallback={null}
50
- >
44
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
51
45
  {children}
52
46
  </ResourceProvider>
53
47
  ),
@@ -26,10 +26,7 @@ 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
30
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
31
- fallback={null}
32
- >
29
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
33
30
  {children}
34
31
  </ResourceProvider>
35
32
  ),
@@ -42,10 +39,7 @@ describe('useVerifyOrgProjects', () => {
42
39
  it('should return null and not observe state if projectIds is missing or empty', () => {
43
40
  const {result: resultUndefined} = renderHook(() => useVerifyOrgProjects(false, undefined), {
44
41
  wrapper: ({children}) => (
45
- <ResourceProvider
46
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
47
- fallback={null}
48
- >
42
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
49
43
  {children}
50
44
  </ResourceProvider>
51
45
  ),
@@ -55,10 +49,7 @@ describe('useVerifyOrgProjects', () => {
55
49
 
56
50
  const {result: resultEmpty} = renderHook(() => useVerifyOrgProjects(false, []), {
57
51
  wrapper: ({children}) => (
58
- <ResourceProvider
59
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
60
- fallback={null}
61
- >
52
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
62
53
  {children}
63
54
  </ResourceProvider>
64
55
  ),
@@ -73,10 +64,7 @@ describe('useVerifyOrgProjects', () => {
73
64
 
74
65
  const {result} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
75
66
  wrapper: ({children}) => (
76
- <ResourceProvider
77
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
78
- fallback={null}
79
- >
67
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
80
68
  {children}
81
69
  </ResourceProvider>
82
70
  ),
@@ -92,10 +80,7 @@ describe('useVerifyOrgProjects', () => {
92
80
 
93
81
  const {result} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
94
82
  wrapper: ({children}) => (
95
- <ResourceProvider
96
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
97
- fallback={null}
98
- >
83
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
99
84
  {children}
100
85
  </ResourceProvider>
101
86
  ),
@@ -117,10 +102,7 @@ describe('useVerifyOrgProjects', () => {
117
102
 
118
103
  const {result} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
119
104
  wrapper: ({children}) => (
120
- <ResourceProvider
121
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
122
- fallback={null}
123
- >
105
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
124
106
  {children}
125
107
  </ResourceProvider>
126
108
  ),
@@ -142,10 +124,7 @@ describe('useVerifyOrgProjects', () => {
142
124
 
143
125
  const {unmount} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
144
126
  wrapper: ({children}) => (
145
- <ResourceProvider
146
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
147
- fallback={null}
148
- >
127
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
149
128
  {children}
150
129
  </ResourceProvider>
151
130
  ),
@@ -173,10 +152,7 @@ describe('useVerifyOrgProjects', () => {
173
152
  {
174
153
  initialProps: {disabled: false, pIds: testProjectIds},
175
154
  wrapper: ({children}) => (
176
- <ResourceProvider
177
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
178
- fallback={null}
179
- >
155
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
180
156
  {children}
181
157
  </ResourceProvider>
182
158
  ),
@@ -27,13 +27,20 @@ export function useVerifyOrgProjects(disabled = false, projectIds?: string[]): s
27
27
  const instance = useSanityInstance()
28
28
  const [error, setError] = useState<string | null>(null)
29
29
 
30
+ const isInactive = disabled || !projectIds || projectIds.length === 0
31
+
32
+ // Reset stale errors when verification turns off so the next activation
33
+ // doesn't briefly leak the previous result.
34
+ const [prevInactive, setPrevInactive] = useState(isInactive)
35
+ if (prevInactive !== isInactive) {
36
+ setPrevInactive(isInactive)
37
+ if (isInactive) setError(null)
38
+ }
39
+
30
40
  useEffect(() => {
31
- if (disabled || !projectIds || projectIds.length === 0) {
32
- if (error !== null) setError(null)
33
- return
34
- }
41
+ if (isInactive) return
35
42
 
36
- const verificationObservable$ = observeOrganizationVerificationState(instance, projectIds)
43
+ const verificationObservable$ = observeOrganizationVerificationState(instance, projectIds!)
37
44
 
38
45
  const subscription = verificationObservable$.subscribe((result: OrgVerificationResult) => {
39
46
  setError(result.error)
@@ -42,7 +49,7 @@ export function useVerifyOrgProjects(disabled = false, projectIds?: string[]): s
42
49
  return () => {
43
50
  subscription.unsubscribe()
44
51
  }
45
- }, [instance, disabled, error, projectIds])
52
+ }, [instance, isInactive, projectIds])
46
53
 
47
54
  return error
48
55
  }
@@ -6,10 +6,7 @@ import {useClient} from './useClient'
6
6
 
7
7
  describe('useClient', () => {
8
8
  const wrapper = ({children}: {children: React.ReactNode}) => (
9
- <ResourceProvider
10
- resource={{projectId: 'test-project', dataset: 'test-dataset'}}
11
- fallback={null}
12
- >
9
+ <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
13
10
  {children}
14
11
  </ResourceProvider>
15
12
  )
@@ -39,4 +39,5 @@ export const useClient = createStateSourceHook({
39
39
  }
40
40
  return getClientState(instance, options)
41
41
  },
42
+ getConfig: (options: ClientOptions) => options,
42
43
  })
@@ -0,0 +1,32 @@
1
+ import {renderHook as reactRenderHook} from '@testing-library/react'
2
+ import {type ReactNode} from 'react'
3
+ import {describe, expect, it} from 'vitest'
4
+
5
+ import {renderHook} from '../../../test/test-utils'
6
+ import {ResourceProvider} from '../../context/ResourceProvider'
7
+ import {useResource} from './useResource'
8
+
9
+ describe('useResource', () => {
10
+ it('returns the resource from the instance config when no explicit resource is set', () => {
11
+ // test-utils wraps with ResourceProvider projectId="test" dataset="test"
12
+ const {result} = renderHook(() => useResource())
13
+ expect(result.current).toEqual({projectId: 'test', dataset: 'test'})
14
+ })
15
+
16
+ it('returns the explicit resource when ResourceProvider has a resource prop', () => {
17
+ const resource = {projectId: 'explicit-project', dataset: 'explicit-dataset'}
18
+ const {result} = reactRenderHook(() => useResource(), {
19
+ wrapper: ({children}: {children: ReactNode}) => (
20
+ <ResourceProvider resource={resource} fallback={null}>
21
+ {children}
22
+ </ResourceProvider>
23
+ ),
24
+ })
25
+ expect(result.current).toEqual(resource)
26
+ })
27
+
28
+ it('returns undefined when no resource or instance config is available', () => {
29
+ const {result} = reactRenderHook(() => useResource())
30
+ expect(result.current).toBeUndefined()
31
+ })
32
+ })
@@ -0,0 +1,24 @@
1
+ import {type DocumentResource} from '@sanity/sdk'
2
+
3
+ import {useEffectiveContextResource} from '../helpers/useNormalizedResourceOptions'
4
+
5
+ /**
6
+ * Returns the currently active `DocumentResource` for the nearest resource context.
7
+ *
8
+ * Resolves in priority order:
9
+ * 1. A `resource` prop on the nearest `<ResourceProvider>`
10
+ * 2. The `projectId`/`dataset` from the current `SanityInstance` config
11
+ * 3. `undefined` when neither is available
12
+ *
13
+ * @public
14
+ * @category Platform
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * const resource = useResource()
19
+ * console.log(resource?.projectId, resource?.dataset)
20
+ * ```
21
+ */
22
+ export function useResource(): DocumentResource | undefined {
23
+ return useEffectiveContextResource()
24
+ }
@@ -1,7 +1,7 @@
1
- import {createSanityInstance, type SanityInstance} from '@sanity/sdk'
1
+ import {createSanityInstance, type SanityConfig, type SanityInstance} from '@sanity/sdk'
2
2
  import {renderHook} from '@testing-library/react'
3
3
  import {type ReactNode} from 'react'
4
- import {describe, expect, it} from 'vitest'
4
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
5
5
 
6
6
  import {SanityInstanceContext} from '../../context/SanityInstanceContext'
7
7
  import {useSanityInstance} from './useSanityInstance'
@@ -16,20 +16,89 @@ describe('useSanityInstance', () => {
16
16
  }
17
17
 
18
18
  it('should return the Sanity instance from context', () => {
19
- const instance = createSanityInstance()
19
+ // Create a Sanity instance
20
+ const instance = createSanityInstance({projectId: 'test-project', dataset: 'test-dataset'})
20
21
 
22
+ // Render the hook with the wrapper that provides the context
21
23
  const {result} = renderHook(() => useSanityInstance(), {
22
24
  wrapper: createWrapper(instance),
23
25
  })
24
26
 
27
+ // Check that the correct instance is returned
25
28
  expect(result.current).toBe(instance)
26
29
  })
27
30
 
28
31
  it('should throw an error if no instance is found in context', () => {
32
+ // Expect the hook to throw when no instance is in context
29
33
  expect(() => {
30
34
  renderHook(() => useSanityInstance(), {
31
35
  wrapper: createWrapper(null),
32
36
  })
33
37
  }).toThrow('SanityInstance context not found')
34
38
  })
39
+
40
+ it('should return the current instance when no config is provided', () => {
41
+ // Create a Sanity instance
42
+ const instance = createSanityInstance({projectId: 'gp', dataset: 'gp-ds'})
43
+
44
+ // Render the hook with the wrapper that provides the context
45
+ const {result} = renderHook(() => useSanityInstance(), {
46
+ wrapper: createWrapper(instance),
47
+ })
48
+
49
+ // Should return the instance
50
+ expect(result.current).toBe(instance)
51
+ })
52
+
53
+ describe('deprecated config parameter', () => {
54
+ let warnSpy: ReturnType<typeof vi.spyOn>
55
+
56
+ beforeEach(() => {
57
+ warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
58
+ })
59
+
60
+ afterEach(() => {
61
+ warnSpy.mockRestore()
62
+ })
63
+
64
+ it('should return the current context instance regardless of config', () => {
65
+ const instance = createSanityInstance({projectId: 'test-project', dataset: 'test-dataset'})
66
+ const requestedConfig: SanityConfig = {projectId: 'test-project', dataset: 'test-dataset'}
67
+
68
+ const {result} = renderHook(() => useSanityInstance(requestedConfig), {
69
+ wrapper: createWrapper(instance),
70
+ })
71
+
72
+ expect(result.current).toBe(instance)
73
+ })
74
+
75
+ it('should throw if no instance in context even when config is provided', () => {
76
+ expect(() => {
77
+ renderHook(() => useSanityInstance({projectId: 'test'}), {
78
+ wrapper: createWrapper(null),
79
+ })
80
+ }).toThrow('SanityInstance context not found')
81
+ })
82
+
83
+ it('warns once when a config argument is passed', () => {
84
+ const instance = createSanityInstance({projectId: 'test-project', dataset: 'test-dataset'})
85
+ const {rerender} = renderHook(() => useSanityInstance({projectId: 'test-project'}), {
86
+ wrapper: createWrapper(instance),
87
+ })
88
+
89
+ expect(warnSpy).toHaveBeenCalledTimes(1)
90
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('[useSanityInstance]'))
91
+
92
+ rerender()
93
+ rerender()
94
+ expect(warnSpy).toHaveBeenCalledTimes(1)
95
+ })
96
+
97
+ it('does not warn when no config is passed', () => {
98
+ const instance = createSanityInstance({projectId: 'test-project', dataset: 'test-dataset'})
99
+ renderHook(() => useSanityInstance(), {wrapper: createWrapper(instance)})
100
+
101
+ expect(warnSpy).not.toHaveBeenCalled()
102
+ })
103
+ })
35
104
  })
@@ -1,34 +1,56 @@
1
- import {type SanityInstance} from '@sanity/sdk'
1
+ import {type SanityConfig, type SanityInstance} from '@sanity/sdk'
2
2
  import {useContext} from 'react'
3
3
 
4
4
  import {SanityInstanceContext} from '../../context/SanityInstanceContext'
5
5
 
6
+ const warnedCallers = new Set<string>()
7
+
6
8
  /**
7
- * Retrieves the current Sanity instance from React context.
9
+ * Retrieves the current Sanity instance from context
8
10
  *
9
11
  * @public
10
12
  *
11
13
  * @category Platform
12
- * @returns The current instance
14
+ * @param config - Deprecated. Formerly used to match against the instance hierarchy.
15
+ * @returns The current Sanity instance
13
16
  *
14
17
  * @remarks
15
- * This hook accesses the nearest Sanity instance from React context,
16
- * provided by a `ResourceProvider` or `SanityApp`.
18
+ * This hook accesses the nearest Sanity instance from the React context.
19
+ * The hook must be used within a component wrapped by a `ResourceProvider` or `SanityApp`.
17
20
  *
18
21
  * @example Get the current instance
19
22
  * ```tsx
20
23
  * const instance = useSanityInstance()
21
- * console.log(instance.config)
24
+ * console.log(instance.config.projectId)
22
25
  * ```
23
26
  *
24
27
  * @throws Error if no SanityInstance is found in context
25
28
  */
26
- export const useSanityInstance = (): SanityInstance => {
29
+ export const useSanityInstance = (
30
+ /**
31
+ * @deprecated Passing a config to match against the instance hierarchy is deprecated.
32
+ * Use `useSanityInstance()` without arguments instead.
33
+ */
34
+ config?: SanityConfig,
35
+ ): SanityInstance => {
36
+ if (config !== undefined) {
37
+ const caller = new Error().stack?.split('\n')[2]?.trim() ?? 'unknown'
38
+ if (!warnedCallers.has(caller)) {
39
+ warnedCallers.add(caller)
40
+ // eslint-disable-next-line no-console
41
+ console.warn(
42
+ '[useSanityInstance] Passing a config argument is deprecated and has no effect. ' +
43
+ 'SDK apps use a single instance for all resources, so the config argument is no longer needed. ' +
44
+ 'Call useSanityInstance() without arguments instead, or useResource() to get your currently active resource.',
45
+ )
46
+ }
47
+ }
48
+
27
49
  const instance = useContext(SanityInstanceContext)
28
50
 
29
51
  if (!instance) {
30
52
  throw new Error(
31
- 'SanityInstance context not found. Please ensure that your component is wrapped in a ResourceProvider or a SanityApp component.',
53
+ `SanityInstance context not found. Please ensure that your component is wrapped in a ResourceProvider or a SanityApp component.`,
32
54
  )
33
55
  }
34
56