@sanity/sdk-react 3.0.0-rc.1 → 3.0.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 (85) hide show
  1. package/dist/_exports/dashboard.d.ts +209 -0
  2. package/dist/_exports/dashboard.d.ts.map +1 -0
  3. package/dist/_exports/dashboard.js +278 -0
  4. package/dist/_exports/dashboard.js.map +1 -0
  5. package/dist/index.d.ts +2186 -2774
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +3953 -1951
  8. package/dist/index.js.map +1 -1
  9. package/dist/useStudioWorkspacesByProjectIdDataset-I4S3CuR5.js +177 -0
  10. package/dist/useStudioWorkspacesByProjectIdDataset-I4S3CuR5.js.map +1 -0
  11. package/package.json +19 -19
  12. package/src/_exports/dashboard.ts +11 -0
  13. package/src/_exports/sdk-react.ts +11 -18
  14. package/src/components/SDKProvider.test.tsx +5 -5
  15. package/src/components/SanityApp.tsx +2 -3
  16. package/src/components/auth/AuthBoundary.test.tsx +1 -19
  17. package/src/components/auth/AuthBoundary.tsx +7 -6
  18. package/src/components/auth/LoginError.tsx +2 -3
  19. package/src/context/ComlinkTokenRefresh.tsx +3 -5
  20. package/src/context/{WorkbenchTokenRefresh.test.tsx → DashboardTokenRefresh.test.tsx} +26 -26
  21. package/src/context/{WorkbenchTokenRefresh.tsx → DashboardTokenRefresh.tsx} +14 -14
  22. package/src/context/OrganizationResourcesProvider.test.tsx +9 -9
  23. package/src/context/OrganizationResourcesProvider.tsx +2 -2
  24. package/src/context/ResourceProvider.test.tsx +21 -0
  25. package/src/context/ResourceProvider.tsx +4 -1
  26. package/src/context/{workbenchToken.ts → dashboardToken.ts} +13 -13
  27. package/src/hooks/agent/agentActions.test.tsx +1 -1
  28. package/src/hooks/agent/agentActions.ts +1 -1
  29. package/src/hooks/comlink/useFrameConnection.test.tsx +3 -2
  30. package/src/hooks/comlink/useFrameConnection.ts +1 -1
  31. package/src/hooks/comlink/useWindowConnection.test.tsx +4 -3
  32. package/src/hooks/comlink/useWindowConnection.ts +3 -4
  33. package/src/hooks/comments/useCommentActions.test.tsx +150 -0
  34. package/src/hooks/comments/useCommentActions.ts +109 -0
  35. package/src/hooks/comments/useCommentList.ts +79 -0
  36. package/src/hooks/comments/useCommentThreads.test.tsx +107 -0
  37. package/src/hooks/comments/useCommentThreads.ts +73 -0
  38. package/src/hooks/comments/useComments.test.tsx +242 -0
  39. package/src/hooks/comments/useComments.ts +59 -0
  40. package/src/hooks/context/useSanityInstance.test.tsx +2 -54
  41. package/src/hooks/context/useSanityInstance.ts +2 -24
  42. package/src/hooks/{agent → dashboard}/useAgentResourceContext.ts +2 -2
  43. package/src/hooks/dashboard/useFavorite.test.tsx +101 -0
  44. package/src/hooks/dashboard/useFavorite.ts +34 -0
  45. package/src/hooks/dashboard/useFavoriteContext.ts +61 -0
  46. package/src/hooks/dashboard/{useDashboardNavigate.test.ts → useNavigate.test.ts} +3 -3
  47. package/src/hooks/dashboard/{useDashboardNavigate.ts → useNavigate.ts} +5 -5
  48. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +2 -1
  49. package/src/hooks/{auth/useDashboardOrganizationId.test.tsx → dashboard/useOrganizationId.test.tsx} +4 -4
  50. package/src/hooks/{auth/useDashboardOrganizationId.tsx → dashboard/useOrganizationId.tsx} +2 -2
  51. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.ts +2 -1
  52. package/src/hooks/dashboard/useUpdateFavorite.test.tsx +146 -0
  53. package/src/hooks/dashboard/useUpdateFavorite.ts +74 -0
  54. package/src/hooks/dashboard/useWindowTitle.ts +1 -1
  55. package/src/hooks/datasets/useDatasets.test.tsx +29 -22
  56. package/src/hooks/datasets/useDatasets.ts +31 -53
  57. package/src/hooks/documents/useDocuments.ts +2 -7
  58. package/src/hooks/helpers/useNormalizedResourceOptions.test.tsx +0 -27
  59. package/src/hooks/helpers/useNormalizedResourceOptions.ts +14 -25
  60. package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +2 -7
  61. package/src/hooks/presence/usePresence.ts +14 -1
  62. package/src/hooks/presence/usePresenceForDocument.test.tsx +141 -0
  63. package/src/hooks/presence/usePresenceForDocument.ts +104 -0
  64. package/src/hooks/presence/useReportPresence.test.tsx +202 -0
  65. package/src/hooks/presence/useReportPresence.ts +170 -0
  66. package/src/hooks/preview/useDocumentPreview.tsx +2 -6
  67. package/src/hooks/projects/useProjects.ts +0 -8
  68. package/src/hooks/query/useQuery.ts +3 -8
  69. package/src/hooks/releases/useActiveReleases.ts +2 -2
  70. package/src/hooks/releases/useAllReleases.ts +2 -2
  71. package/src/hooks/users/useUser.ts +2 -8
  72. package/src/hooks/users/useUsers.ts +1 -2
  73. package/src/hooks/applications/useCreateUserApplication.test-d.ts +0 -14
  74. package/src/hooks/applications/useCreateUserApplication.ts +0 -11
  75. package/src/hooks/applications/useDeleteUserApplication.test-d.ts +0 -14
  76. package/src/hooks/applications/useDeleteUserApplication.ts +0 -11
  77. package/src/hooks/applications/useUpdateUserApplication.test-d.ts +0 -14
  78. package/src/hooks/applications/useUpdateUserApplication.ts +0 -11
  79. package/src/hooks/applications/useUserApplication.test-d.ts +0 -11
  80. package/src/hooks/applications/useUserApplication.ts +0 -14
  81. package/src/hooks/applications/useUserApplications.test-d.ts +0 -11
  82. package/src/hooks/applications/useUserApplications.ts +0 -14
  83. package/src/hooks/dashboard/useManageFavorite.test.tsx +0 -379
  84. package/src/hooks/dashboard/useManageFavorite.ts +0 -173
  85. /package/src/hooks/{agent → dashboard}/useAgentResourceContext.test.tsx +0 -0
