@sanity/sdk 0.0.0-alpha.21 → 0.0.0-alpha.23

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 (127) hide show
  1. package/dist/index.d.ts +428 -325
  2. package/dist/index.js +1618 -1553
  3. package/dist/index.js.map +1 -1
  4. package/package.json +6 -7
  5. package/src/_exports/index.ts +31 -30
  6. package/src/auth/authStore.test.ts +149 -104
  7. package/src/auth/authStore.ts +51 -100
  8. package/src/auth/handleAuthCallback.test.ts +67 -34
  9. package/src/auth/handleAuthCallback.ts +8 -7
  10. package/src/auth/logout.test.ts +61 -29
  11. package/src/auth/logout.ts +26 -28
  12. package/src/auth/refreshStampedToken.test.ts +9 -9
  13. package/src/auth/refreshStampedToken.ts +62 -56
  14. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +5 -5
  15. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +45 -47
  16. package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +4 -5
  17. package/src/auth/subscribeToStorageEventsAndSetToken.ts +22 -24
  18. package/src/client/clientStore.test.ts +131 -67
  19. package/src/client/clientStore.ts +117 -116
  20. package/src/comlink/controller/actions/destroyController.test.ts +38 -13
  21. package/src/comlink/controller/actions/destroyController.ts +11 -15
  22. package/src/comlink/controller/actions/getOrCreateChannel.test.ts +56 -27
  23. package/src/comlink/controller/actions/getOrCreateChannel.ts +37 -35
  24. package/src/comlink/controller/actions/getOrCreateController.test.ts +27 -16
  25. package/src/comlink/controller/actions/getOrCreateController.ts +23 -22
  26. package/src/comlink/controller/actions/releaseChannel.test.ts +37 -13
  27. package/src/comlink/controller/actions/releaseChannel.ts +22 -21
  28. package/src/comlink/controller/comlinkControllerStore.test.ts +65 -36
  29. package/src/comlink/controller/comlinkControllerStore.ts +44 -5
  30. package/src/comlink/node/actions/getOrCreateNode.test.ts +31 -15
  31. package/src/comlink/node/actions/getOrCreateNode.ts +30 -29
  32. package/src/comlink/node/actions/releaseNode.test.ts +75 -55
  33. package/src/comlink/node/actions/releaseNode.ts +19 -21
  34. package/src/comlink/node/comlinkNodeStore.test.ts +6 -11
  35. package/src/comlink/node/comlinkNodeStore.ts +22 -5
  36. package/src/config/authConfig.ts +79 -0
  37. package/src/config/sanityConfig.ts +48 -0
  38. package/src/datasets/datasets.test.ts +2 -2
  39. package/src/datasets/datasets.ts +18 -5
  40. package/src/document/actions.test.ts +22 -10
  41. package/src/document/actions.ts +44 -56
  42. package/src/document/applyDocumentActions.test.ts +96 -36
  43. package/src/document/applyDocumentActions.ts +140 -99
  44. package/src/document/documentStore.test.ts +103 -155
  45. package/src/document/documentStore.ts +247 -237
  46. package/src/document/listen.ts +56 -55
  47. package/src/document/patchOperations.ts +0 -43
  48. package/src/document/permissions.test.ts +25 -12
  49. package/src/document/permissions.ts +11 -4
  50. package/src/document/processActions.test.ts +41 -8
  51. package/src/document/reducers.test.ts +87 -16
  52. package/src/document/reducers.ts +2 -2
  53. package/src/document/sharedListener.test.ts +34 -16
  54. package/src/document/sharedListener.ts +33 -11
  55. package/src/preview/getPreviewState.test.ts +40 -39
  56. package/src/preview/getPreviewState.ts +68 -56
  57. package/src/preview/previewConstants.ts +43 -0
  58. package/src/preview/previewQuery.test.ts +1 -1
  59. package/src/preview/previewQuery.ts +4 -5
  60. package/src/preview/previewStore.test.ts +13 -58
  61. package/src/preview/previewStore.ts +7 -21
  62. package/src/preview/resolvePreview.test.ts +33 -104
  63. package/src/preview/resolvePreview.ts +11 -21
  64. package/src/preview/subscribeToStateAndFetchBatches.test.ts +96 -97
  65. package/src/preview/subscribeToStateAndFetchBatches.ts +85 -81
  66. package/src/preview/util.ts +1 -0
  67. package/src/project/project.test.ts +3 -3
  68. package/src/project/project.ts +28 -5
  69. package/src/projection/getProjectionState.test.ts +69 -49
  70. package/src/projection/getProjectionState.ts +42 -50
  71. package/src/projection/projectionQuery.ts +1 -1
  72. package/src/projection/projectionStore.test.ts +13 -51
  73. package/src/projection/projectionStore.ts +6 -18
  74. package/src/projection/resolveProjection.test.ts +32 -127
  75. package/src/projection/resolveProjection.ts +15 -28
  76. package/src/projection/subscribeToStateAndFetchBatches.test.ts +105 -90
  77. package/src/projection/subscribeToStateAndFetchBatches.ts +94 -81
  78. package/src/projection/util.ts +2 -0
  79. package/src/projects/projects.test.ts +13 -4
  80. package/src/projects/projects.ts +6 -1
  81. package/src/query/queryStore.test.ts +10 -47
  82. package/src/query/queryStore.ts +151 -133
  83. package/src/query/queryStoreConstants.ts +2 -0
  84. package/src/store/createActionBinder.test.ts +153 -0
  85. package/src/store/createActionBinder.ts +176 -0
  86. package/src/store/createSanityInstance.test.ts +84 -0
  87. package/src/store/createSanityInstance.ts +124 -0
  88. package/src/store/createStateSourceAction.test.ts +196 -0
  89. package/src/store/createStateSourceAction.ts +260 -0
  90. package/src/store/createStoreInstance.test.ts +81 -0
  91. package/src/store/createStoreInstance.ts +80 -0
  92. package/src/store/createStoreState.test.ts +85 -0
  93. package/src/store/createStoreState.ts +92 -0
  94. package/src/store/defineStore.test.ts +18 -0
  95. package/src/store/defineStore.ts +81 -0
  96. package/src/users/reducers.test.ts +318 -0
  97. package/src/users/reducers.ts +88 -0
  98. package/src/users/types.ts +46 -4
  99. package/src/users/usersConstants.ts +4 -0
  100. package/src/users/usersStore.test.ts +350 -223
  101. package/src/users/usersStore.ts +285 -149
  102. package/src/utils/createFetcherStore.test.ts +6 -7
  103. package/src/utils/createFetcherStore.ts +150 -153
  104. package/src/{common/util.test.ts → utils/hashString.test.ts} +1 -1
  105. package/src/auth/fetchLoginUrls.test.ts +0 -163
  106. package/src/auth/fetchLoginUrls.ts +0 -74
  107. package/src/common/createLiveEventSubscriber.test.ts +0 -121
  108. package/src/common/createLiveEventSubscriber.ts +0 -55
  109. package/src/common/types.ts +0 -4
  110. package/src/instance/identity.test.ts +0 -46
  111. package/src/instance/identity.ts +0 -29
  112. package/src/instance/sanityInstance.test.ts +0 -77
  113. package/src/instance/sanityInstance.ts +0 -57
  114. package/src/instance/types.ts +0 -37
  115. package/src/preview/getPreviewProjection.ts +0 -45
  116. package/src/resources/README.md +0 -370
  117. package/src/resources/createAction.test.ts +0 -101
  118. package/src/resources/createAction.ts +0 -44
  119. package/src/resources/createResource.test.ts +0 -112
  120. package/src/resources/createResource.ts +0 -102
  121. package/src/resources/createStateSourceAction.test.ts +0 -114
  122. package/src/resources/createStateSourceAction.ts +0 -83
  123. package/src/resources/createStore.test.ts +0 -67
  124. package/src/resources/createStore.ts +0 -46
  125. package/src/store/createStore.test.ts +0 -108
  126. package/src/store/createStore.ts +0 -106
  127. /package/src/{common/util.ts → utils/hashString.ts} +0 -0
