@sanity/sdk 2.4.0 → 2.6.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 +346 -110
- package/dist/index.js +428 -136
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
- package/src/_exports/index.ts +15 -3
- 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/client/clientStore.test.ts +45 -43
- package/src/client/clientStore.ts +23 -9
- 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/loggingConfig.ts +149 -0
- package/src/config/sanityConfig.ts +47 -23
- 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/query/queryStore.ts +3 -1
- 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 +54 -28
- package/src/store/createSanityInstance.test.ts +85 -1
- package/src/store/createSanityInstance.ts +53 -4
- 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
- package/src/utils/logger-usage-example.md +141 -0
- package/src/utils/logger.test.ts +757 -0
- package/src/utils/logger.ts +537 -0
|
@@ -140,6 +140,52 @@ export function processActions({
|
|
|
140
140
|
switch (action.type) {
|
|
141
141
|
case 'document.create': {
|
|
142
142
|
const documentId = getId(action.documentId)
|
|
143
|
+
|
|
144
|
+
if (action.liveEdit) {
|
|
145
|
+
// For liveEdit documents, create directly without draft/published logic
|
|
146
|
+
if (working[documentId]) {
|
|
147
|
+
throw new ActionError({
|
|
148
|
+
documentId,
|
|
149
|
+
transactionId,
|
|
150
|
+
message: `This document already exists.`,
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const newDocBase = {_type: action.documentType, _id: documentId}
|
|
155
|
+
const newDocWorking = {_type: action.documentType, _id: documentId}
|
|
156
|
+
const mutations: Mutation[] = [{create: newDocWorking}]
|
|
157
|
+
|
|
158
|
+
base = processMutations({
|
|
159
|
+
documents: base,
|
|
160
|
+
transactionId,
|
|
161
|
+
mutations: [{create: newDocBase}],
|
|
162
|
+
timestamp,
|
|
163
|
+
})
|
|
164
|
+
working = processMutations({
|
|
165
|
+
documents: working,
|
|
166
|
+
transactionId,
|
|
167
|
+
mutations,
|
|
168
|
+
timestamp,
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
if (!checkGrant(grants.create, working[documentId] as SanityDocument)) {
|
|
172
|
+
throw new PermissionActionError({
|
|
173
|
+
documentId,
|
|
174
|
+
transactionId,
|
|
175
|
+
message: `You do not have permission to create document "${documentId}".`,
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
outgoingMutations.push(...mutations)
|
|
180
|
+
outgoingActions.push({
|
|
181
|
+
actionType: 'sanity.action.document.create',
|
|
182
|
+
publishedId: documentId,
|
|
183
|
+
attributes: newDocWorking,
|
|
184
|
+
})
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Standard draft/published logic
|
|
143
189
|
const draftId = getDraftId(documentId)
|
|
144
190
|
const publishedId = getPublishedId(documentId)
|
|
145
191
|
|
|
@@ -198,6 +244,39 @@ export function processActions({
|
|
|
198
244
|
|
|
199
245
|
case 'document.delete': {
|
|
200
246
|
const documentId = action.documentId
|
|
247
|
+
|
|
248
|
+
if (action.liveEdit) {
|
|
249
|
+
// For liveEdit documents, delete directly
|
|
250
|
+
if (!working[documentId]) {
|
|
251
|
+
throw new ActionError({
|
|
252
|
+
documentId,
|
|
253
|
+
transactionId,
|
|
254
|
+
message: 'The document you are trying to delete does not exist.',
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!checkGrant(grants.update, working[documentId])) {
|
|
259
|
+
throw new PermissionActionError({
|
|
260
|
+
documentId,
|
|
261
|
+
transactionId,
|
|
262
|
+
message: `You do not have permission to delete this document.`,
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const mutations: Mutation[] = [{delete: {id: documentId}}]
|
|
267
|
+
|
|
268
|
+
base = processMutations({documents: base, transactionId, mutations, timestamp})
|
|
269
|
+
working = processMutations({documents: working, transactionId, mutations, timestamp})
|
|
270
|
+
|
|
271
|
+
outgoingMutations.push(...mutations)
|
|
272
|
+
outgoingActions.push({
|
|
273
|
+
actionType: 'sanity.action.document.delete',
|
|
274
|
+
publishedId: documentId,
|
|
275
|
+
})
|
|
276
|
+
continue
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Standard draft/published logic
|
|
201
280
|
const draftId = getDraftId(documentId)
|
|
202
281
|
const publishedId = getPublishedId(documentId)
|
|
203
282
|
|
|
@@ -240,6 +319,16 @@ export function processActions({
|
|
|
240
319
|
|
|
241
320
|
case 'document.discard': {
|
|
242
321
|
const documentId = getId(action.documentId)
|
|
322
|
+
|
|
323
|
+
if (action.liveEdit) {
|
|
324
|
+
throw new ActionError({
|
|
325
|
+
documentId,
|
|
326
|
+
transactionId,
|
|
327
|
+
message: `Cannot discard changes for liveEdit document "${documentId}". LiveEdit documents do not support drafts.`,
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Standard draft/published logic
|
|
243
332
|
const draftId = getDraftId(documentId)
|
|
244
333
|
const mutations: Mutation[] = [{delete: {id: draftId}}]
|
|
245
334
|
|
|
@@ -272,6 +361,70 @@ export function processActions({
|
|
|
272
361
|
|
|
273
362
|
case 'document.edit': {
|
|
274
363
|
const documentId = getId(action.documentId)
|
|
364
|
+
|
|
365
|
+
if (action.liveEdit) {
|
|
366
|
+
// For liveEdit documents, edit directly without draft logic
|
|
367
|
+
const userPatches = action.patches?.map((patch) => ({patch: {id: documentId, ...patch}}))
|
|
368
|
+
|
|
369
|
+
// skip this action if there are no associated patches
|
|
370
|
+
if (!userPatches?.length) continue
|
|
371
|
+
|
|
372
|
+
if (!working[documentId] || !base[documentId]) {
|
|
373
|
+
throw new ActionError({
|
|
374
|
+
documentId,
|
|
375
|
+
transactionId,
|
|
376
|
+
message: `Cannot edit document because it does not exist.`,
|
|
377
|
+
})
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const baseBefore = base[documentId] as SanityDocument
|
|
381
|
+
if (userPatches) {
|
|
382
|
+
base = processMutations({
|
|
383
|
+
documents: base,
|
|
384
|
+
transactionId,
|
|
385
|
+
mutations: userPatches,
|
|
386
|
+
timestamp,
|
|
387
|
+
})
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const baseAfter = base[documentId] as SanityDocument
|
|
391
|
+
const patches = diffValue(baseBefore, baseAfter)
|
|
392
|
+
|
|
393
|
+
const workingBefore = working[documentId] as SanityDocument
|
|
394
|
+
if (!checkGrant(grants.update, workingBefore)) {
|
|
395
|
+
throw new PermissionActionError({
|
|
396
|
+
documentId,
|
|
397
|
+
transactionId,
|
|
398
|
+
message: `You do not have permission to edit document "${documentId}".`,
|
|
399
|
+
})
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const workingMutations = patches.map((patch) => ({patch: {id: documentId, ...patch}}))
|
|
403
|
+
|
|
404
|
+
working = processMutations({
|
|
405
|
+
documents: working,
|
|
406
|
+
transactionId,
|
|
407
|
+
mutations: workingMutations,
|
|
408
|
+
timestamp,
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
outgoingMutations.push(...workingMutations)
|
|
412
|
+
outgoingActions.push(
|
|
413
|
+
...patches.map(
|
|
414
|
+
(patch): HttpAction => ({
|
|
415
|
+
actionType: 'sanity.action.document.edit',
|
|
416
|
+
// Server requires draftId to have drafts. prefix for validation, even for liveEdit
|
|
417
|
+
draftId: getDraftId(documentId),
|
|
418
|
+
publishedId: documentId,
|
|
419
|
+
patch: patch as PatchOperations,
|
|
420
|
+
}),
|
|
421
|
+
),
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
continue
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Standard draft/published logic
|
|
275
428
|
const draftId = getDraftId(documentId)
|
|
276
429
|
const publishedId = getPublishedId(documentId)
|
|
277
430
|
const userPatches = action.patches?.map((patch) => ({patch: {id: draftId, ...patch}}))
|
|
@@ -362,6 +515,16 @@ export function processActions({
|
|
|
362
515
|
|
|
363
516
|
case 'document.publish': {
|
|
364
517
|
const documentId = getId(action.documentId)
|
|
518
|
+
|
|
519
|
+
if (action.liveEdit) {
|
|
520
|
+
throw new ActionError({
|
|
521
|
+
documentId,
|
|
522
|
+
transactionId,
|
|
523
|
+
message: `Cannot publish liveEdit document "${documentId}". LiveEdit documents do not support drafts or publishing.`,
|
|
524
|
+
})
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Standard draft/published logic
|
|
365
528
|
const draftId = getDraftId(documentId)
|
|
366
529
|
const publishedId = getPublishedId(documentId)
|
|
367
530
|
|
|
@@ -428,6 +591,16 @@ export function processActions({
|
|
|
428
591
|
|
|
429
592
|
case 'document.unpublish': {
|
|
430
593
|
const documentId = getId(action.documentId)
|
|
594
|
+
|
|
595
|
+
if (action.liveEdit) {
|
|
596
|
+
throw new ActionError({
|
|
597
|
+
documentId,
|
|
598
|
+
transactionId,
|
|
599
|
+
message: `Cannot unpublish liveEdit document "${documentId}". LiveEdit documents do not support drafts or publishing.`,
|
|
600
|
+
})
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Standard draft/published logic
|
|
431
604
|
const draftId = getDraftId(documentId)
|
|
432
605
|
const publishedId = getPublishedId(documentId)
|
|
433
606
|
|
package/src/document/reducers.ts
CHANGED
|
@@ -20,6 +20,7 @@ export type SyncTransactionState = Pick<
|
|
|
20
20
|
|
|
21
21
|
type ActionMap = {
|
|
22
22
|
create: 'sanity.action.document.version.create'
|
|
23
|
+
createLiveEdit: 'sanity.action.document.create'
|
|
23
24
|
discard: 'sanity.action.document.version.discard'
|
|
24
25
|
unpublish: 'sanity.action.document.unpublish'
|
|
25
26
|
delete: 'sanity.action.document.delete'
|
|
@@ -34,6 +35,7 @@ type OptimisticLock = {
|
|
|
34
35
|
|
|
35
36
|
export type HttpAction =
|
|
36
37
|
| {actionType: ActionMap['create']; publishedId: string; attributes: SanityDocumentLike}
|
|
38
|
+
| {actionType: ActionMap['createLiveEdit']; publishedId: string; attributes: SanityDocumentLike}
|
|
37
39
|
| {actionType: ActionMap['discard']; versionId: string; purge?: boolean}
|
|
38
40
|
| {actionType: ActionMap['unpublish']; draftId: string; publishedId: string}
|
|
39
41
|
| {actionType: ActionMap['delete']; publishedId: string; includeDrafts?: string[]}
|
|
@@ -550,13 +552,19 @@ export function removeSubscriptionIdFromDocument(
|
|
|
550
552
|
export function manageSubscriberIds(
|
|
551
553
|
{state}: StoreContext<SyncTransactionState>,
|
|
552
554
|
documentId: string | string[],
|
|
555
|
+
options?: {expandDraftPublished?: boolean},
|
|
553
556
|
): () => void {
|
|
557
|
+
const expandDraftPublished = options?.expandDraftPublished ?? true
|
|
554
558
|
const documentIds = Array.from(
|
|
555
559
|
new Set(
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
+
expandDraftPublished
|
|
561
|
+
? (Array.isArray(documentId) ? documentId : [documentId]).flatMap((id) => [
|
|
562
|
+
getPublishedId(id),
|
|
563
|
+
getDraftId(id),
|
|
564
|
+
])
|
|
565
|
+
: Array.isArray(documentId)
|
|
566
|
+
? documentId
|
|
567
|
+
: [documentId],
|
|
560
568
|
),
|
|
561
569
|
)
|
|
562
570
|
const subscriptionId = insecureRandomId()
|
|
@@ -579,8 +587,7 @@ export function manageSubscriberIds(
|
|
|
579
587
|
}
|
|
580
588
|
}
|
|
581
589
|
|
|
582
|
-
export function getDocumentIdsFromActions(
|
|
583
|
-
const actions = Array.isArray(action) ? action : [action]
|
|
590
|
+
export function getDocumentIdsFromActions(actions: DocumentAction[]): string[] {
|
|
584
591
|
return Array.from(
|
|
585
592
|
new Set(
|
|
586
593
|
actions
|
|
@@ -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
|
}),
|
package/src/query/queryStore.ts
CHANGED
|
@@ -58,7 +58,9 @@ export interface QueryOptions<
|
|
|
58
58
|
TQuery extends string = string,
|
|
59
59
|
TDataset extends string = string,
|
|
60
60
|
TProjectId extends string = string,
|
|
61
|
-
>
|
|
61
|
+
>
|
|
62
|
+
extends
|
|
63
|
+
Pick<ResponseQueryOptions, 'useCdn' | 'cache' | 'next' | 'cacheMode' | 'tag'>,
|
|
62
64
|
DatasetHandle<TDataset, TProjectId> {
|
|
63
65
|
query: TQuery
|
|
64
66
|
params?: Record<string, unknown>
|
|
@@ -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) =>
|