@@ -0,0 +1,202 @@
1
+ import {reportPresence} from '@sanity/sdk'
2
+ import {act, renderHook} from '@testing-library/react'
3
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
4
+
5
+ import {ResourceProvider} from '../../context/ResourceProvider'
6
+ import {useReportPresence} from './useReportPresence'
7
+
8
+ vi.mock('@sanity/sdk', async (importOriginal) => {
9
+ const actual = await importOriginal<typeof import('@sanity/sdk')>()
10
+ return {
11
+ ...actual,
12
+ reportPresence: vi.fn(),
13
+ }
14
+ })
15
+
16
+ const reported = () => vi.mocked(reportPresence).mock.calls.map(([, params]) => params.locations)
17
+
18
+ const wrapper = ({children}: {children: React.ReactNode}) => (
19
+ <ResourceProvider projectId="p" dataset="d" fallback={null}>
20
+ {children}
21
+ </ResourceProvider>
22
+ )
23
+
24
+ describe('useReportPresence', () => {
25
+ beforeEach(() => {
26
+ vi.clearAllMocks()
27
+ vi.useFakeTimers()
28
+ })
29
+
30
+ afterEach(() => {
31
+ vi.useRealTimers()
32
+ })
33
+
34
+ it('reports the document with no path or selection keys when neither was given', () => {
35
+ renderHook(() => useReportPresence({documentId: 'doc-1', documentType: 'movie'}), {wrapper})
36
+
37
+ // Keys absent rather than set to `undefined`. The id stays unresolved here:
38
+ // core turns a perspective into a specific document id, so the read and write
39
+ // sides cannot drift apart.
40
+ expect(reported()).toEqual([[{documentId: 'doc-1'}]])
41
+ })
42
+
43
+ it('forwards an explicit perspective for core to resolve', () => {
44
+ renderHook(
45
+ () =>
46
+ useReportPresence({
47
+ documentId: 'doc-1',
48
+ documentType: 'movie',
49
+ perspective: {releaseName: 'autumn'},
50
+ }),
51
+ {wrapper},
52
+ )
53
+
54
+ expect(reported()[0][0]).toMatchObject({
55
+ documentId: 'doc-1',
56
+ perspective: {releaseName: 'autumn'},
57
+ })
58
+ })
59
+
60
+ it('forwards liveEdit, which core resolves to the published document', () => {
61
+ renderHook(
62
+ () => useReportPresence({documentId: 'doc-1', documentType: 'movie', liveEdit: true}),
63
+ {wrapper},
64
+ )
65
+
66
+ expect(reported()[0][0]).toMatchObject({documentId: 'doc-1', liveEdit: true})
67
+ })
68
+
69
+ it('picks up an ambient perspective from the provider', () => {
70
+ // The whole point of resolving after normalization: a perspective set once on
71
+ // `ResourceProvider` has to reach presence without every call site passing it.
72
+ const ambient = ({children}: {children: React.ReactNode}) => (
73
+ <ResourceProvider projectId="p" dataset="d" perspective="published" fallback={null}>
74
+ {children}
75
+ </ResourceProvider>
76
+ )
77
+
78
+ renderHook(() => useReportPresence({documentId: 'doc-1', documentType: 'movie'}), {
79
+ wrapper: ambient,
80
+ })
81
+
82
+ expect(reported()[0][0]).toMatchObject({documentId: 'doc-1', perspective: 'published'})
83
+ })
84
+
85
+ it('announces a field path', () => {
86
+ renderHook(
87
+ () => useReportPresence({documentId: 'doc-1', documentType: 'movie', path: ['title']}),
88
+ {wrapper},
89
+ )
90
+
91
+ expect(reported()[0][0].path).toEqual(['title'])
92
+ })
93
+
94
+ it('carries keyed segments and a selection', () => {
95
+ const path = ['body', {_key: 'b1'}, 'children', {_key: 's1'}, 'text']
96
+ const selection = {
97
+ anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 1},
98
+ focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 4},
99
+ }
100
+
101
+ renderHook(
102
+ () => useReportPresence({documentId: 'doc-1', documentType: 'movie', path, selection}),
103
+ {wrapper},
104
+ )
105
+
106
+ expect(reported()[0][0].path).toEqual(path)
107
+ expect(reported()[0][0].selection).toEqual(selection)
108
+ })
109
+
110
+ it('does not re-announce when a caller passes fresh literals each render', () => {
111
+ const {rerender} = renderHook(
112
+ () =>
113
+ useReportPresence({
114
+ documentId: 'doc-1',
115
+ documentType: 'movie',
116
+ // A new array identity on every render, which is what real callers do.
117
+ path: ['title'],
118
+ }),
119
+ {wrapper},
120
+ )
121
+
122
+ rerender()
123
+ rerender()
124
+
125
+ expect(reported()).toHaveLength(1)
126
+ })
127
+
128
+ it('throttles a burst and announces the final position', () => {
129
+ const {rerender} = renderHook(
130
+ ({path}: {path: string[]}) =>
131
+ useReportPresence({documentId: 'doc-1', documentType: 'movie', path}),
132
+ {wrapper, initialProps: {path: ['a']}},
133
+ )
134
+
135
+ expect(reported()).toHaveLength(1)
136
+
137
+ rerender({path: ['b']})
138
+ rerender({path: ['c']})
139
+ expect(reported()).toHaveLength(1)
140
+
141
+ // Trailing edge, so the position the user actually settled on is the one sent.
142
+ act(() => {
143
+ vi.advanceTimersByTime(1000)
144
+ })
145
+ expect(reported()).toHaveLength(2)
146
+ expect(reported()[1][0].path).toEqual(['c'])
147
+ })
148
+
149
+ it('announces selections more often than field focus', () => {
150
+ const selectionAt = (offset: number) => ({
151
+ anchor: {path: [{_key: 'b1'}], offset},
152
+ focus: {path: [{_key: 'b1'}], offset},
153
+ })
154
+
155
+ const {rerender} = renderHook(
156
+ ({offset}: {offset: number}) =>
157
+ useReportPresence({
158
+ documentId: 'doc-1',
159
+ documentType: 'movie',
160
+ path: ['body'],
161
+ selection: selectionAt(offset),
162
+ }),
163
+ {wrapper, initialProps: {offset: 1}},
164
+ )
165
+
166
+ expect(reported()).toHaveLength(1)
167
+ rerender({offset: 2})
168
+
169
+ // Would still be pending at the 1000ms focus interval. A caret that lags a
170
+ // full second reads as broken, so selections use 250ms.
171
+ act(() => {
172
+ vi.advanceTimersByTime(250)
173
+ })
174
+ expect(reported()).toHaveLength(2)
175
+ })
176
+
177
+ it('clears its location on unmount, staying present but nowhere', () => {
178
+ const {unmount} = renderHook(
179
+ () => useReportPresence({documentId: 'doc-1', documentType: 'movie'}),
180
+ {wrapper},
181
+ )
182
+
183
+ unmount()
184
+
185
+ expect(reported().at(-1)).toEqual([])
186
+ })
187
+
188
+ it('does not clear on every location change, only on unmount', () => {
189
+ const {rerender} = renderHook(
190
+ ({path}: {path: string[]}) =>
191
+ useReportPresence({documentId: 'doc-1', documentType: 'movie', path}),
192
+ {wrapper, initialProps: {path: ['a']}},
193
+ )
194
+
195
+ rerender({path: ['b']})
196
+ act(() => {
197
+ vi.advanceTimersByTime(1000)
198
+ })
199
+
200
+ expect(reported().every((locations) => locations.length === 1)).toBe(true)
201
+ })
202
+ })
@@ -0,0 +1,170 @@
1
+ import {
2
+ isMediaLibraryResource,
3
+ type PresenceSelection,
4
+ reportPresence,
5
+ type ReportPresenceOptions,
6
+ } from '@sanity/sdk'
7
+ import {type Path} from '@sanity/types'
8
+ import {useEffect, useMemo, useRef} from 'react'
9
+
10
+ import {type DocumentHandle} from '../../config/handles'
11
+ import {useSanityInstance} from '../context/useSanityInstance'
12
+ import {useNormalizedResourceOptions} from '../helpers/useNormalizedResourceOptions'
13
+ import {trackHookUsage} from '../helpers/useTrackHookUsage'
14
+
15
+ /**
16
+ * Field focus moves at human speed, so a second between announcements is plenty.
17
+ */
18
+ const FOCUS_THROTTLE_MS = 1000
19
+
20
+ /**
21
+ * A caret that trails a second behind reads as broken rather than as latency, so
22
+ * reporting a `selection` announces more often. The Studio uses a flat 1000ms for
23
+ * both, which is why its remote carets feel sluggish.
24
+ */
25
+ const SELECTION_THROTTLE_MS = 250
26
+
27
+ /** @beta */
28
+ export interface UseReportPresenceOptions extends DocumentHandle {
29
+ /**
30
+ * The focused field path. Omit it for document-level presence. Keyed and numeric
31
+ * segments are supported, so array items and Portable Text spans can be
32
+ * addressed.
33
+ */
34
+ path?: Path
35
+
36
+ /** The Portable Text caret, when the focused field is a Portable Text field. */
37
+ selection?: PresenceSelection
38
+
39
+ /** Overrides the throttle interval. Mainly useful in tests. */
40
+ throttleMs?: number
41
+ }
42
+
43
+ /**
44
+ * Announces that the current user is in a document, so that other clients in the
45
+ * same project and dataset can show them.
46
+ *
47
+ * Writing presence is opt-in. Reading it with `usePresenceForDocument` or
48
+ * `usePresence` never announces anything, and this hook is the only thing that
49
+ * makes an app visible to others. That includes the Studio, which shares the same
50
+ * presence room and will show these users in its navbar and field indicators.
51
+ *
52
+ * Announcements are throttled, collapsed over a short window, and then repeated
53
+ * every 30 seconds while the user is idle. That repeat is what tells peers the
54
+ * session is still alive, so the intended usage is to mount this hook for as long
55
+ * as the user is in the document. On unmount the location is cleared, leaving the
56
+ * user present in the app but not in any particular document.
57
+ *
58
+ * The perspective decides which specific document is reported: the draft under
59
+ * `drafts`, the published document under `published`, a version under a release.
60
+ * It is taken from `ResourceProvider` unless you pass one on the handle. That
61
+ * matters for interoperability, because the Studio's field indicators compare the
62
+ * exact id its form is on, so a mismatch shows your user at document level while
63
+ * never lighting up a field.
64
+ *
65
+ * Presence is scoped to a single project and dataset. It is not a list of everyone
66
+ * signed in to your organization.
67
+ *
68
+ * @example Document-level presence
69
+ * ```tsx
70
+ * function DocumentEditor({documentId, documentType}: DocumentHandle) {
71
+ * useReportPresence({documentId, documentType})
72
+ * return <Editor />
73
+ * }
74
+ * ```
75
+ *
76
+ * @example Presence in a release version
77
+ * ```tsx
78
+ * // The document id stays plain; the perspective selects the version.
79
+ * useReportPresence({documentId, documentType, perspective: {releaseName: 'autumn'}})
80
+ * ```
81
+ *
82
+ * @example Field-level presence
83
+ * ```tsx
84
+ * function TitleField({documentId, documentType}: DocumentHandle) {
85
+ * const [focused, setFocused] = useState(false)
86
+ * useReportPresence({documentId, documentType, path: focused ? ['title'] : undefined})
87
+ * return <input onFocus={() => setFocused(true)} onBlur={() => setFocused(false)} />
88
+ * }
89
+ * ```
90
+ *
91
+ * @beta
92
+ */
93
+ export function useReportPresence(options: UseReportPresenceOptions): void {
94
+ const {path, selection, throttleMs, ...handle} = options
95
+
96
+ const normalizedOptions = useNormalizedResourceOptions(handle)
97
+ if (normalizedOptions.resource && isMediaLibraryResource(normalizedOptions.resource)) {
98
+ throw new Error(
99
+ 'useReportPresence() does not support media library resources. Presence tracking requires a canvas or dataset resource.',
100
+ )
101
+ }
102
+
103
+ const sanityInstance = useSanityInstance()
104
+ trackHookUsage(sanityInstance, 'useReportPresence')
105
+
106
+ const {resource, perspective} = normalizedOptions
107
+ const {documentId, liveEdit} = options
108
+
109
+ const interval = throttleMs ?? (selection ? SELECTION_THROTTLE_MS : FOCUS_THROTTLE_MS)
110
+
111
+ // Compared by value, because callers write `path={['title']}` inline and a fresh
112
+ // array identity every render must not mean a fresh announcement every render.
113
+ const locationKey = useMemo(
114
+ () =>
115
+ JSON.stringify([documentId, path ?? [], selection ?? null, perspective ?? null, liveEdit]),
116
+ [documentId, path, selection, perspective, liveEdit],
117
+ )
118
+
119
+ // Rebuilt from the key rather than from the props, so the effect below can
120
+ // depend on it honestly instead of suppressing the exhaustive-deps rule.
121
+ const location = useMemo<ReportPresenceOptions>(() => {
122
+ const [id, parsedPath, parsedSelection, parsedPerspective, parsedLiveEdit] = JSON.parse(
123
+ locationKey,
124
+ ) as [string, Path, PresenceSelection, ReportPresenceOptions['perspective'] | null, boolean?]
125
+ return {
126
+ documentId: id,
127
+ // Forwarded rather than resolved here: core turns the perspective into the
128
+ // specific document id, so the read and write sides cannot drift apart.
129
+ ...(parsedPerspective ? {perspective: parsedPerspective} : {}),
130
+ ...(parsedLiveEdit ? {liveEdit: parsedLiveEdit} : {}),
131
+ ...(parsedPath.length > 0 ? {path: parsedPath} : {}),
132
+ ...(parsedSelection ? {selection: parsedSelection} : {}),
133
+ }
134
+ }, [locationKey])
135
+
136
+ const lastSentAt = useRef(0)
137
+ const pending = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
138
+
139
+ useEffect(() => {
140
+ // Captured in this effect's closure rather than read from a ref, so nothing is
141
+ // written during render. A newer location re-runs the effect, which clears the
142
+ // pending timeout below and schedules the newer value instead.
143
+ const send = () => {
144
+ lastSentAt.current = Date.now()
145
+ reportPresence(sanityInstance, {
146
+ ...(resource ? {resource} : {}),
147
+ locations: [location],
148
+ })
149
+ }
150
+
151
+ const elapsed = Date.now() - lastSentAt.current
152
+ if (elapsed >= interval) {
153
+ send()
154
+ } else {
155
+ // Trailing edge, so the position the user settled on is the one announced.
156
+ clearTimeout(pending.current)
157
+ pending.current = setTimeout(send, interval - elapsed)
158
+ }
159
+
160
+ return () => clearTimeout(pending.current)
161
+ }, [location, interval, sanityInstance, resource])
162
+
163
+ // Kept separate from the throttled effect so it runs on unmount only, rather
164
+ // than every time the reported location changes.
165
+ useEffect(() => {
166
+ return () => {
167
+ reportPresence(sanityInstance, {...(resource ? {resource} : {}), locations: []})
168
+ }
169
+ }, [sanityInstance, resource])
170
+ }
@@ -1,9 +1,5 @@
1
- import {
2
- PREVIEW_PROJECTION,
3
- type PreviewQueryResult,
4
- type PreviewValue,
5
- transformProjectionToPreview,
6
- } from '@sanity/sdk'
1
+ import {type PreviewQueryResult, type PreviewValue} from '@sanity/sdk'
2
+ import {PREVIEW_PROJECTION, transformProjectionToPreview} from '@sanity/sdk/_internal'
7
3
  import {useMemo} from 'react'