@@ -1,8 +1,13 @@
1
1
  import {omit} from 'lodash-es'
2
2
 
3
- import {type DocumentHandle} from '../document/patchOperations'
4
- import {createAction} from '../resources/createAction'
5
- import {createStateSourceAction, type StateSource} from '../resources/createStateSourceAction'
3
+ import {type DocumentHandle} from '../config/sanityConfig'
4
+ import {bindActionByDataset} from '../store/createActionBinder'
5
+ import {type SanityInstance} from '../store/createSanityInstance'
6
+ import {
7
+ createStateSourceAction,
8
+ type SelectorContext,
9
+ type StateSource,
10
+ } from '../store/createStateSourceAction'
6
11
  import {getPublishedId, insecureRandomId} from '../utils/ids'
7
12
  import {
8
13
  previewStore,
@@ -15,65 +20,72 @@ import {STABLE_EMPTY_PREVIEW} from './util'
15
20
  /**
16
21
  * @beta
17
22
  */
18
- export interface GetPreviewStateOptions {
19
- document: DocumentHandle
20
- }
21
-
22
- const _getPreviewState = createStateSourceAction(
23
- previewStore,
24
- (state, {document}: GetPreviewStateOptions) => state.values[document._id] ?? STABLE_EMPTY_PREVIEW,
25
- )
23
+ export type GetPreviewStateOptions = DocumentHandle
26
24
 
27
25
  /**
28
26
  * @beta
29
27
  */
30
- export const getPreviewState = createAction(previewStore, ({state}) => {
31
- return function ({document}: GetPreviewStateOptions): StateSource<ValuePending<PreviewValue>> {
32
- const {_id, _type: documentType} = document
33
- const documentId = getPublishedId(_id)
34
- const previewState = _getPreviewState(this, {document})
28
+ export function getPreviewState<TResult extends object>(
29
+ instance: SanityInstance,
30
+ options: GetPreviewStateOptions,
31
+ ): StateSource<ValuePending<TResult>>
32
+ /**
33
+ * @beta
34
+ */
35
+ export function getPreviewState(
36
+ instance: SanityInstance,
37
+ options: GetPreviewStateOptions,
38
+ ): StateSource<ValuePending<PreviewValue>>
39
+ /**
40
+ * @beta
41
+ */
42
+ export function getPreviewState(
43
+ ...args: Parameters<typeof _getPreviewState>
44
+ ): StateSource<ValuePending<object>> {
45
+ return _getPreviewState(...args)
46
+ }
35
47
 
36
- return {
37
- ...previewState,
38
- subscribe: (subscriber) => {
39
- const subscriptionId = insecureRandomId()
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)
40
61
 
41
- state.set('addSubscription', (prev) => ({
42
- documentTypes: {
43
- ...prev.documentTypes,
44
- [documentId]: documentType,
45
- },
46
- subscriptions: {
47
- ...prev.subscriptions,
48
- [documentId]: {
49
- ...prev.subscriptions[documentId],
50
- [subscriptionId]: true,
51
- },
62
+ state.set('addSubscription', (prev) => ({
63
+ subscriptions: {
64
+ ...prev.subscriptions,
65
+ [documentId]: {
66
+ ...prev.subscriptions[documentId],
67
+ [subscriptionId]: true,
52
68
  },
53
- }))
54
-
55
- const unsubscribe = previewState.subscribe(subscriber)
69
+ },
70
+ }))
56
71
 
57
- return () => {
58
- unsubscribe()
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
59
78
 
60
- state.set('removeSubscription', (prev): Partial<PreviewStoreState> => {
61
- const documentSubscriptions = omit(prev.subscriptions[documentId], subscriptionId)
62
- const hasSubscribers = !!Object.keys(documentSubscriptions).length
63
- const prevValue = prev.values[documentId]
64
- const previewValue = prevValue?.data ? prevValue.data : null
65
-
66
- return {
67
- subscriptions: hasSubscribers
68
- ? {...prev.subscriptions, [documentId]: documentSubscriptions}
69
- : omit(prev.subscriptions, documentId),
70
- values: hasSubscribers
71
- ? prev.values
72
- : {...prev.values, [documentId]: {data: previewValue, isPending: false}},
73
- }
74
- })
75
- }
76
- },
77
- }
78
- }
79
- })
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
+ )
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The fields to check for a title.
3
+ * The order of the items in the array defines the priority.
4
+ *
5
+ * @internal
6
+ */
7
+ export const TITLE_CANDIDATES = ['title', 'name', 'label', 'heading', 'header', 'caption']
8
+
9
+ /**
10
+ * The fields to check for a subtitle.
11
+ * The order of the items in the array defines the priority.
12
+ *
13
+ * @internal
14
+ */
15
+ export const SUBTITLE_CANDIDATES = ['description', 'subtitle', ...TITLE_CANDIDATES]
16
+
17
+ /**
18
+ * Generates a GROQ projection for preview data without requiring a schema.
19
+ * Uses common field names to make educated guesses about which fields to use.
20
+ *
21
+ * @internal
22
+ */
23
+ export const PREVIEW_PROJECTION = `{
24
+ // Get all potential title fields
25
+ "titleCandidates": {
26
+ ${TITLE_CANDIDATES.map((field) => `"${field}": ${field}`).join(',\n ')}
27
+ },
28
+ // Get all potential subtitle fields
29
+ "subtitleCandidates": {
30
+ ${SUBTITLE_CANDIDATES.map((field) => `"${field}": ${field}`).join(',\n ')}
31
+ },
32
+ "media": coalesce(
33
+ select(
34
+ defined(asset) => {"type": "image-asset", "_ref": asset._ref},
35
+ defined(image.asset) => {"type": "image-asset", "_ref": image.asset._ref},
36
+ defined(mainImage.asset) => {"type": "image-asset", "_ref": mainImage.asset._ref},
37
+ null
38
+ )
39
+ ),
40
+ _type,
41
+ _id,
42
+ _updatedAt
43
+ }`
@@ -1,6 +1,6 @@
1
1
  import {describe, expect, it} from 'vitest'
