@sanity/sdk 0.0.0-chore-react-18-compat.1 → 0.0.0-chore-react-18-compat.3
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 +441 -322
- package/dist/index.js +1685 -1481
- package/dist/index.js.map +1 -1
- package/package.json +13 -15
- package/src/_exports/index.ts +32 -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 +197 -91
- package/src/auth/refreshStampedToken.ts +170 -59
- 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 -238
- 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 +188 -72
- package/src/projection/getProjectionState.ts +92 -62
- package/src/projection/projectionQuery.test.ts +114 -12
- package/src/projection/projectionQuery.ts +75 -32
- package/src/projection/projectionStore.test.ts +13 -51
- package/src/projection/projectionStore.ts +6 -43
- package/src/projection/resolveProjection.test.ts +32 -127
- package/src/projection/resolveProjection.ts +16 -28
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +203 -116
- package/src/projection/subscribeToStateAndFetchBatches.ts +140 -85
- package/src/projection/types.ts +50 -0
- package/src/projection/util.ts +3 -1
- 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/utils/createGroqSearchFilter.test.ts +75 -0
- package/src/utils/createGroqSearchFilter.ts +85 -0
- package/src/{common/util.test.ts → utils/hashString.test.ts} +1 -1
- package/dist/index.cjs +0 -4888
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -2121
- 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
|
@@ -1,104 +1,159 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {isEqual} from 'lodash-es'
|
|
2
2
|
import {
|
|
3
3
|
combineLatest,
|
|
4
4
|
debounceTime,
|
|
5
|
+
defer,
|
|
5
6
|
distinctUntilChanged,
|
|
6
7
|
EMPTY,
|
|
8
|
+
filter,
|
|
9
|
+
from,
|
|
7
10
|
map,
|
|
8
11
|
Observable,
|
|
9
12
|
pairwise,
|
|
10
13
|
startWith,
|
|
14
|
+
Subscription,
|
|
11
15
|
switchMap,
|
|
12
16
|
tap,
|
|
13
|
-
withLatestFrom,
|
|
14
17
|
} from 'rxjs'
|
|
15
18
|
|
|
16
|
-
import {
|
|
17
|
-
import {type
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
import {getQueryState, resolveQuery} from '../query/queryStore'
|
|
20
|
+
import {type StoreContext} from '../store/defineStore'
|
|
21
|
+
import {
|
|
22
|
+
createProjectionQuery,
|
|
23
|
+
processProjectionQuery,
|
|
24
|
+
type ProjectionQueryResult,
|
|
25
|
+
} from './projectionQuery'
|
|
26
|
+
import {type ProjectionStoreState} from './types'
|
|
27
|
+
import {PROJECTION_PERSPECTIVE, PROJECTION_TAG} from './util'
|
|
21
28
|
|
|
22
29
|
const BATCH_DEBOUNCE_TIME = 50
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
31
|
+
const isSetEqual = <T>(a: Set<T>, b: Set<T>) =>
|
|
32
|
+
a.size === b.size && Array.from(a).every((i) => b.has(i))
|
|
33
|
+
|
|
34
|
+
export const subscribeToStateAndFetchBatches = ({
|
|
35
|
+
state,
|
|
36
|
+
instance,
|
|
37
|
+
}: StoreContext<ProjectionStoreState>): Subscription => {
|
|
38
|
+
const documentProjections$ = state.observable.pipe(
|
|
39
|
+
map((s) => s.documentProjections),
|
|
40
|
+
distinctUntilChanged(isEqual),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const activeDocumentIds$ = state.observable.pipe(
|
|
44
|
+
map(({subscriptions}) => new Set(Object.keys(subscriptions))),
|
|
45
|
+
distinctUntilChanged(isSetEqual),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
const pendingUpdateSubscription = activeDocumentIds$
|
|
49
|
+
.pipe(
|
|
50
|
+
debounceTime(BATCH_DEBOUNCE_TIME),
|
|
51
|
+
startWith(new Set<string>()),
|
|
52
|
+
pairwise(),
|
|
53
|
+
tap(([prevIds, currIds]) => {
|
|
54
|
+
const newIds = [...currIds].filter((id) => !prevIds.has(id))
|
|
55
|
+
if (newIds.length === 0) return
|
|
56
|
+
|
|
57
|
+
state.set('updatingPending', (prev) => {
|
|
58
|
+
const nextValues = {...prev.values}
|
|
59
|
+
for (const id of newIds) {
|
|
60
|
+
const projectionsForDoc = prev.documentProjections[id]
|
|
61
|
+
if (!projectionsForDoc) continue
|
|
62
|
+
|
|
63
|
+
const currentValuesForDoc = prev.values[id] ?? {}
|
|
64
|
+
const updatedValuesForDoc = {...currentValuesForDoc}
|
|
65
|
+
|
|
66
|
+
for (const hash in projectionsForDoc) {
|
|
67
|
+
const currentValue = updatedValuesForDoc[hash]
|
|
68
|
+
updatedValuesForDoc[hash] = {
|
|
69
|
+
data: currentValue?.data ?? null,
|
|
70
|
+
isPending: true,
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
nextValues[id] = updatedValuesForDoc
|
|
74
|
+
}
|
|
75
|
+
return {values: nextValues}
|
|
76
|
+
})
|
|
77
|
+
}),
|
|
78
|
+
)
|
|
79
|
+
.subscribe()
|
|
80
|
+
|
|
81
|
+
const queryTrigger$ = combineLatest([activeDocumentIds$, documentProjections$]).pipe(
|
|
82
|
+
debounceTime(BATCH_DEBOUNCE_TIME),
|
|
83
|
+
distinctUntilChanged(isEqual),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const queryExecutionSubscription = queryTrigger$
|
|
87
|
+
.pipe(
|
|
88
|
+
switchMap(([ids, documentProjections]) => {
|
|
89
|
+
if (!ids.size) return EMPTY
|
|
90
|
+
const {query, params} = createProjectionQuery(ids, documentProjections)
|
|
91
|
+
const controller = new AbortController()
|
|
92
|
+
|
|
93
|
+
return new Observable<ProjectionQueryResult[]>((observer) => {
|
|
94
|
+
const {getCurrent, observable} = getQueryState<ProjectionQueryResult[]>(instance, query, {
|
|
95
|
+
params,
|
|
96
|
+
tag: PROJECTION_TAG,
|
|
97
|
+
perspective: PROJECTION_PERSPECTIVE,
|
|
59
98
|
})
|
|
99
|
+
|
|
100
|
+
const source$ = defer(() => {
|
|
101
|
+
if (getCurrent() === undefined) {
|
|
102
|
+
return from(
|
|
103
|
+
resolveQuery<ProjectionQueryResult[]>(instance, query, {
|
|
104
|
+
params,
|
|
105
|
+
tag: PROJECTION_TAG,
|
|
106
|
+
perspective: PROJECTION_PERSPECTIVE,
|
|
107
|
+
signal: controller.signal,
|
|
108
|
+
}),
|
|
109
|
+
).pipe(switchMap(() => observable))
|
|
110
|
+
}
|
|
111
|
+
return observable
|
|
112
|
+
}).pipe(filter((result): result is ProjectionQueryResult[] => result !== undefined))
|
|
113
|
+
|
|
114
|
+
const subscription = source$.subscribe(observer)
|
|
115
|
+
|
|
116
|
+
return () => {
|
|
117
|
+
if (!controller.signal.aborted) {
|
|
118
|
+
controller.abort()
|
|
119
|
+
}
|
|
120
|
+
subscription.unsubscribe()
|
|
121
|
+
}
|
|
122
|
+
}).pipe(map((data) => ({data, ids})))
|
|
123
|
+
}),
|
|
124
|
+
map(({ids, data}) =>
|
|
125
|
+
processProjectionQuery({
|
|
126
|
+
projectId: instance.config.projectId!,
|
|
127
|
+
dataset: instance.config.dataset!,
|
|
128
|
+
ids,
|
|
129
|
+
results: data,
|
|
60
130
|
}),
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
tag: PROJECTION_TAG,
|
|
77
|
-
lastLiveEventId,
|
|
78
|
-
})
|
|
79
|
-
.pipe(map((response) => ({...response, ids})))
|
|
80
|
-
}),
|
|
81
|
-
map(({ids, result, syncTags}) => ({
|
|
82
|
-
syncTags,
|
|
83
|
-
values: processProjectionQuery({
|
|
84
|
-
projectId: instance.identity.projectId,
|
|
85
|
-
dataset: instance.identity.dataset,
|
|
86
|
-
ids,
|
|
87
|
-
results: result,
|
|
88
|
-
}),
|
|
89
|
-
})),
|
|
90
|
-
)
|
|
91
|
-
.subscribe({
|
|
92
|
-
next: ({syncTags = [], values}) => {
|
|
93
|
-
state.set('updateResult', (prev) => ({
|
|
94
|
-
values: {...prev.values, ...values},
|
|
95
|
-
syncTags: syncTags.reduce<Record<SyncTag, true>>((acc, next) => {
|
|
96
|
-
acc[next] = true
|
|
97
|
-
return acc
|
|
98
|
-
}, {}),
|
|
99
|
-
}))
|
|
100
|
-
},
|
|
131
|
+
),
|
|
132
|
+
)
|
|
133
|
+
.subscribe({
|
|
134
|
+
next: (processedValues) => {
|
|
135
|
+
state.set('updateResult', (prev) => {
|
|
136
|
+
const nextValues = {...prev.values}
|
|
137
|
+
for (const docId in processedValues) {
|
|
138
|
+
if (processedValues[docId]) {
|
|
139
|
+
nextValues[docId] = {
|
|
140
|
+
...(prev.values[docId] ?? {}),
|
|
141
|
+
...processedValues[docId],
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return {values: nextValues}
|
|
101
146
|
})
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
147
|
+
},
|
|
148
|
+
error: (err) => {
|
|
149
|
+
// eslint-disable-next-line no-console
|
|
150
|
+
console.error('Error fetching projection batches:', err)
|
|
151
|
+
// TODO: Potentially update state to reflect error state for affected projections?
|
|
152
|
+
},
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
return new Subscription(() => {
|
|
156
|
+
pendingUpdateSubscription.unsubscribe()
|
|
157
|
+
queryExecutionSubscription.unsubscribe()
|
|
158
|
+
})
|
|
159
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @public
|
|
3
|
+
*/
|
|
4
|
+
export type ValidProjection = `{${string}}`
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @public
|
|
8
|
+
* The result of a projection query
|
|
9
|
+
*/
|
|
10
|
+
export interface ProjectionValuePending<TValue extends object> {
|
|
11
|
+
data: TValue | null
|
|
12
|
+
isPending: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface DocumentProjectionValues<TValue extends object = object> {
|
|
16
|
+
[projectionHash: string]: ProjectionValuePending<TValue>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface DocumentProjections {
|
|
20
|
+
[projectionHash: string]: ValidProjection
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface DocumentProjectionSubscriptions {
|
|
24
|
+
[projectionHash: string]: {
|
|
25
|
+
[subscriptionId: string]: true
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ProjectionStoreState<TValue extends object = object> {
|
|
30
|
+
/**
|
|
31
|
+
* A map of document IDs to their projection values, organized by projection hash
|
|
32
|
+
*/
|
|
33
|
+
values: {
|
|
34
|
+
[documentId: string]: DocumentProjectionValues<TValue>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A map of document IDs to their projections, organized by projection hash
|
|
39
|
+
*/
|
|
40
|
+
documentProjections: {
|
|
41
|
+
[documentId: string]: DocumentProjections
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A map of document IDs to their subscriptions, organized by projection hash
|
|
46
|
+
*/
|
|
47
|
+
subscriptions: {
|
|
48
|
+
[documentId: string]: DocumentProjectionSubscriptions
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/projection/util.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {type ValidProjection} from './
|
|
1
|
+
import {type ValidProjection} from './types'
|
|
2
2
|
|
|
3
3
|
export const PROJECTION_TAG = 'sdk.projection'
|
|
4
|
+
export const PROJECTION_PERSPECTIVE = 'drafts'
|
|
5
|
+
export const PROJECTION_STATE_CLEAR_DELAY = 1000
|
|
4
6
|
|
|
5
7
|
export const STABLE_EMPTY_PROJECTION = {
|
|
6
8
|
data: null,
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import {type SanityClient} from '@sanity/client'
|
|
2
2
|
import {of} from 'rxjs'
|
|
3
|
-
import {describe, it} from 'vitest'
|
|
3
|
+
import {afterEach, beforeEach, describe, it} from 'vitest'
|
|
4
4
|
|
|
5
5
|
import {getClientState} from '../client/clientStore'
|
|
6
|
-
import {createSanityInstance} from '../
|
|
7
|
-
import {type StateSource} from '../
|
|
6
|
+
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
7
|
+
import {type StateSource} from '../store/createStateSourceAction'
|
|
8
8
|
import {resolveProjects} from './projects'
|
|
9
9
|
|
|
10
10
|
vi.mock('../client/clientStore')
|
|
11
11
|
|
|
12
12
|
describe('projects', () => {
|
|
13
|
+
let instance: SanityInstance
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
instance.dispose()
|
|
21
|
+
})
|
|
22
|
+
|
|
13
23
|
it('calls the `client.observable.projects.list` method on the client and returns the result', async () => {
|
|
14
|
-
const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
15
24
|
const projects = [{id: 'a'}, {id: 'b'}]
|
|
16
25
|
const list = vi.fn().mockReturnValue(of(projects))
|
|
17
26
|
|
package/src/projects/projects.ts
CHANGED
|
@@ -3,11 +3,16 @@ import {switchMap} from 'rxjs'
|
|
|
3
3
|
import {getClientState} from '../client/clientStore'
|
|
4
4
|
import {createFetcherStore} from '../utils/createFetcherStore'
|
|
5
5
|
|
|
6
|
+
const API_VERSION = 'v2025-02-19'
|
|
7
|
+
|
|
6
8
|
const projects = createFetcherStore({
|
|
7
9
|
name: 'Projects',
|
|
8
10
|
getKey: () => 'projects',
|
|
9
11
|
fetcher: (instance) => () =>
|
|
10
|
-
getClientState(instance, {
|
|
12
|
+
getClientState(instance, {
|
|
13
|
+
apiVersion: API_VERSION,
|
|
14
|
+
scope: 'global',
|
|
15
|
+
}).observable.pipe(
|
|
11
16
|
switchMap((client) => client.observable.projects.list({includeMembers: false})),
|
|
12
17
|
),
|
|
13
18
|
})
|
|
@@ -3,10 +3,9 @@ import {delay, filter, firstValueFrom, Observable, of, Subject} from 'rxjs'
|
|
|
3
3
|
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
4
4
|
|
|
5
5
|
import {getClientState} from '../client/clientStore'
|
|
6
|
-
import {createSanityInstance} from '../
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {errorHandler, getQueryState, queryStore, resolveQuery} from './queryStore'
|
|
6
|
+
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
7
|
+
import {type StateSource} from '../store/createStateSourceAction'
|
|
8
|
+
import {getQueryState, resolveQuery} from './queryStore'
|
|
10
9
|
|
|
11
10
|
vi.mock('./queryStoreConstants', async (importOriginal) => ({
|
|
12
11
|
...(await importOriginal<typeof import('./queryStoreConstants')>()),
|
|
@@ -18,6 +17,7 @@ vi.mock('../client/clientStore', () => ({
|
|
|
18
17
|
}))
|
|
19
18
|
|
|
20
19
|
describe('queryStore', () => {
|
|
20
|
+
let instance: SanityInstance
|
|
21
21
|
let liveEvents: Subject<LiveEvent>
|
|
22
22
|
let fetch: SanityClient['observable']['fetch']
|
|
23
23
|
// Mock data for testing
|
|
@@ -29,6 +29,8 @@ describe('queryStore', () => {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
beforeEach(() => {
|
|
32
|
+
instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
33
|
+
|
|
32
34
|
fetch = vi
|
|
33
35
|
.fn()
|
|
34
36
|
.mockReturnValue(
|
|
@@ -50,8 +52,11 @@ describe('queryStore', () => {
|
|
|
50
52
|
} as StateSource<SanityClient>)
|
|
51
53
|
})
|
|
52
54
|
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
instance.dispose()
|
|
57
|
+
})
|
|
58
|
+
|
|
53
59
|
it('initializes query state and cleans up after unsubscribe', async () => {
|
|
54
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
55
60
|
const query = '*[_type == "movie"]'
|
|
56
61
|
const state = getQueryState(instance, query)
|
|
57
62
|
|
|
@@ -78,12 +83,9 @@ describe('queryStore', () => {
|
|
|
78
83
|
|
|
79
84
|
// Verify state is cleared
|
|
80
85
|
expect(state.getCurrent()).toBeUndefined()
|
|
81
|
-
|
|
82
|
-
instance.dispose()
|
|
83
86
|
})
|
|
84
87
|
|
|
85
88
|
it('maintains state when multiple subscribers exist', async () => {
|
|
86
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
87
89
|
const query = '*[_type == "movie"]'
|
|
88
90
|
const state = getQueryState(instance, query)
|
|
89
91
|
|
|
@@ -117,12 +119,9 @@ describe('queryStore', () => {
|
|
|
117
119
|
|
|
118
120
|
// Verify state is cleared after all subscribers are gone
|
|
119
121
|
expect(state.getCurrent()).toBeUndefined()
|
|
120
|
-
|
|
121
|
-
instance.dispose()
|
|
122
122
|
})
|
|
123
123
|
|
|
124
124
|
it('resolveQuery works without affecting subscriber cleanup', async () => {
|
|
125
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
126
125
|
const query = '*[_type == "movie"]'
|
|
127
126
|
|
|
128
127
|
const state = getQueryState(instance, query)
|
|
@@ -151,12 +150,9 @@ describe('queryStore', () => {
|
|
|
151
150
|
unsubscribe()
|
|
152
151
|
await new Promise((resolve) => setTimeout(resolve, 20))
|
|
153
152
|
expect(state.getCurrent()).toBeUndefined()
|
|
154
|
-
|
|
155
|
-
instance.dispose()
|
|
156
153
|
})
|
|
157
154
|
|
|
158
155
|
it('handles abort signal in resolveQuery', async () => {
|
|
159
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
160
156
|
const query = '*[_type == "movie"]'
|
|
161
157
|
const abortController = new AbortController()
|
|
162
158
|
|
|
@@ -171,8 +167,6 @@ describe('queryStore', () => {
|
|
|
171
167
|
|
|
172
168
|
// Verify state is cleared after abort
|
|
173
169
|
expect(getQueryState(instance, query).getCurrent()).toBeUndefined()
|
|
174
|
-
|
|
175
|
-
instance.dispose()
|
|
176
170
|
})
|
|
177
171
|
|
|
178
172
|
it('refetches query when receiving live event with matching sync tag', async () => {
|
|
@@ -190,7 +184,6 @@ describe('queryStore', () => {
|
|
|
190
184
|
),
|
|
191
185
|
)
|
|
192
186
|
|
|
193
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
194
187
|
const query = '*[_type == "movie"]'
|
|
195
188
|
const state = getQueryState<{_id: string; _type: string; title: string}[]>(instance, query)
|
|
196
189
|
|
|
@@ -214,7 +207,6 @@ describe('queryStore', () => {
|
|
|
214
207
|
expect(result).toContainEqual(updatedMovie)
|
|
215
208
|
|
|
216
209
|
unsubscribe()
|
|
217
|
-
instance.dispose()
|
|
218
210
|
})
|
|
219
211
|
|
|
220
212
|
it('does not refetch for non-matching sync tags', async () => {
|
|
@@ -223,7 +215,6 @@ describe('queryStore', () => {
|
|
|
223
215
|
of({result: mockData.movies, syncTags: mockSyncTags, ms: 0}).pipe(delay(0)),
|
|
224
216
|
)
|
|
225
217
|
|
|
226
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
227
218
|
const query = '*[_type == "movie"]'
|
|
228
219
|
const state = getQueryState(instance, query)
|
|
229
220
|
|
|
@@ -243,7 +234,6 @@ describe('queryStore', () => {
|
|
|
243
234
|
expect(fetch).toHaveBeenCalledTimes(1)
|
|
244
235
|
|
|
245
236
|
unsubscribe()
|
|
246
|
-
instance.dispose()
|
|
247
237
|
})
|
|
248
238
|
|
|
249
239
|
it('handles multiple live events with same sync tag', async () => {
|
|
@@ -258,7 +248,6 @@ describe('queryStore', () => {
|
|
|
258
248
|
of({result: mockData.movies, syncTags: mockSyncTags, ms: 0}).pipe(delay(0)),
|
|
259
249
|
)
|
|
260
250
|
|
|
261
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
262
251
|
const query = '*[_type == "movie"]'
|
|
263
252
|
const state = getQueryState(instance, query)
|
|
264
253
|
|
|
@@ -284,11 +273,9 @@ describe('queryStore', () => {
|
|
|
284
273
|
expect(vi.mocked(fetch).mock.calls[2][2]?.lastLiveEventId).toBe('event2')
|
|
285
274
|
|
|
286
275
|
unsubscribe()
|
|
287
|
-
instance.dispose()
|
|
288
276
|
})
|
|
289
277
|
|
|
290
278
|
it('handles errors in query fetching', async () => {
|
|
291
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
292
279
|
const errorMessage = 'Query failed'
|
|
293
280
|
|
|
294
281
|
// Override fetch to simulate error
|
|
@@ -306,29 +293,9 @@ describe('queryStore', () => {
|
|
|
306
293
|
expect(() => state.getCurrent()).toThrow(errorMessage)
|
|
307
294
|
|
|
308
295
|
unsubscribe()
|
|
309
|
-
instance.dispose()
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
it('throws an error if an errorHandler has been called', () => {
|
|
313
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
314
|
-
const query = '*[_type == "movie"]'
|
|
315
|
-
const state = getQueryState(instance, query)
|
|
316
|
-
|
|
317
|
-
const resource = getOrCreateResource(instance, queryStore)
|
|
318
|
-
|
|
319
|
-
// Create an error and call the error handler
|
|
320
|
-
const testError = new Error('Global error from error handler')
|
|
321
|
-
const handler = errorHandler({state: resource.state, instance})
|
|
322
|
-
handler(testError)
|
|
323
|
-
|
|
324
|
-
// Verify the error is thrown when accessing state
|
|
325
|
-
expect(() => state.getCurrent()).toThrow('Global error from error handler')
|
|
326
|
-
|
|
327
|
-
instance.dispose()
|
|
328
296
|
})
|
|
329
297
|
|
|
330
298
|
it('delays query state removal after unsubscribe', async () => {
|
|
331
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
332
299
|
const query = '*[_type == "movie"]'
|
|
333
300
|
const state = getQueryState(instance, query)
|
|
334
301
|
const unsubscribe = state.subscribe()
|
|
@@ -342,12 +309,9 @@ describe('queryStore', () => {
|
|
|
342
309
|
// Wait for the cleanup delay and then state should be removed
|
|
343
310
|
await new Promise((resolve) => setTimeout(resolve, 20))
|
|
344
311
|
expect(state.getCurrent()).toBeUndefined()
|
|
345
|
-
|
|
346
|
-
instance.dispose()
|
|
347
312
|
})
|
|
348
313
|
|
|
349
314
|
it('preserves query state if a new subscriber subscribes before cleanup delay', async () => {
|
|
350
|
-
const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
351
315
|
const query = '*[_type == "movie"]'
|
|
352
316
|
const state = getQueryState(instance, query)
|
|
353
317
|
const unsubscribe1 = state.subscribe()
|
|
@@ -374,6 +338,5 @@ describe('queryStore', () => {
|
|
|
374
338
|
{_id: 'movie2', _type: 'movie', title: 'Movie 2'},
|
|
375
339
|
])
|
|
376
340
|
unsubscribe2()
|
|
377
|
-
instance.dispose()
|
|
378
341
|
})
|
|
379
342
|
})
|