@sanity/sdk 3.0.0-rc.0 → 3.0.0-rc.1
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/createGroqSearchFilter.d.ts +1147 -0
- package/dist/_chunks-dts/createGroqSearchFilter.d.ts.map +1 -0
- package/dist/_chunks-es/_internal.js +134 -0
- package/dist/_chunks-es/_internal.js.map +1 -0
- package/dist/_chunks-es/createGroqSearchFilter.js +1864 -0
- package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -0
- package/dist/_chunks-es/telemetryManager.js +93 -0
- package/dist/_chunks-es/telemetryManager.js.map +1 -0
- package/dist/_exports/_internal.d.ts +95 -0
- package/dist/_exports/_internal.d.ts.map +1 -0
- package/dist/_exports/_internal.js +24 -0
- package/dist/_exports/_internal.js.map +1 -0
- package/dist/index.d.ts +1079 -1076
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1967 -2302
- package/dist/index.js.map +1 -1
- package/package.json +48 -47
- package/src/_exports/_internal.ts +26 -0
- package/src/_exports/index.ts +120 -11
- package/src/access/checkPermissions.test.ts +55 -0
- package/src/access/checkPermissions.ts +54 -0
- package/src/agent/agentActions.test.ts +16 -60
- package/src/agent/agentActions.ts +25 -38
- package/src/applications/applications.test-d.ts +83 -0
- package/src/applications/applications.test.ts +130 -0
- package/src/applications/applications.ts +364 -0
- package/src/applications/userApplications.test.ts +126 -0
- package/src/applications/userApplications.ts +261 -0
- package/src/auth/authLogger.ts +30 -0
- package/src/auth/authMode.test.ts +25 -0
- package/src/auth/authMode.ts +6 -3
- package/src/auth/authStore.test.ts +194 -13
- package/src/auth/authStore.ts +59 -26
- package/src/auth/getOrganizationVerificationState.test.ts +18 -17
- package/src/auth/getOrganizationVerificationState.ts +4 -4
- package/src/auth/handleAuthCallback.test.ts +35 -1
- package/src/auth/handleAuthCallback.ts +25 -6
- package/src/auth/logout.test.ts +75 -2
- package/src/auth/logout.ts +23 -4
- package/src/auth/refreshStampedToken.test.ts +219 -469
- package/src/auth/refreshStampedToken.ts +79 -195
- package/src/auth/studioAuth.ts +1 -1
- package/src/auth/studioModeAuth.ts +2 -1
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +146 -13
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +37 -6
- package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +2 -0
- package/src/client/clientStore.test.ts +62 -37
- package/src/client/clientStore.ts +45 -75
- package/src/client/liveEvents.test.ts +151 -0
- package/src/client/liveEvents.ts +107 -0
- package/src/comlink/controller/actions/destroyController.test.ts +4 -1
- package/src/comlink/controller/actions/getOrCreateChannel.test.ts +4 -1
- package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
- package/src/comlink/controller/actions/getOrCreateController.test.ts +4 -1
- package/src/comlink/controller/actions/releaseChannel.test.ts +1 -1
- package/src/comlink/controller/comlinkControllerStore.test.ts +4 -1
- package/src/comlink/node/actions/getOrCreateNode.test.ts +9 -3
- package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
- package/src/comlink/node/actions/releaseNode.test.ts +7 -4
- package/src/comlink/node/comlinkNodeStore.test.ts +2 -2
- package/src/comlink/node/getNodeState.test.ts +1 -1
- package/src/comlink/node/getNodeState.ts +2 -1
- package/src/config/__tests__/handles.test.ts +18 -12
- package/src/config/handles.ts +25 -7
- package/src/config/sanityConfig.ts +109 -79
- package/src/datasets/datasets.test.ts +2 -2
- package/src/datasets/datasets.ts +10 -4
- package/src/document/actions.test.ts +115 -33
- package/src/document/actions.ts +212 -15
- package/src/document/applyDocumentActions.test.ts +38 -12
- package/src/document/applyDocumentActions.ts +30 -9
- package/src/document/documentConstants.ts +7 -0
- package/src/document/documentStore.concurrency.test.ts +1306 -0
- package/src/document/documentStore.test.ts +673 -278
- package/src/document/documentStore.ts +162 -71
- package/src/document/events.test.ts +57 -2
- package/src/document/events.ts +82 -26
- package/src/document/listen.test.ts +56 -0
- package/src/document/listen.ts +74 -21
- package/src/document/listenerEventOperators.test.ts +311 -0
- package/src/document/listenerEventOperators.ts +217 -0
- package/src/document/patchOperations.test.ts +49 -2
- package/src/document/patchOperations.ts +50 -0
- package/src/document/permissions.test.ts +105 -60
- package/src/document/permissions.ts +44 -19
- package/src/document/processActions/create.ts +138 -0
- package/src/document/processActions/delete.ts +100 -0
- package/src/document/processActions/discard.ts +63 -0
- package/src/document/processActions/edit.ts +154 -0
- package/src/document/processActions/processActions.ts +218 -0
- package/src/document/processActions/publish.ts +120 -0
- package/src/document/processActions/releaseArchive.ts +77 -0
- package/src/document/processActions/releaseCreate.ts +59 -0
- package/src/document/processActions/releaseDelete.ts +65 -0
- package/src/document/processActions/releaseEdit.ts +37 -0
- package/src/document/processActions/releasePublish.ts +45 -0
- package/src/document/processActions/releaseSchedule.ts +87 -0
- package/src/document/processActions/releaseUtil.ts +31 -0
- package/src/document/processActions/shared.ts +308 -0
- package/src/document/processActions/unpublish.ts +85 -0
- package/src/document/processActions.test.ts +1129 -121
- package/src/document/reducers.test.ts +639 -64
- package/src/document/reducers.ts +187 -48
- package/src/document/resourceRules.test.ts +178 -0
- package/src/document/resourceRules.ts +117 -0
- package/src/document/sharedListener.test.ts +16 -14
- package/src/document/sharedListener.ts +24 -8
- package/src/document/util.ts +14 -0
- package/src/favorites/favorites.test.ts +5 -6
- package/src/installations/installations.test-d.ts +68 -0
- package/src/installations/installations.test.ts +62 -0
- package/src/installations/installations.ts +202 -0
- package/src/organization/organization.test-d.ts +36 -0
- package/src/organization/organization.test.ts +138 -0
- package/src/organization/organization.ts +138 -0
- package/src/organizations/organizations.test-d.ts +56 -0
- package/src/organizations/organizations.test.ts +150 -0
- package/src/organizations/organizations.ts +107 -0
- package/src/presence/bifurTransport.test.ts +46 -6
- package/src/presence/bifurTransport.ts +19 -2
- package/src/presence/presenceStore.test.ts +94 -23
- package/src/presence/presenceStore.ts +85 -29
- package/src/preview/getPreviewState.test.ts +137 -0
- package/src/preview/getPreviewState.ts +69 -0
- package/src/preview/previewProjectionUtils.test.ts +9 -22
- package/src/preview/previewProjectionUtils.ts +7 -3
- package/src/preview/resolvePreview.test.ts +64 -0
- package/src/preview/resolvePreview.ts +43 -0
- package/src/preview/types.ts +10 -0
- package/src/project/project.test-d.ts +37 -0
- package/src/project/project.test.ts +157 -12
- package/src/project/project.ts +138 -25
- package/src/projection/getProjectionState.test.ts +18 -20
- package/src/projection/getProjectionState.ts +23 -3
- package/src/projection/projectionQuery.ts +5 -5
- package/src/projection/projectionStore.test.ts +1 -1
- package/src/projection/resolveProjection.test.ts +1 -2
- package/src/projection/resolveProjection.ts +16 -0
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
- package/src/projection/subscribeToStateAndFetchBatches.ts +32 -23
- package/src/projection/types.ts +8 -0
- package/src/projects/projects.test-d.ts +32 -0
- package/src/projects/projects.test.ts +108 -42
- package/src/projects/projects.ts +63 -27
- package/src/query/queryStore.test.ts +259 -123
- package/src/query/queryStore.ts +78 -63
- package/src/query/reducers.test.ts +0 -42
- package/src/query/reducers.ts +2 -18
- package/src/releases/getPerspectiveState.test.ts +17 -23
- package/src/releases/getPerspectiveState.ts +8 -20
- package/src/releases/observeReleases.test.ts +127 -0
- package/src/releases/observeReleases.ts +102 -0
- package/src/releases/releasesStore.test.ts +100 -53
- package/src/releases/releasesStore.ts +69 -44
- package/src/releases/utils/sortReleases.test.ts +38 -2
- package/src/releases/utils/sortReleases.ts +15 -10
- package/src/store/createActionBinder.test.ts +87 -90
- package/src/store/createActionBinder.ts +79 -25
- package/src/store/createSanityInstance.test.ts +83 -13
- package/src/store/createSanityInstance.ts +77 -2
- package/src/store/createStateSourceAction.test.ts +64 -2
- package/src/store/createStateSourceAction.ts +39 -44
- package/src/store/createStoreInstance.test.ts +4 -2
- package/src/store/fetcherStore.test-d.ts +121 -0
- package/src/store/fetcherStore.test.ts +299 -0
- package/src/store/fetcherStore.ts +801 -0
- package/src/telemetry/environment.test.ts +119 -0
- package/src/telemetry/environment.ts +92 -0
- package/src/telemetry/events.ts +42 -0
- package/src/telemetry/initTelemetry.test.ts +432 -0
- package/src/telemetry/initTelemetry.ts +218 -0
- package/src/telemetry/telemetryManager.test.ts +327 -0
- package/src/telemetry/telemetryManager.ts +199 -0
- package/src/users/reducers.test.ts +6 -1
- package/src/users/reducers.ts +5 -6
- package/src/users/types.ts +4 -4
- package/src/users/usersStore.test.ts +16 -12
- package/src/users/usersStore.ts +5 -1
- package/src/utils/buildQuery.test.ts +14 -0
- package/src/utils/buildQuery.ts +17 -0
- package/src/utils/createFetcherStore.test.ts +7 -5
- package/src/utils/createFetcherStore.ts +8 -5
- package/src/utils/getStagingApiHost.test.ts +21 -0
- package/src/utils/getStagingApiHost.ts +14 -0
- package/src/utils/ids.test.ts +1 -29
- package/src/utils/ids.ts +0 -10
- package/src/utils/includeShape.ts +36 -0
- package/src/utils/isImportError.test.ts +72 -0
- package/src/utils/isImportError.ts +34 -0
- package/src/utils/logger.test.ts +12 -0
- package/src/utils/logger.ts +8 -3
- package/src/utils/object.test.ts +95 -0
- package/src/utils/object.ts +142 -0
- package/src/utils/setCleanupTimeout.ts +37 -0
- package/src/document/processActions.ts +0 -707
- package/src/utils/defineIntent.test.ts +0 -477
- package/src/utils/defineIntent.ts +0 -244
|
@@ -2,6 +2,7 @@ import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
|
2
2
|
|
|
3
3
|
import {type DocumentResource} from '../config/sanityConfig'
|
|
4
4
|
import {
|
|
5
|
+
bindActionByDataset,
|
|
5
6
|
bindActionByResource,
|
|
6
7
|
bindActionByResourceAndPerspective,
|
|
7
8
|
bindActionGlobally,
|
|
@@ -29,7 +30,7 @@ describe('createActionBinder', () => {
|
|
|
29
30
|
return context.state.counter
|
|
30
31
|
})
|
|
31
32
|
const boundAction = binder(storeDefinition, action)
|
|
32
|
-
const instance = createSanityInstance()
|
|
33
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
33
34
|
|
|
34
35
|
// First call creates store instance
|
|
35
36
|
const result1 = boundAction(instance, 5)
|
|
@@ -43,9 +44,9 @@ describe('createActionBinder', () => {
|
|
|
43
44
|
})
|
|
44
45
|
|
|
45
46
|
it('should create separate store instances for different composite keys', () => {
|
|
46
|
-
const binder = createActionBinder((
|
|
47
|
-
|
|
48
|
-
})
|
|
47
|
+
const binder = createActionBinder(({config: {projectId, dataset}}, ..._rest) => ({
|
|
48
|
+
name: `${projectId}.${dataset}`,
|
|
49
|
+
}))
|
|
49
50
|
const storeDefinition = {
|
|
50
51
|
name: 'TestStore',
|
|
51
52
|
getInitialState: () => ({counter: 0}),
|
|
@@ -55,8 +56,8 @@ describe('createActionBinder', () => {
|
|
|
55
56
|
return context.state.counter
|
|
56
57
|
})
|
|
57
58
|
const boundAction = binder(storeDefinition, action)
|
|
58
|
-
const instanceA = createSanityInstance()
|
|
59
|
-
const instanceB = createSanityInstance()
|
|
59
|
+
const instanceA = createSanityInstance({projectId: 'p1', dataset: 'd1'})
|
|
60
|
+
const instanceB = createSanityInstance({projectId: 'p2', dataset: 'd2'})
|
|
60
61
|
|
|
61
62
|
const resultA = boundAction(instanceA, 3)
|
|
62
63
|
const resultB = boundAction(instanceB, 4)
|
|
@@ -74,8 +75,8 @@ describe('createActionBinder', () => {
|
|
|
74
75
|
}
|
|
75
76
|
const action = vi.fn((context) => context.state.counter)
|
|
76
77
|
const boundAction = binder(storeDefinition, action)
|
|
77
|
-
const instance1 = createSanityInstance()
|
|
78
|
-
const instance2 = createSanityInstance()
|
|
78
|
+
const instance1 = createSanityInstance({projectId: 'p1', dataset: 'd1'})
|
|
79
|
+
const instance2 = createSanityInstance({projectId: 'p1', dataset: 'd1'})
|
|
79
80
|
|
|
80
81
|
// Call action on both instances
|
|
81
82
|
boundAction(instance1)
|
|
@@ -95,6 +96,34 @@ describe('createActionBinder', () => {
|
|
|
95
96
|
})
|
|
96
97
|
})
|
|
97
98
|
|
|
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
|
+
|
|
98
127
|
describe('bindActionGlobally', () => {
|
|
99
128
|
it('should work correctly ignoring config in key generation', () => {
|
|
100
129
|
const storeDefinition = {
|
|
@@ -105,8 +134,8 @@ describe('bindActionGlobally', () => {
|
|
|
105
134
|
const boundAction = bindActionGlobally(storeDefinition, action)
|
|
106
135
|
|
|
107
136
|
// Create instances with different configs
|
|
108
|
-
const instance1 = createSanityInstance()
|
|
109
|
-
const instance2 = createSanityInstance()
|
|
137
|
+
const instance1 = createSanityInstance({projectId: 'any', dataset: 'any'})
|
|
138
|
+
const instance2 = createSanityInstance({projectId: 'different', dataset: 'config'})
|
|
110
139
|
|
|
111
140
|
// Both instances should use the same store
|
|
112
141
|
const result1 = boundAction(instance1, 42)
|
|
@@ -133,47 +162,42 @@ describe('bindActionGlobally', () => {
|
|
|
133
162
|
})
|
|
134
163
|
|
|
135
164
|
describe('bindActionByResource', () => {
|
|
136
|
-
it('should
|
|
165
|
+
it('should throw an error when provided an invalid resource', () => {
|
|
137
166
|
const storeDefinition = {
|
|
138
|
-
name: '
|
|
167
|
+
name: 'SourceStore',
|
|
139
168
|
getInitialState: () => ({counter: 0}),
|
|
140
169
|
}
|
|
141
|
-
const action = vi.fn((
|
|
170
|
+
const action = vi.fn((_context) => 'success')
|
|
142
171
|
const boundAction = bindActionByResource(storeDefinition, action)
|
|
143
|
-
const instance = createSanityInstance({
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
expect.objectContaining({
|
|
149
|
-
name: 'named-proj.named-ds',
|
|
150
|
-
resource: {projectId: 'named-proj', dataset: 'named-ds'},
|
|
151
|
-
}),
|
|
152
|
-
)
|
|
172
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
173
|
+
|
|
174
|
+
expect(() =>
|
|
175
|
+
boundAction(instance, {resource: {invalid: 'resource'} as unknown as DocumentResource}),
|
|
176
|
+
).toThrow('Received invalid resource:')
|
|
153
177
|
})
|
|
154
178
|
|
|
155
|
-
it('should throw an error when provided
|
|
179
|
+
it('should throw an error when no resource provided and projectId/dataset are missing', () => {
|
|
156
180
|
const storeDefinition = {
|
|
157
|
-
name: '
|
|
181
|
+
name: 'SourceStore',
|
|
158
182
|
getInitialState: () => ({counter: 0}),
|
|
159
183
|
}
|
|
160
184
|
const action = vi.fn((_context) => 'success')
|
|
161
185
|
const boundAction = bindActionByResource(storeDefinition, action)
|
|
162
|
-
const instance = createSanityInstance()
|
|
186
|
+
const instance = createSanityInstance({projectId: '', dataset: ''})
|
|
163
187
|
|
|
164
|
-
expect(() =>
|
|
165
|
-
|
|
166
|
-
)
|
|
188
|
+
expect(() => boundAction(instance, {})).toThrow(
|
|
189
|
+
'This API requires a project ID and dataset configured.',
|
|
190
|
+
)
|
|
167
191
|
})
|
|
168
192
|
|
|
169
193
|
it('should work correctly with a valid dataset resource', () => {
|
|
170
194
|
const storeDefinition = {
|
|
171
|
-
name: '
|
|
195
|
+
name: 'SourceStore',
|
|
172
196
|
getInitialState: () => ({counter: 0}),
|
|
173
197
|
}
|
|
174
198
|
const action = vi.fn((_context) => 'success')
|
|
175
199
|
const boundAction = bindActionByResource(storeDefinition, action)
|
|
176
|
-
const instance = createSanityInstance()
|
|
200
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
177
201
|
|
|
178
202
|
const result = boundAction(instance, {
|
|
179
203
|
resource: {projectId: 'proj2', dataset: 'ds2'},
|
|
@@ -190,7 +214,7 @@ describe('bindActionByResourceAndPerspective', () => {
|
|
|
190
214
|
}
|
|
191
215
|
const action = vi.fn((_context) => 'success')
|
|
192
216
|
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
193
|
-
const instance = createSanityInstance()
|
|
217
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
194
218
|
|
|
195
219
|
expect(() =>
|
|
196
220
|
boundAction(instance, {
|
|
@@ -200,35 +224,34 @@ describe('bindActionByResourceAndPerspective', () => {
|
|
|
200
224
|
).toThrow('Received invalid resource:')
|
|
201
225
|
})
|
|
202
226
|
|
|
203
|
-
it('should
|
|
227
|
+
it('should throw an error when no resource provided and projectId/dataset are missing', () => {
|
|
204
228
|
const storeDefinition = {
|
|
205
229
|
name: 'PerspectiveStore',
|
|
206
230
|
getInitialState: () => ({counter: 0}),
|
|
207
231
|
}
|
|
208
232
|
const action = vi.fn((_context) => 'success')
|
|
209
233
|
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
210
|
-
const instance = createSanityInstance()
|
|
234
|
+
const instance = createSanityInstance({projectId: '', dataset: ''})
|
|
211
235
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
})
|
|
216
|
-
expect(result).toBe('success')
|
|
236
|
+
expect(() => boundAction(instance, {perspective: 'drafts'})).toThrow(
|
|
237
|
+
'This API requires a project ID and dataset configured.',
|
|
238
|
+
)
|
|
217
239
|
})
|
|
218
240
|
|
|
219
|
-
it('should
|
|
241
|
+
it('should work correctly with a valid dataset resource and explicit perspective', () => {
|
|
220
242
|
const storeDefinition = {
|
|
221
243
|
name: 'PerspectiveStore',
|
|
222
244
|
getInitialState: () => ({counter: 0}),
|
|
223
245
|
}
|
|
224
246
|
const action = vi.fn((_context) => 'success')
|
|
225
247
|
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
226
|
-
const instance = createSanityInstance({})
|
|
248
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
227
249
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
)
|
|
250
|
+
const result = boundAction(instance, {
|
|
251
|
+
resource: {projectId: 'proj2', dataset: 'ds2'},
|
|
252
|
+
perspective: 'drafts',
|
|
253
|
+
})
|
|
254
|
+
expect(result).toBe('success')
|
|
232
255
|
})
|
|
233
256
|
|
|
234
257
|
it('should work correctly with valid dataset resource and no perspective (falls back to drafts)', () => {
|
|
@@ -238,7 +261,7 @@ describe('bindActionByResourceAndPerspective', () => {
|
|
|
238
261
|
}
|
|
239
262
|
const action = vi.fn((_context) => 'success')
|
|
240
263
|
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
241
|
-
const instance = createSanityInstance()
|
|
264
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
242
265
|
|
|
243
266
|
const result = boundAction(instance, {
|
|
244
267
|
resource: {projectId: 'proj1', dataset: 'ds1'},
|
|
@@ -246,7 +269,7 @@ describe('bindActionByResourceAndPerspective', () => {
|
|
|
246
269
|
expect(result).toBe('success')
|
|
247
270
|
})
|
|
248
271
|
|
|
249
|
-
it('should
|
|
272
|
+
it('should use instance.config.perspective when options.perspective is not provided', () => {
|
|
250
273
|
const storeDefinition = {
|
|
251
274
|
name: 'PerspectiveStore',
|
|
252
275
|
getInitialState: () => ({counter: 0}),
|
|
@@ -254,13 +277,15 @@ describe('bindActionByResourceAndPerspective', () => {
|
|
|
254
277
|
const action = vi.fn((context) => context.key)
|
|
255
278
|
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
256
279
|
const instance = createSanityInstance({
|
|
280
|
+
projectId: 'proj1',
|
|
281
|
+
dataset: 'ds1',
|
|
257
282
|
perspective: 'published',
|
|
258
283
|
})
|
|
259
284
|
|
|
260
|
-
const result = boundAction(instance, {
|
|
285
|
+
const result = boundAction(instance, {})
|
|
261
286
|
expect(result).toEqual(
|
|
262
287
|
expect.objectContaining({
|
|
263
|
-
name: '
|
|
288
|
+
name: 'proj1.ds1:published',
|
|
264
289
|
perspective: 'published',
|
|
265
290
|
}),
|
|
266
291
|
)
|
|
@@ -277,18 +302,13 @@ describe('bindActionByResourceAndPerspective', () => {
|
|
|
277
302
|
})
|
|
278
303
|
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
279
304
|
// Use unique project/dataset so we don't reuse stores from other tests
|
|
280
|
-
const instance = createSanityInstance(
|
|
305
|
+
const instance = createSanityInstance({
|
|
306
|
+
projectId: 'perspective-isolation',
|
|
307
|
+
dataset: 'ds1',
|
|
308
|
+
})
|
|
281
309
|
|
|
282
|
-
const resultDrafts = boundAction(
|
|
283
|
-
|
|
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
|
-
)
|
|
310
|
+
const resultDrafts = boundAction(instance, {perspective: 'drafts'}, 3)
|
|
311
|
+
const resultPublished = boundAction(instance, {perspective: 'published'}, 4)
|
|
292
312
|
|
|
293
313
|
expect(resultDrafts).toBe(3)
|
|
294
314
|
expect(resultPublished).toBe(4)
|
|
@@ -303,10 +323,9 @@ describe('bindActionByResourceAndPerspective', () => {
|
|
|
303
323
|
}
|
|
304
324
|
const action = vi.fn((_context) => 'success')
|
|
305
325
|
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
306
|
-
const instance = createSanityInstance()
|
|
326
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
307
327
|
|
|
308
328
|
const result = boundAction(instance, {
|
|
309
|
-
resource: {projectId: 'proj1', dataset: 'ds1'},
|
|
310
329
|
perspective: {releaseName: 'release1'},
|
|
311
330
|
})
|
|
312
331
|
expect(result).toBe('success')
|
|
@@ -320,23 +339,6 @@ describe('bindActionByResourceAndPerspective', () => {
|
|
|
320
339
|
)
|
|
321
340
|
})
|
|
322
341
|
|
|
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
342
|
it('should reuse same store when same resource and perspective are used', () => {
|
|
341
343
|
const storeDefinition = {
|
|
342
344
|
name: 'PerspectiveStore',
|
|
@@ -348,18 +350,13 @@ describe('bindActionByResourceAndPerspective', () => {
|
|
|
348
350
|
})
|
|
349
351
|
const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
|
|
350
352
|
// Use unique project/dataset so we don't reuse stores from other tests
|
|
351
|
-
const instance = createSanityInstance(
|
|
353
|
+
const instance = createSanityInstance({
|
|
354
|
+
projectId: 'perspective-reuse',
|
|
355
|
+
dataset: 'ds1',
|
|
356
|
+
})
|
|
352
357
|
|
|
353
|
-
const result1 = boundAction(
|
|
354
|
-
|
|
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
|
-
)
|
|
358
|
+
const result1 = boundAction(instance, {perspective: 'drafts'}, 2)
|
|
359
|
+
const result2 = boundAction(instance, {perspective: 'drafts'}, 3)
|
|
363
360
|
|
|
364
361
|
expect(result1).toBe(2)
|
|
365
362
|
expect(result2).toBe(5)
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import {type ClientPerspective} from '@sanity/client'
|
|
2
|
+
|
|
1
3
|
import {
|
|
4
|
+
type DatasetHandle,
|
|
2
5
|
type DocumentResource,
|
|
3
6
|
isCanvasResource,
|
|
4
7
|
isDatasetResource,
|
|
5
8
|
isMediaLibraryResource,
|
|
6
|
-
type
|
|
7
|
-
type ResourceHandle,
|
|
9
|
+
type ReleasePerspective,
|
|
8
10
|
} from '../config/sanityConfig'
|
|
9
11
|
import {isReleasePerspective} from '../releases/utils/isReleasePerspective'
|
|
10
12
|
import {type SanityInstance} from './createSanityInstance'
|
|
@@ -17,8 +19,14 @@ export interface BoundResourceKey {
|
|
|
17
19
|
resource: DocumentResource
|
|
18
20
|
}
|
|
19
21
|
export interface BoundPerspectiveKey extends BoundResourceKey {
|
|
20
|
-
perspective:
|
|
22
|
+
perspective: ClientPerspective | ReleasePerspective
|
|
23
|
+
}
|
|
24
|
+
interface BoundDatasetKey {
|
|
25
|
+
name: string
|
|
26
|
+
projectId: string
|
|
27
|
+
dataset: string
|
|
21
28
|
}
|
|
29
|
+
|
|
22
30
|
/**
|
|
23
31
|
* Defines a store action that operates on a specific state type
|
|
24
32
|
*/
|
|
@@ -115,29 +123,75 @@ export function createActionBinder<
|
|
|
115
123
|
}
|
|
116
124
|
}
|
|
117
125
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Binds an action to a store that's scoped to a specific project and dataset
|
|
128
|
+
*
|
|
129
|
+
* @remarks
|
|
130
|
+
* This creates actions that operate on state isolated to a specific projectId and dataset.
|
|
131
|
+
* Different project/dataset combinations will have separate states.
|
|
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
|
+
})
|
|
124
168
|
|
|
125
169
|
const createResourceKey = (
|
|
126
170
|
instance: SanityInstance,
|
|
127
171
|
resource?: DocumentResource,
|
|
128
172
|
): BoundResourceKey => {
|
|
173
|
+
let name: string | undefined
|
|
174
|
+
let resourceForKey: DocumentResource | undefined
|
|
129
175
|
if (resource) {
|
|
130
|
-
|
|
176
|
+
resourceForKey = resource
|
|
177
|
+
if (isDatasetResource(resource)) {
|
|
178
|
+
name = `${resource.projectId}.${resource.dataset}`
|
|
179
|
+
} else if (isMediaLibraryResource(resource)) {
|
|
180
|
+
name = `media-library:${resource.mediaLibraryId}`
|
|
181
|
+
} else if (isCanvasResource(resource)) {
|
|
182
|
+
name = `canvas:${resource.canvasId}`
|
|
183
|
+
} else {
|
|
184
|
+
throw new Error(`Received invalid resource: ${JSON.stringify(resource)}`)
|
|
185
|
+
}
|
|
186
|
+
return {name, resource: resourceForKey}
|
|
131
187
|
}
|
|
132
188
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
'Provide a "default" resource in your resources map, or pass an explicit resource.',
|
|
138
|
-
)
|
|
189
|
+
// TODO: remove reference to instance.config when we get to v3
|
|
190
|
+
const {projectId, dataset} = instance.config
|
|
191
|
+
if (!projectId || !dataset) {
|
|
192
|
+
throw new Error('This API requires a project ID and dataset configured.')
|
|
139
193
|
}
|
|
140
|
-
return {name:
|
|
194
|
+
return {name: `${projectId}.${dataset}`, resource: {projectId, dataset}}
|
|
141
195
|
}
|
|
142
196
|
|
|
143
197
|
/**
|
|
@@ -145,6 +199,8 @@ const createResourceKey = (
|
|
|
145
199
|
**/
|
|
146
200
|
export const bindActionByResource = createActionBinder<
|
|
147
201
|
BoundResourceKey,
|
|
202
|
+
// this implies resources is optional to keep backwards compatibility
|
|
203
|
+
// but in reality, we'll always pass a resource (since we'll defer to the instance until v3)
|
|
148
204
|
[{resource?: DocumentResource}, ...unknown[]]
|
|
149
205
|
>((instance, {resource}) => {
|
|
150
206
|
return createResourceKey(instance, resource)
|
|
@@ -161,7 +217,6 @@ export const bindActionByResource = createActionBinder<
|
|
|
161
217
|
* can isolate single queries by perspective.
|
|
162
218
|
*
|
|
163
219
|
* @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
|
|
165
220
|
*
|
|
166
221
|
* @example
|
|
167
222
|
* ```ts
|
|
@@ -187,7 +242,7 @@ export const bindActionByResource = createActionBinder<
|
|
|
187
242
|
*/
|
|
188
243
|
export const bindActionByResourceAndPerspective = createActionBinder<
|
|
189
244
|
BoundPerspectiveKey,
|
|
190
|
-
[
|
|
245
|
+
[DatasetHandle, ...unknown[]]
|
|
191
246
|
>((instance, options): BoundPerspectiveKey => {
|
|
192
247
|
const {resource, perspective} = options
|
|
193
248
|
// TODO: remove reference to instance.config.perspective when we get to v3
|
|
@@ -198,15 +253,14 @@ export const bindActionByResourceAndPerspective = createActionBinder<
|
|
|
198
253
|
} else if (typeof utilizedPerspective === 'string') {
|
|
199
254
|
perspectiveKey = utilizedPerspective
|
|
200
255
|
} else {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
)
|
|
256
|
+
// "StackablePerspective", shouldn't be a common case, but just in case
|
|
257
|
+
perspectiveKey = JSON.stringify(utilizedPerspective)
|
|
204
258
|
}
|
|
205
|
-
const
|
|
259
|
+
const sourceKey = createResourceKey(instance, resource)
|
|
206
260
|
|
|
207
261
|
return {
|
|
208
|
-
name: `${
|
|
209
|
-
resource:
|
|
262
|
+
name: `${sourceKey.name}:${perspectiveKey}`,
|
|
263
|
+
resource: sourceKey.resource,
|
|
210
264
|
perspective: utilizedPerspective,
|
|
211
265
|
}
|
|
212
266
|
})
|
|
@@ -5,14 +5,14 @@ import {createSanityInstance} from './createSanityInstance'
|
|
|
5
5
|
|
|
6
6
|
describe('createSanityInstance', () => {
|
|
7
7
|
it('should create an instance with a unique instanceId and given config', () => {
|
|
8
|
-
const instance = createSanityInstance()
|
|
8
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
9
9
|
expect(typeof instance.instanceId).toBe('string')
|
|
10
|
-
expect(instance.config).toEqual({})
|
|
10
|
+
expect(instance.config).toEqual({projectId: 'proj1', dataset: 'ds1'})
|
|
11
11
|
expect(instance.isDisposed()).toBe(false)
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
it('should dispose an instance and call onDispose callbacks', () => {
|
|
15
|
-
const instance = createSanityInstance()
|
|
15
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
16
16
|
const callback = vi.fn()
|
|
17
17
|
instance.onDispose(callback)
|
|
18
18
|
instance.dispose()
|
|
@@ -21,7 +21,7 @@ describe('createSanityInstance', () => {
|
|
|
21
21
|
})
|
|
22
22
|
|
|
23
23
|
it('should not call onDispose callbacks more than once when disposed multiple times', () => {
|
|
24
|
-
const instance = createSanityInstance()
|
|
24
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
25
25
|
const callback = vi.fn()
|
|
26
26
|
instance.onDispose(callback)
|
|
27
27
|
instance.dispose()
|
|
@@ -29,6 +29,60 @@ describe('createSanityInstance', () => {
|
|
|
29
29
|
expect(callback).toHaveBeenCalledTimes(1)
|
|
30
30
|
})
|
|
31
31
|
|
|
32
|
+
it('should create a child instance with merged config and correct parent', () => {
|
|
33
|
+
const parent = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
34
|
+
const child = parent.createChild({dataset: 'ds2'})
|
|
35
|
+
expect(child.config).toEqual({projectId: 'proj1', dataset: 'ds2'})
|
|
36
|
+
expect(child.getParent()).toBe(parent)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should match an instance in the hierarchy using match', () => {
|
|
40
|
+
// three-level hierarchy
|
|
41
|
+
const grandparent = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
42
|
+
const parent = grandparent.createChild({projectId: 'proj2'})
|
|
43
|
+
const child = parent.createChild({dataset: 'ds2'})
|
|
44
|
+
|
|
45
|
+
expect(child.config).toEqual({projectId: 'proj2', dataset: 'ds2'})
|
|
46
|
+
expect(parent.config).toEqual({projectId: 'proj2', dataset: 'ds1'})
|
|
47
|
+
|
|
48
|
+
expect(child.match({dataset: 'ds2'})).toBe(child)
|
|
49
|
+
expect(child.match({projectId: 'proj2'})).toBe(child)
|
|
50
|
+
expect(child.match({projectId: 'proj1'})).toBe(grandparent)
|
|
51
|
+
expect(parent.match({projectId: 'proj1'})).toBe(grandparent)
|
|
52
|
+
expect(grandparent.match({projectId: 'proj1'})).toBe(grandparent)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should match `undefined` when the desired resource ID should not be set on an instance', () => {
|
|
56
|
+
const noProjectOrDataset = createSanityInstance()
|
|
57
|
+
const noDataset = noProjectOrDataset.createChild({projectId: 'proj1'})
|
|
58
|
+
const leaf = noDataset.createChild({dataset: 'ds1'})
|
|
59
|
+
|
|
60
|
+
// no keys means anything (in this case, self) will match
|
|
61
|
+
expect(leaf.match({})).toBe(leaf)
|
|
62
|
+
|
|
63
|
+
// `[resourceId]: undefined` means match an instance with no dataset set
|
|
64
|
+
expect(leaf.match({dataset: undefined})).toBe(noDataset)
|
|
65
|
+
expect(noDataset.match({dataset: undefined})).toBe(noDataset)
|
|
66
|
+
expect(leaf.match({projectId: undefined})).toBe(noProjectOrDataset)
|
|
67
|
+
expect(noDataset.match({projectId: undefined})).toBe(noProjectOrDataset)
|
|
68
|
+
expect(noProjectOrDataset.match({projectId: undefined})).toBe(noProjectOrDataset)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should return undefined when no match is found', () => {
|
|
72
|
+
const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
|
|
73
|
+
expect(instance.match({dataset: 'non-existent'})).toBeUndefined()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('should inherit and merge auth config', () => {
|
|
77
|
+
const parent = createSanityInstance({
|
|
78
|
+
projectId: 'proj1',
|
|
79
|
+
dataset: 'ds1',
|
|
80
|
+
auth: {apiHost: 'api.sanity.work'},
|
|
81
|
+
})
|
|
82
|
+
const child = parent.createChild({auth: {token: 'my-token'}})
|
|
83
|
+
expect(child.config.auth).toEqual({apiHost: 'api.sanity.work', token: 'my-token'})
|
|
84
|
+
})
|
|
85
|
+
|
|
32
86
|
describe('logging', () => {
|
|
33
87
|
const mockHandler: LogHandler = {
|
|
34
88
|
error: vi.fn(),
|
|
@@ -52,30 +106,31 @@ describe('createSanityInstance', () => {
|
|
|
52
106
|
})
|
|
53
107
|
|
|
54
108
|
it('should log instance creation at info level', () => {
|
|
55
|
-
createSanityInstance()
|
|
109
|
+
createSanityInstance({projectId: 'test-proj', dataset: 'test-ds'})
|
|
56
110
|
|
|
57
111
|
expect(mockHandler.info).toHaveBeenCalledWith(
|
|
58
112
|
expect.stringContaining('[INFO] [sdk]'),
|
|
59
113
|
expect.objectContaining({
|
|
60
|
-
|
|
61
|
-
|
|
114
|
+
hasProjectId: true,
|
|
115
|
+
hasDataset: true,
|
|
62
116
|
}),
|
|
63
117
|
)
|
|
64
118
|
})
|
|
65
119
|
|
|
66
120
|
it('should log configuration details at debug level', () => {
|
|
67
|
-
createSanityInstance({
|
|
121
|
+
createSanityInstance({projectId: 'test-proj', dataset: 'test-ds'})
|
|
68
122
|
|
|
69
123
|
expect(mockHandler.debug).toHaveBeenCalledWith(
|
|
70
124
|
expect.stringContaining('[DEBUG] [sdk]'),
|
|
71
125
|
expect.objectContaining({
|
|
72
|
-
|
|
126
|
+
projectId: 'test-proj',
|
|
127
|
+
dataset: 'test-ds',
|
|
73
128
|
}),
|
|
74
129
|
)
|
|
75
130
|
})
|
|
76
131
|
|
|
77
132
|
it('should log instance disposal', () => {
|
|
78
|
-
const instance = createSanityInstance()
|
|
133
|
+
const instance = createSanityInstance({projectId: 'test-proj'})
|
|
79
134
|
vi.clearAllMocks() // Clear creation logs
|
|
80
135
|
|
|
81
136
|
instance.dispose()
|
|
@@ -86,11 +141,26 @@ describe('createSanityInstance', () => {
|
|
|
86
141
|
)
|
|
87
142
|
})
|
|
88
143
|
|
|
89
|
-
it('should
|
|
90
|
-
createSanityInstance()
|
|
144
|
+
it('should log child instance creation at debug level', () => {
|
|
145
|
+
const parent = createSanityInstance({projectId: 'parent-proj'})
|
|
146
|
+
vi.clearAllMocks() // Clear parent creation logs
|
|
147
|
+
|
|
148
|
+
parent.createChild({dataset: 'child-ds'})
|
|
149
|
+
|
|
150
|
+
expect(mockHandler.debug).toHaveBeenCalledWith(
|
|
151
|
+
expect.stringContaining('Creating child instance'),
|
|
152
|
+
expect.objectContaining({
|
|
153
|
+
overridingDataset: true,
|
|
154
|
+
}),
|
|
155
|
+
)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('should include instance context in logs', () => {
|
|
159
|
+
createSanityInstance({projectId: 'my-project', dataset: 'my-dataset'})
|
|
91
160
|
|
|
161
|
+
// Check that logs include the instance context (project and dataset)
|
|
92
162
|
expect(mockHandler.info).toHaveBeenCalledWith(
|
|
93
|
-
expect.stringMatching(/\[
|
|
163
|
+
expect.stringMatching(/\[project:my-project\].*\[dataset:my-dataset\]/),
|
|
94
164
|
expect.anything(),
|
|
95
165
|
)
|
|
96
166
|
})
|