2
2
 
3
- import {SUBTITLE_CANDIDATES, TITLE_CANDIDATES} from './getPreviewProjection'
3
+ import {SUBTITLE_CANDIDATES, TITLE_CANDIDATES} from './previewConstants'
4
4
  import {createPreviewQuery, normalizeMedia, processPreviewQuery} from './previewQuery'
5
5
  import {STABLE_EMPTY_PREVIEW} from './util'
6
6
 
@@ -1,8 +1,8 @@
1
1
  import {isObject} from 'lodash-es'
2
2
 
3
- import {hashString} from '../common/util'
3
+ import {hashString} from '../utils/hashString'
4
4
  import {getDraftId, getPublishedId} from '../utils/ids'
5
- import {getPreviewProjection, SUBTITLE_CANDIDATES, TITLE_CANDIDATES} from './getPreviewProjection'
5
+ import {PREVIEW_PROJECTION, SUBTITLE_CANDIDATES, TITLE_CANDIDATES} from './previewConstants'
6
6
  import {
7
7
  type PreviewQueryResult,
8
8
  type PreviewStoreState,
@@ -142,11 +142,10 @@ interface CreatePreviewQueryResult {
142
142
  export function createPreviewQuery(documentIds: Set<string>): CreatePreviewQueryResult {
143
143
  // Create arrays of draft and published IDs
144
144
  const allIds = Array.from(documentIds).flatMap((id) => [getPublishedId(id), getDraftId(id)])
145
- const projection = getPreviewProjection()
146
- const queryHash = hashString(projection)
145
+ const queryHash = hashString(PREVIEW_PROJECTION)
147
146
 
148
147
  return {
149
- query: `*[_id in $__ids_${queryHash}]${projection}`,
148
+ query: `*[_id in $__ids_${queryHash}]${PREVIEW_PROJECTION}`,
150
149
  params: {
151
150
  [`__ids_${queryHash}`]: allIds,
152
151
  },
@@ -1,73 +1,28 @@
1
- import {type Subscription} from 'rxjs'
2
- import {describe, expect, it, type Mock, vi} from 'vitest'
1
+ import {Observable} from 'rxjs'
2
+ import {describe, it, vi} from 'vitest'
3
3
 
4
- import {createSanityInstance} from '../instance/sanityInstance'
5
- import {createResourceState} from '../resources/createResource'
4
+ import {createSanityInstance} from '../store/createSanityInstance'
5
+ import {createStoreInstance} from '../store/createStoreInstance'
6
6
  import {previewStore} from './previewStore'
7
7
  import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
8
- import {PREVIEW_TAG} from './util'
9
8
 
10
- // Mock the module with a factory function
11
- vi.mock('../common/createLiveEventSubscriber', () => {
12
- const mockLiveSubscriber = vi.fn()
13
- return {
14
- createLiveEventSubscriber: vi.fn(() => mockLiveSubscriber),
15
- }
16
- })
17
-
18
- vi.mock('./subscribeToStateAndFetchBatches', () => ({
19
- subscribeToStateAndFetchBatches: vi.fn(),
20
- }))
9
+ vi.mock('./subscribeToStateAndFetchBatches')
21
10
 
22
11
  describe('previewStore', () => {
23
12
  it('is a resource that initializes with state and subscriptions', async () => {
24
- const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
25
- const initialState = previewStore.getInitialState(instance)
26
-
27
- expect(initialState).toEqual({
28
- documentTypes: {},
29
- lastLiveEventId: null,
30
- subscriptions: {},
31
- syncTags: {},
32
- values: {},
33
- })
34
- const liveUnsubscribe = vi.fn()
35
- const stateUnsubscribe = vi.fn()
36
-
37
- const {createLiveEventSubscriber} = vi.mocked(
38
- await import('../common/createLiveEventSubscriber'),
13
+ const teardown = vi.fn()
14
+ const subscriber = vi.fn().mockReturnValue(teardown)
15
+ vi.mocked(subscribeToStateAndFetchBatches).mockReturnValue(
16
+ new Observable(subscriber).subscribe(),
39
17
  )
40
- const mockLiveSubscriber = vi.mocked(createLiveEventSubscriber(PREVIEW_TAG)).mockReturnValue({
41
- unsubscribe: liveUnsubscribe,
42
- } as unknown as Subscription)
43
18
 
44
- ;(subscribeToStateAndFetchBatches as Mock).mockImplementation(() => ({
45
- unsubscribe: stateUnsubscribe,
46
- }))
47
-
48
- const dispose = previewStore.initialize!.call(
49
- {
50
- instance,
51
- state: createResourceState(initialState),
52
- },
53
- instance,
54
- )
55
-
56
- // Verify the factory was called with the correct tag
57
- expect(createLiveEventSubscriber).toHaveBeenCalledWith(PREVIEW_TAG)
19
+ const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
58
20
 
59
- // Verify the subscriber was called with the correct context
60
- expect(mockLiveSubscriber).toHaveBeenCalledWith({
61
- instance,
62
- state: expect.any(Object),
63
- })
64
- expect(mockLiveSubscriber).toHaveBeenCalledOnce()
21
+ const {state, dispose} = createStoreInstance(instance, previewStore)
65
22
 
66
- expect(subscribeToStateAndFetchBatches).toHaveBeenCalledOnce()
23
+ expect(subscribeToStateAndFetchBatches).toHaveBeenCalledWith({instance, state})
67
24
 
68
25
  dispose()
69
-
70
- expect(liveUnsubscribe).toHaveBeenCalledOnce()
71
- expect(stateUnsubscribe).toHaveBeenCalledOnce()
26
+ instance.dispose()
72
27
  })
73
28
  })
@@ -1,8 +1,5 @@
1
- import {createLiveEventSubscriber} from '../common/createLiveEventSubscriber'
2
- import {type LiveEventAwareState} from '../common/types'
3
- import {createResource} from '../resources/createResource'
1
+ import {defineStore} from '../store/defineStore'
4
2
  import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
5
- import {PREVIEW_TAG} from './util'
6
3
 
7
4
  export interface PreviewQueryResult {
8
5
  _id: string
@@ -27,7 +24,7 @@ export interface PreviewMedia {
27
24
  /**
28
25
  * Represents the set of values displayed as a preview for a given Sanity document.
29
26
  * This includes a primary title, a secondary subtitle, an optional piece of media associated
30
- * with the document, and the documents status.
27
+ * with the document, and the document's status.
31
28
  *
32
29
  * @public
33
30
  */
@@ -77,32 +74,21 @@ export type ValuePending<T> = {
77
74
  /**
78
75
  * @public
79
76
  */
80
- export interface PreviewStoreState extends LiveEventAwareState {
77
+ export interface PreviewStoreState {
81
78
  values: {[TDocumentId in string]?: ValuePending<PreviewValue>}
82
- documentTypes: {[TDocumentId in string]?: string}
83
79
  subscriptions: {[TDocumentId in string]?: {[TSubscriptionId in string]?: true}}
84
80
  }
85
81
 
86
- export const previewStore = createResource<PreviewStoreState>({
82
+ export const previewStore = defineStore<PreviewStoreState>({
87
83
  name: 'Preview',
88
84
  getInitialState() {
89
85
  return {
90
- documentTypes: {},
91
- lastLiveEventId: null,
92
86
  subscriptions: {},
93
- syncTags: {},
94
87
  values: {},
95
88
  }
96
89
  },
97
- initialize() {
98
- const subscribeToLiveAndSetLastLiveEventId =
99
- createLiveEventSubscriber<PreviewStoreState>(PREVIEW_TAG)
100
- const stateSubscriptionForBatches = subscribeToStateAndFetchBatches(this)
101
- const liveSubscription = subscribeToLiveAndSetLastLiveEventId(this)
102
-
103
- return () => {
104
- stateSubscriptionForBatches.unsubscribe()
105
- liveSubscription.unsubscribe()
106
- }
90
+ initialize: (context) => {
91
+ const subscription = subscribeToStateAndFetchBatches(context)
92
+ return () => subscription.unsubscribe
107
93
  },
108
94
  })
@@ -1,119 +1,48 @@
1
- import {describe, it, type Mock} from 'vitest'
2
-
3
- import {createSanityInstance} from '../instance/sanityInstance'
4
- import {
5
- createResourceState,
6
- getOrCreateResource,
7
- type InitializedResource,
8
- type ResourceState,
9
- } from '../resources/createResource'
10
- import {insecureRandomId} from '../utils/ids'
11
- import {
12
- previewStore,
13
- type PreviewStoreState,
14
- type PreviewValue,
15
- type ValuePending,
16
- } from './previewStore'
1
+ import {type SanityDocumentLike} from '@sanity/types'
2
+ import {of} from 'rxjs'
3
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
4
+
5
+ import {type DocumentHandle} from '../config/sanityConfig'
6
+ import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
7
+ import {type StateSource} from '../store/createStateSourceAction'
8
+ import {getPreviewState} from './getPreviewState'
9
+ import {type PreviewValue, type ValuePending} from './previewStore'
17
10
  import {resolvePreview} from './resolvePreview'
18
11
 
19
- vi.mock('../utils/ids', async (importOriginal) => {
20
- const util = await importOriginal<typeof import('../utils/ids')>()
21
- return {...util, insecureRandomId: vi.fn(util.insecureRandomId)}
22
- })
23
-
24
- vi.mock('../resources/createResource', async (importOriginal) => {
25
- const original = await importOriginal<typeof import('../resources/createResource')>()
26
- return {...original, getOrCreateResource: vi.fn()}
27
- })
12
+ vi.mock('./getPreviewState')
28
13
 
29
14
  describe('resolvePreview', () => {
30
- const instance = createSanityInstance({projectId: 'exampleProject', dataset: 'exampleDataset'})
31
- const document = {_id: 'exampleId', _type: 'exampleType'}
32
- const initialState: PreviewStoreState = {
33
- documentTypes: {},
34
- lastLiveEventId: null,
35
- subscriptions: {},
36
- syncTags: {},
37
- values: {},
38
- }
39
- let state: ResourceState<PreviewStoreState>
15
+ let instance: SanityInstance
40
16
 
41
17
  beforeEach(() => {
42
- state = createResourceState(initialState)
18
+ vi.resetAllMocks()
19
+ // Create a mock that returns the correct ValuePending type
20
+ vi.mocked(getPreviewState).mockReturnValue({
21
+ observable: of({
22
+ data: {title: 'test'},
23
+ isPending: false,
24
+ } as ValuePending<PreviewValue>),
25
+ } as StateSource<ValuePending<PreviewValue>>)
26
+
27
+ instance = createSanityInstance({projectId: 'p', dataset: 'd'})
43
28
  })
44
29
 
45
- it('subscribes and resolves when the preview value is non-null', async () => {
46
- expect(state.get().subscriptions).toEqual({})
47
- ;(insecureRandomId as Mock).mockImplementationOnce(() => 'pseudoRandomId')
48
-
49
- const previewPromise = resolvePreview({state, instance}, {document})
50
- expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
51
-
52
- state.set('updateDifferentDocument', (prev) => ({
53
- values: {
54
- ...prev.values,
55
- differentId: {data: {title: 'Different Document'}, isPending: false},
56
- },
57
- }))
58
-
59
- expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
60
-
61
- state.set('updateCorrectDocumentButNull', (prev) => ({
62
- values: {...prev.values, exampleId: {data: null, isPending: true}},
63
- }))
64
-
65
- expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
66
-
67
- state.set('updateCorrectDocument', (prev) => ({
68
- values: {...prev.values, exampleId: {data: {title: 'Correct Document'}, isPending: false}},
69
- }))
70
-
71
- const preview = await previewPromise
72
- expect(preview).toEqual({data: {title: 'Correct Document'}, isPending: false})
73
-
74
- // subscription is removed after
75
- expect(state.get().subscriptions).toEqual({})
30
+ afterEach(() => {
31
+ instance.dispose()
76
32
  })
77
33
 
78
- it('resolves with the next emitted state (not current state)', async () => {
79
- const currentValue: ValuePending<PreviewValue> = {
80
- data: {title: 'Correct Document'},
81
- isPending: false,
34
+ it('resolves a preview and returns the first emitted value with results', async () => {
35
+ const docHandle: DocumentHandle<SanityDocumentLike> = {
36
+ documentId: 'doc123',
37
+ documentType: 'movie',
82
38
  }
83
- state.set('setInitialDocument', (prev) => ({
84
- values: {...prev.values, exampleId: currentValue},
85
- }))
86
- vi.mocked(insecureRandomId).mockImplementationOnce(() => 'pseudoRandomId')
87
- expect(state.get().subscriptions).toEqual({})
88
-
89
- const previewPromise = resolvePreview({state, instance}, {document})
90
- expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
91
-
92
- state.set('updateDifferentDocument', (prev) => ({
93
- values: {
94
- ...prev.values,
95
- differentId: {data: {title: 'Different Document'}, isPending: false},
96
- },
97
- }))
98
- expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
99
39
 
100
- state.set('updateWithCurrentValue', (prev) => ({
101
- values: {...prev.values, exampleId: currentValue},
102
- }))
103
- expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
40
+ const result = await resolvePreview(instance, docHandle)
104
41
 
105
- state.set('updateWithNewValue', (prev) => ({
106
- values: {...prev.values, exampleId: {data: {title: 'New Value'}, isPending: false}},
107
- }))
108
- expect(state.get().subscriptions).toEqual({})
109
-
110
- const preview = await previewPromise
111
- expect(preview).toEqual({data: {title: 'New Value'}, isPending: false})
112
- })
113
-
114
- it('calls getOrCreateResource if no state is provided', () => {
115
- vi.mocked(getOrCreateResource).mockReturnValue({state} as InitializedResource<unknown>)
116
- resolvePreview(instance, {document})
117
- expect(getOrCreateResource).toHaveBeenCalledWith(instance, previewStore)
42
+ expect(getPreviewState).toHaveBeenCalledWith(instance, docHandle)
43
+ expect(result).toEqual({
44
+ data: {title: 'test'},
45
+ isPending: false,
46
+ })
118
47
  })
119
48
  })
@@ -1,30 +1,20 @@
1
- import {type DocumentHandle} from '../document/patchOperations'
2
- import {createAction} from '../resources/createAction'
1
+ import {filter, firstValueFrom} from 'rxjs'
2
+
3
+ import {type DocumentHandle} from '../config/sanityConfig'
4
+ import {bindActionByDataset} from '../store/createActionBinder'
3
5
  import {getPreviewState} from './getPreviewState'
4
- import {previewStore, type PreviewValue, type ValuePending} from './previewStore'
6
+ import {previewStore} from './previewStore'
5
7
 
6
8
  /**
7
9
  * @beta
8
10
  */
9
- export interface ResolvePreviewOptions {
10
- document: DocumentHandle
11
- }
11
+ export type ResolvePreviewOptions = DocumentHandle
12
12
 
13
13
  /**
14
14
  * @beta
15
15
  */
16
- export const resolvePreview = createAction(previewStore, () => {
17
- return function ({document}: ResolvePreviewOptions) {
18
- const {getCurrent, subscribe} = getPreviewState(this, {document})
19
-
20
- return new Promise<ValuePending<PreviewValue>>((resolve) => {
21
- const unsubscribe = subscribe(() => {
22
- const current = getCurrent()
23
- if (current?.data) {
24
- resolve(current)
25
- unsubscribe()
26
- }
27
- })
28
- })
29
- }
30
- })
16
+ export const resolvePreview = bindActionByDataset(
17
+ previewStore,
18
+ ({instance}, docHandle: ResolvePreviewOptions) =>
19
+ firstValueFrom(getPreviewState(instance, docHandle).observable.pipe(filter((i) => !!i.data))),
20
+ )