@sanity/sdk 2.8.0 → 2.10.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 (111) hide show
  1. package/dist/_chunks-dts/utils.d.ts +2450 -0
  2. package/dist/_chunks-es/_internal.js +129 -0
  3. package/dist/_chunks-es/_internal.js.map +1 -0
  4. package/dist/_chunks-es/createGroqSearchFilter.js +1537 -0
  5. package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -0
  6. package/dist/_chunks-es/telemetryManager.js +87 -0
  7. package/dist/_chunks-es/telemetryManager.js.map +1 -0
  8. package/dist/_chunks-es/version.js +7 -0
  9. package/dist/_chunks-es/version.js.map +1 -0
  10. package/dist/_exports/_internal.d.ts +64 -0
  11. package/dist/_exports/_internal.js +20 -0
  12. package/dist/_exports/_internal.js.map +1 -0
  13. package/dist/index.d.ts +2 -2343
  14. package/dist/index.js +465 -1813
  15. package/dist/index.js.map +1 -1
  16. package/package.json +17 -12
  17. package/src/_exports/_internal.ts +14 -0
  18. package/src/_exports/index.ts +18 -1
  19. package/src/auth/authStore.test.ts +150 -1
  20. package/src/auth/authStore.ts +11 -11
  21. package/src/auth/dashboardAuth.ts +2 -2
  22. package/src/auth/handleAuthCallback.ts +9 -3
  23. package/src/auth/logout.test.ts +1 -1
  24. package/src/auth/logout.ts +1 -1
  25. package/src/auth/refreshStampedToken.test.ts +118 -1
  26. package/src/auth/refreshStampedToken.ts +3 -2
  27. package/src/auth/standaloneAuth.ts +9 -3
  28. package/src/auth/studioAuth.ts +34 -7
  29. package/src/auth/studioModeAuth.ts +2 -1
  30. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +10 -2
  31. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +5 -1
  32. package/src/auth/subscribeToStorageEventsAndSetToken.ts +2 -2
  33. package/src/auth/utils.ts +33 -0
  34. package/src/client/clientStore.test.ts +44 -30
  35. package/src/client/clientStore.ts +49 -48
  36. package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
  37. package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
  38. package/src/comlink/node/getNodeState.ts +2 -1
  39. package/src/config/sanityConfig.ts +78 -12
  40. package/src/document/actions.ts +18 -11
  41. package/src/document/applyDocumentActions.test.ts +7 -6
  42. package/src/document/applyDocumentActions.ts +10 -4
  43. package/src/document/documentStore.test.ts +542 -188
  44. package/src/document/documentStore.ts +142 -76
  45. package/src/document/events.ts +7 -2
  46. package/src/document/permissions.test.ts +18 -16
  47. package/src/document/permissions.ts +35 -11
  48. package/src/document/processActions.test.ts +359 -32
  49. package/src/document/processActions.ts +106 -78
  50. package/src/document/reducers.test.ts +117 -29
  51. package/src/document/reducers.ts +47 -40
  52. package/src/document/sharedListener.ts +16 -6
  53. package/src/document/util.ts +14 -0
  54. package/src/favorites/favorites.test.ts +9 -2
  55. package/src/presence/bifurTransport.test.ts +46 -6
  56. package/src/presence/bifurTransport.ts +19 -2
  57. package/src/presence/presenceStore.test.ts +96 -0
  58. package/src/presence/presenceStore.ts +96 -24
  59. package/src/preview/getPreviewState.test.ts +115 -98
  60. package/src/preview/getPreviewState.ts +38 -60
  61. package/src/preview/previewProjectionUtils.test.ts +179 -0
  62. package/src/preview/previewProjectionUtils.ts +93 -0
  63. package/src/preview/resolvePreview.test.ts +42 -25
  64. package/src/preview/resolvePreview.ts +33 -10
  65. package/src/preview/{previewStore.ts → types.ts} +8 -17
  66. package/src/projection/getProjectionState.test.ts +16 -16
  67. package/src/projection/getProjectionState.ts +6 -5
  68. package/src/projection/projectionQuery.ts +2 -3
  69. package/src/projection/projectionStore.test.ts +2 -2
  70. package/src/projection/resolveProjection.ts +2 -2
  71. package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
  72. package/src/projection/subscribeToStateAndFetchBatches.ts +12 -11
  73. package/src/projection/types.ts +1 -1
  74. package/src/query/queryStore.test.ts +12 -12
  75. package/src/query/queryStore.ts +12 -11
  76. package/src/query/reducers.ts +3 -3
  77. package/src/releases/getPerspectiveState.ts +7 -6
  78. package/src/releases/releasesStore.test.ts +20 -5
  79. package/src/releases/releasesStore.ts +20 -8
  80. package/src/store/createActionBinder.test.ts +31 -31
  81. package/src/store/createActionBinder.ts +43 -38
  82. package/src/store/createSanityInstance.ts +2 -3
  83. package/src/store/createStateSourceAction.test.ts +62 -0
  84. package/src/store/createStateSourceAction.ts +34 -39
  85. package/src/telemetry/__telemetry__/sdk.telemetry.ts +42 -0
  86. package/src/telemetry/devMode.test.ts +52 -0
  87. package/src/telemetry/devMode.ts +40 -0
  88. package/src/telemetry/initTelemetry.test.ts +225 -0
  89. package/src/telemetry/initTelemetry.ts +205 -0
  90. package/src/telemetry/telemetryManager.test.ts +263 -0
  91. package/src/telemetry/telemetryManager.ts +187 -0
  92. package/src/users/reducers.ts +3 -4
  93. package/src/users/usersStore.test.ts +1 -0
  94. package/src/users/usersStore.ts +5 -1
  95. package/src/utils/createFetcherStore.test.ts +6 -4
  96. package/src/utils/createFetcherStore.ts +8 -5
  97. package/src/utils/getStagingApiHost.test.ts +21 -0
  98. package/src/utils/getStagingApiHost.ts +14 -0
  99. package/src/utils/ids.test.ts +1 -29
  100. package/src/utils/ids.ts +0 -10
  101. package/src/utils/isImportError.test.ts +72 -0
  102. package/src/utils/isImportError.ts +34 -0
  103. package/src/utils/object.test.ts +95 -0
  104. package/src/utils/object.ts +142 -0
  105. package/src/utils/setCleanupTimeout.ts +24 -0
  106. package/src/preview/previewQuery.test.ts +0 -236
  107. package/src/preview/previewQuery.ts +0 -153
  108. package/src/preview/previewStore.test.ts +0 -36
  109. package/src/preview/subscribeToStateAndFetchBatches.test.ts +0 -221
  110. package/src/preview/subscribeToStateAndFetchBatches.ts +0 -112
  111. package/src/preview/util.ts +0 -13
