@sanity/sdk 2.4.0 → 2.5.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/index.d.ts +35 -90
- package/dist/index.js +237 -111
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
- package/src/auth/authStore.test.ts +13 -13
- package/src/auth/refreshStampedToken.test.ts +16 -16
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +6 -6
- package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +4 -4
- package/src/comlink/controller/actions/destroyController.test.ts +2 -2
- package/src/comlink/controller/actions/getOrCreateChannel.test.ts +6 -6
- package/src/comlink/controller/actions/getOrCreateController.test.ts +5 -5
- package/src/comlink/controller/actions/getOrCreateController.ts +1 -1
- package/src/comlink/controller/actions/releaseChannel.test.ts +3 -2
- package/src/comlink/controller/comlinkControllerStore.test.ts +4 -4
- package/src/comlink/node/actions/getOrCreateNode.test.ts +7 -7
- package/src/comlink/node/actions/releaseNode.test.ts +2 -2
- package/src/comlink/node/comlinkNodeStore.test.ts +4 -3
- package/src/config/sanityConfig.ts +8 -3
- package/src/document/actions.ts +11 -7
- package/src/document/applyDocumentActions.test.ts +9 -6
- package/src/document/applyDocumentActions.ts +9 -49
- package/src/document/documentStore.test.ts +128 -115
- package/src/document/documentStore.ts +40 -10
- package/src/document/permissions.test.ts +9 -9
- package/src/document/permissions.ts +17 -7
- package/src/document/processActions.test.ts +248 -0
- package/src/document/processActions.ts +173 -0
- package/src/document/reducers.ts +13 -6
- package/src/presence/presenceStore.ts +13 -7
- package/src/preview/previewStore.test.ts +10 -2
- package/src/preview/previewStore.ts +2 -1
- package/src/preview/subscribeToStateAndFetchBatches.test.ts +8 -5
- package/src/preview/subscribeToStateAndFetchBatches.ts +9 -3
- package/src/projection/projectionStore.test.ts +18 -2
- package/src/projection/projectionStore.ts +2 -1
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +6 -5
- package/src/projection/subscribeToStateAndFetchBatches.ts +9 -3
- package/src/releases/getPerspectiveState.ts +2 -2
- package/src/releases/releasesStore.ts +10 -4
- package/src/store/createActionBinder.test.ts +8 -6
- package/src/store/createActionBinder.ts +44 -29
- package/src/store/createStateSourceAction.test.ts +12 -11
- package/src/store/createStateSourceAction.ts +6 -6
- package/src/store/createStoreInstance.test.ts +29 -16
- package/src/store/createStoreInstance.ts +6 -5
- package/src/store/defineStore.test.ts +1 -1
- package/src/store/defineStore.ts +12 -7
|
@@ -4,7 +4,7 @@ import {combineLatest, distinctUntilChanged, filter, map, of, Subscription, swit
|
|
|
4
4
|
|
|
5
5
|
import {getTokenState} from '../auth/authStore'
|
|
6
6
|
import {getClient} from '../client/clientStore'
|
|
7
|
-
import {bindActionByDataset} from '../store/createActionBinder'
|
|
7
|
+
import {bindActionByDataset, type BoundDatasetKey} from '../store/createActionBinder'
|
|
8
8
|
import {createStateSourceAction, type SelectorContext} from '../store/createStateSourceAction'
|
|
9
9
|
import {defineStore, type StoreContext} from '../store/defineStore'
|
|
10
10
|
import {type SanityUser} from '../users/types'
|
|
@@ -23,15 +23,21 @@ const getInitialState = (): PresenceStoreState => ({
|
|
|
23
23
|
})
|
|
24
24
|
|
|
25
25
|
/** @public */
|
|
26
|
-
export const presenceStore = defineStore<PresenceStoreState>({
|
|
26
|
+
export const presenceStore = defineStore<PresenceStoreState, BoundDatasetKey>({
|
|
27
27
|
name: 'presence',
|
|
28
28
|
getInitialState,
|
|
29
|
-
initialize: (context: StoreContext<PresenceStoreState>) => {
|
|
30
|
-
const {
|
|
29
|
+
initialize: (context: StoreContext<PresenceStoreState, BoundDatasetKey>) => {
|
|
30
|
+
const {
|
|
31
|
+
instance,
|
|
32
|
+
state,
|
|
33
|
+
key: {projectId, dataset},
|
|
34
|
+
} = context
|
|
31
35
|
const sessionId = crypto.randomUUID()
|
|
32
36
|
|
|
33
37
|
const client = getClient(instance, {
|
|
34
38
|
apiVersion: '2022-06-30',
|
|
39
|
+
projectId,
|
|
40
|
+
dataset,
|
|
35
41
|
})
|
|
36
42
|
|
|
37
43
|
const token$ = getTokenState(instance).observable.pipe(distinctUntilChanged())
|
|
@@ -112,9 +118,9 @@ const selectPresence = createSelector(
|
|
|
112
118
|
export const getPresence = bindActionByDataset(
|
|
113
119
|
presenceStore,
|
|
114
120
|
createStateSourceAction({
|
|
115
|
-
selector: (context: SelectorContext<PresenceStoreState
|
|
121
|
+
selector: (context: SelectorContext<PresenceStoreState>, _?): UserPresence[] =>
|
|
116
122
|
selectPresence(context.state),
|
|
117
|
-
onSubscribe: (context) => {
|
|
123
|
+
onSubscribe: (context: StoreContext<PresenceStoreState, BoundDatasetKey>, _?) => {
|
|
118
124
|
const userIds$ = context.state.observable.pipe(
|
|
119
125
|
map((state) =>
|
|
120
126
|
Array.from(state.locations.values())
|
|
@@ -134,7 +140,7 @@ export const getPresence = bindActionByDataset(
|
|
|
134
140
|
getUserState(context.instance, {
|
|
135
141
|
userId,
|
|
136
142
|
resourceType: 'project',
|
|
137
|
-
projectId: context.
|
|
143
|
+
projectId: context.key.projectId,
|
|
138
144
|
}).pipe(filter((v): v is NonNullable<typeof v> => !!v)),
|
|
139
145
|
)
|
|
140
146
|
return combineLatest(userObservables)
|
|
@@ -18,9 +18,17 @@ describe('previewStore', () => {
|
|
|
18
18
|
|
|
19
19
|
const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
20
20
|
|
|
21
|
-
const {state, dispose} = createStoreInstance(
|
|
21
|
+
const {state, dispose} = createStoreInstance(
|
|
22
|
+
instance,
|
|
23
|
+
{name: 'p.d', projectId: 'p', dataset: 'd'},
|
|
24
|
+
previewStore,
|
|
25
|
+
)
|
|
22
26
|
|
|
23
|
-
expect(subscribeToStateAndFetchBatches).toHaveBeenCalledWith({
|
|
27
|
+
expect(subscribeToStateAndFetchBatches).toHaveBeenCalledWith({
|
|
28
|
+
instance,
|
|
29
|
+
state,
|
|
30
|
+
key: {name: 'p.d', projectId: 'p', dataset: 'd'},
|
|
31
|
+
})
|
|
24
32
|
|
|
25
33
|
dispose()
|
|
26
34
|
instance.dispose()
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {type BoundDatasetKey} from '../store/createActionBinder'
|
|
1
2
|
import {defineStore} from '../store/defineStore'
|
|
2
3
|
import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
|
|
3
4
|
|
|
@@ -79,7 +80,7 @@ export interface PreviewStoreState {
|
|
|
79
80
|
subscriptions: {[TDocumentId in string]?: {[TSubscriptionId in string]?: true}}
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
export const previewStore = defineStore<PreviewStoreState>({
|
|
83
|
+
export const previewStore = defineStore<PreviewStoreState, BoundDatasetKey>({
|
|
83
84
|
name: 'Preview',
|
|
84
85
|
getInitialState() {
|
|
85
86
|
return {
|
|
@@ -2,6 +2,7 @@ import {NEVER, Observable, type Observer} from 'rxjs'
|
|
|
2
2
|
import {describe, expect, it, vi} from 'vitest'
|
|
3
3
|
|
|
4
4
|
import {getQueryState, resolveQuery} from '../query/queryStore'
|
|
5
|
+
import {type BoundDatasetKey} from '../store/createActionBinder'
|
|
5
6
|
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
6
7
|
import {type StateSource} from '../store/createStateSourceAction'
|
|
7
8
|
import {createStoreState, type StoreState} from '../store/createStoreState'
|
|
@@ -14,6 +15,7 @@ vi.mock('../query/queryStore')
|
|
|
14
15
|
describe('subscribeToStateAndFetchBatches', () => {
|
|
15
16
|
let instance: SanityInstance
|
|
16
17
|
let state: StoreState<PreviewStoreState>
|
|
18
|
+
let key: BoundDatasetKey
|
|
17
19
|
|
|
18
20
|
beforeEach(() => {
|
|
19
21
|
vi.clearAllMocks()
|
|
@@ -22,6 +24,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
22
24
|
subscriptions: {},
|
|
23
25
|
values: {},
|
|
24
26
|
})
|
|
27
|
+
key = {name: 'test.test', projectId: 'test', dataset: 'test'}
|
|
25
28
|
|
|
26
29
|
vi.mocked(getQueryState).mockReturnValue({
|
|
27
30
|
getCurrent: () => undefined,
|
|
@@ -36,7 +39,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
36
39
|
})
|
|
37
40
|
|
|
38
41
|
it('batches rapid subscription changes into single requests', async () => {
|
|
39
|
-
const subscription = subscribeToStateAndFetchBatches({instance, state})
|
|
42
|
+
const subscription = subscribeToStateAndFetchBatches({instance, state, key})
|
|
40
43
|
|
|
41
44
|
// Add multiple subscriptions rapidly
|
|
42
45
|
state.set('addSubscription1', {
|
|
@@ -77,7 +80,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
77
80
|
observable: new Observable(subscriber),
|
|
78
81
|
} as StateSource<PreviewQueryResult[] | undefined>)
|
|
79
82
|
|
|
80
|
-
const subscription = subscribeToStateAndFetchBatches({instance, state})
|
|
83
|
+
const subscription = subscribeToStateAndFetchBatches({instance, state, key})
|
|
81
84
|
|
|
82
85
|
expect(subscriber).not.toHaveBeenCalled()
|
|
83
86
|
|
|
@@ -126,7 +129,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
126
129
|
subscriptions: {doc1: {sub1: true}},
|
|
127
130
|
})
|
|
128
131
|
|
|
129
|
-
const subscription = subscribeToStateAndFetchBatches({instance, state})
|
|
132
|
+
const subscription = subscribeToStateAndFetchBatches({instance, state, key})
|
|
130
133
|
|
|
131
134
|
// Add a subscription for a document already in the batch
|
|
132
135
|
state.set('addSubscriptionAlreadyInBatch', (prev) => ({
|
|
@@ -152,7 +155,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
152
155
|
|
|
153
156
|
it('cancels and restarts fetches when subscription set changes', async () => {
|
|
154
157
|
const abortSpy = vi.spyOn(AbortController.prototype, 'abort')
|
|
155
|
-
const subscription = subscribeToStateAndFetchBatches({instance, state})
|
|
158
|
+
const subscription = subscribeToStateAndFetchBatches({instance, state, key})
|
|
156
159
|
|
|
157
160
|
// Add initial subscription
|
|
158
161
|
state.set('addSubscription1', {
|
|
@@ -182,7 +185,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
182
185
|
observable: new Observable(subscriber),
|
|
183
186
|
} as StateSource<PreviewQueryResult[] | undefined>)
|
|
184
187
|
|
|
185
|
-
const subscription = subscribeToStateAndFetchBatches({instance, state})
|
|
188
|
+
const subscription = subscribeToStateAndFetchBatches({instance, state, key})
|
|
186
189
|
|
|
187
190
|
// Add a subscription
|
|
188
191
|
state.set('addSubscription', {
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from 'rxjs'
|
|
16
16
|
|
|
17
17
|
import {getQueryState, resolveQuery} from '../query/queryStore'
|
|
18
|
+
import {type BoundDatasetKey} from '../store/createActionBinder'
|
|
18
19
|
import {type StoreContext} from '../store/defineStore'
|
|
19
20
|
import {createPreviewQuery, processPreviewQuery} from './previewQuery'
|
|
20
21
|
import {type PreviewQueryResult, type PreviewStoreState} from './previewStore'
|
|
@@ -28,7 +29,8 @@ const isSetEqual = <T>(a: Set<T>, b: Set<T>) =>
|
|
|
28
29
|
export const subscribeToStateAndFetchBatches = ({
|
|
29
30
|
state,
|
|
30
31
|
instance,
|
|
31
|
-
|
|
32
|
+
key: {projectId, dataset},
|
|
33
|
+
}: StoreContext<PreviewStoreState, BoundDatasetKey>): Subscription => {
|
|
32
34
|
const newSubscriberIds$ = state.observable.pipe(
|
|
33
35
|
map(({subscriptions}) => new Set(Object.keys(subscriptions))),
|
|
34
36
|
distinctUntilChanged(isSetEqual),
|
|
@@ -64,6 +66,8 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
64
66
|
params,
|
|
65
67
|
tag: PREVIEW_TAG,
|
|
66
68
|
perspective: PREVIEW_PERSPECTIVE,
|
|
69
|
+
projectId,
|
|
70
|
+
dataset,
|
|
67
71
|
})
|
|
68
72
|
const source$ = defer(() => {
|
|
69
73
|
if (getCurrent() === undefined) {
|
|
@@ -74,6 +78,8 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
74
78
|
tag: PREVIEW_TAG,
|
|
75
79
|
perspective: PREVIEW_PERSPECTIVE,
|
|
76
80
|
signal: controller.signal,
|
|
81
|
+
projectId,
|
|
82
|
+
dataset,
|
|
77
83
|
}),
|
|
78
84
|
).pipe(switchMap(() => observable))
|
|
79
85
|
}
|
|
@@ -91,8 +97,8 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
91
97
|
}),
|
|
92
98
|
map(({ids, data}) => ({
|
|
93
99
|
values: processPreviewQuery({
|
|
94
|
-
projectId
|
|
95
|
-
dataset
|
|
100
|
+
projectId,
|
|
101
|
+
dataset,
|
|
96
102
|
ids,
|
|
97
103
|
results: data,
|
|
98
104
|
}),
|
|
@@ -26,10 +26,26 @@ describe('projectionStore', () => {
|
|
|
26
26
|
|
|
27
27
|
const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
28
28
|
|
|
29
|
-
const {state, dispose} = createStoreInstance(
|
|
29
|
+
const {state, dispose} = createStoreInstance(
|
|
30
|
+
instance,
|
|
31
|
+
{
|
|
32
|
+
name: 'p.d',
|
|
33
|
+
projectId: 'p',
|
|
34
|
+
dataset: 'd',
|
|
35
|
+
},
|
|
36
|
+
projectionStore,
|
|
37
|
+
)
|
|
30
38
|
|
|
31
39
|
expect(subscribeToStateAndFetchBatches).toHaveBeenCalledOnce()
|
|
32
|
-
expect(subscribeToStateAndFetchBatches).toHaveBeenCalledWith({
|
|
40
|
+
expect(subscribeToStateAndFetchBatches).toHaveBeenCalledWith({
|
|
41
|
+
instance,
|
|
42
|
+
state,
|
|
43
|
+
key: {
|
|
44
|
+
name: 'p.d',
|
|
45
|
+
projectId: 'p',
|
|
46
|
+
dataset: 'd',
|
|
47
|
+
},
|
|
48
|
+
})
|
|
33
49
|
|
|
34
50
|
dispose()
|
|
35
51
|
instance.dispose()
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import {type BoundDatasetKey} from '../store/createActionBinder'
|
|
1
2
|
import {defineStore} from '../store/defineStore'
|
|
2
3
|
import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
|
|
3
4
|
import {type ProjectionStoreState} from './types'
|
|
4
5
|
|
|
5
|
-
export const projectionStore = defineStore<ProjectionStoreState>({
|
|
6
|
+
export const projectionStore = defineStore<ProjectionStoreState, BoundDatasetKey>({
|
|
6
7
|
name: 'Projection',
|
|
7
8
|
getInitialState() {
|
|
8
9
|
return {
|
|
@@ -15,6 +15,7 @@ vi.mock('../query/queryStore')
|
|
|
15
15
|
describe('subscribeToStateAndFetchBatches', () => {
|
|
16
16
|
let instance: SanityInstance
|
|
17
17
|
let state: StoreState<ProjectionStoreState>
|
|
18
|
+
const key = {name: 'test.test', projectId: 'test', dataset: 'test'}
|
|
18
19
|
|
|
19
20
|
beforeEach(() => {
|
|
20
21
|
vi.clearAllMocks()
|
|
@@ -38,7 +39,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
38
39
|
})
|
|
39
40
|
|
|
40
41
|
it('batches rapid subscription changes into single requests', async () => {
|
|
41
|
-
const subscription = subscribeToStateAndFetchBatches({instance, state})
|
|
42
|
+
const subscription = subscribeToStateAndFetchBatches({instance, state, key})
|
|
42
43
|
const projection = '{title, description}'
|
|
43
44
|
const projectionHash = hashString(projection)
|
|
44
45
|
|
|
@@ -95,7 +96,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
95
96
|
observable: new Observable(subscriber),
|
|
96
97
|
} as StateSource<ProjectionQueryResult[] | undefined>)
|
|
97
98
|
|
|
98
|
-
const subscription = subscribeToStateAndFetchBatches({instance, state})
|
|
99
|
+
const subscription = subscribeToStateAndFetchBatches({instance, state, key})
|
|
99
100
|
const projection = '{title}'
|
|
100
101
|
const projectionHash = hashString(projection)
|
|
101
102
|
|
|
@@ -166,7 +167,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
166
167
|
subscriptions: {doc1: {[projectionHash]: {sub1: true}}},
|
|
167
168
|
})
|
|
168
169
|
|
|
169
|
-
const subscription = subscribeToStateAndFetchBatches({instance, state})
|
|
170
|
+
const subscription = subscribeToStateAndFetchBatches({instance, state, key})
|
|
170
171
|
|
|
171
172
|
// Add another subscription for doc1 (same hash)
|
|
172
173
|
state.set('addSubscriptionAlreadyInBatch', (prev) => ({
|
|
@@ -218,7 +219,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
218
219
|
|
|
219
220
|
it('cancels and restarts fetches when subscription set changes', async () => {
|
|
220
221
|
const abortSpy = vi.spyOn(AbortController.prototype, 'abort')
|
|
221
|
-
const subscription = subscribeToStateAndFetchBatches({instance, state})
|
|
222
|
+
const subscription = subscribeToStateAndFetchBatches({instance, state, key})
|
|
222
223
|
const projection = '{title, description}'
|
|
223
224
|
const projectionHash = hashString(projection)
|
|
224
225
|
const projection2 = '{_id}' // Different projection
|
|
@@ -266,7 +267,7 @@ describe('subscribeToStateAndFetchBatches', () => {
|
|
|
266
267
|
observable: new Observable(subscriber),
|
|
267
268
|
} as StateSource<ProjectionQueryResult[] | undefined>)
|
|
268
269
|
|
|
269
|
-
const subscription = subscribeToStateAndFetchBatches({instance, state})
|
|
270
|
+
const subscription = subscribeToStateAndFetchBatches({instance, state, key})
|
|
270
271
|
const projection = '{title, description}'
|
|
271
272
|
const projectionHash = hashString(projection)
|
|
272
273
|
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from 'rxjs'
|
|
18
18
|
|
|
19
19
|
import {getQueryState, resolveQuery} from '../query/queryStore'
|
|
20
|
+
import {type BoundDatasetKey} from '../store/createActionBinder'
|
|
20
21
|
import {type StoreContext} from '../store/defineStore'
|
|
21
22
|
import {
|
|
22
23
|
createProjectionQuery,
|
|
@@ -34,7 +35,8 @@ const isSetEqual = <T>(a: Set<T>, b: Set<T>) =>
|
|
|
34
35
|
export const subscribeToStateAndFetchBatches = ({
|
|
35
36
|
state,
|
|
36
37
|
instance,
|
|
37
|
-
|
|
38
|
+
key: {projectId, dataset},
|
|
39
|
+
}: StoreContext<ProjectionStoreState, BoundDatasetKey>): Subscription => {
|
|
38
40
|
const documentProjections$ = state.observable.pipe(
|
|
39
41
|
map((s) => s.documentProjections),
|
|
40
42
|
distinctUntilChanged(isEqual),
|
|
@@ -94,6 +96,8 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
94
96
|
const {getCurrent, observable} = getQueryState<ProjectionQueryResult[]>(instance, {
|
|
95
97
|
query,
|
|
96
98
|
params,
|
|
99
|
+
projectId,
|
|
100
|
+
dataset,
|
|
97
101
|
tag: PROJECTION_TAG,
|
|
98
102
|
perspective: PROJECTION_PERSPECTIVE,
|
|
99
103
|
})
|
|
@@ -104,6 +108,8 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
104
108
|
resolveQuery<ProjectionQueryResult[]>(instance, {
|
|
105
109
|
query,
|
|
106
110
|
params,
|
|
111
|
+
projectId,
|
|
112
|
+
dataset,
|
|
107
113
|
tag: PROJECTION_TAG,
|
|
108
114
|
perspective: PROJECTION_PERSPECTIVE,
|
|
109
115
|
signal: controller.signal,
|
|
@@ -125,8 +131,8 @@ export const subscribeToStateAndFetchBatches = ({
|
|
|
125
131
|
}),
|
|
126
132
|
map(({ids, data}) =>
|
|
127
133
|
processProjectionQuery({
|
|
128
|
-
projectId
|
|
129
|
-
dataset
|
|
134
|
+
projectId,
|
|
135
|
+
dataset,
|
|
130
136
|
ids,
|
|
131
137
|
results: data,
|
|
132
138
|
}),
|
|
@@ -17,13 +17,13 @@ const DEFAULT_PERSPECTIVE = 'drafts'
|
|
|
17
17
|
// Cache for options
|
|
18
18
|
const optionsCache = new Map<string, Map<string, PerspectiveHandle>>()
|
|
19
19
|
|
|
20
|
-
const selectInstancePerspective = (context: SelectorContext<ReleasesStoreState
|
|
20
|
+
const selectInstancePerspective = (context: SelectorContext<ReleasesStoreState>, _?: unknown) =>
|
|
21
21
|
context.instance.config.perspective
|
|
22
22
|
const selectActiveReleases = (context: SelectorContext<ReleasesStoreState>) =>
|
|
23
23
|
context.state.activeReleases
|
|
24
24
|
const selectOptions = (
|
|
25
25
|
_context: SelectorContext<ReleasesStoreState>,
|
|
26
|
-
options?:
|
|
26
|
+
options: PerspectiveHandle & {projectId?: string; dataset?: string},
|
|
27
27
|
) => options
|
|
28
28
|
|
|
29
29
|
const memoizedOptionsSelector = createSelector(
|
|
@@ -3,7 +3,7 @@ import {type SanityDocument} from '@sanity/types'
|
|
|
3
3
|
import {catchError, EMPTY, retry, switchMap, timer} from 'rxjs'
|
|
4
4
|
|
|
5
5
|
import {getClientState} from '../client/clientStore'
|
|
6
|
-
import {bindActionByDataset} from '../store/createActionBinder'
|
|
6
|
+
import {bindActionByDataset, type BoundDatasetKey} from '../store/createActionBinder'
|
|
7
7
|
import {createStateSourceAction} from '../store/createStateSourceAction'
|
|
8
8
|
import {defineStore, type StoreContext} from '../store/defineStore'
|
|
9
9
|
import {listenQuery} from '../utils/listenQuery'
|
|
@@ -32,7 +32,7 @@ export interface ReleasesStoreState {
|
|
|
32
32
|
error?: unknown
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export const releasesStore = defineStore<ReleasesStoreState>({
|
|
35
|
+
export const releasesStore = defineStore<ReleasesStoreState, BoundDatasetKey>({
|
|
36
36
|
name: 'Releases',
|
|
37
37
|
getInitialState: (): ReleasesStoreState => ({
|
|
38
38
|
activeReleases: undefined,
|
|
@@ -50,17 +50,23 @@ export const releasesStore = defineStore<ReleasesStoreState>({
|
|
|
50
50
|
export const getActiveReleasesState = bindActionByDataset(
|
|
51
51
|
releasesStore,
|
|
52
52
|
createStateSourceAction({
|
|
53
|
-
selector: ({state}) => state.activeReleases,
|
|
53
|
+
selector: ({state}, _?) => state.activeReleases,
|
|
54
54
|
}),
|
|
55
55
|
)
|
|
56
56
|
|
|
57
57
|
const RELEASES_QUERY = 'releases::all()'
|
|
58
58
|
const QUERY_PARAMS = {}
|
|
59
59
|
|
|
60
|
-
const subscribeToReleases = ({
|
|
60
|
+
const subscribeToReleases = ({
|
|
61
|
+
instance,
|
|
62
|
+
state,
|
|
63
|
+
key: {projectId, dataset},
|
|
64
|
+
}: StoreContext<ReleasesStoreState, BoundDatasetKey>) => {
|
|
61
65
|
return getClientState(instance, {
|
|
62
66
|
apiVersion: '2025-04-10',
|
|
63
67
|
perspective: 'raw',
|
|
68
|
+
projectId,
|
|
69
|
+
dataset,
|
|
64
70
|
})
|
|
65
71
|
.observable.pipe(
|
|
66
72
|
switchMap((client: SanityClient) =>
|
|
@@ -12,7 +12,7 @@ beforeEach(() => vi.mocked(createStoreInstance).mockClear())
|
|
|
12
12
|
|
|
13
13
|
describe('createActionBinder', () => {
|
|
14
14
|
it('should bind an action and call it with correct context and parameters, using caching', () => {
|
|
15
|
-
const binder = createActionBinder(() => '')
|
|
15
|
+
const binder = createActionBinder((..._rest) => ({name: ''}))
|
|
16
16
|
const storeDefinition = {
|
|
17
17
|
name: 'TestStore',
|
|
18
18
|
getInitialState: () => ({counter: 0}),
|
|
@@ -37,7 +37,9 @@ describe('createActionBinder', () => {
|
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
it('should create separate store instances for different composite keys', () => {
|
|
40
|
-
const binder = createActionBinder(({projectId, dataset}) =>
|
|
40
|
+
const binder = createActionBinder(({config: {projectId, dataset}}, ..._rest) => ({
|
|
41
|
+
name: `${projectId}.${dataset}`,
|
|
42
|
+
}))
|
|
41
43
|
const storeDefinition = {
|
|
42
44
|
name: 'TestStore',
|
|
43
45
|
getInitialState: () => ({counter: 0}),
|
|
@@ -59,7 +61,7 @@ describe('createActionBinder', () => {
|
|
|
59
61
|
})
|
|
60
62
|
|
|
61
63
|
it('should dispose the store instance when the last instance is disposed', () => {
|
|
62
|
-
const binder = createActionBinder(() => '')
|
|
64
|
+
const binder = createActionBinder((..._rest) => ({name: ''}))
|
|
63
65
|
const storeDefinition = {
|
|
64
66
|
name: 'TestStore',
|
|
65
67
|
getInitialState: () => ({counter: 0}),
|
|
@@ -93,10 +95,10 @@ describe('bindActionByDataset', () => {
|
|
|
93
95
|
name: 'DSStore',
|
|
94
96
|
getInitialState: () => ({counter: 0}),
|
|
95
97
|
}
|
|
96
|
-
const action = vi.fn((_context, value: string) => value)
|
|
98
|
+
const action = vi.fn((_context, {value}: {value: string}) => value)
|
|
97
99
|
const boundAction = bindActionByDataset(storeDefinition, action)
|
|
98
100
|
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
99
|
-
const result = boundAction(instance, 'hello')
|
|
101
|
+
const result = boundAction(instance, {value: 'hello'})
|
|
100
102
|
expect(result).toBe('hello')
|
|
101
103
|
})
|
|
102
104
|
|
|
@@ -105,7 +107,7 @@ describe('bindActionByDataset', () => {
|
|
|
105
107
|
name: 'DSStore',
|
|
106
108
|
getInitialState: () => ({counter: 0}),
|
|
107
109
|
}
|
|
108
|
-
const action = vi.fn((_context) => 'fail')
|
|
110
|
+
const action = vi.fn((_context, _?) => 'fail')
|
|
109
111
|
const boundAction = bindActionByDataset(storeDefinition, action)
|
|
110
112
|
// Instance with missing dataset
|
|
111
113
|
const instance = createSanityInstance({projectId: 'proj1', dataset: ''})
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import {type DocumentSource,
|
|
1
|
+
import {type DocumentSource, SOURCE_ID} from '../config/sanityConfig'
|
|
2
2
|
import {type SanityInstance} from './createSanityInstance'
|
|
3
3
|
import {createStoreInstance, type StoreInstance} from './createStoreInstance'
|
|
4
4
|
import {type StoreState} from './createStoreState'
|
|
5
5
|
import {type StoreContext, type StoreDefinition} from './defineStore'
|
|
6
6
|
|
|
7
|
+
export type BoundDatasetKey = {
|
|
8
|
+
name: string
|
|
9
|
+
projectId: string
|
|
10
|
+
dataset: string
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
/**
|
|
8
14
|
* Defines a store action that operates on a specific state type
|
|
9
15
|
*/
|
|
10
|
-
export type StoreAction<TState, TParams extends unknown[], TReturn> = (
|
|
11
|
-
context: StoreContext<TState>,
|
|
16
|
+
export type StoreAction<TState, TParams extends unknown[], TReturn, TKey = unknown> = (
|
|
17
|
+
context: StoreContext<TState, TKey>,
|
|
12
18
|
...params: TParams
|
|
13
19
|
) => TReturn
|
|
14
20
|
|
|
@@ -43,9 +49,10 @@ export type BoundStoreAction<_TState, TParams extends unknown[], TReturn> = (
|
|
|
43
49
|
* )
|
|
44
50
|
* ```
|
|
45
51
|
*/
|
|
46
|
-
export function createActionBinder<
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
export function createActionBinder<
|
|
53
|
+
TKey extends {name: string},
|
|
54
|
+
TKeyParams extends unknown[] = unknown[],
|
|
55
|
+
>(keyFn: (instance: SanityInstance, ...params: TKeyParams) => TKey) {
|
|
49
56
|
const instanceRegistry = new Map<string, Set<string>>()
|
|
50
57
|
const storeRegistry = new Map<string, StoreInstance<unknown>>()
|
|
51
58
|
|
|
@@ -57,12 +64,12 @@ export function createActionBinder<TKeyParams extends unknown[]>(
|
|
|
57
64
|
* @returns A function that executes the action with a Sanity instance
|
|
58
65
|
*/
|
|
59
66
|
return function bindAction<TState, TParams extends TKeyParams, TReturn>(
|
|
60
|
-
storeDefinition: StoreDefinition<TState>,
|
|
61
|
-
action: StoreAction<TState, TParams, TReturn>,
|
|
67
|
+
storeDefinition: StoreDefinition<TState, TKey>,
|
|
68
|
+
action: StoreAction<TState, TParams, TReturn, TKey>,
|
|
62
69
|
): BoundStoreAction<TState, TParams, TReturn> {
|
|
63
70
|
return function boundAction(instance: SanityInstance, ...params: TParams) {
|
|
64
|
-
const
|
|
65
|
-
const compositeKey = storeDefinition.name + (
|
|
71
|
+
const key = keyFn(instance, ...params)
|
|
72
|
+
const compositeKey = storeDefinition.name + (key.name ? `:${key.name}` : '')
|
|
66
73
|
|
|
67
74
|
// Get or create instance set for this composite key
|
|
68
75
|
let instances = instanceRegistry.get(compositeKey)
|
|
@@ -89,12 +96,12 @@ export function createActionBinder<TKeyParams extends unknown[]>(
|
|
|
89
96
|
// Get or create store instance
|
|
90
97
|
let storeInstance = storeRegistry.get(compositeKey)
|
|
91
98
|
if (!storeInstance) {
|
|
92
|
-
storeInstance = createStoreInstance(instance, storeDefinition)
|
|
99
|
+
storeInstance = createStoreInstance(instance, key, storeDefinition)
|
|
93
100
|
storeRegistry.set(compositeKey, storeInstance)
|
|
94
101
|
}
|
|
95
102
|
|
|
96
103
|
// Execute action with store context
|
|
97
|
-
return action({instance, state: storeInstance.state as StoreState<TState
|
|
104
|
+
return action({instance, state: storeInstance.state as StoreState<TState>, key}, ...params)
|
|
98
105
|
}
|
|
99
106
|
}
|
|
100
107
|
}
|
|
@@ -130,31 +137,39 @@ export function createActionBinder<TKeyParams extends unknown[]>(
|
|
|
130
137
|
* fetchDocument(sanityInstance, 'doc123')
|
|
131
138
|
* ```
|
|
132
139
|
*/
|
|
133
|
-
export const bindActionByDataset = createActionBinder<
|
|
140
|
+
export const bindActionByDataset = createActionBinder<
|
|
141
|
+
BoundDatasetKey,
|
|
142
|
+
[(object & {projectId?: string; dataset?: string})?, ...unknown[]]
|
|
143
|
+
>((instance, options) => {
|
|
144
|
+
const projectId = options?.projectId ?? instance.config.projectId
|
|
145
|
+
const dataset = options?.dataset ?? instance.config.dataset
|
|
134
146
|
if (!projectId || !dataset) {
|
|
135
147
|
throw new Error('This API requires a project ID and dataset configured.')
|
|
136
148
|
}
|
|
137
|
-
return `${projectId}.${dataset}
|
|
149
|
+
return {name: `${projectId}.${dataset}`, projectId, dataset}
|
|
138
150
|
})
|
|
139
151
|
|
|
140
152
|
/**
|
|
141
153
|
* Binds an action to a store that's scoped to a specific document source.
|
|
142
154
|
**/
|
|
143
|
-
export const bindActionBySource = createActionBinder<
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
155
|
+
export const bindActionBySource = createActionBinder<
|
|
156
|
+
{name: string},
|
|
157
|
+
[{source?: DocumentSource}, ...unknown[]]
|
|
158
|
+
>((instance, {source}) => {
|
|
159
|
+
if (source) {
|
|
160
|
+
const id = source[SOURCE_ID]
|
|
161
|
+
if (!id) throw new Error('Invalid source (missing ID information)')
|
|
162
|
+
if (Array.isArray(id)) return {name: id.join(':')}
|
|
163
|
+
return {name: `${id.projectId}.${id.dataset}`}
|
|
164
|
+
}
|
|
151
165
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
166
|
+
const {projectId, dataset} = instance.config
|
|
167
|
+
|
|
168
|
+
if (!projectId || !dataset) {
|
|
169
|
+
throw new Error('This API requires a project ID and dataset configured.')
|
|
170
|
+
}
|
|
171
|
+
return {name: `${projectId}.${dataset}`}
|
|
172
|
+
})
|
|
158
173
|
|
|
159
174
|
/**
|
|
160
175
|
* Binds an action to a global store that's shared across all Sanity instances
|
|
@@ -194,4 +209,4 @@ export const bindActionBySource = createActionBinder<[{source?: DocumentSource},
|
|
|
194
209
|
* getCurrentUser(sanityInstance)
|
|
195
210
|
* ```
|
|
196
211
|
*/
|
|
197
|
-
export const bindActionGlobally = createActionBinder
|
|
212
|
+
export const bindActionGlobally = createActionBinder((..._rest) => ({name: 'global'}))
|