@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.
- package/dist/_chunks-dts/utils.d.ts +2450 -0
- package/dist/_chunks-es/_internal.js +129 -0
- package/dist/_chunks-es/_internal.js.map +1 -0
- package/dist/_chunks-es/createGroqSearchFilter.js +1537 -0
- package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -0
- package/dist/_chunks-es/telemetryManager.js +87 -0
- package/dist/_chunks-es/telemetryManager.js.map +1 -0
- package/dist/_chunks-es/version.js +7 -0
- package/dist/_chunks-es/version.js.map +1 -0
- package/dist/_exports/_internal.d.ts +64 -0
- package/dist/_exports/_internal.js +20 -0
- package/dist/_exports/_internal.js.map +1 -0
- package/dist/index.d.ts +2 -2343
- package/dist/index.js +465 -1813
- package/dist/index.js.map +1 -1
- package/package.json +17 -12
- package/src/_exports/_internal.ts +14 -0
- package/src/_exports/index.ts +18 -1
- package/src/auth/authStore.test.ts +150 -1
- package/src/auth/authStore.ts +11 -11
- package/src/auth/dashboardAuth.ts +2 -2
- package/src/auth/handleAuthCallback.ts +9 -3
- package/src/auth/logout.test.ts +1 -1
- package/src/auth/logout.ts +1 -1
- package/src/auth/refreshStampedToken.test.ts +118 -1
- package/src/auth/refreshStampedToken.ts +3 -2
- package/src/auth/standaloneAuth.ts +9 -3
- package/src/auth/studioAuth.ts +34 -7
- package/src/auth/studioModeAuth.ts +2 -1
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +10 -2
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +5 -1
- package/src/auth/subscribeToStorageEventsAndSetToken.ts +2 -2
- package/src/auth/utils.ts +33 -0
- package/src/client/clientStore.test.ts +44 -30
- package/src/client/clientStore.ts +49 -48
- package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
- package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
- package/src/comlink/node/getNodeState.ts +2 -1
- package/src/config/sanityConfig.ts +78 -12
- package/src/document/actions.ts +18 -11
- package/src/document/applyDocumentActions.test.ts +7 -6
- package/src/document/applyDocumentActions.ts +10 -4
- package/src/document/documentStore.test.ts +542 -188
- package/src/document/documentStore.ts +142 -76
- package/src/document/events.ts +7 -2
- package/src/document/permissions.test.ts +18 -16
- package/src/document/permissions.ts +35 -11
- package/src/document/processActions.test.ts +359 -32
- package/src/document/processActions.ts +106 -78
- package/src/document/reducers.test.ts +117 -29
- package/src/document/reducers.ts +47 -40
- package/src/document/sharedListener.ts +16 -6
- package/src/document/util.ts +14 -0
- package/src/favorites/favorites.test.ts +9 -2
- package/src/presence/bifurTransport.test.ts +46 -6
- package/src/presence/bifurTransport.ts +19 -2
- package/src/presence/presenceStore.test.ts +96 -0
- package/src/presence/presenceStore.ts +96 -24
- package/src/preview/getPreviewState.test.ts +115 -98
- package/src/preview/getPreviewState.ts +38 -60
- package/src/preview/previewProjectionUtils.test.ts +179 -0
- package/src/preview/previewProjectionUtils.ts +93 -0
- package/src/preview/resolvePreview.test.ts +42 -25
- package/src/preview/resolvePreview.ts +33 -10
- package/src/preview/{previewStore.ts → types.ts} +8 -17
- package/src/projection/getProjectionState.test.ts +16 -16
- package/src/projection/getProjectionState.ts +6 -5
- package/src/projection/projectionQuery.ts +2 -3
- package/src/projection/projectionStore.test.ts +2 -2
- package/src/projection/resolveProjection.ts +2 -2
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
- package/src/projection/subscribeToStateAndFetchBatches.ts +12 -11
- package/src/projection/types.ts +1 -1
- package/src/query/queryStore.test.ts +12 -12
- package/src/query/queryStore.ts +12 -11
- package/src/query/reducers.ts +3 -3
- package/src/releases/getPerspectiveState.ts +7 -6
- package/src/releases/releasesStore.test.ts +20 -5
- package/src/releases/releasesStore.ts +20 -8
- package/src/store/createActionBinder.test.ts +31 -31
- package/src/store/createActionBinder.ts +43 -38
- package/src/store/createSanityInstance.ts +2 -3
- package/src/store/createStateSourceAction.test.ts +62 -0
- package/src/store/createStateSourceAction.ts +34 -39
- package/src/telemetry/__telemetry__/sdk.telemetry.ts +42 -0
- package/src/telemetry/devMode.test.ts +52 -0
- package/src/telemetry/devMode.ts +40 -0
- package/src/telemetry/initTelemetry.test.ts +225 -0
- package/src/telemetry/initTelemetry.ts +205 -0
- package/src/telemetry/telemetryManager.test.ts +263 -0
- package/src/telemetry/telemetryManager.ts +187 -0
- package/src/users/reducers.ts +3 -4
- package/src/users/usersStore.test.ts +1 -0
- package/src/users/usersStore.ts +5 -1
- package/src/utils/createFetcherStore.test.ts +6 -4
- package/src/utils/createFetcherStore.ts +8 -5
- package/src/utils/getStagingApiHost.test.ts +21 -0
- package/src/utils/getStagingApiHost.ts +14 -0
- package/src/utils/ids.test.ts +1 -29
- package/src/utils/ids.ts +0 -10
- package/src/utils/isImportError.test.ts +72 -0
- package/src/utils/isImportError.ts +34 -0
- package/src/utils/object.test.ts +95 -0
- package/src/utils/object.ts +142 -0
- package/src/utils/setCleanupTimeout.ts +24 -0
- package/src/preview/previewQuery.test.ts +0 -236
- package/src/preview/previewQuery.ts +0 -153
- package/src/preview/previewStore.test.ts +0 -36
- package/src/preview/subscribeToStateAndFetchBatches.test.ts +0 -221
- package/src/preview/subscribeToStateAndFetchBatches.ts +0 -112
- 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 {
|
|
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 {
|
|
8
|
-
|
|
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,
|
|
51
|
+
export const presenceStore = defineStore<PresenceStoreState, BoundResourceKey>({
|
|
27
52
|
name: 'presence',
|
|
28
53
|
getInitialState,
|
|
29
|
-
initialize: (context: StoreContext<PresenceStoreState,
|
|
54
|
+
initialize: (context: StoreContext<PresenceStoreState, BoundResourceKey>) => {
|
|
30
55
|
const {
|
|
31
56
|
instance,
|
|
32
57
|
state,
|
|
33
|
-
key: {
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
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
|
-
|
|
118
|
-
export const getPresence = bindActionByDataset(
|
|
171
|
+
const _getPresence = bindActionByResource(
|
|
119
172
|
presenceStore,
|
|
120
173
|
createStateSourceAction({
|
|
121
|
-
selector: (context: SelectorContext<PresenceStoreState
|
|
174
|
+
selector: (context: SelectorContext<PresenceStoreState>): UserPresence[] =>
|
|
122
175
|
selectPresence(context.state),
|
|
123
|
-
onSubscribe: (context: StoreContext<PresenceStoreState,
|
|
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
|
-
|
|
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
|
-
|
|
143
|
-
|
|
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 {
|
|
2
|
-
import {describe, it} from 'vitest'
|
|
1
|
+
import {of} from 'rxjs'
|
|
2
|
+
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {type
|
|
6
|
-
import {
|
|
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
|
|
9
|
-
import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
|
|
10
|
-
import {STABLE_EMPTY_PREVIEW} from './util'
|
|
9
|
+
import {type PreviewQueryResult} from './types'
|
|
11
10
|
|
|
12
|
-
vi.mock('../
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
exampleId: {pseudoRandomId1: true},
|
|
50
|
+
const previewState = getPreviewState(instance, {
|
|
51
|
+
documentId: 'doc1',
|
|
52
|
+
documentType: 'article',
|
|
86
53
|
})
|
|
87
54
|
|
|
88
|
-
|
|
89
|
-
|
|
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('
|
|
93
|
-
const
|
|
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
|
-
|
|
96
|
-
values: {...prev.values, [docHandle.documentId]: {data: {title: 'Foo'}, isPending: true}},
|
|
97
|
-
}))
|
|
86
|
+
const result = previewState.getCurrent()
|
|
98
87
|
|
|
99
|
-
|
|
100
|
-
|
|
88
|
+
expect(result.data).toBeNull()
|
|
89
|
+
expect(result.isPending).toBe(true)
|
|
90
|
+
})
|
|
101
91
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
data: {title: 'Foo'},
|
|
110
|
-
isPending: true,
|
|
124
|
+
const previewState = getPreviewState(instance, {
|
|
125
|
+
documentId: 'doc1',
|
|
126
|
+
documentType: 'article',
|
|
111
127
|
})
|
|
112
128
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
expect(
|
|
116
|
-
|
|
117
|
-
|
|
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 {
|
|
1
|
+
import {map} from 'rxjs'
|
|
2
2
|
|
|
3
3
|
import {type DocumentHandle} from '../config/sanityConfig'
|
|
4
|
-
import {
|
|
4
|
+
import {getProjectionState} from '../projection/getProjectionState'
|
|
5
5
|
import {type SanityInstance} from '../store/createSanityInstance'
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const previewValue = prevValue?.data ? prevValue.data : null
|
|
57
|
+
return {
|
|
58
|
+
data: previewValue,
|
|
59
|
+
isPending: current.isPending,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
78
62
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
+
}
|