@sanity/sdk 2.9.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 +105 -51
- package/dist/_chunks-es/createGroqSearchFilter.js +131 -54
- package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -1
- package/dist/_chunks-es/version.js +1 -1
- package/dist/_exports/_internal.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +119 -73
- package/dist/index.js.map +1 -1
- package/package.json +8 -10
- package/src/_exports/index.ts +8 -0
- package/src/client/clientStore.test.ts +30 -30
- package/src/client/clientStore.ts +47 -47
- package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
- package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
- package/src/config/sanityConfig.ts +72 -12
- package/src/document/applyDocumentActions.test.ts +7 -7
- package/src/document/applyDocumentActions.ts +5 -5
- package/src/document/documentStore.test.ts +68 -62
- package/src/document/documentStore.ts +36 -36
- package/src/document/processActions.ts +2 -2
- package/src/document/reducers.ts +4 -4
- package/src/document/sharedListener.ts +7 -7
- package/src/presence/bifurTransport.test.ts +46 -6
- package/src/presence/bifurTransport.ts +13 -1
- package/src/presence/presenceStore.test.ts +96 -0
- package/src/presence/presenceStore.ts +96 -24
- package/src/preview/getPreviewState.ts +1 -1
- package/src/preview/previewProjectionUtils.test.ts +4 -4
- package/src/preview/previewProjectionUtils.ts +7 -7
- package/src/preview/resolvePreview.ts +5 -1
- package/src/projection/getProjectionState.ts +4 -4
- 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/query/queryStore.test.ts +12 -12
- package/src/query/queryStore.ts +10 -10
- package/src/query/reducers.ts +3 -3
- package/src/releases/getPerspectiveState.ts +5 -5
- package/src/releases/releasesStore.test.ts +6 -6
- package/src/releases/releasesStore.ts +9 -9
- package/src/store/createActionBinder.test.ts +31 -31
- package/src/store/createActionBinder.ts +43 -38
- package/src/store/createSanityInstance.ts +2 -3
- package/src/users/reducers.ts +3 -4
- package/src/utils/createFetcherStore.ts +6 -4
- 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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {type SanityProjectionResult} from 'groq'
|
|
2
2
|
import {filter, firstValueFrom} from 'rxjs'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {bindActionByResourceAndPerspective} from '../store/createActionBinder'
|
|
5
5
|
import {type SanityInstance} from '../store/createSanityInstance'
|
|
6
6
|
import {getProjectionState, type ProjectionOptions} from './getProjectionState'
|
|
7
7
|
import {projectionStore} from './projectionStore'
|
|
@@ -38,7 +38,7 @@ export function resolveProjection(
|
|
|
38
38
|
/**
|
|
39
39
|
* @beta
|
|
40
40
|
*/
|
|
41
|
-
const _resolveProjection =
|
|
41
|
+
const _resolveProjection = bindActionByResourceAndPerspective(
|
|
42
42
|
projectionStore,
|
|
43
43
|
(
|
|
44
44
|
{instance}: {instance: SanityInstance},
|
|
@@ -17,7 +17,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
17
17
|
let state: StoreState<ProjectionStoreState>
|
|
18
18
|
const key = {
|
|
19
19
|
name: 'test.test:drafts',
|
|
20
|
-
|
|
20
|
+
resource: {projectId: 'test', dataset: 'test'},
|
|
21
21
|
perspective: 'drafts' as const,
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {DocumentId} from '@sanity/id-utils'
|
|
2
2
|
import {
|
|
3
3
|
combineLatest,
|
|
4
4
|
debounceTime,
|
|
@@ -16,10 +16,11 @@ import {
|
|
|
16
16
|
tap,
|
|
17
17
|
} from 'rxjs'
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import {isDatasetResource} from '../config/sanityConfig'
|
|
20
20
|
import {getQueryState, resolveQuery} from '../query/queryStore'
|
|
21
21
|
import {type BoundPerspectiveKey} from '../store/createActionBinder'
|
|
22
22
|
import {type StoreContext} from '../store/defineStore'
|
|
23
|
+
import {isDeepEqual} from '../utils/object'
|
|
23
24
|
import {
|
|
24
25
|
createProjectionQuery,
|
|
25
26
|
processProjectionQuery,
|
|
@@ -42,22 +43,22 @@ interface StatusQueryResult {
|
|
|
42
43
|
export const subscribeToStateAndFetchBatches = ({
|
|
43
44
|
state,
|
|
44
45
|
instance,
|
|
45
|
-
key: {
|
|
46
|
+
key: {resource, perspective},
|
|
46
47
|
}: StoreContext<ProjectionStoreState, BoundPerspectiveKey>): Subscription => {
|
|
47
48
|
const documentProjections$ = state.observable.pipe(
|
|
48
49
|
map((s) => s.documentProjections),
|
|
49
|
-
distinctUntilChanged(
|
|
50
|
+
distinctUntilChanged(isDeepEqual),
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
const activeDocumentIds$ = state.observable.pipe(
|
|
53
|
-
map(({subscriptions}) => new Set(Object.keys(subscriptions))),
|
|
54
|
+
map(({subscriptions}) => new Set(Object.keys(subscriptions).map((id) => DocumentId(id)))),
|
|
54
55
|
distinctUntilChanged(isSetEqual),
|
|
55
56
|
)
|
|
56
57
|
|
|
57
58
|
const pendingUpdateSubscription = activeDocumentIds$
|
|
58
59
|
.pipe(
|
|
59
60
|
debounceTime(BATCH_DEBOUNCE_TIME),
|
|
60
|
-
startWith(new Set<
|
|
61
|
+
startWith(new Set<DocumentId>()),
|
|
61
62
|
pairwise(),
|
|
62
63
|
tap(([prevIds, currIds]) => {
|
|
63
64
|
const newIds = [...currIds].filter((id) => !prevIds.has(id))
|
|
@@ -89,7 +90,7 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
89
90
|
|
|
90
91
|
const queryTrigger$ = combineLatest([activeDocumentIds$, documentProjections$]).pipe(
|
|
91
92
|
debounceTime(BATCH_DEBOUNCE_TIME),
|
|
92
|
-
distinctUntilChanged(
|
|
93
|
+
distinctUntilChanged(isDeepEqual),
|
|
93
94
|
)
|
|
94
95
|
|
|
95
96
|
const queryExecutionSubscription = queryTrigger$
|
|
@@ -113,7 +114,7 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
113
114
|
perspective,
|
|
114
115
|
},
|
|
115
116
|
// temporary guard here until we're ready for everything to be queried via global API
|
|
116
|
-
...(
|
|
117
|
+
...(resource && !isDatasetResource(resource) ? {resource} : {}),
|
|
117
118
|
})
|
|
118
119
|
|
|
119
120
|
const querySource$ = defer(() => {
|
|
@@ -128,7 +129,7 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
128
129
|
perspective,
|
|
129
130
|
},
|
|
130
131
|
// temporary guard here until we're ready for everything to be queried via global API in v3
|
|
131
|
-
...(
|
|
132
|
+
...(resource && !isDatasetResource(resource) ? {resource} : {}),
|
|
132
133
|
}),
|
|
133
134
|
).pipe(switchMap(() => observable))
|
|
134
135
|
}
|
|
@@ -154,7 +155,7 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
154
155
|
perspective: 'raw',
|
|
155
156
|
},
|
|
156
157
|
// temporary guard here until we're ready for everything to be queried via global API
|
|
157
|
-
...(
|
|
158
|
+
...(resource && !isDatasetResource(resource) ? {resource} : {}),
|
|
158
159
|
})
|
|
159
160
|
|
|
160
161
|
const statusQuerySource$ = defer(() => {
|
|
@@ -169,7 +170,7 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
169
170
|
perspective: 'raw',
|
|
170
171
|
},
|
|
171
172
|
// temporary guard here until we're ready for everything to be queried via global API
|
|
172
|
-
...(
|
|
173
|
+
...(resource && !isDatasetResource(resource) ? {resource} : {}),
|
|
173
174
|
}),
|
|
174
175
|
).pipe(switchMap(() => observable))
|
|
175
176
|
}
|
|
@@ -3,7 +3,7 @@ import {delay, filter, firstValueFrom, Observable, of, Subject} from 'rxjs'
|
|
|
3
3
|
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
4
4
|
|
|
5
5
|
import {getClientState} from '../client/clientStore'
|
|
6
|
-
import {
|
|
6
|
+
import {isCanvasResource} from '../config/sanityConfig'
|
|
7
7
|
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
8
8
|
import {type StateSource} from '../store/createStateSourceAction'
|
|
9
9
|
import {getQueryState, resolveQuery} from './queryStore'
|
|
@@ -448,21 +448,21 @@ describe('queryStore', () => {
|
|
|
448
448
|
base.dispose()
|
|
449
449
|
})
|
|
450
450
|
|
|
451
|
-
it('uses
|
|
451
|
+
it('uses resource from params when passed in query options (listenForNewSubscribersAndFetch)', async () => {
|
|
452
452
|
const query = '*[_type == "movie"]'
|
|
453
453
|
const mediaLibrarySource = {mediaLibraryId: 'ml123'}
|
|
454
454
|
|
|
455
|
-
const state = getQueryState(instance, {query,
|
|
455
|
+
const state = getQueryState(instance, {query, resource: mediaLibrarySource})
|
|
456
456
|
const unsubscribe = state.subscribe()
|
|
457
457
|
|
|
458
458
|
await firstValueFrom(state.observable.pipe(filter((i) => i !== undefined)))
|
|
459
459
|
|
|
460
|
-
// Verify getClientState was called with the
|
|
461
|
-
// This call includes projectId, dataset, and
|
|
460
|
+
// Verify getClientState was called with the resource from params in listenForNewSubscribersAndFetch
|
|
461
|
+
// This call includes projectId, dataset, and resource
|
|
462
462
|
expect(getClientState).toHaveBeenCalledWith(
|
|
463
463
|
instance,
|
|
464
464
|
expect.objectContaining({
|
|
465
|
-
|
|
465
|
+
resource: expect.objectContaining({
|
|
466
466
|
mediaLibraryId: 'ml123',
|
|
467
467
|
}),
|
|
468
468
|
}),
|
|
@@ -471,22 +471,22 @@ describe('queryStore', () => {
|
|
|
471
471
|
unsubscribe()
|
|
472
472
|
})
|
|
473
473
|
|
|
474
|
-
it('uses
|
|
474
|
+
it('uses resource from store context key when not a dataset resource (listenToLiveClientAndSetLastLiveEventIds)', async () => {
|
|
475
475
|
const query = '*[_type == "movie"]'
|
|
476
476
|
const canvasSource = {canvasId: 'canvas456'}
|
|
477
477
|
|
|
478
|
-
const state = getQueryState(instance, {query,
|
|
478
|
+
const state = getQueryState(instance, {query, resource: canvasSource})
|
|
479
479
|
const unsubscribe = state.subscribe()
|
|
480
480
|
|
|
481
481
|
await firstValueFrom(state.observable.pipe(filter((i) => i !== undefined)))
|
|
482
482
|
|
|
483
|
-
// Verify getClientState was called with the canvas
|
|
484
|
-
// The
|
|
485
|
-
// This call only has apiVersion and
|
|
483
|
+
// Verify getClientState was called with the canvas resource for live events
|
|
484
|
+
// The resource is extracted from the store key and passed when it's not a dataset resource
|
|
485
|
+
// This call only has apiVersion and resource (no projectId/dataset)
|
|
486
486
|
const calls = vi.mocked(getClientState).mock.calls
|
|
487
487
|
const liveClientCall = calls.find(
|
|
488
488
|
([_instance, options]) =>
|
|
489
|
-
|
|
489
|
+
isCanvasResource(options.resource!) && options.resource.canvasId === 'canvas456',
|
|
490
490
|
)
|
|
491
491
|
expect(liveClientCall).toBeDefined()
|
|
492
492
|
|
package/src/query/queryStore.ts
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from 'rxjs'
|
|
25
25
|
|
|
26
26
|
import {getClientState} from '../client/clientStore'
|
|
27
|
-
import {type DatasetHandle,
|
|
27
|
+
import {type DatasetHandle, isDatasetResource} from '../config/sanityConfig'
|
|
28
28
|
/*
|
|
29
29
|
* Although this is an import dependency cycle, it is not a logical cycle:
|
|
30
30
|
* 1. queryStore uses getPerspectiveState when resolving release perspectives
|
|
@@ -35,7 +35,7 @@ import {type DatasetHandle, isDatasetSource} from '../config/sanityConfig'
|
|
|
35
35
|
// eslint-disable-next-line import/no-cycle
|
|
36
36
|
import {getPerspectiveState} from '../releases/getPerspectiveState'
|
|
37
37
|
import {isReleasePerspective} from '../releases/utils/isReleasePerspective'
|
|
38
|
-
import {
|
|
38
|
+
import {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'
|
|
39
39
|
import {type SanityInstance} from '../store/createSanityInstance'
|
|
40
40
|
import {
|
|
41
41
|
createStateSourceAction,
|
|
@@ -117,7 +117,7 @@ function normalizeOptionsWithPerspective(
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
const queryStore = defineStore<QueryStoreState,
|
|
120
|
+
const queryStore = defineStore<QueryStoreState, BoundResourceKey>({
|
|
121
121
|
name: 'QueryStore',
|
|
122
122
|
getInitialState: () => ({queries: {}}),
|
|
123
123
|
initialize(context) {
|
|
@@ -173,7 +173,7 @@ const listenForNewSubscribersAndFetch = ({state, instance}: StoreContext<QuerySt
|
|
|
173
173
|
projectId,
|
|
174
174
|
dataset,
|
|
175
175
|
tag,
|
|
176
|
-
|
|
176
|
+
resource,
|
|
177
177
|
perspective: perspectiveFromOptions,
|
|
178
178
|
...restOptions
|
|
179
179
|
} = parseQueryKey(group$.key)
|
|
@@ -190,7 +190,7 @@ const listenForNewSubscribersAndFetch = ({state, instance}: StoreContext<QuerySt
|
|
|
190
190
|
apiVersion: QUERY_STORE_API_VERSION,
|
|
191
191
|
projectId,
|
|
192
192
|
dataset,
|
|
193
|
-
|
|
193
|
+
resource,
|
|
194
194
|
}).observable
|
|
195
195
|
|
|
196
196
|
return combineLatest({
|
|
@@ -226,12 +226,12 @@ const listenForNewSubscribersAndFetch = ({state, instance}: StoreContext<QuerySt
|
|
|
226
226
|
const listenToLiveClientAndSetLastLiveEventIds = ({
|
|
227
227
|
state,
|
|
228
228
|
instance,
|
|
229
|
-
key: {
|
|
230
|
-
}: StoreContext<QueryStoreState,
|
|
229
|
+
key: {resource},
|
|
230
|
+
}: StoreContext<QueryStoreState, BoundResourceKey>) => {
|
|
231
231
|
const liveMessages$ = getClientState(instance, {
|
|
232
232
|
apiVersion: QUERY_STORE_API_VERSION,
|
|
233
233
|
// temporary guard here until we're ready for everything to be queried via global api
|
|
234
|
-
...(
|
|
234
|
+
...(resource && !isDatasetResource(resource) ? {resource} : {}),
|
|
235
235
|
}).observable.pipe(
|
|
236
236
|
switchMap((client) =>
|
|
237
237
|
defer(() =>
|
|
@@ -316,7 +316,7 @@ export function getQueryState(
|
|
|
316
316
|
): ReturnType<typeof _getQueryState> {
|
|
317
317
|
return _getQueryState(...args)
|
|
318
318
|
}
|
|
319
|
-
const _getQueryState =
|
|
319
|
+
const _getQueryState = bindActionByResource(
|
|
320
320
|
queryStore,
|
|
321
321
|
createStateSourceAction({
|
|
322
322
|
selector: ({state, instance}: SelectorContext<QueryStoreState>, options: QueryOptions) => {
|
|
@@ -375,7 +375,7 @@ export function resolveQuery<TData>(
|
|
|
375
375
|
export function resolveQuery(...args: Parameters<typeof _resolveQuery>): Promise<unknown> {
|
|
376
376
|
return _resolveQuery(...args)
|
|
377
377
|
}
|
|
378
|
-
const _resolveQuery =
|
|
378
|
+
const _resolveQuery = bindActionByResource(
|
|
379
379
|
queryStore,
|
|
380
380
|
({state, instance}, {signal, ...options}: ResolveQueryOptions) => {
|
|
381
381
|
const normalized = normalizeOptionsWithPerspective(instance, options)
|
package/src/query/reducers.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {omitProperty} from '../utils/object'
|
|
2
2
|
|
|
3
3
|
interface QueryState {
|
|
4
4
|
syncTags?: string[]
|
|
@@ -54,7 +54,7 @@ export const removeSubscriber =
|
|
|
54
54
|
const prevQuery = prev.queries[key]
|
|
55
55
|
if (!prevQuery) return prev
|
|
56
56
|
const subscribers = prevQuery.subscribers.filter((id) => id !== subscriptionId)
|
|
57
|
-
if (!subscribers.length) return {...prev, queries:
|
|
57
|
+
if (!subscribers.length) return {...prev, queries: omitProperty(prev.queries, key)}
|
|
58
58
|
return {...prev, queries: {...prev.queries, [key]: {...prevQuery, subscribers}}}
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -64,7 +64,7 @@ export const cancelQuery =
|
|
|
64
64
|
const prevQuery = prev.queries[key]
|
|
65
65
|
if (!prevQuery) return prev
|
|
66
66
|
if (prevQuery.subscribers.length) return prev
|
|
67
|
-
return {...prev, queries:
|
|
67
|
+
return {...prev, queries: omitProperty(prev.queries, key)}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export const initializeQuery =
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {createSelector} from 'reselect'
|
|
2
2
|
|
|
3
|
-
import {type
|
|
4
|
-
import {
|
|
3
|
+
import {type DocumentResource, type PerspectiveHandle} from '../config/sanityConfig'
|
|
4
|
+
import {bindActionByResource, type BoundStoreAction} from '../store/createActionBinder'
|
|
5
5
|
import {createStateSourceAction, type SelectorContext} from '../store/createStateSourceAction'
|
|
6
6
|
/*
|
|
7
7
|
* Although this is an import dependency cycle, it is not a logical cycle:
|
|
@@ -26,7 +26,7 @@ const selectActiveReleases = (context: SelectorContext<ReleasesStoreState>) =>
|
|
|
26
26
|
context.state.activeReleases
|
|
27
27
|
const selectOptions = (
|
|
28
28
|
_context: SelectorContext<ReleasesStoreState>,
|
|
29
|
-
options: PerspectiveHandle & {projectId?: string; dataset?: string;
|
|
29
|
+
options: PerspectiveHandle & {projectId?: string; dataset?: string; resource?: DocumentResource},
|
|
30
30
|
) => options
|
|
31
31
|
|
|
32
32
|
const memoizedOptionsSelector = createSelector(
|
|
@@ -105,11 +105,11 @@ let _boundGetPerspectiveState: BoundGetPerspectiveState | undefined
|
|
|
105
105
|
*/
|
|
106
106
|
export const getPerspectiveState: BoundGetPerspectiveState = (instance, ...rest) => {
|
|
107
107
|
if (!_boundGetPerspectiveState) {
|
|
108
|
-
_boundGetPerspectiveState =
|
|
108
|
+
_boundGetPerspectiveState = bindActionByResource(
|
|
109
109
|
releasesStore,
|
|
110
110
|
_getPerspectiveStateSelector,
|
|
111
111
|
) as BoundGetPerspectiveState
|
|
112
112
|
}
|
|
113
|
-
//
|
|
113
|
+
// bindActionByResource keyFn destructures { resource } from the first param, so pass {} when no options
|
|
114
114
|
return _boundGetPerspectiveState(instance, ...(rest.length ? rest : [{}]))
|
|
115
115
|
}
|
|
@@ -39,7 +39,7 @@ describe('releasesStore', () => {
|
|
|
39
39
|
expect.objectContaining({
|
|
40
40
|
query: 'releases::all()',
|
|
41
41
|
perspective: 'raw',
|
|
42
|
-
|
|
42
|
+
resource: undefined,
|
|
43
43
|
tag: 'releases',
|
|
44
44
|
}),
|
|
45
45
|
)
|
|
@@ -73,7 +73,7 @@ describe('releasesStore', () => {
|
|
|
73
73
|
} as ReleaseDocument,
|
|
74
74
|
]
|
|
75
75
|
|
|
76
|
-
const state = getActiveReleasesState(instance, {
|
|
76
|
+
const state = getActiveReleasesState(instance, {resource: {projectId: 'test', dataset: 'test'}})
|
|
77
77
|
|
|
78
78
|
const [observer] = subscriber.mock.lastCall!
|
|
79
79
|
|
|
@@ -92,7 +92,7 @@ describe('releasesStore', () => {
|
|
|
92
92
|
observable: releasesSubject.asObservable(),
|
|
93
93
|
} as StateSource<ReleaseDocument[] | undefined>)
|
|
94
94
|
|
|
95
|
-
const state = getActiveReleasesState(instance, {
|
|
95
|
+
const state = getActiveReleasesState(instance, {resource: {projectId: 'test', dataset: 'test'}})
|
|
96
96
|
|
|
97
97
|
// Initial state should be default
|
|
98
98
|
expect(state.getCurrent()).toBeUndefined() // Default initial state
|
|
@@ -139,7 +139,7 @@ describe('releasesStore', () => {
|
|
|
139
139
|
observable: of([]),
|
|
140
140
|
} as StateSource<ReleaseDocument[] | undefined>)
|
|
141
141
|
|
|
142
|
-
const state = getActiveReleasesState(instance, {
|
|
142
|
+
const state = getActiveReleasesState(instance, {resource: {projectId: 'test', dataset: 'test'}})
|
|
143
143
|
|
|
144
144
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
145
145
|
|
|
@@ -153,7 +153,7 @@ describe('releasesStore', () => {
|
|
|
153
153
|
getCurrent: () => null as unknown as ReleaseDocument[] | undefined,
|
|
154
154
|
observable: of(null as unknown as ReleaseDocument[] | undefined),
|
|
155
155
|
} as StateSource<ReleaseDocument[] | undefined>)
|
|
156
|
-
const state = getActiveReleasesState(instance, {
|
|
156
|
+
const state = getActiveReleasesState(instance, {resource: {projectId: 'test', dataset: 'test'}})
|
|
157
157
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
158
158
|
expect(state.getCurrent()).toEqual([])
|
|
159
159
|
|
|
@@ -175,7 +175,7 @@ describe('releasesStore', () => {
|
|
|
175
175
|
observable: subject.asObservable(),
|
|
176
176
|
} as StateSource<ReleaseDocument[] | undefined>)
|
|
177
177
|
|
|
178
|
-
const state = getActiveReleasesState(instance, {
|
|
178
|
+
const state = getActiveReleasesState(instance, {resource: {projectId: 'test', dataset: 'test'}})
|
|
179
179
|
|
|
180
180
|
subject.error(new Error('Query failed'))
|
|
181
181
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {type SanityDocument} from '@sanity/types'
|
|
2
2
|
import {map} from 'rxjs'
|
|
3
3
|
|
|
4
|
-
import {type
|
|
4
|
+
import {type DocumentResource, isDatasetResource} from '../config/sanityConfig'
|
|
5
5
|
/*
|
|
6
6
|
* Although this is an import dependency cycle, it is not a logical cycle:
|
|
7
7
|
* 1. releasesStore uses queryStore as a data source
|
|
@@ -11,7 +11,7 @@ import {type DocumentSource, isDatasetSource} from '../config/sanityConfig'
|
|
|
11
11
|
*/
|
|
12
12
|
// eslint-disable-next-line import/no-cycle
|
|
13
13
|
import {getQueryState} from '../query/queryStore'
|
|
14
|
-
import {
|
|
14
|
+
import {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'
|
|
15
15
|
import {type SanityInstance} from '../store/createSanityInstance'
|
|
16
16
|
import {createStateSourceAction, type StateSource} from '../store/createStateSourceAction'
|
|
17
17
|
import {defineStore, type StoreContext} from '../store/defineStore'
|
|
@@ -41,7 +41,7 @@ export interface ReleasesStoreState {
|
|
|
41
41
|
error?: unknown
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export const releasesStore = defineStore<ReleasesStoreState,
|
|
44
|
+
export const releasesStore = defineStore<ReleasesStoreState, BoundResourceKey>({
|
|
45
45
|
name: 'Releases',
|
|
46
46
|
getInitialState: (): ReleasesStoreState => ({
|
|
47
47
|
activeReleases: undefined,
|
|
@@ -56,7 +56,7 @@ export const releasesStore = defineStore<ReleasesStoreState, BoundSourceKey>({
|
|
|
56
56
|
* Get the active releases from the store.
|
|
57
57
|
* @internal
|
|
58
58
|
*/
|
|
59
|
-
const _getActiveReleasesState =
|
|
59
|
+
const _getActiveReleasesState = bindActionByResource(
|
|
60
60
|
releasesStore,
|
|
61
61
|
createStateSourceAction({
|
|
62
62
|
selector: ({state}, _?) => state.activeReleases,
|
|
@@ -69,9 +69,9 @@ const _getActiveReleasesState = bindActionBySource(
|
|
|
69
69
|
*/
|
|
70
70
|
export const getActiveReleasesState = (
|
|
71
71
|
instance: SanityInstance,
|
|
72
|
-
options?: {
|
|
72
|
+
options?: {resource?: DocumentResource},
|
|
73
73
|
): StateSource<ReleaseDocument[] | undefined> =>
|
|
74
|
-
//
|
|
74
|
+
// bindActionByResource keyFn destructures { resource } from the first param, so pass {} when no options
|
|
75
75
|
_getActiveReleasesState(instance, options ?? {})
|
|
76
76
|
|
|
77
77
|
const RELEASES_QUERY = 'releases::all()'
|
|
@@ -79,12 +79,12 @@ const RELEASES_QUERY = 'releases::all()'
|
|
|
79
79
|
const subscribeToReleases = ({
|
|
80
80
|
instance,
|
|
81
81
|
state,
|
|
82
|
-
key: {
|
|
83
|
-
}: StoreContext<ReleasesStoreState,
|
|
82
|
+
key: {resource},
|
|
83
|
+
}: StoreContext<ReleasesStoreState, BoundResourceKey>) => {
|
|
84
84
|
const {observable: releases$} = getQueryState<ReleaseDocument[]>(instance, {
|
|
85
85
|
query: RELEASES_QUERY,
|
|
86
86
|
perspective: 'raw',
|
|
87
|
-
|
|
87
|
+
resource: resource && !isDatasetResource(resource) ? resource : undefined,
|
|
88
88
|
tag: 'releases',
|
|
89
89
|
})
|
|
90
90
|
return releases$
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
2
2
|
|
|
3
|
-
import {type
|
|
3
|
+
import {type DocumentResource} from '../config/sanityConfig'
|
|
4
4
|
import {
|
|
5
5
|
bindActionByDataset,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
bindActionByResource,
|
|
7
|
+
bindActionByResourceAndPerspective,
|
|
8
8
|
bindActionGlobally,
|
|
9
9
|
createActionBinder,
|
|
10
10
|
} from './createActionBinder'
|
|
@@ -161,28 +161,28 @@ describe('bindActionGlobally', () => {
|
|
|
161
161
|
})
|
|
162
162
|
})
|
|
163
163
|
|
|
164
|
-
describe('
|
|
165
|
-
it('should throw an error when provided an invalid
|
|
164
|
+
describe('bindActionByResource', () => {
|
|
165
|
+
it('should throw an error when provided an invalid resource', () => {
|
|
166
166
|
const storeDefinition = {
|
|
167
167
|
name: 'SourceStore',
|
|
168
168
|
getInitialState: () => ({counter: 0}),
|
|
169
169
|
}
|
|
170
170
|
const action = vi.fn((_context) => 'success')
|
|
171
|
-
const boundAction =
|
|
171
|
+
const boundAction = bindActionByResource(storeDefinition, action)
|
|
172
172
|
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
173
173
|
|
|
174
174
|
expect(() =>
|
|
175
|
-
boundAction(instance, {
|
|
176
|
-
).toThrow('Received invalid
|
|
175
|
+
boundAction(instance, {resource: {invalid: 'resource'} as unknown as DocumentResource}),
|
|
176
|
+
).toThrow('Received invalid resource:')
|
|
177
177
|
})
|
|
178
178
|
|
|
179
|
-
it('should throw an error when no
|
|
179
|
+
it('should throw an error when no resource provided and projectId/dataset are missing', () => {
|
|
180
180
|
const storeDefinition = {
|
|
181
181
|
name: 'SourceStore',
|
|
182
182
|
getInitialState: () => ({counter: 0}),
|
|
183
183
|
}
|
|
184
184
|
const action = vi.fn((_context) => 'success')
|
|
185
|
-
const boundAction =
|
|
185
|
+
const boundAction = bindActionByResource(storeDefinition, action)
|
|
186
186
|
const instance = createSanityInstance({projectId: '', dataset: ''})
|
|
187
187
|
|
|
188
188
|
expect(() => boundAction(instance, {})).toThrow(
|
|
@@ -190,47 +190,47 @@ describe('bindActionBySource', () => {
|
|
|
190
190
|
)
|
|
191
191
|
})
|
|
192
192
|
|
|
193
|
-
it('should work correctly with a valid dataset
|
|
193
|
+
it('should work correctly with a valid dataset resource', () => {
|
|
194
194
|
const storeDefinition = {
|
|
195
195
|
name: 'SourceStore',
|
|
196
196
|
getInitialState: () => ({counter: 0}),
|
|
197
197
|
}
|
|
198
198
|
const action = vi.fn((_context) => 'success')
|
|
199
|
-
const boundAction =
|
|
199
|
+
const boundAction = bindActionByResource(storeDefinition, action)
|
|
200
200
|
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
201
201
|
|
|
202
202
|
const result = boundAction(instance, {
|
|
203
|
-
|
|
203
|
+
resource: {projectId: 'proj2', dataset: 'ds2'},
|
|
204
204
|
})
|
|
205
205
|
expect(result).toBe('success')
|
|
206
206
|
})
|
|
207
207
|
})
|
|
208
208
|
|
|
209
|
-
describe('
|
|
210
|
-
it('should throw an error when provided an invalid
|
|
209
|
+
describe('bindActionByResourceAndPerspective', () => {
|
|
210
|
+
it('should throw an error when provided an invalid resource', () => {
|
|
211
211
|
const storeDefinition = {
|
|
212
212
|
name: 'PerspectiveStore',
|
|
213
213
|
getInitialState: () => ({counter: 0}),
|
|
214
214
|
}
|
|
215
215
|
const action = vi.fn((_context) => 'success')
|
|
216
|
-
const boundAction =
|
|
216
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
217
217
|
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
218
218
|
|
|
219
219
|
expect(() =>
|
|
220
220
|
boundAction(instance, {
|
|
221
|
-
|
|
221
|
+
resource: {invalid: 'resource'} as unknown as DocumentResource,
|
|
222
222
|
perspective: 'drafts',
|
|
223
223
|
}),
|
|
224
|
-
).toThrow('Received invalid
|
|
224
|
+
).toThrow('Received invalid resource:')
|
|
225
225
|
})
|
|
226
226
|
|
|
227
|
-
it('should throw an error when no
|
|
227
|
+
it('should throw an error when no resource provided and projectId/dataset are missing', () => {
|
|
228
228
|
const storeDefinition = {
|
|
229
229
|
name: 'PerspectiveStore',
|
|
230
230
|
getInitialState: () => ({counter: 0}),
|
|
231
231
|
}
|
|
232
232
|
const action = vi.fn((_context) => 'success')
|
|
233
|
-
const boundAction =
|
|
233
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
234
234
|
const instance = createSanityInstance({projectId: '', dataset: ''})
|
|
235
235
|
|
|
236
236
|
expect(() => boundAction(instance, {perspective: 'drafts'})).toThrow(
|
|
@@ -238,33 +238,33 @@ describe('bindActionBySourceAndPerspective', () => {
|
|
|
238
238
|
)
|
|
239
239
|
})
|
|
240
240
|
|
|
241
|
-
it('should work correctly with a valid dataset
|
|
241
|
+
it('should work correctly with a valid dataset resource and explicit perspective', () => {
|
|
242
242
|
const storeDefinition = {
|
|
243
243
|
name: 'PerspectiveStore',
|
|
244
244
|
getInitialState: () => ({counter: 0}),
|
|
245
245
|
}
|
|
246
246
|
const action = vi.fn((_context) => 'success')
|
|
247
|
-
const boundAction =
|
|
247
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
248
248
|
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
249
249
|
|
|
250
250
|
const result = boundAction(instance, {
|
|
251
|
-
|
|
251
|
+
resource: {projectId: 'proj2', dataset: 'ds2'},
|
|
252
252
|
perspective: 'drafts',
|
|
253
253
|
})
|
|
254
254
|
expect(result).toBe('success')
|
|
255
255
|
})
|
|
256
256
|
|
|
257
|
-
it('should work correctly with valid dataset
|
|
257
|
+
it('should work correctly with valid dataset resource and no perspective (falls back to drafts)', () => {
|
|
258
258
|
const storeDefinition = {
|
|
259
259
|
name: 'PerspectiveStore',
|
|
260
260
|
getInitialState: () => ({counter: 0}),
|
|
261
261
|
}
|
|
262
262
|
const action = vi.fn((_context) => 'success')
|
|
263
|
-
const boundAction =
|
|
263
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
264
264
|
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
265
265
|
|
|
266
266
|
const result = boundAction(instance, {
|
|
267
|
-
|
|
267
|
+
resource: {projectId: 'proj1', dataset: 'ds1'},
|
|
268
268
|
})
|
|
269
269
|
expect(result).toBe('success')
|
|
270
270
|
})
|
|
@@ -275,7 +275,7 @@ describe('bindActionBySourceAndPerspective', () => {
|
|
|
275
275
|
getInitialState: () => ({counter: 0}),
|
|
276
276
|
}
|
|
277
277
|
const action = vi.fn((context) => context.key)
|
|
278
|
-
const boundAction =
|
|
278
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
279
279
|
const instance = createSanityInstance({
|
|
280
280
|
projectId: 'proj1',
|
|
281
281
|
dataset: 'ds1',
|
|
@@ -300,7 +300,7 @@ describe('bindActionBySourceAndPerspective', () => {
|
|
|
300
300
|
context.state.counter += increment
|
|
301
301
|
return context.state.counter
|
|
302
302
|
})
|
|
303
|
-
const boundAction =
|
|
303
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
304
304
|
// Use unique project/dataset so we don't reuse stores from other tests
|
|
305
305
|
const instance = createSanityInstance({
|
|
306
306
|
projectId: 'perspective-isolation',
|
|
@@ -322,7 +322,7 @@ describe('bindActionBySourceAndPerspective', () => {
|
|
|
322
322
|
getInitialState: () => ({counter: 0}),
|
|
323
323
|
}
|
|
324
324
|
const action = vi.fn((_context) => 'success')
|
|
325
|
-
const boundAction =
|
|
325
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
326
326
|
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
327
327
|
|
|
328
328
|
const result = boundAction(instance, {
|
|
@@ -339,7 +339,7 @@ describe('bindActionBySourceAndPerspective', () => {
|
|
|
339
339
|
)
|
|
340
340
|
})
|
|
341
341
|
|
|
342
|
-
it('should reuse same store when same
|
|
342
|
+
it('should reuse same store when same resource and perspective are used', () => {
|
|
343
343
|
const storeDefinition = {
|
|
344
344
|
name: 'PerspectiveStore',
|
|
345
345
|
getInitialState: () => ({counter: 0}),
|
|
@@ -348,7 +348,7 @@ describe('bindActionBySourceAndPerspective', () => {
|
|
|
348
348
|
context.state.counter += increment
|
|
349
349
|
return context.state.counter
|
|
350
350
|
})
|
|
351
|
-
const boundAction =
|
|
351
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
352
352
|
// Use unique project/dataset so we don't reuse stores from other tests
|
|
353
353
|
const instance = createSanityInstance({
|
|
354
354
|
projectId: 'perspective-reuse',
|