@@ -1,20 +1,45 @@
1
- import {type SanityClient} from '@sanity/client'
2
1
  import {createSelector} from 'reselect'
3
- import {combineLatest, distinctUntilChanged, filter, map, of, Subscription, switchMap} from 'rxjs'
2
+ import {
3
+ catchError,
4
+ combineLatest,
5
+ distinctUntilChanged,
6
+ EMPTY,
7
+ filter,
8
+ first,
9
+ map,
10
+ type Observable,
11
+ of,
12
+ Subscription,
13
+ switchMap,
14
+ } from 'rxjs'
4
15
 
5
16
  import {getTokenState} from '../auth/authStore'
6
17
  import {getClient} from '../client/clientStore'
7
- import {bindActionByDataset, type BoundDatasetKey} from '../store/createActionBinder'
8
- import {createStateSourceAction, type SelectorContext} from '../store/createStateSourceAction'
18
+ import {
19
+ type DocumentResource,
20
+ isCanvasResource,
21
+ isDatasetResource,
22
+ isMediaLibraryResource,
23
+ } from '../config/sanityConfig'
24
+ import {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'
25
+ import {type SanityInstance} from '../store/createSanityInstance'
26
+ import {
27
+ createStateSourceAction,
28
+ type SelectorContext,
29
+ type StateSource,
30
+ } from '../store/createStateSourceAction'
9
31
  import {defineStore, type StoreContext} from '../store/defineStore'
10
32
  import {type SanityUser} from '../users/types'
11
33
  import {getUserState} from '../users/usersStore'
12
34
  import {createBifurTransport} from './bifurTransport'
13
35
  import {type PresenceLocation, type TransportEvent, type UserPresence} from './types'
14
36
 
37
+ const PRESENCE_API_VERSION = '2026-03-30'
38
+
15
39
  type PresenceStoreState = {
16
40
  locations: Map<string, {userId: string; locations: PresenceLocation[]}>
17
41
  users: Record<string, SanityUser | undefined>
42
+ organizationId?: string
18
43
  }
19
44
 
20
45
  const getInitialState = (): PresenceStoreState => ({
@@ -23,27 +48,40 @@ const getInitialState = (): PresenceStoreState => ({
23
48
  })
24
49
 
25
50
  /** @public */
26
- export const presenceStore = defineStore<PresenceStoreState, BoundDatasetKey>({
51
+ export const presenceStore = defineStore<PresenceStoreState, BoundResourceKey>({
27
52
  name: 'presence',
28
53
  getInitialState,
29
- initialize: (context: StoreContext<PresenceStoreState, BoundDatasetKey>) => {
54
+ initialize: (context: StoreContext<PresenceStoreState, BoundResourceKey>) => {
30
55
  const {
31
56
  instance,
32
57
  state,
33
- key: {projectId, dataset},
58
+ key: {resource},
34
59
  } = context
60
+
61
+ if (isMediaLibraryResource(resource)) {
62
+ throw new Error('Presence is not supported for media library resources.')
63
+ }
64
+
35
65
  const sessionId = crypto.randomUUID()
36
66
 
37
- const client = getClient(instance, {
38
- apiVersion: '2022-06-30',
39
- projectId,
40
- dataset,
41
- })
67
+ // Dataset resources must use the project hostname so the socket URL is project-specific.
68
+ // Canvas resources use the global API endpoint via the resource config.
69
+ const client = isDatasetResource(resource)
70
+ ? getClient(instance, {
71
+ apiVersion: PRESENCE_API_VERSION,
72
+ projectId: resource.projectId,
73
+ dataset: resource.dataset,
74
+ useProjectHostname: true,
75
+ })
76
+ : getClient(instance, {
77
+ apiVersion: PRESENCE_API_VERSION,
78
+ resource,
79
+ })
42
80
 
43
81
  const token$ = getTokenState(instance).observable.pipe(distinctUntilChanged())
44
82
 
45
83
  const [incomingEvents$, dispatch] = createBifurTransport({
46
- client: client as SanityClient,
84
+ client,
47
85
  token$,
48
86
  sessionId,
49
87
  })
@@ -81,6 +119,22 @@ export const presenceStore = defineStore<PresenceStoreState, BoundDatasetKey>({
81
119
 
82
120
  dispatch({type: 'rollCall'}).subscribe()
83
121
 
122
+ // Canvas resources need the organizationId to resolve users — fetch it once from the canvas endpoint
123
+ if (isCanvasResource(resource)) {
124
+ const globalClient = getClient(instance, {apiVersion: PRESENCE_API_VERSION})
125
+ subscription.add(
126
+ globalClient.observable
127
+ .request<{organizationId: string}>({
128
+ uri: `/canvases/${resource.canvasId}`,
129
+ tag: 'canvases.get',
130
+ })
131
+ .pipe(catchError(() => EMPTY))
132
+ .subscribe(({organizationId}) => {
133
+ state.set('presence/organizationId', (prev) => ({...prev, organizationId}))
134
+ }),
135
+ )
136
+ }
137
+
84
138
  return () => {
85
139
  dispatch({type: 'disconnect'}).subscribe()
86
140
  subscription.unsubscribe()
@@ -114,13 +168,13 @@ const selectPresence = createSelector(
114
168
  },
115
169
  )
116
170
 
117
- /** @public */
118
- export const getPresence = bindActionByDataset(
171
+ const _getPresence = bindActionByResource(
119
172
  presenceStore,
120
173
  createStateSourceAction({
121
- selector: (context: SelectorContext<PresenceStoreState>, _?): UserPresence[] =>
174
+ selector: (context: SelectorContext<PresenceStoreState>): UserPresence[] =>
122
175
  selectPresence(context.state),
123
- onSubscribe: (context: StoreContext<PresenceStoreState, BoundDatasetKey>, _?) => {
176
+ onSubscribe: (context: StoreContext<PresenceStoreState, BoundResourceKey>) => {
177
+ const resource = context.key.resource
124
178
  const userIds$ = context.state.observable.pipe(
125
179
  map((state) =>
126
180
  Array.from(state.locations.values())
@@ -130,26 +184,34 @@ export const getPresence = bindActionByDataset(
130
184
  distinctUntilChanged((a, b) => a.length === b.length && a.every((v, i) => v === b[i])),
131
185
  )
132
186
 
133
- const subscription = userIds$
187
+ // For canvas resources, wait for organizationId to be fetched and stored in state.
188
+ // For dataset resources, emit undefined immediately so the stream isn't blocked.
189
+ const organizationId$: Observable<string | undefined> = isCanvasResource(resource)
190
+ ? context.state.observable.pipe(
191
+ map((s) => s.organizationId),
192
+ filter((id): id is string => id !== undefined),
193
+ first(),
194
+ )
195
+ : of(undefined)
196
+
197
+ const subscription = combineLatest([userIds$, organizationId$])
134
198
  .pipe(
135
- switchMap((userIds) => {
199
+ switchMap(([userIds, organizationId]) => {
136
200
  if (userIds.length === 0) {
137
201
  return of([])
138
202
  }
139
203
  const userObservables = userIds.map((userId) =>
140
204
  getUserState(context.instance, {
141
205
  userId,
142
- resourceType: 'project',
143
- projectId: context.key.projectId,
206
+ ...(isDatasetResource(resource)
207
+ ? {resourceType: 'project', projectId: resource.projectId}
208
+ : {resourceType: 'organization', organizationId}),
144
209
  }).pipe(filter((v): v is NonNullable<typeof v> => !!v)),
145
210
  )
146
211
  return combineLatest(userObservables)
147
212
  }),
148
213
  )
149
214
  .subscribe((users) => {
150
- if (!users) {
151
- return
152
- }
153
215
  context.state.set('presence/users', (prevState) => ({
154
216
  ...prevState,
155
217
  users: {
@@ -167,3 +229,13 @@ export const getPresence = bindActionByDataset(
167
229
  },
168
230
  }),
169
231
  )
232
+
233
+ /** @beta */
234
+ export function getPresence(
235
+ instance: SanityInstance,
236
+ params?: {resource?: DocumentResource},
237
+ ): StateSource<UserPresence[]> {
238
+ // bit of a hack to support the old bound action by dataset
239
+ // in reality, this will always be passed a resource
240
+ return _getPresence(instance, params ?? {})
241
+ }
@@ -1,120 +1,137 @@
1
- import {NEVER} from 'rxjs'
2
- import {describe, it} from 'vitest'
1
+ import {of} from 'rxjs'
2
+ import {beforeEach, describe, expect, it, vi} from 'vitest'
3
3
 
4
- import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
5
- import {type StoreState} from '../store/createStoreState'
6
- import {insecureRandomId} from '../utils/ids'
4
+ import {getProjectionState} from '../projection/getProjectionState'
5
+ import {type ProjectionValuePending} from '../projection/types'
6
+ import {createSanityInstance} from '../store/createSanityInstance'
7
+ import {type StateSource} from '../store/createStateSourceAction'
7
8
  import {getPreviewState} from './getPreviewState'
8
- import {type PreviewStoreState} from './previewStore'
9
- import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
10
- import {STABLE_EMPTY_PREVIEW} from './util'
9
+ import {type PreviewQueryResult} from './types'
11
10
 
12
- vi.mock('../utils/ids', async (importOriginal) => {
13
- const util = await importOriginal<typeof import('../utils/ids')>()
14
- return {...util, insecureRandomId: vi.fn(util.insecureRandomId)}
15
- })
16
-
17
- vi.mock('./subscribeToStateAndFetchBatches.ts')
11
+ vi.mock('../projection/getProjectionState')
18
12
 
19
13
  describe('getPreviewState', () => {
20
- let instance: SanityInstance
21
- const docHandle = {documentId: 'exampleId', documentType: 'exampleType'}
22
- let state: StoreState<PreviewStoreState & {extra?: unknown}>
23
-
24
14
  beforeEach(() => {
25
- // capture state
26
- vi.mocked(subscribeToStateAndFetchBatches).mockImplementation((context) => {
27
- state = context.state
28
- return NEVER.subscribe()
29
- })
30
-
31
- instance = createSanityInstance({projectId: 'exampleProject', dataset: 'exampleDataset'})
15
+ vi.clearAllMocks()
32
16
  })
33
17
 
34
- afterEach(() => {
35
- instance.dispose()
36
- })
37
-
38
- it('returns a state source that emits when the preview value changes', () => {
39
- const previewState = getPreviewState(instance, docHandle)
40
- expect(previewState.getCurrent()).toBe(STABLE_EMPTY_PREVIEW)
41
-
42
- const subscriber = vi.fn()
43
- previewState.subscribe(subscriber)
44
-
45
- // emit unrelated state changes
46
- state.set('updateLastLiveEventId', {extra: 'unrelated change'})
47
- expect(subscriber).toHaveBeenCalledTimes(0)
48
-
49
- state.set('relatedChange', (prev) => ({
50
- values: {...prev.values, exampleId: {data: {title: 'Changed!'}, isPending: false}},
51
- }))
52
- expect(subscriber).toHaveBeenCalledTimes(1)
53
-
54
- state.set('unrelatedChange', (prev) => ({
55
- values: {
56
- ...prev.values,
57
- unrelatedId: {data: {title: 'Unrelated Document'}, isPending: false},
58
- },
59
- }))
60
- expect(subscriber).toHaveBeenCalledTimes(1)
61
-
62
- state.set('relatedChange', (prev) => ({
63
- values: {...prev.values, exampleId: {data: {title: 'Changed again!'}, isPending: false}},
64
- }))
65
- expect(subscriber).toHaveBeenCalledTimes(2)
66
- })
67
-
68
- it('adds a subscription ID and document type to the state on subscription', () => {
69
- const previewState = getPreviewState(instance, docHandle)
70
-
71
- expect(state.get().subscriptions).toEqual({})
72
- vi.mocked(insecureRandomId)
73
- .mockImplementationOnce(() => 'pseudoRandomId1')
74
- .mockImplementationOnce(() => 'pseudoRandomId2')
75
-
76
- const unsubscribe1 = previewState.subscribe(vi.fn())
77
- const unsubscribe2 = previewState.subscribe(vi.fn())
78
-
79
- expect(state.get().subscriptions).toEqual({
80
- exampleId: {pseudoRandomId1: true, pseudoRandomId2: true},
18
+ it('transforms projection result to preview format', () => {
19
+ const mockProjectionResult: PreviewQueryResult = {
20
+ _id: 'doc1',
21
+ _type: 'article',
22
+ _updatedAt: '2024-01-01',
23
+ titleCandidates: {title: 'Test Title'},
24
+ subtitleCandidates: {description: 'Test Description'},
25
+ media: null,
26
+ }
27
+
28
+ const mockProjectionState = {
29
+ getCurrent: vi.fn().mockReturnValue({
30
+ data: mockProjectionResult,
31
+ isPending: false,
32
+ } as ProjectionValuePending<PreviewQueryResult>),
33
+ subscribe: vi.fn(),
34
+ observable: of({
35
+ data: mockProjectionResult,
36
+ isPending: false,
37
+ } as ProjectionValuePending<PreviewQueryResult>),
38
+ }
39
+
40
+ vi.mocked(getProjectionState).mockReturnValue(
41
+ mockProjectionState as unknown as StateSource<
42
+ ProjectionValuePending<Record<string, unknown>> | undefined
43
+ >,
44
+ )
45
+
46
+ const instance = createSanityInstance({
47
+ projectId: 'test-project',
48
+ dataset: 'test-dataset',
81
49
  })
82
-
83
- unsubscribe2()
84
- expect(state.get().subscriptions).toEqual({
85
- exampleId: {pseudoRandomId1: true},
50
+ const previewState = getPreviewState(instance, {
51
+ documentId: 'doc1',
52
+ documentType: 'article',
86
53
  })
87
54
 
88
- unsubscribe1()
89
- expect(state.get().subscriptions).toEqual({})
55
+ const result = previewState.getCurrent()
56
+
57
+ expect(result.data).toEqual({
58
+ title: 'Test Title',
59
+ subtitle: 'Test Description',
60
+ media: null,
61
+ })
62
+ expect(result.isPending).toBe(false)
90
63
  })
91
64
 
92
- it('resets to pending false on unsubscribe if the subscription is the last one', () => {
93
- const previewState = getPreviewState(instance, docHandle)
65
+ it('returns null data when projection result is null', () => {
66
+ const mockProjectionState = {
67
+ getCurrent: vi.fn().mockReturnValue({
68
+ data: null,
69
+ isPending: true,
70
+ }),
71
+ subscribe: vi.fn(),
72
+ observable: of({data: null, isPending: true}),
73
+ }
74
+
75
+ vi.mocked(getProjectionState).mockReturnValue(mockProjectionState)
76
+
77
+ const instance = createSanityInstance({
78
+ projectId: 'test-project',
79
+ dataset: 'test-dataset',
80
+ })
81
+ const previewState = getPreviewState(instance, {
82
+ documentId: 'doc1',
83
+ documentType: 'article',
84
+ })
94
85
 
95
- state.set('presetValueToPending', (prev) => ({
96
- values: {...prev.values, [docHandle.documentId]: {data: {title: 'Foo'}, isPending: true}},
97
- }))
86
+ const result = previewState.getCurrent()
98
87
 
99
- const unsubscribe1 = previewState.subscribe(vi.fn())
100
- const unsubscribe2 = previewState.subscribe(vi.fn())
88
+ expect(result.data).toBeNull()
89
+ expect(result.isPending).toBe(true)
90
+ })
101
91
 
102
- expect(state.get().values[docHandle.documentId]).toEqual({
103
- data: {title: 'Foo'},
104
- isPending: true,
92
+ it('uses fallback title when no title candidates exist', () => {
93
+ const mockProjectionResult: PreviewQueryResult = {
94
+ _id: 'doc1',
95
+ _type: 'article',
96
+ _updatedAt: '2024-01-01',
97
+ titleCandidates: {},
98
+ subtitleCandidates: {},
99
+ media: null,
100
+ }
101
+
102
+ const mockProjectionState = {
103
+ getCurrent: vi.fn().mockReturnValue({
104
+ data: mockProjectionResult,
105
+ isPending: false,
106
+ } as ProjectionValuePending<PreviewQueryResult>),
107
+ subscribe: vi.fn(),
108
+ observable: of({
109
+ data: mockProjectionResult,
110
+ isPending: false,
111
+ } as ProjectionValuePending<PreviewQueryResult>),
112
+ }
113
+
114
+ vi.mocked(getProjectionState).mockReturnValue(
115
+ mockProjectionState as unknown as StateSource<
116
+ ProjectionValuePending<Record<string, unknown>> | undefined
117
+ >,
118
+ )
119
+
120
+ const instance = createSanityInstance({
121
+ projectId: 'test-project',
122
+ dataset: 'test-dataset',
105
123
  })
106
-
107
- unsubscribe1()
108
- expect(state.get().values[docHandle.documentId]).toEqual({
109
- data: {title: 'Foo'},
110
- isPending: true,
124
+ const previewState = getPreviewState(instance, {
125
+ documentId: 'doc1',
126
+ documentType: 'article',
111
127
  })
112
128
 
113
- unsubscribe2()
114
- expect(state.get().subscriptions).toEqual({})
115
- expect(state.get().values[docHandle.documentId]).toEqual({
116
- data: {title: 'Foo'},
117
- isPending: false,
129
+ const result = previewState.getCurrent()
130
+
131
+ expect(result.data).toEqual({
132
+ title: 'article: doc1',
133
+ subtitle: undefined,
134
+ media: null,
118
135
  })
119
136
  })
120
137
  })
@@ -1,29 +1,22 @@
1
- import {omit} from 'lodash-es'
1
+ import {map} from 'rxjs'
2
2
 
3
3
  import {type DocumentHandle} from '../config/sanityConfig'
4
- import {bindActionByDataset} from '../store/createActionBinder'
4
+ import {getProjectionState} from '../projection/getProjectionState'
5
5
  import {type SanityInstance} from '../store/createSanityInstance'
6
- import {
7
- createStateSourceAction,
8
- type SelectorContext,
9
- type StateSource,
10
- } from '../store/createStateSourceAction'
11
- import {getPublishedId, insecureRandomId} from '../utils/ids'
12
- import {
13
- previewStore,
14
- type PreviewStoreState,
15
- type PreviewValue,
16
- type ValuePending,
17
- } from './previewStore'
18
- import {STABLE_EMPTY_PREVIEW} from './util'
6
+ import {type StateSource} from '../store/createStateSourceAction'
7
+ import {PREVIEW_PROJECTION} from './previewConstants'
8
+ import {transformProjectionToPreview} from './previewProjectionUtils'
9
+ import {type PreviewQueryResult, type PreviewValue, type ValuePending} from './types'
19
10
 
20
11
  /**
21
12
  * @beta
13
+ * @deprecated This type is deprecated and will be removed in a future release.
22
14
  */
23
15
  export type GetPreviewStateOptions = DocumentHandle
24
16
 
25
17
  /**
26
18
  * @beta
19
+ * @deprecated This function is deprecated and will be removed in a future release.
27
20
  */
28
21
  export function getPreviewState<TResult extends object>(
29
22
  instance: SanityInstance,
@@ -31,6 +24,7 @@ export function getPreviewState<TResult extends object>(
31
24
  ): StateSource<ValuePending<TResult>>
32
25
  /**
33
26
  * @beta
27
+ * @deprecated This function is deprecated and will be removed in a future release.
34
28
  */
35
29
  export function getPreviewState(
36
30
  instance: SanityInstance,
@@ -38,54 +32,38 @@ export function getPreviewState(
38
32
  ): StateSource<ValuePending<PreviewValue>>
39
33
  /**
40
34
  * @beta
35
+ * @deprecated This function is deprecated and will be removed in a future release.
41
36
  */
42
37
  export function getPreviewState(
43
- ...args: Parameters<typeof _getPreviewState>
44
- ): StateSource<ValuePending<object>> {
45
- return _getPreviewState(...args)
46
- }
38
+ instance: SanityInstance,
39
+ options: GetPreviewStateOptions,
40
+ ): StateSource<ValuePending<PreviewValue>> {
41
+ // Get the projection state
42
+ const projectionState = getProjectionState<PreviewQueryResult>(instance, {
43
+ ...options,
44
+ projection: PREVIEW_PROJECTION,
45
+ })
47
46
 
48
- /**
49
- * @beta
50
- */
51
- export const _getPreviewState = bindActionByDataset(
52
- previewStore,
53
- createStateSourceAction({
54
- selector: (
55
- {state}: SelectorContext<PreviewStoreState>,
56
- docHandle: GetPreviewStateOptions,
57
- ): ValuePending<object> => state.values[docHandle.documentId] ?? STABLE_EMPTY_PREVIEW,
58
- onSubscribe: ({state}, docHandle: GetPreviewStateOptions) => {
59
- const subscriptionId = insecureRandomId()
60
- const documentId = getPublishedId(docHandle.documentId)
47
+ // Transform helper to convert projection result to preview format
48
+ const transformResult = (
49
+ current: ReturnType<typeof projectionState.getCurrent>,
50
+ ): ValuePending<PreviewValue> => {
51
+ if (!current || current.data === null) {
52
+ return {data: null, isPending: current?.isPending ?? false}
53
+ }
61
54
 
62
- state.set('addSubscription', (prev) => ({
63
- subscriptions: {
64
- ...prev.subscriptions,
65
- [documentId]: {
66
- ...prev.subscriptions[documentId],
67
- [subscriptionId]: true,
68
- },
69
- },
70
- }))
55
+ const previewValue = transformProjectionToPreview(instance, current.data, options.resource)
71
56
 
72
- return () => {
73
- state.set('removeSubscription', (prev): Partial<PreviewStoreState> => {
74
- const documentSubscriptions = omit(prev.subscriptions[documentId], subscriptionId)
75
- const hasSubscribers = !!Object.keys(documentSubscriptions).length
76
- const prevValue = prev.values[documentId]
77
- const previewValue = prevValue?.data ? prevValue.data : null
57
+ return {
58
+ data: previewValue,
59
+ isPending: current.isPending,
60
+ }
61
+ }
78
62
 
79
- return {
80
- subscriptions: hasSubscribers
81
- ? {...prev.subscriptions, [documentId]: documentSubscriptions}
82
- : omit(prev.subscriptions, documentId),
83
- values: hasSubscribers
84
- ? prev.values
85
- : {...prev.values, [documentId]: {data: previewValue, isPending: false}},
86
- }
87
- })
88
- }
89
- },
90
- }),
91
- )
63
+ // Wrap the state source to transform projection results to preview format
64
+ return {
65
+ getCurrent: () => transformResult(projectionState.getCurrent()),
66
+ subscribe: (callback) => projectionState.subscribe(callback),
67
+ observable: projectionState.observable.pipe(map(transformResult)),
68
+ }
69
+ }