8
4
 
9
5
  import {type DocumentHandle} from '../../config/handles'
@@ -2,14 +2,6 @@ import {type Project, projects, type ProjectsOptions} from '@sanity/sdk'
2
2
 
3
3
  import {createFetcherHook, type FetcherHookResult} from '../helpers/createFetcherHook'
4
4
 
5
- /**
6
- * @public
7
- * @category Types
8
- * @interface
9
- * @deprecated use the Project type directly.
10
- */
11
- export type ProjectWithoutMembers = Project
12
-
13
5
  /**
14
6
  * Returns metadata for each project you have access to.
15
7
  *
@@ -1,10 +1,5 @@
1
- import {
2
- getQueryKey,
3
- getQueryState,
4
- parseQueryKey,
5
- type QueryOptions,
6
- resolveQuery,
7
- } from '@sanity/sdk'
1
+ import {getQueryState, type QueryOptions, resolveQuery} from '@sanity/sdk'
2
+ import {getQueryKey, parseQueryKey} from '@sanity/sdk/_internal'
8
3
  import {type SanityQueryResult} from 'groq'
9
4
  import {useEffect, useMemo, useRef, useState, useSyncExternalStore, useTransition} from 'react'
10
5
 
@@ -162,7 +157,7 @@ export function useQuery(options: WithResourceNameSupport<QueryOptions>): {
162
157
  const [isPending, startTransition] = useTransition()
163
158
 
164
159
  // Get the unique key for this query and its options (using normalized options)
165
- const queryKey = getQueryKey(normalized)
160
+ const queryKey = getQueryKey(instance, normalized)
166
161
  // Use a deferred state to avoid immediate re-renders when the query changes
167
162
  const [deferredQueryKey, setDeferredQueryKey] = useState(queryKey)
168
163
 
@@ -1,8 +1,8 @@
1
1
  import {
2
+ type DatasetHandle,
2
3
  type DocumentResource,
3
4
  getActiveReleasesState,
4
5
  type ReleaseDocument,
5
- type SanityConfig,
6
6
  type SanityInstance,
7
7
  type StateSource,
8
8
  } from '@sanity/sdk'
@@ -48,7 +48,7 @@ const useActiveReleasesValue: UseActiveReleasesValue = createStateSourceHook({
48
48
  * ```
49
49
  */
