@sanity/sdk 0.0.0-alpha.21 → 0.0.0-alpha.23
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 +428 -325
- package/dist/index.js +1618 -1553
- package/dist/index.js.map +1 -1
- package/package.json +6 -7
- package/src/_exports/index.ts +31 -30
- package/src/auth/authStore.test.ts +149 -104
- package/src/auth/authStore.ts +51 -100
- package/src/auth/handleAuthCallback.test.ts +67 -34
- package/src/auth/handleAuthCallback.ts +8 -7
- package/src/auth/logout.test.ts +61 -29
- package/src/auth/logout.ts +26 -28
- package/src/auth/refreshStampedToken.test.ts +9 -9
- package/src/auth/refreshStampedToken.ts +62 -56
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +5 -5
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +45 -47
- package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +4 -5
- package/src/auth/subscribeToStorageEventsAndSetToken.ts +22 -24
- package/src/client/clientStore.test.ts +131 -67
- package/src/client/clientStore.ts +117 -116
- package/src/comlink/controller/actions/destroyController.test.ts +38 -13
- package/src/comlink/controller/actions/destroyController.ts +11 -15
- package/src/comlink/controller/actions/getOrCreateChannel.test.ts +56 -27
- package/src/comlink/controller/actions/getOrCreateChannel.ts +37 -35
- package/src/comlink/controller/actions/getOrCreateController.test.ts +27 -16
- package/src/comlink/controller/actions/getOrCreateController.ts +23 -22
- package/src/comlink/controller/actions/releaseChannel.test.ts +37 -13
- package/src/comlink/controller/actions/releaseChannel.ts +22 -21
- package/src/comlink/controller/comlinkControllerStore.test.ts +65 -36
- package/src/comlink/controller/comlinkControllerStore.ts +44 -5
- package/src/comlink/node/actions/getOrCreateNode.test.ts +31 -15
- package/src/comlink/node/actions/getOrCreateNode.ts +30 -29
- package/src/comlink/node/actions/releaseNode.test.ts +75 -55
- package/src/comlink/node/actions/releaseNode.ts +19 -21
- package/src/comlink/node/comlinkNodeStore.test.ts +6 -11
- package/src/comlink/node/comlinkNodeStore.ts +22 -5
- package/src/config/authConfig.ts +79 -0
- package/src/config/sanityConfig.ts +48 -0
- package/src/datasets/datasets.test.ts +2 -2
- package/src/datasets/datasets.ts +18 -5
- package/src/document/actions.test.ts +22 -10
- package/src/document/actions.ts +44 -56
- package/src/document/applyDocumentActions.test.ts +96 -36
- package/src/document/applyDocumentActions.ts +140 -99
- package/src/document/documentStore.test.ts +103 -155
- package/src/document/documentStore.ts +247 -237
- package/src/document/listen.ts +56 -55
- package/src/document/patchOperations.ts +0 -43
- package/src/document/permissions.test.ts +25 -12
- package/src/document/permissions.ts +11 -4
- package/src/document/processActions.test.ts +41 -8
- package/src/document/reducers.test.ts +87 -16
- package/src/document/reducers.ts +2 -2
- package/src/document/sharedListener.test.ts +34 -16
- package/src/document/sharedListener.ts +33 -11
- package/src/preview/getPreviewState.test.ts +40 -39
- package/src/preview/getPreviewState.ts +68 -56
- package/src/preview/previewConstants.ts +43 -0
- package/src/preview/previewQuery.test.ts +1 -1
- package/src/preview/previewQuery.ts +4 -5
- package/src/preview/previewStore.test.ts +13 -58
- package/src/preview/previewStore.ts +7 -21
- package/src/preview/resolvePreview.test.ts +33 -104
- package/src/preview/resolvePreview.ts +11 -21
- package/src/preview/subscribeToStateAndFetchBatches.test.ts +96 -97
- package/src/preview/subscribeToStateAndFetchBatches.ts +85 -81
- package/src/preview/util.ts +1 -0
- package/src/project/project.test.ts +3 -3
- package/src/project/project.ts +28 -5
- package/src/projection/getProjectionState.test.ts +69 -49
- package/src/projection/getProjectionState.ts +42 -50
- package/src/projection/projectionQuery.ts +1 -1
- package/src/projection/projectionStore.test.ts +13 -51
- package/src/projection/projectionStore.ts +6 -18
- package/src/projection/resolveProjection.test.ts +32 -127
- package/src/projection/resolveProjection.ts +15 -28
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +105 -90
- package/src/projection/subscribeToStateAndFetchBatches.ts +94 -81
- package/src/projection/util.ts +2 -0
- package/src/projects/projects.test.ts +13 -4
- package/src/projects/projects.ts +6 -1
- package/src/query/queryStore.test.ts +10 -47
- package/src/query/queryStore.ts +151 -133
- package/src/query/queryStoreConstants.ts +2 -0
- package/src/store/createActionBinder.test.ts +153 -0
- package/src/store/createActionBinder.ts +176 -0
- package/src/store/createSanityInstance.test.ts +84 -0
- package/src/store/createSanityInstance.ts +124 -0
- package/src/store/createStateSourceAction.test.ts +196 -0
- package/src/store/createStateSourceAction.ts +260 -0
- package/src/store/createStoreInstance.test.ts +81 -0
- package/src/store/createStoreInstance.ts +80 -0
- package/src/store/createStoreState.test.ts +85 -0
- package/src/store/createStoreState.ts +92 -0
- package/src/store/defineStore.test.ts +18 -0
- package/src/store/defineStore.ts +81 -0
- package/src/users/reducers.test.ts +318 -0
- package/src/users/reducers.ts +88 -0
- package/src/users/types.ts +46 -4
- package/src/users/usersConstants.ts +4 -0
- package/src/users/usersStore.test.ts +350 -223
- package/src/users/usersStore.ts +285 -149
- package/src/utils/createFetcherStore.test.ts +6 -7
- package/src/utils/createFetcherStore.ts +150 -153
- package/src/{common/util.test.ts → utils/hashString.test.ts} +1 -1
- package/src/auth/fetchLoginUrls.test.ts +0 -163
- package/src/auth/fetchLoginUrls.ts +0 -74
- package/src/common/createLiveEventSubscriber.test.ts +0 -121
- package/src/common/createLiveEventSubscriber.ts +0 -55
- package/src/common/types.ts +0 -4
- package/src/instance/identity.test.ts +0 -46
- package/src/instance/identity.ts +0 -29
- package/src/instance/sanityInstance.test.ts +0 -77
- package/src/instance/sanityInstance.ts +0 -57
- package/src/instance/types.ts +0 -37
- package/src/preview/getPreviewProjection.ts +0 -45
- package/src/resources/README.md +0 -370
- package/src/resources/createAction.test.ts +0 -101
- package/src/resources/createAction.ts +0 -44
- package/src/resources/createResource.test.ts +0 -112
- package/src/resources/createResource.ts +0 -102
- package/src/resources/createStateSourceAction.test.ts +0 -114
- package/src/resources/createStateSourceAction.ts +0 -83
- package/src/resources/createStore.test.ts +0 -67
- package/src/resources/createStore.ts +0 -46
- package/src/store/createStore.test.ts +0 -108
- package/src/store/createStore.ts +0 -106
- /package/src/{common/util.ts → utils/hashString.ts} +0 -0
|
@@ -50,7 +50,14 @@ describe('queueTransaction', () => {
|
|
|
50
50
|
|
|
51
51
|
const transaction: QueuedTransaction = {
|
|
52
52
|
transactionId: 'txn1',
|
|
53
|
-
actions: [
|
|
53
|
+
actions: [
|
|
54
|
+
{
|
|
55
|
+
type: 'document.edit',
|
|
56
|
+
documentId: 'doc1',
|
|
57
|
+
documentType: 'book',
|
|
58
|
+
patches: [{set: {foo: 'bar'}}],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
const newState = queueTransaction(initialState, transaction)
|
|
@@ -77,7 +84,14 @@ describe('removeQueuedTransaction', () => {
|
|
|
77
84
|
queued: [
|
|
78
85
|
{
|
|
79
86
|
transactionId: 'txn1',
|
|
80
|
-
actions: [
|
|
87
|
+
actions: [
|
|
88
|
+
{
|
|
89
|
+
type: 'document.edit',
|
|
90
|
+
documentId: 'doc1',
|
|
91
|
+
documentType: 'book',
|
|
92
|
+
patches: [{set: {foo: 'bar'}}],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
81
95
|
},
|
|
82
96
|
],
|
|
83
97
|
applied: [],
|
|
@@ -123,13 +137,13 @@ describe('applyFirstQueuedTransaction', () => {
|
|
|
123
137
|
})
|
|
124
138
|
|
|
125
139
|
it('returns unchanged state if any required document is not yet loaded', () => {
|
|
126
|
-
// If a document
|
|
140
|
+
// If a document's local value is undefined, the reducer should do nothing.
|
|
127
141
|
const draftId = getDraftId('doc1')
|
|
128
142
|
const state: SyncTransactionState = {
|
|
129
143
|
queued: [
|
|
130
144
|
{
|
|
131
145
|
transactionId: 'txn2',
|
|
132
|
-
actions: [{type: 'document.discard', documentId: 'doc1'}],
|
|
146
|
+
actions: [{type: 'document.discard', documentId: 'doc1', documentType: 'book'}],
|
|
133
147
|
},
|
|
134
148
|
],
|
|
135
149
|
applied: [],
|
|
@@ -148,7 +162,7 @@ describe('applyFirstQueuedTransaction', () => {
|
|
|
148
162
|
queued: [
|
|
149
163
|
{
|
|
150
164
|
transactionId: 'txn-missing-grants',
|
|
151
|
-
actions: [{type: 'document.discard', documentId: 'doc1'}],
|
|
165
|
+
actions: [{type: 'document.discard', documentId: 'doc1', documentType: 'book'}],
|
|
152
166
|
},
|
|
153
167
|
],
|
|
154
168
|
applied: [],
|
|
@@ -171,7 +185,7 @@ describe('applyFirstQueuedTransaction', () => {
|
|
|
171
185
|
queued: [
|
|
172
186
|
{
|
|
173
187
|
transactionId: 'txn3',
|
|
174
|
-
actions: [{type: 'document.discard', documentId: 'doc1'}],
|
|
188
|
+
actions: [{type: 'document.discard', documentId: 'doc1', documentType: 'book'}],
|
|
175
189
|
},
|
|
176
190
|
],
|
|
177
191
|
applied: [],
|
|
@@ -188,7 +202,7 @@ describe('applyFirstQueuedTransaction', () => {
|
|
|
188
202
|
expect(newState.queued).toHaveLength(0)
|
|
189
203
|
// One applied transaction should have been added.
|
|
190
204
|
expect(newState.applied).toHaveLength(1)
|
|
191
|
-
// For a discard action, the draft
|
|
205
|
+
// For a discard action, the draft's local value becomes null.
|
|
192
206
|
expect(newState.documentStates[draftId]?.local).toBeNull()
|
|
193
207
|
// The published version remains unchanged.
|
|
194
208
|
expect(newState.documentStates[pubId]?.local).toEqual(initialPub)
|
|
@@ -205,8 +219,18 @@ describe('batchAppliedTransactions', () => {
|
|
|
205
219
|
const appliedTx: AppliedTransaction = {
|
|
206
220
|
transactionId: 'txn4',
|
|
207
221
|
actions: [
|
|
208
|
-
{
|
|
209
|
-
|
|
222
|
+
{
|
|
223
|
+
type: 'document.edit',
|
|
224
|
+
documentId: 'doc1',
|
|
225
|
+
documentType: 'book',
|
|
226
|
+
patches: [{set: {foo: 'a'}}],
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
type: 'document.edit',
|
|
230
|
+
documentId: 'doc1',
|
|
231
|
+
documentType: 'book',
|
|
232
|
+
patches: [{set: {bar: 'b'}}],
|
|
233
|
+
},
|
|
210
234
|
],
|
|
211
235
|
disableBatching: false,
|
|
212
236
|
outgoingActions: [],
|
|
@@ -235,7 +259,14 @@ describe('batchAppliedTransactions', () => {
|
|
|
235
259
|
const draftId = getDraftId('doc1')
|
|
236
260
|
const appliedTx1: AppliedTransaction = {
|
|
237
261
|
transactionId: 'txn5',
|
|
238
|
-
actions: [
|
|
262
|
+
actions: [
|
|
263
|
+
{
|
|
264
|
+
type: 'document.edit',
|
|
265
|
+
documentId: 'doc1',
|
|
266
|
+
documentType: 'book',
|
|
267
|
+
patches: [{set: {foo: 'a'}}],
|
|
268
|
+
},
|
|
269
|
+
],
|
|
239
270
|
disableBatching: false,
|
|
240
271
|
outgoingActions: [
|
|
241
272
|
{
|
|
@@ -254,7 +285,14 @@ describe('batchAppliedTransactions', () => {
|
|
|
254
285
|
}
|
|
255
286
|
const appliedTx2: AppliedTransaction = {
|
|
256
287
|
transactionId: 'txn6',
|
|
257
|
-
actions: [
|
|
288
|
+
actions: [
|
|
289
|
+
{
|
|
290
|
+
type: 'document.edit',
|
|
291
|
+
documentId: 'doc1',
|
|
292
|
+
documentType: 'book',
|
|
293
|
+
patches: [{set: {bar: 'b'}}],
|
|
294
|
+
},
|
|
295
|
+
],
|
|
258
296
|
disableBatching: false,
|
|
259
297
|
outgoingActions: [
|
|
260
298
|
{
|
|
@@ -287,7 +325,14 @@ describe('batchAppliedTransactions', () => {
|
|
|
287
325
|
const draftId = getDraftId('docA')
|
|
288
326
|
const appliedTx: AppliedTransaction = {
|
|
289
327
|
transactionId: 'txn-disable',
|
|
290
|
-
actions: [
|
|
328
|
+
actions: [
|
|
329
|
+
{
|
|
330
|
+
type: 'document.edit',
|
|
331
|
+
documentId: 'docA',
|
|
332
|
+
documentType: 'book',
|
|
333
|
+
patches: [{set: {foo: 'a'}}],
|
|
334
|
+
},
|
|
335
|
+
],
|
|
291
336
|
disableBatching: true, // already set to true
|
|
292
337
|
outgoingActions: [],
|
|
293
338
|
outgoingMutations: [],
|
|
@@ -328,7 +373,14 @@ describe('transitionAppliedTransactionsToOutgoing', () => {
|
|
|
328
373
|
applied: [
|
|
329
374
|
{
|
|
330
375
|
transactionId: 'txn7',
|
|
331
|
-
actions: [
|
|
376
|
+
actions: [
|
|
377
|
+
{
|
|
378
|
+
type: 'document.edit',
|
|
379
|
+
documentId: 'doc1',
|
|
380
|
+
documentType: 'book',
|
|
381
|
+
patches: [{set: {foo: 'new'}}],
|
|
382
|
+
},
|
|
383
|
+
],
|
|
332
384
|
disableBatching: false,
|
|
333
385
|
outgoingActions: [],
|
|
334
386
|
outgoingMutations: [],
|
|
@@ -381,7 +433,14 @@ describe('cleanupOutgoingTransaction', () => {
|
|
|
381
433
|
applied: [],
|
|
382
434
|
outgoing: {
|
|
383
435
|
transactionId: 'txn8',
|
|
384
|
-
actions: [
|
|
436
|
+
actions: [
|
|
437
|
+
{
|
|
438
|
+
type: 'document.edit',
|
|
439
|
+
documentId: 'doc1',
|
|
440
|
+
documentType: 'book',
|
|
441
|
+
patches: [{set: {foo: 'x'}}],
|
|
442
|
+
},
|
|
443
|
+
],
|
|
385
444
|
disableBatching: false,
|
|
386
445
|
batchedTransactionIds: ['txn8'],
|
|
387
446
|
outgoingActions: [],
|
|
@@ -417,7 +476,12 @@ describe('revertOutgoingTransaction', () => {
|
|
|
417
476
|
{
|
|
418
477
|
transactionId: 'txn9',
|
|
419
478
|
actions: [
|
|
420
|
-
{
|
|
479
|
+
{
|
|
480
|
+
type: 'document.edit',
|
|
481
|
+
documentId: 'doc1',
|
|
482
|
+
documentType: 'book',
|
|
483
|
+
patches: [{set: {foo: 'reverted'}}],
|
|
484
|
+
},
|
|
421
485
|
],
|
|
422
486
|
disableBatching: false,
|
|
423
487
|
outgoingActions: [],
|
|
@@ -431,7 +495,14 @@ describe('revertOutgoingTransaction', () => {
|
|
|
431
495
|
],
|
|
432
496
|
outgoing: {
|
|
433
497
|
transactionId: 'txnOut',
|
|
434
|
-
actions: [
|
|
498
|
+
actions: [
|
|
499
|
+
{
|
|
500
|
+
type: 'document.edit',
|
|
501
|
+
documentId: 'doc1',
|
|
502
|
+
documentType: 'book',
|
|
503
|
+
patches: [{set: {foo: 'changed'}}],
|
|
504
|
+
},
|
|
505
|
+
],
|
|
435
506
|
disableBatching: false,
|
|
436
507
|
batchedTransactionIds: ['txnOut'],
|
|
437
508
|
outgoingActions: [],
|
package/src/document/reducers.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {getPublishedId} from '@sanity/client/csm'
|
|
|
2
2
|
import {type Mutation, type PatchOperations, type SanityDocumentLike} from '@sanity/types'
|
|
3
3
|
import {omit} from 'lodash-es'
|
|
4
4
|
|
|
5
|
-
import {type
|
|
5
|
+
import {type StoreContext} from '../store/defineStore'
|
|
6
6
|
import {getDraftId, insecureRandomId} from '../utils/ids'
|
|
7
7
|
import {type DocumentAction} from './actions'
|
|
8
8
|
import {DOCUMENT_STATE_CLEAR_DELAY} from './documentConstants'
|
|
@@ -548,7 +548,7 @@ export function removeSubscriptionIdFromDocument(
|
|
|
548
548
|
}
|
|
549
549
|
|
|
550
550
|
export function manageSubscriberIds(
|
|
551
|
-
state:
|
|
551
|
+
{state}: StoreContext<SyncTransactionState>,
|
|
552
552
|
documentId: string | string[],
|
|
553
553
|
): () => void {
|
|
554
554
|
const documentIds = Array.from(
|
|
@@ -12,8 +12,8 @@ import {bufferTime, catchError, toArray} from 'rxjs/operators'
|
|
|
12
12
|
import {afterEach, beforeEach, describe, expect, it, type Mock, vi} from 'vitest'
|
|
13
13
|
|
|
14
14
|
import {getClientState} from '../client/clientStore'
|
|
15
|
-
import {createSanityInstance} from '../
|
|
16
|
-
import {type StateSource} from '../
|
|
15
|
+
import {createSanityInstance} from '../store/createSanityInstance'
|
|
16
|
+
import {type StateSource} from '../store/createStateSourceAction'
|
|
17
17
|
import {createFetchDocument, createSharedListener} from './sharedListener'
|
|
18
18
|
|
|
19
19
|
const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
@@ -28,7 +28,7 @@ describe('createSharedListener', () => {
|
|
|
28
28
|
beforeEach(() => {
|
|
29
29
|
// Create a subject to simulate the events coming from client.listen.
|
|
30
30
|
fakeListenSubject = new Subject()
|
|
31
|
-
// Create a fake client whose listen() method returns our subject
|
|
31
|
+
// Create a fake client whose listen() method returns our subject's observable.
|
|
32
32
|
fakeClient = {
|
|
33
33
|
listen: vi.fn(() => fakeListenSubject.asObservable()),
|
|
34
34
|
} as unknown as SanityClient
|
|
@@ -43,7 +43,7 @@ describe('createSharedListener', () => {
|
|
|
43
43
|
})
|
|
44
44
|
|
|
45
45
|
it('should call client.listen with the expected parameters', () => {
|
|
46
|
-
createSharedListener(instance).subscribe()
|
|
46
|
+
createSharedListener(instance).events.subscribe()
|
|
47
47
|
expect(fakeClient.listen).toHaveBeenCalledTimes(1)
|
|
48
48
|
expect(fakeClient.listen).toHaveBeenCalledWith(
|
|
49
49
|
'*',
|
|
@@ -62,9 +62,9 @@ describe('createSharedListener', () => {
|
|
|
62
62
|
const mutationEvent: MutationEvent = {type: 'mutation'} as MutationEvent
|
|
63
63
|
const reconnectEvent: ReconnectEvent = {type: 'reconnect'} as ReconnectEvent
|
|
64
64
|
|
|
65
|
-
const
|
|
65
|
+
const sharedListener = createSharedListener(instance)
|
|
66
66
|
// Start collecting the emitted events.
|
|
67
|
-
const eventsPromise = lastValueFrom(
|
|
67
|
+
const eventsPromise = lastValueFrom(sharedListener.events.pipe(toArray()))
|
|
68
68
|
|
|
69
69
|
// Emit events in order.
|
|
70
70
|
fakeListenSubject.next(welcomeEvent)
|
|
@@ -80,16 +80,16 @@ describe('createSharedListener', () => {
|
|
|
80
80
|
it('should replay the welcome event for new subscribers', async () => {
|
|
81
81
|
const welcomeEvent: WelcomeEvent = {type: 'welcome', listenerName: 'listener'}
|
|
82
82
|
|
|
83
|
-
const
|
|
83
|
+
const sharedListener = createSharedListener(instance)
|
|
84
84
|
// First subscription: emit welcome and complete.
|
|
85
|
-
const firstPromise = lastValueFrom(
|
|
85
|
+
const firstPromise = lastValueFrom(sharedListener.events.pipe(toArray()))
|
|
86
86
|
fakeListenSubject.next(welcomeEvent)
|
|
87
87
|
fakeListenSubject.complete()
|
|
88
88
|
const firstEvents = await firstPromise
|
|
89
89
|
expect(firstEvents).toEqual([welcomeEvent])
|
|
90
90
|
|
|
91
91
|
// New subscriber should immediately receive the replayed welcome event.
|
|
92
|
-
const secondEvents = await lastValueFrom(
|
|
92
|
+
const secondEvents = await lastValueFrom(sharedListener.events.pipe(toArray()))
|
|
93
93
|
expect(secondEvents).toEqual([welcomeEvent])
|
|
94
94
|
})
|
|
95
95
|
|
|
@@ -97,8 +97,8 @@ describe('createSharedListener', () => {
|
|
|
97
97
|
const mutationEvent = {type: 'mutation'} as MutationEvent
|
|
98
98
|
const reconnectEvent = {type: 'reconnect'} as ReconnectEvent
|
|
99
99
|
|
|
100
|
-
const
|
|
101
|
-
const eventsPromise = lastValueFrom(
|
|
100
|
+
const sharedListener = createSharedListener(instance)
|
|
101
|
+
const eventsPromise = lastValueFrom(sharedListener.events.pipe(toArray()))
|
|
102
102
|
fakeListenSubject.next(mutationEvent)
|
|
103
103
|
fakeListenSubject.next(reconnectEvent)
|
|
104
104
|
fakeListenSubject.complete()
|
|
@@ -110,11 +110,11 @@ describe('createSharedListener', () => {
|
|
|
110
110
|
const welcomeEvent = {type: 'welcome'} as WelcomeEvent
|
|
111
111
|
const mutationEvent = {type: 'mutation'} as MutationEvent
|
|
112
112
|
|
|
113
|
-
const
|
|
113
|
+
const sharedListener = createSharedListener(instance)
|
|
114
114
|
|
|
115
115
|
// Subscribe two observers concurrently.
|
|
116
|
-
const subscriber1 =
|
|
117
|
-
const subscriber2 =
|
|
116
|
+
const subscriber1 = sharedListener.events.pipe(bufferTime(0))
|
|
117
|
+
const subscriber2 = sharedListener.events.pipe(bufferTime(0))
|
|
118
118
|
const combined = forkJoin([subscriber1, subscriber2])
|
|
119
119
|
|
|
120
120
|
const result = lastValueFrom(combined)
|
|
@@ -133,9 +133,9 @@ describe('createSharedListener', () => {
|
|
|
133
133
|
|
|
134
134
|
it('should propagate errors from the underlying client.listen observable', async () => {
|
|
135
135
|
const errorMessage = 'Test error'
|
|
136
|
-
const
|
|
136
|
+
const sharedListener = createSharedListener(instance)
|
|
137
137
|
|
|
138
|
-
const error$ =
|
|
138
|
+
const error$ = sharedListener.events.pipe(
|
|
139
139
|
toArray(),
|
|
140
140
|
catchError((err) => of(err)),
|
|
141
141
|
)
|
|
@@ -145,6 +145,24 @@ describe('createSharedListener', () => {
|
|
|
145
145
|
expect(result).toBeInstanceOf(Error)
|
|
146
146
|
expect(result.message).toBe(errorMessage)
|
|
147
147
|
})
|
|
148
|
+
|
|
149
|
+
it('should stop emitting events after calling dispose', async () => {
|
|
150
|
+
const welcomeEvent: WelcomeEvent = {type: 'welcome', listenerName: 'listener'}
|
|
151
|
+
const mutationEvent: MutationEvent = {type: 'mutation'} as MutationEvent
|
|
152
|
+
const sharedListener = createSharedListener(instance)
|
|
153
|
+
|
|
154
|
+
const events: ListenEvent<SanityDocument>[] = []
|
|
155
|
+
const subscription = sharedListener.events.subscribe((event) => {
|
|
156
|
+
events.push(event)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
fakeListenSubject.next(welcomeEvent)
|
|
160
|
+
sharedListener.dispose()
|
|
161
|
+
fakeListenSubject.next(mutationEvent)
|
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
163
|
+
expect(events).toEqual([welcomeEvent])
|
|
164
|
+
subscription.unsubscribe()
|
|
165
|
+
})
|
|
148
166
|
})
|
|
149
167
|
|
|
150
168
|
describe('createFetchDocument', () => {
|
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
import {type ListenEvent, type SanityDocument} from '@sanity/client'
|
|
2
2
|
import {createDocumentLoaderFromClient} from '@sanity/mutate/_unstable_store'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
first,
|
|
5
|
+
map,
|
|
6
|
+
merge,
|
|
7
|
+
Observable,
|
|
8
|
+
partition,
|
|
9
|
+
share,
|
|
10
|
+
shareReplay,
|
|
11
|
+
Subject,
|
|
12
|
+
switchMap,
|
|
13
|
+
takeUntil,
|
|
14
|
+
} from 'rxjs'
|
|
4
15
|
|
|
5
16
|
import {getClientState} from '../client/clientStore'
|
|
6
|
-
import {type SanityInstance} from '../
|
|
17
|
+
import {type SanityInstance} from '../store/createSanityInstance'
|
|
7
18
|
|
|
8
19
|
const API_VERSION = 'vX'
|
|
9
20
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
export interface SharedListener {
|
|
22
|
+
events: Observable<ListenEvent<SanityDocument>>
|
|
23
|
+
dispose: () => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createSharedListener(instance: SanityInstance): SharedListener {
|
|
27
|
+
const dispose$ = new Subject<void>()
|
|
28
|
+
const events$ = getClientState(instance, {
|
|
29
|
+
apiVersion: API_VERSION,
|
|
30
|
+
}).observable.pipe(
|
|
14
31
|
switchMap((client) =>
|
|
15
32
|
// TODO: it seems like the client.listen method is not emitting disconnected
|
|
16
33
|
// events. this is important to ensure we have an up to date version of the
|
|
@@ -29,15 +46,20 @@ export function createSharedListener(
|
|
|
29
46
|
},
|
|
30
47
|
),
|
|
31
48
|
),
|
|
49
|
+
takeUntil(dispose$),
|
|
32
50
|
share(),
|
|
33
51
|
)
|
|
34
52
|
|
|
35
53
|
const [welcome$, mutation$] = partition(events$, (e) => e.type === 'welcome')
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
events: merge(
|
|
57
|
+
// we replay the welcome event because that event kicks off fetching the document
|
|
58
|
+
welcome$.pipe(shareReplay(1)),
|
|
59
|
+
mutation$,
|
|
60
|
+
),
|
|
61
|
+
dispose: () => dispose$.next(),
|
|
62
|
+
}
|
|
41
63
|
}
|
|
42
64
|
|
|
43
65
|
export function createFetchDocument(instance: SanityInstance) {
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
getOrCreateResource,
|
|
7
|
-
type ResourceState,
|
|
8
|
-
} from '../resources/createResource'
|
|
1
|
+
import {NEVER} from 'rxjs'
|
|
2
|
+
import {describe, it} from 'vitest'
|
|
3
|
+
|
|
4
|
+
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
5
|
+
import {type StoreState} from '../store/createStoreState'
|
|
9
6
|
import {insecureRandomId} from '../utils/ids'
|
|
10
7
|
import {getPreviewState} from './getPreviewState'
|
|
11
|
-
import {
|
|
8
|
+
import {type PreviewStoreState} from './previewStore'
|
|
9
|
+
import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
|
|
12
10
|
import {STABLE_EMPTY_PREVIEW} from './util'
|
|
13
11
|
|
|
14
12
|
vi.mock('../utils/ids', async (importOriginal) => {
|
|
@@ -16,36 +14,36 @@ vi.mock('../utils/ids', async (importOriginal) => {
|
|
|
16
14
|
return {...util, insecureRandomId: vi.fn(util.insecureRandomId)}
|
|
17
15
|
})
|
|
18
16
|
|
|
19
|
-
vi.mock('
|
|
20
|
-
const original = await importOriginal<typeof import('../resources/createResource')>()
|
|
21
|
-
return {...original, getOrCreateResource: vi.fn()}
|
|
22
|
-
})
|
|
17
|
+
vi.mock('./subscribeToStateAndFetchBatches.ts')
|
|
23
18
|
|
|
24
19
|
describe('getPreviewState', () => {
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
documentTypes: {},
|
|
29
|
-
lastLiveEventId: null,
|
|
30
|
-
subscriptions: {},
|
|
31
|
-
syncTags: {},
|
|
32
|
-
values: {},
|
|
33
|
-
}
|
|
34
|
-
let state: ResourceState<PreviewStoreState>
|
|
20
|
+
let instance: SanityInstance
|
|
21
|
+
const docHandle = {documentId: 'exampleId', documentType: 'exampleType'}
|
|
22
|
+
let state: StoreState<PreviewStoreState & {extra?: unknown}>
|
|
35
23
|
|
|
36
24
|
beforeEach(() => {
|
|
37
|
-
|
|
25
|
+
// capture state
|
|
26
|
+
vi.mocked(subscribeToStateAndFetchBatches).mockImplementation((context) => {
|
|
27
|
+
state = context.state
|
|
28
|
+
return NEVER.subscribe()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
instance = createSanityInstance({projectId: 'exampleProject', dataset: 'exampleDataset'})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
instance.dispose()
|
|
38
36
|
})
|
|
39
37
|
|
|
40
38
|
it('returns a state source that emits when the preview value changes', () => {
|
|
41
|
-
const previewState = getPreviewState(
|
|
39
|
+
const previewState = getPreviewState(instance, docHandle)
|
|
42
40
|
expect(previewState.getCurrent()).toBe(STABLE_EMPTY_PREVIEW)
|
|
43
41
|
|
|
44
42
|
const subscriber = vi.fn()
|
|
45
43
|
previewState.subscribe(subscriber)
|
|
46
44
|
|
|
47
45
|
// emit unrelated state changes
|
|
48
|
-
state.set('updateLastLiveEventId', {
|
|
46
|
+
state.set('updateLastLiveEventId', {extra: 'unrelated change'})
|
|
49
47
|
expect(subscriber).toHaveBeenCalledTimes(0)
|
|
50
48
|
|
|
51
49
|
state.set('relatedChange', (prev) => ({
|
|
@@ -68,7 +66,7 @@ describe('getPreviewState', () => {
|
|
|
68
66
|
})
|
|
69
67
|
|
|
70
68
|
it('adds a subscription ID and document type to the state on subscription', () => {
|
|
71
|
-
const previewState = getPreviewState(
|
|
69
|
+
const previewState = getPreviewState(instance, docHandle)
|
|
72
70
|
|
|
73
71
|
expect(state.get().subscriptions).toEqual({})
|
|
74
72
|
vi.mocked(insecureRandomId)
|
|
@@ -92,28 +90,31 @@ describe('getPreviewState', () => {
|
|
|
92
90
|
})
|
|
93
91
|
|
|
94
92
|
it('resets to pending false on unsubscribe if the subscription is the last one', () => {
|
|
93
|
+
const previewState = getPreviewState(instance, docHandle)
|
|
94
|
+
|
|
95
95
|
state.set('presetValueToPending', (prev) => ({
|
|
96
|
-
values: {...prev.values, [
|
|
96
|
+
values: {...prev.values, [docHandle.documentId]: {data: {title: 'Foo'}, isPending: true}},
|
|
97
97
|
}))
|
|
98
98
|
|
|
99
|
-
const previewState = getPreviewState({state, instance}, {document})
|
|
100
|
-
|
|
101
99
|
const unsubscribe1 = previewState.subscribe(vi.fn())
|
|
102
100
|
const unsubscribe2 = previewState.subscribe(vi.fn())
|
|
103
101
|
|
|
104
|
-
expect(state.get().values[
|
|
102
|
+
expect(state.get().values[docHandle.documentId]).toEqual({
|
|
103
|
+
data: {title: 'Foo'},
|
|
104
|
+
isPending: true,
|
|
105
|
+
})
|
|
105
106
|
|
|
106
107
|
unsubscribe1()
|
|
107
|
-
expect(state.get().values[
|
|
108
|
+
expect(state.get().values[docHandle.documentId]).toEqual({
|
|
109
|
+
data: {title: 'Foo'},
|
|
110
|
+
isPending: true,
|
|
111
|
+
})
|
|
108
112
|
|
|
109
113
|
unsubscribe2()
|
|
110
114
|
expect(state.get().subscriptions).toEqual({})
|
|
111
|
-
expect(state.get().values[
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
;(getOrCreateResource as Mock).mockReturnValue({state})
|
|
116
|
-
getPreviewState(instance, {document})
|
|
117
|
-
expect(getOrCreateResource).toHaveBeenCalledWith(instance, previewStore)
|
|
115
|
+
expect(state.get().values[docHandle.documentId]).toEqual({
|
|
116
|
+
data: {title: 'Foo'},
|
|
117
|
+
isPending: false,
|
|
118
|
+
})
|
|
118
119
|
})
|
|
119
120
|
})
|