@sanity/sdk 2.8.0 → 3.0.0-rc.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 +228 -239
- package/dist/index.js +287 -454
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/_exports/index.ts +16 -17
- package/src/agent/agentActions.test.ts +60 -16
- package/src/agent/agentActions.ts +29 -20
- package/src/auth/authMode.test.ts +0 -25
- package/src/auth/authMode.ts +3 -6
- package/src/auth/authStore.test.ts +129 -66
- package/src/auth/authStore.ts +9 -11
- package/src/auth/dashboardAuth.ts +2 -2
- package/src/auth/getOrganizationVerificationState.test.ts +10 -11
- package/src/auth/handleAuthCallback.test.ts +0 -12
- package/src/auth/handleAuthCallback.ts +9 -3
- package/src/auth/logout.test.ts +0 -6
- package/src/auth/refreshStampedToken.test.ts +121 -17
- package/src/auth/standaloneAuth.ts +9 -3
- package/src/auth/studioAuth.ts +35 -8
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +9 -3
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +1 -1
- package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +0 -2
- package/src/auth/subscribeToStorageEventsAndSetToken.ts +2 -2
- package/src/auth/utils.ts +33 -0
- package/src/client/clientStore.test.ts +14 -61
- package/src/client/clientStore.ts +52 -28
- package/src/comlink/controller/actions/destroyController.test.ts +1 -4
- package/src/comlink/controller/actions/getOrCreateChannel.test.ts +1 -4
- package/src/comlink/controller/actions/getOrCreateController.test.ts +1 -4
- package/src/comlink/controller/actions/releaseChannel.test.ts +1 -1
- package/src/comlink/controller/comlinkControllerStore.test.ts +1 -4
- package/src/comlink/node/actions/getOrCreateNode.test.ts +1 -4
- package/src/comlink/node/actions/releaseNode.test.ts +1 -4
- package/src/comlink/node/comlinkNodeStore.test.ts +2 -2
- package/src/comlink/node/getNodeState.test.ts +1 -1
- package/src/config/__tests__/handles.test.ts +12 -18
- package/src/config/handles.ts +7 -25
- package/src/config/sanityConfig.ts +99 -52
- package/src/datasets/datasets.test.ts +2 -2
- package/src/datasets/datasets.ts +4 -10
- package/src/document/actions.test.ts +33 -4
- package/src/document/actions.ts +3 -10
- package/src/document/applyDocumentActions.test.ts +17 -18
- package/src/document/applyDocumentActions.ts +9 -12
- package/src/document/documentStore.test.ts +303 -133
- package/src/document/documentStore.ts +70 -61
- package/src/document/permissions.test.ts +44 -8
- package/src/document/processActions.test.ts +77 -7
- package/src/document/reducers.test.ts +35 -3
- package/src/document/sharedListener.test.ts +13 -13
- package/src/document/sharedListener.ts +8 -3
- package/src/favorites/favorites.test.ts +10 -2
- package/src/presence/presenceStore.test.ts +34 -9
- package/src/presence/presenceStore.ts +29 -13
- package/src/preview/previewProjectionUtils.test.ts +192 -0
- package/src/preview/previewProjectionUtils.ts +88 -0
- package/src/preview/{previewStore.ts → types.ts} +6 -25
- package/src/project/project.test.ts +1 -1
- package/src/project/project.ts +14 -20
- package/src/projection/getProjectionState.test.ts +4 -2
- package/src/projection/getProjectionState.ts +2 -21
- package/src/projection/projectionQuery.ts +2 -3
- package/src/projection/projectionStore.test.ts +3 -3
- package/src/projection/resolveProjection.test.ts +2 -1
- package/src/projection/resolveProjection.ts +2 -18
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +2 -2
- package/src/projection/subscribeToStateAndFetchBatches.ts +23 -36
- package/src/projection/types.ts +1 -9
- package/src/projects/projects.test.ts +1 -1
- package/src/query/queryStore.test.ts +86 -28
- package/src/query/queryStore.ts +23 -38
- package/src/releases/getPerspectiveState.test.ts +14 -13
- package/src/releases/getPerspectiveState.ts +6 -6
- package/src/releases/releasesStore.test.ts +21 -6
- package/src/releases/releasesStore.ts +18 -8
- package/src/store/createActionBinder.test.ts +114 -111
- package/src/store/createActionBinder.ts +52 -101
- package/src/store/createSanityInstance.test.ts +13 -83
- package/src/store/createSanityInstance.ts +2 -78
- package/src/store/createStateSourceAction.test.ts +2 -2
- package/src/store/createStateSourceAction.ts +5 -5
- package/src/store/createStoreInstance.test.ts +2 -4
- package/src/users/reducers.test.ts +1 -6
- package/src/users/reducers.ts +2 -2
- package/src/users/types.ts +4 -4
- package/src/users/usersStore.test.ts +12 -15
- package/src/utils/createFetcherStore.test.ts +1 -1
- package/src/utils/logger.test.ts +0 -12
- package/src/utils/logger.ts +3 -8
- package/src/preview/getPreviewState.test.ts +0 -120
- package/src/preview/getPreviewState.ts +0 -91
- 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/resolvePreview.test.ts +0 -47
- package/src/preview/resolvePreview.ts +0 -20
- package/src/preview/subscribeToStateAndFetchBatches.test.ts +0 -221
- package/src/preview/subscribeToStateAndFetchBatches.ts +0 -112
- package/src/preview/util.ts +0 -13
|
@@ -1,10 +1,9 @@
|
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
bindActionBySourceAndPerspective,
|
|
5
|
+
bindActionByResource,
|
|
6
|
+
bindActionByResourceAndPerspective,
|
|
8
7
|
bindActionGlobally,
|
|
9
8
|
createActionBinder,
|
|
10
9
|
} from './createActionBinder'
|
|
@@ -30,7 +29,7 @@ describe('createActionBinder', () => {
|
|
|
30
29
|
return context.state.counter
|
|
31
30
|
})
|
|
32
31
|
const boundAction = binder(storeDefinition, action)
|
|
33
|
-
const instance = createSanityInstance(
|
|
32
|
+
const instance = createSanityInstance()
|
|
34
33
|
|
|
35
34
|
// First call creates store instance
|
|
36
35
|
const result1 = boundAction(instance, 5)
|
|
@@ -44,9 +43,9 @@ describe('createActionBinder', () => {
|
|
|
44
43
|
})
|
|
45
44
|
|
|
46
45
|
it('should create separate store instances for different composite keys', () => {
|
|
47
|
-
const binder = createActionBinder((
|
|
48
|
-
name:
|
|
49
|
-
})
|
|
46
|
+
const binder = createActionBinder((instance, ..._rest) => {
|
|
47
|
+
return {name: instance.instanceId}
|
|
48
|
+
})
|
|
50
49
|
const storeDefinition = {
|
|
51
50
|
name: 'TestStore',
|
|
52
51
|
getInitialState: () => ({counter: 0}),
|
|
@@ -56,8 +55,8 @@ describe('createActionBinder', () => {
|
|
|
56
55
|
return context.state.counter
|
|
57
56
|
})
|
|
58
57
|
const boundAction = binder(storeDefinition, action)
|
|
59
|
-
const instanceA = createSanityInstance(
|
|
60
|
-
const instanceB = createSanityInstance(
|
|
58
|
+
const instanceA = createSanityInstance()
|
|
59
|
+
const instanceB = createSanityInstance()
|
|
61
60
|
|
|
62
61
|
const resultA = boundAction(instanceA, 3)
|
|
63
62
|
const resultB = boundAction(instanceB, 4)
|
|
@@ -75,8 +74,8 @@ describe('createActionBinder', () => {
|
|
|
75
74
|
}
|
|
76
75
|
const action = vi.fn((context) => context.state.counter)
|
|
77
76
|
const boundAction = binder(storeDefinition, action)
|
|
78
|
-
const instance1 = createSanityInstance(
|
|
79
|
-
const instance2 = createSanityInstance(
|
|
77
|
+
const instance1 = createSanityInstance()
|
|
78
|
+
const instance2 = createSanityInstance()
|
|
80
79
|
|
|
81
80
|
// Call action on both instances
|
|
82
81
|
boundAction(instance1)
|
|
@@ -96,34 +95,6 @@ describe('createActionBinder', () => {
|
|
|
96
95
|
})
|
|
97
96
|
})
|
|
98
97
|
|
|
99
|
-
describe('bindActionByDataset', () => {
|
|
100
|
-
it('should work correctly when projectId and dataset are provided', () => {
|
|
101
|
-
const storeDefinition = {
|
|
102
|
-
name: 'DSStore',
|
|
103
|
-
getInitialState: () => ({counter: 0}),
|
|
104
|
-
}
|
|
105
|
-
const action = vi.fn((_context, {value}: {value: string}) => value)
|
|
106
|
-
const boundAction = bindActionByDataset(storeDefinition, action)
|
|
107
|
-
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
108
|
-
const result = boundAction(instance, {value: 'hello'})
|
|
109
|
-
expect(result).toBe('hello')
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
it('should throw an error if projectId or dataset is missing', () => {
|
|
113
|
-
const storeDefinition = {
|
|
114
|
-
name: 'DSStore',
|
|
115
|
-
getInitialState: () => ({counter: 0}),
|
|
116
|
-
}
|
|
117
|
-
const action = vi.fn((_context, _?) => 'fail')
|
|
118
|
-
const boundAction = bindActionByDataset(storeDefinition, action)
|
|
119
|
-
// Instance with missing dataset
|
|
120
|
-
const instance = createSanityInstance({projectId: 'proj1', dataset: ''})
|
|
121
|
-
expect(() => boundAction(instance)).toThrow(
|
|
122
|
-
'This API requires a project ID and dataset configured.',
|
|
123
|
-
)
|
|
124
|
-
})
|
|
125
|
-
})
|
|
126
|
-
|
|
127
98
|
describe('bindActionGlobally', () => {
|
|
128
99
|
it('should work correctly ignoring config in key generation', () => {
|
|
129
100
|
const storeDefinition = {
|
|
@@ -134,8 +105,8 @@ describe('bindActionGlobally', () => {
|
|
|
134
105
|
const boundAction = bindActionGlobally(storeDefinition, action)
|
|
135
106
|
|
|
136
107
|
// Create instances with different configs
|
|
137
|
-
const instance1 = createSanityInstance(
|
|
138
|
-
const instance2 = createSanityInstance(
|
|
108
|
+
const instance1 = createSanityInstance()
|
|
109
|
+
const instance2 = createSanityInstance()
|
|
139
110
|
|
|
140
111
|
// Both instances should use the same store
|
|
141
112
|
const result1 = boundAction(instance1, 42)
|
|
@@ -161,131 +132,135 @@ describe('bindActionGlobally', () => {
|
|
|
161
132
|
})
|
|
162
133
|
})
|
|
163
134
|
|
|
164
|
-
describe('
|
|
165
|
-
it('should
|
|
135
|
+
describe('bindActionByResource', () => {
|
|
136
|
+
it('should resolve from config.defaultResource when no resource provided', () => {
|
|
166
137
|
const storeDefinition = {
|
|
167
|
-
name: '
|
|
138
|
+
name: 'ResourceNamedStore',
|
|
168
139
|
getInitialState: () => ({counter: 0}),
|
|
169
140
|
}
|
|
170
|
-
const action = vi.fn((
|
|
171
|
-
const boundAction =
|
|
172
|
-
const instance = createSanityInstance({
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
).
|
|
141
|
+
const action = vi.fn((context) => context.key)
|
|
142
|
+
const boundAction = bindActionByResource(storeDefinition, action)
|
|
143
|
+
const instance = createSanityInstance({
|
|
144
|
+
defaultResource: {projectId: 'named-proj', dataset: 'named-ds'},
|
|
145
|
+
})
|
|
146
|
+
const result = boundAction(instance, {})
|
|
147
|
+
expect(result).toEqual(
|
|
148
|
+
expect.objectContaining({
|
|
149
|
+
name: 'named-proj.named-ds',
|
|
150
|
+
resource: {projectId: 'named-proj', dataset: 'named-ds'},
|
|
151
|
+
}),
|
|
152
|
+
)
|
|
177
153
|
})
|
|
178
154
|
|
|
179
|
-
it('should throw an error when
|
|
155
|
+
it('should throw an error when provided an invalid resource', () => {
|
|
180
156
|
const storeDefinition = {
|
|
181
|
-
name: '
|
|
157
|
+
name: 'ResourceStore',
|
|
182
158
|
getInitialState: () => ({counter: 0}),
|
|
183
159
|
}
|
|
184
160
|
const action = vi.fn((_context) => 'success')
|
|
185
|
-
const boundAction =
|
|
186
|
-
const instance = createSanityInstance(
|
|
161
|
+
const boundAction = bindActionByResource(storeDefinition, action)
|
|
162
|
+
const instance = createSanityInstance()
|
|
187
163
|
|
|
188
|
-
expect(() =>
|
|
189
|
-
|
|
190
|
-
)
|
|
164
|
+
expect(() =>
|
|
165
|
+
boundAction(instance, {resource: {invalid: 'resource'} as unknown as DocumentResource}),
|
|
166
|
+
).toThrow('Received invalid resource:')
|
|
191
167
|
})
|
|
192
168
|
|
|
193
|
-
it('should work correctly with a valid dataset
|
|
169
|
+
it('should work correctly with a valid dataset resource', () => {
|
|
194
170
|
const storeDefinition = {
|
|
195
|
-
name: '
|
|
171
|
+
name: 'ResourceStore',
|
|
196
172
|
getInitialState: () => ({counter: 0}),
|
|
197
173
|
}
|
|
198
174
|
const action = vi.fn((_context) => 'success')
|
|
199
|
-
const boundAction =
|
|
200
|
-
const instance = createSanityInstance(
|
|
175
|
+
const boundAction = bindActionByResource(storeDefinition, action)
|
|
176
|
+
const instance = createSanityInstance()
|
|
201
177
|
|
|
202
178
|
const result = boundAction(instance, {
|
|
203
|
-
|
|
179
|
+
resource: {projectId: 'proj2', dataset: 'ds2'},
|
|
204
180
|
})
|
|
205
181
|
expect(result).toBe('success')
|
|
206
182
|
})
|
|
207
183
|
})
|
|
208
184
|
|
|
209
|
-
describe('
|
|
210
|
-
it('should throw an error when provided an invalid
|
|
185
|
+
describe('bindActionByResourceAndPerspective', () => {
|
|
186
|
+
it('should throw an error when provided an invalid resource', () => {
|
|
211
187
|
const storeDefinition = {
|
|
212
188
|
name: 'PerspectiveStore',
|
|
213
189
|
getInitialState: () => ({counter: 0}),
|
|
214
190
|
}
|
|
215
191
|
const action = vi.fn((_context) => 'success')
|
|
216
|
-
const boundAction =
|
|
217
|
-
const instance = createSanityInstance(
|
|
192
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
193
|
+
const instance = createSanityInstance()
|
|
218
194
|
|
|
219
195
|
expect(() =>
|
|
220
196
|
boundAction(instance, {
|
|
221
|
-
|
|
197
|
+
resource: {invalid: 'resource'} as unknown as DocumentResource,
|
|
222
198
|
perspective: 'drafts',
|
|
223
199
|
}),
|
|
224
|
-
).toThrow('Received invalid
|
|
200
|
+
).toThrow('Received invalid resource:')
|
|
225
201
|
})
|
|
226
202
|
|
|
227
|
-
it('should
|
|
203
|
+
it('should work correctly with a valid dataset resource and explicit perspective', () => {
|
|
228
204
|
const storeDefinition = {
|
|
229
205
|
name: 'PerspectiveStore',
|
|
230
206
|
getInitialState: () => ({counter: 0}),
|
|
231
207
|
}
|
|
232
208
|
const action = vi.fn((_context) => 'success')
|
|
233
|
-
const boundAction =
|
|
234
|
-
const instance = createSanityInstance(
|
|
209
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
210
|
+
const instance = createSanityInstance()
|
|
235
211
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
212
|
+
const result = boundAction(instance, {
|
|
213
|
+
resource: {projectId: 'proj2', dataset: 'ds2'},
|
|
214
|
+
perspective: 'drafts',
|
|
215
|
+
})
|
|
216
|
+
expect(result).toBe('success')
|
|
239
217
|
})
|
|
240
218
|
|
|
241
|
-
it('should
|
|
219
|
+
it('should throw an error when no resource provided and no default resource configured', () => {
|
|
242
220
|
const storeDefinition = {
|
|
243
221
|
name: 'PerspectiveStore',
|
|
244
222
|
getInitialState: () => ({counter: 0}),
|
|
245
223
|
}
|
|
246
224
|
const action = vi.fn((_context) => 'success')
|
|
247
|
-
const boundAction =
|
|
248
|
-
const instance = createSanityInstance({
|
|
225
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
226
|
+
const instance = createSanityInstance({})
|
|
249
227
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
expect(result).toBe('success')
|
|
228
|
+
// @ts-expect-error - test invalid argument
|
|
229
|
+
expect(() => boundAction(instance, {perspective: 'drafts' as const})).toThrow(
|
|
230
|
+
'No resource provided and no default resource configured.',
|
|
231
|
+
)
|
|
255
232
|
})
|
|
256
233
|
|
|
257
|
-
it('should work correctly with valid dataset
|
|
234
|
+
it('should work correctly with valid dataset resource and no perspective (falls back to drafts)', () => {
|
|
258
235
|
const storeDefinition = {
|
|
259
236
|
name: 'PerspectiveStore',
|
|
260
237
|
getInitialState: () => ({counter: 0}),
|
|
261
238
|
}
|
|
262
239
|
const action = vi.fn((_context) => 'success')
|
|
263
|
-
const boundAction =
|
|
264
|
-
const instance = createSanityInstance(
|
|
240
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
241
|
+
const instance = createSanityInstance()
|
|
265
242
|
|
|
266
243
|
const result = boundAction(instance, {
|
|
267
|
-
|
|
244
|
+
resource: {projectId: 'proj1', dataset: 'ds1'},
|
|
268
245
|
})
|
|
269
246
|
expect(result).toBe('success')
|
|
270
247
|
})
|
|
271
248
|
|
|
272
|
-
it('should
|
|
249
|
+
it('should fall back to "drafts" when options.perspective is not provided', () => {
|
|
273
250
|
const storeDefinition = {
|
|
274
251
|
name: 'PerspectiveStore',
|
|
275
252
|
getInitialState: () => ({counter: 0}),
|
|
276
253
|
}
|
|
277
254
|
const action = vi.fn((context) => context.key)
|
|
278
|
-
const boundAction =
|
|
255
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
279
256
|
const instance = createSanityInstance({
|
|
280
|
-
projectId: 'proj1',
|
|
281
|
-
dataset: 'ds1',
|
|
282
257
|
perspective: 'published',
|
|
283
258
|
})
|
|
284
259
|
|
|
285
|
-
const result = boundAction(instance, {})
|
|
260
|
+
const result = boundAction(instance, {resource: {projectId: 'proj2', dataset: 'ds2'}})
|
|
286
261
|
expect(result).toEqual(
|
|
287
262
|
expect.objectContaining({
|
|
288
|
-
name: '
|
|
263
|
+
name: 'proj2.ds2:published',
|
|
289
264
|
perspective: 'published',
|
|
290
265
|
}),
|
|
291
266
|
)
|
|
@@ -300,15 +275,20 @@ describe('bindActionBySourceAndPerspective', () => {
|
|
|
300
275
|
context.state.counter += increment
|
|
301
276
|
return context.state.counter
|
|
302
277
|
})
|
|
303
|
-
const boundAction =
|
|
278
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
304
279
|
// Use unique project/dataset so we don't reuse stores from other tests
|
|
305
|
-
const instance = createSanityInstance(
|
|
306
|
-
projectId: 'perspective-isolation',
|
|
307
|
-
dataset: 'ds1',
|
|
308
|
-
})
|
|
280
|
+
const instance = createSanityInstance()
|
|
309
281
|
|
|
310
|
-
const resultDrafts = boundAction(
|
|
311
|
-
|
|
282
|
+
const resultDrafts = boundAction(
|
|
283
|
+
instance,
|
|
284
|
+
{resource: {projectId: 'proj3', dataset: 'ds3'}, perspective: 'drafts'},
|
|
285
|
+
3,
|
|
286
|
+
)
|
|
287
|
+
const resultPublished = boundAction(
|
|
288
|
+
instance,
|
|
289
|
+
{resource: {projectId: 'proj3', dataset: 'ds3'}, perspective: 'published'},
|
|
290
|
+
4,
|
|
291
|
+
)
|
|
312
292
|
|
|
313
293
|
expect(resultDrafts).toBe(3)
|
|
314
294
|
expect(resultPublished).toBe(4)
|
|
@@ -322,10 +302,11 @@ describe('bindActionBySourceAndPerspective', () => {
|
|
|
322
302
|
getInitialState: () => ({counter: 0}),
|
|
323
303
|
}
|
|
324
304
|
const action = vi.fn((_context) => 'success')
|
|
325
|
-
const boundAction =
|
|
326
|
-
const instance = createSanityInstance(
|
|
305
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
306
|
+
const instance = createSanityInstance()
|
|
327
307
|
|
|
328
308
|
const result = boundAction(instance, {
|
|
309
|
+
resource: {projectId: 'proj1', dataset: 'ds1'},
|
|
329
310
|
perspective: {releaseName: 'release1'},
|
|
330
311
|
})
|
|
331
312
|
expect(result).toBe('success')
|
|
@@ -339,7 +320,24 @@ describe('bindActionBySourceAndPerspective', () => {
|
|
|
339
320
|
)
|
|
340
321
|
})
|
|
341
322
|
|
|
342
|
-
it('should
|
|
323
|
+
it('should throw when provided a stackable perspective', () => {
|
|
324
|
+
const storeDefinition = {
|
|
325
|
+
name: 'PerspectiveStore',
|
|
326
|
+
getInitialState: () => ({counter: 0}),
|
|
327
|
+
}
|
|
328
|
+
const action = vi.fn((_context) => 'success')
|
|
329
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
330
|
+
const instance = createSanityInstance()
|
|
331
|
+
|
|
332
|
+
expect(() =>
|
|
333
|
+
boundAction(instance, {
|
|
334
|
+
resource: {projectId: 'proj1', dataset: 'ds1'},
|
|
335
|
+
perspective: ['drafts', 'release1'] as unknown as 'drafts',
|
|
336
|
+
}),
|
|
337
|
+
).toThrow('Stackable perspectives are not supported.')
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it('should reuse same store when same resource and perspective are used', () => {
|
|
343
341
|
const storeDefinition = {
|
|
344
342
|
name: 'PerspectiveStore',
|
|
345
343
|
getInitialState: () => ({counter: 0}),
|
|
@@ -348,15 +346,20 @@ describe('bindActionBySourceAndPerspective', () => {
|
|
|
348
346
|
context.state.counter += increment
|
|
349
347
|
return context.state.counter
|
|
350
348
|
})
|
|
351
|
-
const boundAction =
|
|
349
|
+
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
352
350
|
// Use unique project/dataset so we don't reuse stores from other tests
|
|
353
|
-
const instance = createSanityInstance(
|
|
354
|
-
projectId: 'perspective-reuse',
|
|
355
|
-
dataset: 'ds1',
|
|
356
|
-
})
|
|
351
|
+
const instance = createSanityInstance()
|
|
357
352
|
|
|
358
|
-
const result1 = boundAction(
|
|
359
|
-
|
|
353
|
+
const result1 = boundAction(
|
|
354
|
+
instance,
|
|
355
|
+
{resource: {projectId: 'proj4', dataset: 'ds4'}, perspective: 'drafts'},
|
|
356
|
+
2,
|
|
357
|
+
)
|
|
358
|
+
const result2 = boundAction(
|
|
359
|
+
instance,
|
|
360
|
+
{resource: {projectId: 'proj4', dataset: 'ds4'}, perspective: 'drafts'},
|
|
361
|
+
3,
|
|
362
|
+
)
|
|
360
363
|
|
|
361
364
|
expect(result1).toBe(2)
|
|
362
365
|
expect(result2).toBe(5)
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {type ClientPerspective} from '@sanity/client'
|
|
2
|
-
|
|
3
1
|
import {
|
|
4
|
-
type
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
type
|
|
2
|
+
type DocumentResource,
|
|
3
|
+
isCanvasResource,
|
|
4
|
+
isDatasetResource,
|
|
5
|
+
isMediaLibraryResource,
|
|
6
|
+
type PerspectiveHandle,
|
|
7
|
+
type ResourceHandle,
|
|
10
8
|
} from '../config/sanityConfig'
|
|
11
9
|
import {isReleasePerspective} from '../releases/utils/isReleasePerspective'
|
|
12
10
|
import {type SanityInstance} from './createSanityInstance'
|
|
@@ -14,19 +12,13 @@ import {createStoreInstance, type StoreInstance} from './createStoreInstance'
|
|
|
14
12
|
import {type StoreState} from './createStoreState'
|
|
15
13
|
import {type StoreContext, type StoreDefinition} from './defineStore'
|
|
16
14
|
|
|
17
|
-
export interface
|
|
15
|
+
export interface BoundResourceKey {
|
|
18
16
|
name: string
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
export interface BoundPerspectiveKey extends BoundSourceKey {
|
|
22
|
-
perspective: ClientPerspective | ReleasePerspective
|
|
17
|
+
resource: DocumentResource
|
|
23
18
|
}
|
|
24
|
-
export interface
|
|
25
|
-
|
|
26
|
-
projectId: string
|
|
27
|
-
dataset: string
|
|
19
|
+
export interface BoundPerspectiveKey extends BoundResourceKey {
|
|
20
|
+
perspective: NonNullable<PerspectiveHandle['perspective']>
|
|
28
21
|
}
|
|
29
|
-
|
|
30
22
|
/**
|
|
31
23
|
* Defines a store action that operates on a specific state type
|
|
32
24
|
*/
|
|
@@ -123,95 +115,53 @@ export function createActionBinder<
|
|
|
123
115
|
}
|
|
124
116
|
}
|
|
125
117
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
*
|
|
133
|
-
* @throws Error if projectId or dataset is missing from the Sanity instance config
|
|
134
|
-
*
|
|
135
|
-
* @example
|
|
136
|
-
* ```ts
|
|
137
|
-
* // Define a store
|
|
138
|
-
* const documentStore = defineStore<DocumentState>({
|
|
139
|
-
* name: 'Document',
|
|
140
|
-
* getInitialState: () => ({ documents: {} }),
|
|
141
|
-
* // ...
|
|
142
|
-
* })
|
|
143
|
-
*
|
|
144
|
-
* // Create dataset-specific actions
|
|
145
|
-
* export const fetchDocument = bindActionByDataset(
|
|
146
|
-
* documentStore,
|
|
147
|
-
* ({instance, state}, documentId) => {
|
|
148
|
-
* // This state is isolated to the specific project/dataset
|
|
149
|
-
* // ...fetch logic...
|
|
150
|
-
* }
|
|
151
|
-
* )
|
|
152
|
-
*
|
|
153
|
-
* // Usage
|
|
154
|
-
* fetchDocument(sanityInstance, 'doc123')
|
|
155
|
-
* ```
|
|
156
|
-
*/
|
|
157
|
-
export const bindActionByDataset = createActionBinder<
|
|
158
|
-
BoundDatasetKey,
|
|
159
|
-
[(object & {projectId?: string; dataset?: string})?, ...unknown[]]
|
|
160
|
-
>((instance, options) => {
|
|
161
|
-
const projectId = options?.projectId ?? instance.config.projectId
|
|
162
|
-
const dataset = options?.dataset ?? instance.config.dataset
|
|
163
|
-
if (!projectId || !dataset) {
|
|
164
|
-
throw new Error('This API requires a project ID and dataset configured.')
|
|
165
|
-
}
|
|
166
|
-
return {name: `${projectId}.${dataset}`, projectId, dataset}
|
|
167
|
-
})
|
|
118
|
+
const resourceKeyName = (resource: DocumentResource): string => {
|
|
119
|
+
if (isDatasetResource(resource)) return `${resource.projectId}.${resource.dataset}`
|
|
120
|
+
if (isMediaLibraryResource(resource)) return `media-library:${resource.mediaLibraryId}`
|
|
121
|
+
if (isCanvasResource(resource)) return `canvas:${resource.canvasId}`
|
|
122
|
+
throw new Error(`Received invalid resource: ${JSON.stringify(resource)}`)
|
|
123
|
+
}
|
|
168
124
|
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
name = `${source.projectId}.${source.dataset}`
|
|
176
|
-
} else if (isMediaLibrarySource(source)) {
|
|
177
|
-
name = `media-library:${source.mediaLibraryId}`
|
|
178
|
-
} else if (isCanvasSource(source)) {
|
|
179
|
-
name = `canvas:${source.canvasId}`
|
|
180
|
-
} else {
|
|
181
|
-
throw new Error(`Received invalid source: ${JSON.stringify(source)}`)
|
|
182
|
-
}
|
|
183
|
-
return {name, source: sourceForKey}
|
|
125
|
+
const createResourceKey = (
|
|
126
|
+
instance: SanityInstance,
|
|
127
|
+
resource?: DocumentResource,
|
|
128
|
+
): BoundResourceKey => {
|
|
129
|
+
if (resource) {
|
|
130
|
+
return {name: resourceKeyName(resource), resource}
|
|
184
131
|
}
|
|
185
132
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
133
|
+
const defaultResource = instance.config.defaultResource
|
|
134
|
+
if (!defaultResource) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
'No resource provided and no default resource configured. ' +
|
|
137
|
+
'Provide a "default" resource in your resources map, or pass an explicit resource.',
|
|
138
|
+
)
|
|
190
139
|
}
|
|
191
|
-
return {name:
|
|
140
|
+
return {name: resourceKeyName(defaultResource), resource: defaultResource}
|
|
192
141
|
}
|
|
193
142
|
|
|
194
143
|
/**
|
|
195
|
-
* Binds an action to a store that's scoped to a specific document
|
|
144
|
+
* Binds an action to a store that's scoped to a specific document resource.
|
|
196
145
|
**/
|
|
197
|
-
export const
|
|
198
|
-
|
|
199
|
-
[{
|
|
200
|
-
>((instance, {
|
|
201
|
-
return
|
|
146
|
+
export const bindActionByResource = createActionBinder<
|
|
147
|
+
BoundResourceKey,
|
|
148
|
+
[{resource?: DocumentResource}, ...unknown[]]
|
|
149
|
+
>((instance, {resource}) => {
|
|
150
|
+
return createResourceKey(instance, resource)
|
|
202
151
|
})
|
|
203
152
|
|
|
204
153
|
/**
|
|
205
|
-
* Binds an action to a store that's scoped to a specific document
|
|
154
|
+
* Binds an action to a store that's scoped to a specific document resource and perspective.
|
|
206
155
|
*
|
|
207
156
|
* @remarks
|
|
208
|
-
* This creates actions that operate on state isolated to a specific document
|
|
209
|
-
* Different document
|
|
157
|
+
* This creates actions that operate on state isolated to a specific document resource and perspective.
|
|
158
|
+
* Different document resources and perspectives will have separate states.
|
|
210
159
|
*
|
|
211
160
|
* This is mostly useful for stores that do batch fetching operations, since the query store
|
|
212
161
|
* can isolate single queries by perspective.
|
|
213
162
|
*
|
|
214
|
-
* @throws Error if
|
|
163
|
+
* @throws Error if resource or perspective is missing from the Sanity instance config
|
|
164
|
+
* @throws Error if a stackable perspective (array) is provided, as they are not supported
|
|
215
165
|
*
|
|
216
166
|
* @example
|
|
217
167
|
* ```ts
|
|
@@ -222,11 +172,11 @@ export const bindActionBySource = createActionBinder<
|
|
|
222
172
|
* // ...
|
|
223
173
|
* })
|
|
224
174
|
*
|
|
225
|
-
* // Create
|
|
226
|
-
* export const fetchDocuments =
|
|
175
|
+
* // Create resource-and-perspective-specific actions
|
|
176
|
+
* export const fetchDocuments = bindActionByResourceAndPerspective(
|
|
227
177
|
* documentStore,
|
|
228
178
|
* ({instance, state}, documentId) => {
|
|
229
|
-
* // This state is isolated to the specific document
|
|
179
|
+
* // This state is isolated to the specific document resource and perspective
|
|
230
180
|
* // ...fetch logic...
|
|
231
181
|
* }
|
|
232
182
|
* )
|
|
@@ -235,11 +185,11 @@ export const bindActionBySource = createActionBinder<
|
|
|
235
185
|
* fetchDocument(sanityInstance, 'doc123')
|
|
236
186
|
* ```
|
|
237
187
|
*/
|
|
238
|
-
export const
|
|
188
|
+
export const bindActionByResourceAndPerspective = createActionBinder<
|
|
239
189
|
BoundPerspectiveKey,
|
|
240
|
-
[
|
|
190
|
+
[ResourceHandle, ...unknown[]]
|
|
241
191
|
>((instance, options): BoundPerspectiveKey => {
|
|
242
|
-
const {
|
|
192
|
+
const {resource, perspective} = options
|
|
243
193
|
// TODO: remove reference to instance.config.perspective when we get to v3
|
|
244
194
|
const utilizedPerspective = perspective ?? instance.config.perspective ?? 'drafts'
|
|
245
195
|
let perspectiveKey: string
|
|
@@ -248,14 +198,15 @@ export const bindActionBySourceAndPerspective = createActionBinder<
|
|
|
248
198
|
} else if (typeof utilizedPerspective === 'string') {
|
|
249
199
|
perspectiveKey = utilizedPerspective
|
|
250
200
|
} else {
|
|
251
|
-
|
|
252
|
-
|
|
201
|
+
throw new Error(
|
|
202
|
+
`Stackable perspectives are not supported. Received perspective: ${JSON.stringify(utilizedPerspective)}`,
|
|
203
|
+
)
|
|
253
204
|
}
|
|
254
|
-
const
|
|
205
|
+
const resourceKey = createResourceKey(instance, resource)
|
|
255
206
|
|
|
256
207
|
return {
|
|
257
|
-
name: `${
|
|
258
|
-
|
|
208
|
+
name: `${resourceKey.name}:${perspectiveKey}`,
|
|
209
|
+
resource: resourceKey.resource,
|
|
259
210
|
perspective: utilizedPerspective,
|
|
260
211
|
}
|
|
261
212
|
})
|