@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
|
@@ -1,52 +1,52 @@
|
|
|
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 {getProjectionState} from './getProjectionState'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
8
|
+
import {type ProjectionStoreState} from './projectionStore'
|
|
9
|
+
import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
|
|
10
|
+
import {PROJECTION_STATE_CLEAR_DELAY, STABLE_EMPTY_PROJECTION} from './util'
|
|
13
11
|
|
|
14
12
|
vi.mock('../utils/ids', async (importOriginal) => {
|
|
15
13
|
const util = await importOriginal<typeof import('../utils/ids')>()
|
|
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('getProjectionState', () => {
|
|
25
|
-
|
|
26
|
-
const
|
|
20
|
+
let instance: SanityInstance
|
|
21
|
+
const docHandle = {documentId: 'exampleId', documentType: 'exampleType'}
|
|
27
22
|
const projection = '{exampleProjection}'
|
|
28
|
-
|
|
29
|
-
values: {},
|
|
30
|
-
documentProjections: {},
|
|
31
|
-
subscriptions: {},
|
|
32
|
-
syncTags: {},
|
|
33
|
-
lastLiveEventId: null,
|
|
34
|
-
}
|
|
35
|
-
let state: ResourceState<ProjectionStoreState>
|
|
23
|
+
let state: StoreState<ProjectionStoreState & {extra?: unknown}>
|
|
36
24
|
|
|
37
25
|
beforeEach(() => {
|
|
38
|
-
|
|
26
|
+
// capture state
|
|
27
|
+
vi.mocked(subscribeToStateAndFetchBatches).mockImplementation((context) => {
|
|
28
|
+
state = context.state
|
|
29
|
+
return NEVER.subscribe()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
instance = createSanityInstance({projectId: 'exampleProject', dataset: 'exampleDataset'})
|
|
33
|
+
vi.useFakeTimers() // Enable fake timers for each test
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
instance.dispose()
|
|
38
|
+
vi.useRealTimers() // Restore real timers after each test
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
it('returns a state source that emits when the projection value changes', () => {
|
|
42
|
-
const projectionState = getProjectionState(
|
|
42
|
+
const projectionState = getProjectionState(instance, {projection, ...docHandle})
|
|
43
43
|
expect(projectionState.getCurrent()).toBe(STABLE_EMPTY_PROJECTION)
|
|
44
44
|
|
|
45
45
|
const subscriber = vi.fn()
|
|
46
46
|
projectionState.subscribe(subscriber)
|
|
47
47
|
|
|
48
48
|
// emit unrelated state changes
|
|
49
|
-
state.set('updateLastLiveEventId', {
|
|
49
|
+
state.set('updateLastLiveEventId', {extra: 'unrelated change'})
|
|
50
50
|
expect(subscriber).toHaveBeenCalledTimes(0)
|
|
51
51
|
|
|
52
52
|
state.set('relatedChange', (prev) => ({
|
|
@@ -69,55 +69,75 @@ describe('getProjectionState', () => {
|
|
|
69
69
|
})
|
|
70
70
|
|
|
71
71
|
it('adds a subscription ID and projection to the state on subscription', () => {
|
|
72
|
-
const projectionState = getProjectionState(
|
|
72
|
+
const projectionState = getProjectionState(instance, {projection, ...docHandle})
|
|
73
73
|
|
|
74
74
|
expect(state.get().subscriptions).toEqual({})
|
|
75
|
-
vi.mocked(insecureRandomId)
|
|
76
|
-
.mockImplementationOnce(() => 'pseudoRandomId1')
|
|
77
|
-
.mockImplementationOnce(() => 'pseudoRandomId2')
|
|
75
|
+
vi.mocked(insecureRandomId).mockImplementationOnce(() => 'pseudoRandomId1')
|
|
78
76
|
|
|
79
77
|
const unsubscribe1 = projectionState.subscribe(vi.fn())
|
|
80
|
-
const unsubscribe2 = projectionState.subscribe(vi.fn())
|
|
81
78
|
|
|
82
79
|
expect(state.get().subscriptions).toEqual({
|
|
83
|
-
exampleId: {pseudoRandomId1: true
|
|
80
|
+
exampleId: {pseudoRandomId1: true},
|
|
84
81
|
})
|
|
85
82
|
expect(state.get().documentProjections).toEqual({
|
|
86
83
|
exampleId: projection,
|
|
87
84
|
})
|
|
88
85
|
|
|
89
|
-
|
|
86
|
+
// Unsubscribe the last one - state should NOT clear immediately
|
|
87
|
+
unsubscribe1()
|
|
90
88
|
expect(state.get().subscriptions).toEqual({
|
|
91
89
|
exampleId: {pseudoRandomId1: true},
|
|
92
90
|
})
|
|
91
|
+
// Projection data might also remain initially
|
|
92
|
+
expect(state.get().documentProjections).toEqual({
|
|
93
|
+
exampleId: projection,
|
|
94
|
+
})
|
|
93
95
|
|
|
94
|
-
|
|
96
|
+
// Advance timers past the clear delay
|
|
97
|
+
vi.advanceTimersByTime(PROJECTION_STATE_CLEAR_DELAY)
|
|
98
|
+
|
|
99
|
+
// NOW the state related to this document should be cleared
|
|
95
100
|
expect(state.get().subscriptions).toEqual({})
|
|
101
|
+
expect(state.get().documentProjections).toEqual({exampleId: projection})
|
|
96
102
|
})
|
|
97
103
|
|
|
98
104
|
it('resets to pending false on unsubscribe if the subscription is the last one', () => {
|
|
105
|
+
const projectionState = getProjectionState(instance, {projection, ...docHandle})
|
|
106
|
+
|
|
99
107
|
state.set('presetValueToPending', (prev) => ({
|
|
100
|
-
values: {...prev.values, [
|
|
108
|
+
values: {...prev.values, [docHandle.documentId]: {data: {field: 'Foo'}, isPending: true}},
|
|
101
109
|
}))
|
|
102
110
|
|
|
103
|
-
const projectionState = getProjectionState({state, instance}, {document, projection})
|
|
104
|
-
|
|
105
111
|
const unsubscribe1 = projectionState.subscribe(vi.fn())
|
|
106
|
-
const unsubscribe2 = projectionState.subscribe(vi.fn())
|
|
107
112
|
|
|
108
|
-
expect(state.get().values[
|
|
113
|
+
expect(state.get().values[docHandle.documentId]).toEqual({
|
|
114
|
+
data: {field: 'Foo'},
|
|
115
|
+
isPending: true,
|
|
116
|
+
})
|
|
109
117
|
|
|
118
|
+
// Unsubscribe one - pending state remains
|
|
110
119
|
unsubscribe1()
|
|
111
|
-
expect(state.get().values[
|
|
120
|
+
expect(state.get().values[docHandle.documentId]).toEqual({
|
|
121
|
+
data: {field: 'Foo'},
|
|
122
|
+
isPending: true,
|
|
123
|
+
})
|
|
112
124
|
|
|
113
|
-
|
|
114
|
-
expect(state.get().subscriptions).
|
|
115
|
-
expect(state.get().values[
|
|
116
|
-
|
|
125
|
+
// Unsubscribe the last one - pending state should NOT reset immediately
|
|
126
|
+
expect(Object.keys(state.get().subscriptions['exampleId'] || {}).length).toBeGreaterThan(0)
|
|
127
|
+
expect(state.get().values[docHandle.documentId]).toEqual({
|
|
128
|
+
data: {field: 'Foo'},
|
|
129
|
+
isPending: true, // Still pending
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
// Advance timers past the clear delay
|
|
133
|
+
vi.advanceTimersByTime(PROJECTION_STATE_CLEAR_DELAY)
|
|
117
134
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
135
|
+
// NOW the pending state should be reset
|
|
136
|
+
expect(state.get().values[docHandle.documentId]).toEqual({
|
|
137
|
+
data: {field: 'Foo'},
|
|
138
|
+
isPending: false, // Reset to false
|
|
139
|
+
})
|
|
140
|
+
// And subscriptions should be cleared now
|
|
141
|
+
expect(state.get().subscriptions).toEqual({})
|
|
122
142
|
})
|
|
123
143
|
})
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {omit} from 'lodash-es'
|
|
2
2
|
|
|
3
|
-
import {type DocumentHandle} from '../
|
|
4
|
-
import {
|
|
5
|
-
import {type
|
|
6
|
-
import {
|
|
3
|
+
import {type DocumentHandle} from '../config/sanityConfig'
|
|
4
|
+
import {bindActionByDataset} from '../store/createActionBinder'
|
|
5
|
+
import {type SanityInstance} from '../store/createSanityInstance'
|
|
6
|
+
import {
|
|
7
|
+
createStateSourceAction,
|
|
8
|
+
type SelectorContext,
|
|
9
|
+
type StateSource,
|
|
10
|
+
} from '../store/createStateSourceAction'
|
|
7
11
|
import {getPublishedId, insecureRandomId} from '../utils/ids'
|
|
8
12
|
import {
|
|
9
13
|
projectionStore,
|
|
@@ -11,31 +15,24 @@ import {
|
|
|
11
15
|
type ProjectionValuePending,
|
|
12
16
|
type ValidProjection,
|
|
13
17
|
} from './projectionStore'
|
|
14
|
-
import {STABLE_EMPTY_PROJECTION, validateProjection} from './util'
|
|
18
|
+
import {PROJECTION_STATE_CLEAR_DELAY, STABLE_EMPTY_PROJECTION, validateProjection} from './util'
|
|
15
19
|
|
|
16
|
-
interface GetProjectionStateOptions {
|
|
17
|
-
document: DocumentHandle
|
|
20
|
+
interface GetProjectionStateOptions extends DocumentHandle {
|
|
18
21
|
projection: ValidProjection
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
const getProjectStateSourceAction = createStateSourceAction(
|
|
22
|
-
projectionStore,
|
|
23
|
-
(state, {document}: GetProjectionStateOptions): ProjectionValuePending<object> =>
|
|
24
|
-
state.values[document._id] ?? STABLE_EMPTY_PROJECTION,
|
|
25
|
-
)
|
|
26
|
-
|
|
27
24
|
/**
|
|
28
25
|
* @beta
|
|
29
26
|
*/
|
|
30
27
|
export function getProjectionState<TResult extends object>(
|
|
31
|
-
instance: SanityInstance
|
|
28
|
+
instance: SanityInstance,
|
|
32
29
|
options: GetProjectionStateOptions,
|
|
33
30
|
): StateSource<ProjectionValuePending<TResult>>
|
|
34
31
|
/**
|
|
35
32
|
* @beta
|
|
36
33
|
*/
|
|
37
34
|
export function getProjectionState(
|
|
38
|
-
instance: SanityInstance
|
|
35
|
+
instance: SanityInstance,
|
|
39
36
|
options: GetProjectionStateOptions,
|
|
40
37
|
): StateSource<ProjectionValuePending<Record<string, unknown>>>
|
|
41
38
|
/**
|
|
@@ -50,39 +47,34 @@ export function getProjectionState(
|
|
|
50
47
|
/**
|
|
51
48
|
* @beta
|
|
52
49
|
*/
|
|
53
|
-
export const _getProjectionState =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
subscribe: (subscriber) => {
|
|
65
|
-
const subscriptionId = insecureRandomId()
|
|
50
|
+
export const _getProjectionState = bindActionByDataset(
|
|
51
|
+
projectionStore,
|
|
52
|
+
createStateSourceAction({
|
|
53
|
+
selector: (
|
|
54
|
+
{state}: SelectorContext<ProjectionStoreState>,
|
|
55
|
+
options: GetProjectionStateOptions,
|
|
56
|
+
): ProjectionValuePending<object> =>
|
|
57
|
+
state.values[options.documentId] ?? STABLE_EMPTY_PROJECTION,
|
|
58
|
+
onSubscribe: ({state}, {projection, ...docHandle}: GetProjectionStateOptions) => {
|
|
59
|
+
const subscriptionId = insecureRandomId()
|
|
60
|
+
const documentId = getPublishedId(docHandle.documentId)
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
},
|
|
62
|
+
state.set('addSubscription', (prev) => ({
|
|
63
|
+
documentProjections: {
|
|
64
|
+
...prev.documentProjections,
|
|
65
|
+
[documentId]: validateProjection(projection),
|
|
66
|
+
},
|
|
67
|
+
subscriptions: {
|
|
68
|
+
...prev.subscriptions,
|
|
69
|
+
[documentId]: {
|
|
70
|
+
...prev.subscriptions[documentId],
|
|
71
|
+
[subscriptionId]: true,
|
|
78
72
|
},
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const unsubscribe = projectionState.subscribe(subscriber)
|
|
82
|
-
|
|
83
|
-
return () => {
|
|
84
|
-
unsubscribe()
|
|
73
|
+
},
|
|
74
|
+
}))
|
|
85
75
|
|
|
76
|
+
return () => {
|
|
77
|
+
setTimeout(() => {
|
|
86
78
|
state.set('removeSubscription', (prev): Partial<ProjectionStoreState> => {
|
|
87
79
|
const documentSubscriptions = omit(prev.subscriptions[documentId], subscriptionId)
|
|
88
80
|
const hasSubscribers = !!Object.keys(documentSubscriptions).length
|
|
@@ -98,8 +90,8 @@ export const _getProjectionState = createAction(projectionStore, ({state}) => {
|
|
|
98
90
|
: {...prev.values, [documentId]: {data: projectionValue, isPending: false}},
|
|
99
91
|
}
|
|
100
92
|
})
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
93
|
+
}, PROJECTION_STATE_CLEAR_DELAY)
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
}),
|
|
97
|
+
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {hashString} from '../
|
|
1
|
+
import {hashString} from '../utils/hashString'
|
|
2
2
|
import {getDraftId, getPublishedId} from '../utils/ids'
|
|
3
3
|
import {type ProjectionValuePending, type ValidProjection} from './projectionStore'
|
|
4
4
|
import {validateProjection} from './util'
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {describe,
|
|
1
|
+
import {Observable} from 'rxjs'
|
|
2
|
+
import {describe, it, vi} from 'vitest'
|
|
3
3
|
|
|
4
|
-
import {createSanityInstance} from '../
|
|
5
|
-
import {
|
|
4
|
+
import {createSanityInstance} from '../store/createSanityInstance'
|
|
5
|
+
import {createStoreInstance} from '../store/createStoreInstance'
|
|
6
6
|
import {projectionStore} from './projectionStore'
|
|
7
7
|
import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
|
|
8
|
-
import {PROJECTION_TAG} from './util'
|
|
9
8
|
|
|
10
9
|
// Mock the module with a factory function
|
|
11
10
|
vi.mock('../common/createLiveEventSubscriber', () => {
|
|
@@ -15,61 +14,24 @@ vi.mock('../common/createLiveEventSubscriber', () => {
|
|
|
15
14
|
}
|
|
16
15
|
})
|
|
17
16
|
|
|
18
|
-
vi.mock('./subscribeToStateAndFetchBatches'
|
|
19
|
-
subscribeToStateAndFetchBatches: vi.fn(),
|
|
20
|
-
}))
|
|
17
|
+
vi.mock('./subscribeToStateAndFetchBatches')
|
|
21
18
|
|
|
22
19
|
describe('projectionStore', () => {
|
|
23
20
|
it('is a resource that initializes with state and subscriptions', async () => {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
values: {},
|
|
29
|
-
documentProjections: {},
|
|
30
|
-
subscriptions: {},
|
|
31
|
-
syncTags: {},
|
|
32
|
-
lastLiveEventId: null,
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
const liveUnsubscribe = vi.fn()
|
|
36
|
-
const stateUnsubscribe = vi.fn()
|
|
37
|
-
|
|
38
|
-
const {createLiveEventSubscriber} = vi.mocked(
|
|
39
|
-
await import('../common/createLiveEventSubscriber'),
|
|
21
|
+
const teardown = vi.fn()
|
|
22
|
+
const subscriber = vi.fn().mockReturnValue(teardown)
|
|
23
|
+
vi.mocked(subscribeToStateAndFetchBatches).mockReturnValue(
|
|
24
|
+
new Observable(subscriber).subscribe(),
|
|
40
25
|
)
|
|
41
|
-
const mockLiveSubscriber = vi
|
|
42
|
-
.mocked(createLiveEventSubscriber(PROJECTION_TAG))
|
|
43
|
-
.mockReturnValue({
|
|
44
|
-
unsubscribe: liveUnsubscribe,
|
|
45
|
-
} as unknown as Subscription)
|
|
46
|
-
|
|
47
|
-
;(subscribeToStateAndFetchBatches as Mock).mockImplementation(() => ({
|
|
48
|
-
unsubscribe: stateUnsubscribe,
|
|
49
|
-
}))
|
|
50
26
|
|
|
51
|
-
const
|
|
52
|
-
{
|
|
53
|
-
instance,
|
|
54
|
-
state: createResourceState(initialState),
|
|
55
|
-
},
|
|
56
|
-
instance,
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
// Verify the factory was called with the correct tag
|
|
60
|
-
expect(createLiveEventSubscriber).toHaveBeenCalledWith(PROJECTION_TAG)
|
|
27
|
+
const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
61
28
|
|
|
62
|
-
|
|
63
|
-
expect(mockLiveSubscriber).toHaveBeenCalledWith({
|
|
64
|
-
instance,
|
|
65
|
-
state: expect.any(Object),
|
|
66
|
-
})
|
|
29
|
+
const {state, dispose} = createStoreInstance(instance, projectionStore)
|
|
67
30
|
|
|
68
31
|
expect(subscribeToStateAndFetchBatches).toHaveBeenCalledOnce()
|
|
32
|
+
expect(subscribeToStateAndFetchBatches).toHaveBeenCalledWith({instance, state})
|
|
69
33
|
|
|
70
34
|
dispose()
|
|
71
|
-
|
|
72
|
-
expect(liveUnsubscribe).toHaveBeenCalledOnce()
|
|
73
|
-
expect(stateUnsubscribe).toHaveBeenCalledOnce()
|
|
35
|
+
instance.dispose()
|
|
74
36
|
})
|
|
75
37
|
})
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {type LiveEventAwareState} from '../common/types'
|
|
3
|
-
import {createResource} from '../resources/createResource'
|
|
1
|
+
import {defineStore} from '../store/defineStore'
|
|
4
2
|
import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
|
|
5
|
-
import {PROJECTION_TAG} from './util'
|
|
6
3
|
|
|
7
4
|
/**
|
|
8
5
|
* @beta
|
|
@@ -24,32 +21,23 @@ export interface ProjectionValuePending<TValue extends object> {
|
|
|
24
21
|
isPending: boolean
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
export interface ProjectionStoreState<TValue extends object = object>
|
|
24
|
+
export interface ProjectionStoreState<TValue extends object = object> {
|
|
28
25
|
values: {[TDocumentId in string]?: ProjectionValuePending<TValue>}
|
|
29
26
|
documentProjections: {[TDocumentId in string]?: ValidProjection}
|
|
30
27
|
subscriptions: {[TDocumentId in string]?: {[TSubscriptionId in string]?: true}}
|
|
31
28
|
}
|
|
32
29
|
|
|
33
|
-
export const projectionStore =
|
|
30
|
+
export const projectionStore = defineStore<ProjectionStoreState>({
|
|
34
31
|
name: 'Projection',
|
|
35
32
|
getInitialState() {
|
|
36
33
|
return {
|
|
37
34
|
values: {},
|
|
38
35
|
documentProjections: {},
|
|
39
36
|
subscriptions: {},
|
|
40
|
-
syncTags: {},
|
|
41
|
-
lastLiveEventId: null,
|
|
42
37
|
}
|
|
43
38
|
},
|
|
44
|
-
initialize() {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
const liveSubscription = subscribeToLiveAndSetLastLiveEventId(this)
|
|
48
|
-
const batchSubscription = subscribeToStateAndFetchBatches(this)
|
|
49
|
-
|
|
50
|
-
return () => {
|
|
51
|
-
liveSubscription.unsubscribe()
|
|
52
|
-
batchSubscription.unsubscribe()
|
|
53
|
-
}
|
|
39
|
+
initialize(context) {
|
|
40
|
+
const batchSubscription = subscribeToStateAndFetchBatches(context)
|
|
41
|
+
return () => batchSubscription.unsubscribe()
|
|
54
42
|
},
|
|
55
43
|
})
|
|
@@ -1,144 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from '
|
|
10
|
-
import {insecureRandomId} from '../utils/ids'
|
|
11
|
-
import {
|
|
12
|
-
projectionStore,
|
|
13
|
-
type ProjectionStoreState,
|
|
14
|
-
type ProjectionValuePending,
|
|
15
|
-
} from './projectionStore'
|
|
1
|
+
import {type SanityDocumentLike} from '@sanity/types'
|
|
2
|
+
import {of} from 'rxjs'
|
|
3
|
+
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
4
|
+
|
|
5
|
+
import {type DocumentHandle} from '../config/sanityConfig'
|
|
6
|
+
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
7
|
+
import {type StateSource} from '../store/createStateSourceAction'
|
|
8
|
+
import {getProjectionState} from './getProjectionState'
|
|
9
|
+
import {type ProjectionValuePending, type ValidProjection} from './projectionStore'
|
|
16
10
|
import {resolveProjection} from './resolveProjection'
|
|
17
11
|
|
|
18
|
-
vi.mock('
|
|
19
|
-
const util = await importOriginal<typeof import('../utils/ids')>()
|
|
20
|
-
return {...util, insecureRandomId: vi.fn(util.insecureRandomId)}
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
vi.mock('../resources/createResource', async (importOriginal) => {
|
|
24
|
-
const original = await importOriginal<typeof import('../resources/createResource')>()
|
|
25
|
-
return {...original, getOrCreateResource: vi.fn()}
|
|
26
|
-
})
|
|
12
|
+
vi.mock('./getProjectionState')
|
|
27
13
|
|
|
28
14
|
describe('resolveProjection', () => {
|
|
29
|
-
|
|
30
|
-
const document = {_id: 'exampleId', _type: 'exampleType'}
|
|
31
|
-
const projectionString = '{title, description}'
|
|
32
|
-
const initialState: ProjectionStoreState = {
|
|
33
|
-
documentProjections: {},
|
|
34
|
-
lastLiveEventId: null,
|
|
35
|
-
subscriptions: {},
|
|
36
|
-
syncTags: {},
|
|
37
|
-
values: {},
|
|
38
|
-
}
|
|
39
|
-
let state: ResourceState<ProjectionStoreState>
|
|
15
|
+
let instance: SanityInstance
|
|
40
16
|
|
|
41
17
|
beforeEach(() => {
|
|
42
|
-
|
|
18
|
+
vi.resetAllMocks()
|
|
19
|
+
// Create a mock that returns the correct ProjectionValuePending type
|
|
20
|
+
vi.mocked(getProjectionState).mockReturnValue({
|
|
21
|
+
observable: of({
|
|
22
|
+
data: {title: 'test'},
|
|
23
|
+
isPending: false,
|
|
24
|
+
} as ProjectionValuePending<Record<string, unknown>>),
|
|
25
|
+
} as StateSource<ProjectionValuePending<Record<string, unknown>>>)
|
|
26
|
+
|
|
27
|
+
instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
43
28
|
})
|
|
44
29
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
;(insecureRandomId as Mock).mockImplementationOnce(() => 'pseudoRandomId')
|
|
48
|
-
|
|
49
|
-
const projectionPromise = resolveProjection(
|
|
50
|
-
{state, instance},
|
|
51
|
-
{document, projection: projectionString},
|
|
52
|
-
)
|
|
53
|
-
expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
|
|
54
|
-
expect(state.get().documentProjections).toEqual({exampleId: projectionString})
|
|
55
|
-
|
|
56
|
-
state.set('updateDifferentDocument', (prev) => ({
|
|
57
|
-
values: {
|
|
58
|
-
...prev.values,
|
|
59
|
-
differentId: {data: {title: 'Different Document'}, isPending: false},
|
|
60
|
-
},
|
|
61
|
-
}))
|
|
62
|
-
|
|
63
|
-
expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
|
|
64
|
-
|
|
65
|
-
state.set('updateCorrectDocumentButNull', (prev) => ({
|
|
66
|
-
values: {...prev.values, exampleId: {data: null, isPending: true}},
|
|
67
|
-
}))
|
|
68
|
-
|
|
69
|
-
expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
|
|
70
|
-
|
|
71
|
-
state.set('updateCorrectDocument', (prev) => ({
|
|
72
|
-
values: {
|
|
73
|
-
...prev.values,
|
|
74
|
-
exampleId: {
|
|
75
|
-
data: {title: 'Correct Document', description: 'Test'},
|
|
76
|
-
isPending: false,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
}))
|
|
80
|
-
|
|
81
|
-
const projectionResult = await projectionPromise
|
|
82
|
-
expect(projectionResult).toEqual({
|
|
83
|
-
data: {title: 'Correct Document', description: 'Test'},
|
|
84
|
-
isPending: false,
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
// subscription is removed after
|
|
88
|
-
expect(state.get().subscriptions).toEqual({})
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
instance.dispose()
|
|
89
32
|
})
|
|
90
33
|
|
|
91
|
-
it('resolves
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
34
|
+
it('resolves a projection and returns the first emitted value with results', async () => {
|
|
35
|
+
const docHandle: DocumentHandle<SanityDocumentLike> = {
|
|
36
|
+
documentId: 'doc123',
|
|
37
|
+
documentType: 'movie',
|
|
95
38
|
}
|
|
96
|
-
|
|
97
|
-
values: {...prev.values, exampleId: currentValue},
|
|
98
|
-
}))
|
|
99
|
-
vi.mocked(insecureRandomId).mockImplementationOnce(() => 'pseudoRandomId')
|
|
100
|
-
expect(state.get().subscriptions).toEqual({})
|
|
39
|
+
const projection = '{title}' as ValidProjection
|
|
101
40
|
|
|
102
|
-
const
|
|
103
|
-
{state, instance},
|
|
104
|
-
{document, projection: projectionString},
|
|
105
|
-
)
|
|
106
|
-
expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
|
|
41
|
+
const result = await resolveProjection(instance, {...docHandle, projection})
|
|
107
42
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
differentId: {data: {title: 'Different Document'}, isPending: false},
|
|
112
|
-
},
|
|
113
|
-
}))
|
|
114
|
-
expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
|
|
115
|
-
|
|
116
|
-
state.set('updateWithCurrentValue', (prev) => ({
|
|
117
|
-
values: {...prev.values, exampleId: currentValue},
|
|
118
|
-
}))
|
|
119
|
-
expect(state.get().subscriptions).toEqual({exampleId: {pseudoRandomId: true}})
|
|
120
|
-
|
|
121
|
-
state.set('updateWithNewValue', (prev) => ({
|
|
122
|
-
values: {
|
|
123
|
-
...prev.values,
|
|
124
|
-
exampleId: {
|
|
125
|
-
data: {title: 'New Value', description: 'Updated'},
|
|
126
|
-
isPending: false,
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
}))
|
|
130
|
-
expect(state.get().subscriptions).toEqual({})
|
|
131
|
-
|
|
132
|
-
const projectionResult = await projectionPromise
|
|
133
|
-
expect(projectionResult).toEqual({
|
|
134
|
-
data: {title: 'New Value', description: 'Updated'},
|
|
43
|
+
expect(getProjectionState).toHaveBeenCalledWith(instance, {...docHandle, projection})
|
|
44
|
+
expect(result).toEqual({
|
|
45
|
+
data: {title: 'test'},
|
|
135
46
|
isPending: false,
|
|
136
47
|
})
|
|
137
48
|
})
|
|
138
|
-
|
|
139
|
-
it('calls getOrCreateResource if no state is provided', () => {
|
|
140
|
-
vi.mocked(getOrCreateResource).mockReturnValue({state} as InitializedResource<unknown>)
|
|
141
|
-
resolveProjection(instance, {document, projection: projectionString})
|
|
142
|
-
expect(getOrCreateResource).toHaveBeenCalledWith(instance, projectionStore)
|
|
143
|
-
})
|
|
144
49
|
})
|