50
50
  export function useActiveReleases(
51
- options?: WithResourceNameSupport<SanityConfig> | undefined,
51
+ options?: WithResourceNameSupport<DatasetHandle> | undefined,
52
52
  ): ReleaseDocument[] {
53
53
  const normalizedOptions = useNormalizedResourceOptions(options ?? {})
54
54
  return useActiveReleasesValue(normalizedOptions)
@@ -1,8 +1,8 @@
1
1
  import {
2
+ type DatasetHandle,
2
3
  type DocumentResource,
3
4
  getAllReleasesState,
4
5
  type ReleaseDocument,
5
- type SanityConfig,
6
6
  type SanityInstance,
7
7
  type StateSource,
8
8
  } from '@sanity/sdk'
@@ -55,7 +55,7 @@ const useAllReleasesValue: UseAllReleasesValue = createStateSourceHook({
55
55
  * ```
56
56
  */
57
57
  export function useAllReleases(
58
- options?: WithResourceNameSupport<SanityConfig> | undefined,
58
+ options?: WithResourceNameSupport<DatasetHandle> | undefined,
59
59
  ): ReleaseDocument[] {
60
60
  const normalizedOptions = useNormalizedResourceOptions(options ?? {})
61
61
  return useAllReleasesValue(normalizedOptions)
@@ -1,11 +1,5 @@
1
- import {
2
- type GetUserOptions,
3
- getUsersKey,
4
- getUsersState,
5
- parseUsersKey,
6
- resolveUsers,
7
- type SanityUser,
8
- } from '@sanity/sdk'
1
+ import {type GetUserOptions, getUsersState, resolveUsers, type SanityUser} from '@sanity/sdk'
2
+ import {getUsersKey, parseUsersKey} from '@sanity/sdk/_internal'
9
3
  import {useEffect, useMemo, useState, useSyncExternalStore, useTransition} from 'react'
10
4
 
11
5
  import {useSanityInstance} from '../context/useSanityInstance'
@@ -1,12 +1,11 @@
1
1
  import {
2
- getUsersKey,
3
2
  type GetUsersOptions,
4
3
  getUsersState,
5
4
  loadMoreUsers,
6
- parseUsersKey,
7
5
  resolveUsers,
8
6
  type SanityUser,
9
7
  } from '@sanity/sdk'
8
+ import {getUsersKey, parseUsersKey} from '@sanity/sdk/_internal'
10
9
  import {useCallback, useEffect, useMemo, useState, useSyncExternalStore, useTransition} from 'react'
11
10
 
12
11
  import {useSanityInstance} from '../context/useSanityInstance'
@@ -1,14 +0,0 @@
1
- import {type CreateUserApplicationInput, type UserApplication} from '@sanity/sdk'
2
- import {expectTypeOf, test} from 'vitest'
3
-
4
- import {type MutationHookResult} from '../helpers/createMutationHook'
5
- import {useCreateUserApplication} from './useCreateUserApplication'
6
-
7
- test('useCreateUserApplication — returns the mutation envelope', () => {
8
- const result = useCreateUserApplication()
9
- expectTypeOf(result).toEqualTypeOf<
10
- MutationHookResult<CreateUserApplicationInput, UserApplication>
11
- >()
12
- expectTypeOf(result.mutate).parameter(0).toEqualTypeOf<CreateUserApplicationInput>()
13
- expectTypeOf(result.data).toEqualTypeOf<UserApplication | undefined>()
14
- })
@@ -1,11 +0,0 @@
1
- import {createUserApplication} from '@sanity/sdk'
2
-
3
- import {createMutationHook} from '../helpers/createMutationHook'
4
-
5
- /**
6
- * Creates a user application.
7
- *
8
- * @internal
9
- * @returns The mutation envelope `{mutate, isPending, error, data, reset}`.
10
- */
11
- export const useCreateUserApplication = createMutationHook(createUserApplication)
@@ -1,14 +0,0 @@
1
- import {type DeletedResult, type DeleteUserApplicationInput} from '@sanity/sdk'
2
- import {expectTypeOf, test} from 'vitest'
3
-
4
- import {type MutationHookResult} from '../helpers/createMutationHook'
5
- import {useDeleteUserApplication} from './useDeleteUserApplication'
6
-
7
- test('useDeleteUserApplication — returns the mutation envelope', () => {
8
- const result = useDeleteUserApplication()
9
- expectTypeOf(result).toEqualTypeOf<
10
- MutationHookResult<DeleteUserApplicationInput, DeletedResult>
11
- >()
12
- expectTypeOf(result.mutate).parameter(0).toEqualTypeOf<DeleteUserApplicationInput>()
13
- expectTypeOf(result.data).toEqualTypeOf<DeletedResult | undefined>()
14
- })
@@ -1,11 +0,0 @@
1
- import {deleteUserApplication} from '@sanity/sdk'
2
-
3
- import {createMutationHook} from '../helpers/createMutationHook'
4
-
5
- /**
6
- * Deletes a user application.
7
- *
8
- * @internal
9
- * @returns The mutation envelope `{mutate, isPending, error, data, reset}`.
10
- */
11
- export const useDeleteUserApplication = createMutationHook(deleteUserApplication)
@@ -1,14 +0,0 @@
1
- import {type UpdateUserApplicationInput, type UserApplication} from '@sanity/sdk'
2
- import {expectTypeOf, test} from 'vitest'
3
-
4
- import {type MutationHookResult} from '../helpers/createMutationHook'
5
- import {useUpdateUserApplication} from './useUpdateUserApplication'
6
-
7
- test('useUpdateUserApplication — returns the mutation envelope', () => {
8
- const result = useUpdateUserApplication()
9
- expectTypeOf(result).toEqualTypeOf<
10
- MutationHookResult<UpdateUserApplicationInput, UserApplication>
11
- >()
12
- expectTypeOf(result.mutate).parameter(0).toEqualTypeOf<UpdateUserApplicationInput>()
13
- expectTypeOf(result.data).toEqualTypeOf<UserApplication | undefined>()
14
- })
@@ -1,11 +0,0 @@
1
- import {updateUserApplication} from '@sanity/sdk'
2
-
3
- import {createMutationHook} from '../helpers/createMutationHook'
4
-
5
- /**
6
- * Updates a user application.
7
- *
8
- * @internal
9
- * @returns The mutation envelope `{mutate, isPending, error, data, reset}`.
10
- */
11
- export const useUpdateUserApplication = createMutationHook(updateUserApplication)
@@ -1,11 +0,0 @@
1
- import {type UserApplication} from '@sanity/sdk'
2
- import {expectTypeOf, test} from 'vitest'
3
-
4
- import {type FetcherHookResult} from '../helpers/createFetcherHook'
5
- import {useUserApplication} from './useUserApplication'
6
-
7
- test('useUserApplication — returns a single user application in the result envelope', () => {
8
- const result = useUserApplication('user_app_1')
9
- expectTypeOf(result).toEqualTypeOf<FetcherHookResult<UserApplication>>()
10
- expectTypeOf(result.data).toEqualTypeOf<UserApplication>()
11
- })
@@ -1,14 +0,0 @@
1
- import {userApplication} from '@sanity/sdk'
2
-
3
- import {createFetcherHook} from '../helpers/createFetcherHook'
4
-
5
- /**
6
- * Returns a single user application by id.
7
- *
8
- * The hook suspends until the first fetch succeeds, so `data` is always present.
9
- *
10
- * @public
11
- * @param userApplicationId - The user application id.
12
- * @returns The result envelope `{data, isFetching, error, refetch}`.
13
- */
14
- export const useUserApplication = createFetcherHook(userApplication)
@@ -1,11 +0,0 @@
1
- import {type UserApplication} from '@sanity/sdk'
2
- import {expectTypeOf, test} from 'vitest'
3
-
4
- import {type FetcherHookResult} from '../helpers/createFetcherHook'
5
- import {useUserApplications} from './useUserApplications'
6
-
7
- test('useUserApplications — returns the user applications in the result envelope', () => {
8
- const result = useUserApplications({organizationId: 'org_1'})
9
- expectTypeOf(result).toEqualTypeOf<FetcherHookResult<UserApplication[]>>()
10
- expectTypeOf(result.data).toEqualTypeOf<UserApplication[]>()
11
- })
@@ -1,14 +0,0 @@
1
- import {userApplications} from '@sanity/sdk'
2
-
3
- import {createFetcherHook} from '../helpers/createFetcherHook'
4
-
5
- /**
6
- * Returns the current user's applications for the given organisation.
7
- *
8
- * The hook suspends until the first fetch succeeds, so `data` is always present.
9
- *
10
- * @public
11
- * @param options - Options identifying the organisation.
12
- * @returns The result envelope `{data, isFetching, error, refetch}`.
13
- */
14
- export const useUserApplications = createFetcherHook(userApplications)