@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,123 +1,239 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
type ResourceState,
|
|
8
|
-
} from '../resources/createResource'
|
|
1
|
+
import {NEVER} from 'rxjs'
|
|
2
|
+
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
3
|
+
|
|
4
|
+
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
5
|
+
import {type StoreState} from '../store/createStoreState'
|
|
6
|
+
import {hashString} from '../utils/hashString'
|
|
9
7
|
import {insecureRandomId} from '../utils/ids'
|
|
10
8
|
import {getProjectionState} from './getProjectionState'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
9
|
+
import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
|
|
10
|
+
import {type ProjectionStoreState} from './types'
|
|
11
|
+
import {PROJECTION_STATE_CLEAR_DELAY, STABLE_EMPTY_PROJECTION} from './util'
|
|
12
|
+
// mocking subscription counts is a little tricky.
|
|
13
|
+
// all test ids in this file start at 2 because the first call to onSubscribe
|
|
14
|
+
// happens within createStateSourceAction
|
|
15
|
+
let mockIdCounter = 0
|
|
13
16
|
|
|
14
17
|
vi.mock('../utils/ids', async (importOriginal) => {
|
|
15
18
|
const util = await importOriginal<typeof import('../utils/ids')>()
|
|
16
|
-
|
|
19
|
+
// Mock implementation uses the external counter
|
|
20
|
+
return {
|
|
21
|
+
...util,
|
|
22
|
+
insecureRandomId: vi.fn(() => {
|
|
23
|
+
const id = `testSubId_${++mockIdCounter}`
|
|
24
|
+
return id
|
|
25
|
+
}),
|
|
26
|
+
}
|
|
17
27
|
})
|
|
18
28
|
|
|
19
|
-
vi.mock('
|
|
20
|
-
const original = await importOriginal<typeof import('../resources/createResource')>()
|
|
21
|
-
return {...original, getOrCreateResource: vi.fn()}
|
|
22
|
-
})
|
|
29
|
+
vi.mock('./subscribeToStateAndFetchBatches.ts')
|
|
23
30
|
|
|
24
31
|
describe('getProjectionState', () => {
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
lastLiveEventId: null,
|
|
34
|
-
}
|
|
35
|
-
let state: ResourceState<ProjectionStoreState>
|
|
32
|
+
let instance: SanityInstance
|
|
33
|
+
const docHandle = {documentId: 'exampleId', documentType: 'exampleType'}
|
|
34
|
+
const projection1 = '{exampleProjection1}'
|
|
35
|
+
const hash1 = hashString(projection1)
|
|
36
|
+
const projection2 = '{exampleProjection2}'
|
|
37
|
+
const hash2 = hashString(projection2)
|
|
38
|
+
|
|
39
|
+
let state: StoreState<ProjectionStoreState & {extra?: unknown}>
|
|
36
40
|
|
|
37
41
|
beforeEach(() => {
|
|
38
|
-
|
|
42
|
+
mockIdCounter = 0
|
|
43
|
+
vi.mocked(insecureRandomId).mockClear()
|
|
44
|
+
|
|
45
|
+
// Capture state
|
|
46
|
+
vi.mocked(subscribeToStateAndFetchBatches).mockImplementation((context) => {
|
|
47
|
+
state = context.state
|
|
48
|
+
return NEVER.subscribe()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
instance = createSanityInstance({projectId: 'exampleProject', dataset: 'exampleDataset'})
|
|
52
|
+
vi.useFakeTimers() // Enable fake timers for each test
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
instance.dispose()
|
|
57
|
+
vi.useRealTimers() // Restore real timers
|
|
39
58
|
})
|
|
40
59
|
|
|
41
|
-
it('returns a state source that emits when the projection value changes', () => {
|
|
42
|
-
const projectionState = getProjectionState(
|
|
43
|
-
expect(projectionState.getCurrent()).
|
|
60
|
+
it('returns a state source that emits when the specific projection value changes', () => {
|
|
61
|
+
const projectionState = getProjectionState(instance, {projection: projection1, ...docHandle})
|
|
62
|
+
expect(projectionState.getCurrent()).toEqual(STABLE_EMPTY_PROJECTION)
|
|
44
63
|
|
|
45
64
|
const subscriber = vi.fn()
|
|
46
|
-
projectionState.subscribe(subscriber)
|
|
65
|
+
const unsubscribe = projectionState.subscribe(subscriber)
|
|
47
66
|
|
|
48
|
-
//
|
|
49
|
-
state.set('
|
|
50
|
-
|
|
67
|
+
// 1. Update the specific projection
|
|
68
|
+
state.set('update_doc1_proj1', (prev: ProjectionStoreState) => ({
|
|
69
|
+
values: {
|
|
70
|
+
...prev.values,
|
|
71
|
+
[docHandle.documentId]: {
|
|
72
|
+
...prev.values[docHandle.documentId],
|
|
73
|
+
[hash1]: {data: {name: 'Update 1'}, isPending: false},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
}))
|
|
77
|
+
expect(subscriber).toHaveBeenCalledTimes(1)
|
|
78
|
+
expect(projectionState.getCurrent()).toEqual({data: {name: 'Update 1'}, isPending: false})
|
|
51
79
|
|
|
52
|
-
|
|
53
|
-
|
|
80
|
+
// 2. Update a different projection for the same document
|
|
81
|
+
state.set('update_doc1_proj2', (prev: ProjectionStoreState) => ({
|
|
82
|
+
values: {
|
|
83
|
+
...prev.values,
|
|
84
|
+
[docHandle.documentId]: {
|
|
85
|
+
...prev.values[docHandle.documentId],
|
|
86
|
+
[hash2]: {data: {_type: 'type1'}, isPending: false},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
54
89
|
}))
|
|
90
|
+
// Should NOT trigger the subscriber for projection1
|
|
55
91
|
expect(subscriber).toHaveBeenCalledTimes(1)
|
|
92
|
+
expect(projectionState.getCurrent()).toEqual({data: {name: 'Update 1'}, isPending: false})
|
|
56
93
|
|
|
57
|
-
|
|
94
|
+
// 3. Update a different document
|
|
95
|
+
state.set('update_doc2', (prev: ProjectionStoreState) => ({
|
|
58
96
|
values: {
|
|
59
97
|
...prev.values,
|
|
60
|
-
|
|
98
|
+
doc2: {
|
|
99
|
+
[hash1]: {data: {name: 'Other Doc'}, isPending: false},
|
|
100
|
+
},
|
|
61
101
|
},
|
|
62
102
|
}))
|
|
103
|
+
// Should NOT trigger the subscriber for doc1/projection1
|
|
63
104
|
expect(subscriber).toHaveBeenCalledTimes(1)
|
|
64
105
|
|
|
65
|
-
|
|
66
|
-
|
|
106
|
+
// 4. Update the specific projection again
|
|
107
|
+
state.set('update_doc1_proj1_again', (prev: ProjectionStoreState) => ({
|
|
108
|
+
values: {
|
|
109
|
+
...prev.values,
|
|
110
|
+
[docHandle.documentId]: {
|
|
111
|
+
...prev.values[docHandle.documentId],
|
|
112
|
+
[hash1]: {data: {name: 'Update 2'}, isPending: false},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
67
115
|
}))
|
|
68
116
|
expect(subscriber).toHaveBeenCalledTimes(2)
|
|
117
|
+
expect(projectionState.getCurrent()).toEqual({data: {name: 'Update 2'}, isPending: false})
|
|
118
|
+
|
|
119
|
+
unsubscribe()
|
|
69
120
|
})
|
|
70
121
|
|
|
71
|
-
it('adds a subscription ID and projection to the
|
|
72
|
-
const
|
|
122
|
+
it('adds a subscription ID and projection to the correct hash on subscription and cleans up', () => {
|
|
123
|
+
const projectionState1 = getProjectionState(instance, {projection: projection1, ...docHandle})
|
|
124
|
+
const projectionState2 = getProjectionState(instance, {projection: projection2, ...docHandle})
|
|
73
125
|
|
|
74
126
|
expect(state.get().subscriptions).toEqual({})
|
|
75
|
-
|
|
76
|
-
.mockImplementationOnce(() => 'pseudoRandomId1')
|
|
77
|
-
.mockImplementationOnce(() => 'pseudoRandomId2')
|
|
127
|
+
expect(state.get().documentProjections).toEqual({})
|
|
78
128
|
|
|
79
|
-
const unsubscribe1 =
|
|
80
|
-
|
|
129
|
+
const unsubscribe1 = projectionState1.subscribe(vi.fn()) // Should use ID 3
|
|
130
|
+
expect(state.get().subscriptions).toEqual({
|
|
131
|
+
[docHandle.documentId]: {[hash1]: {testSubId_2: true}},
|
|
132
|
+
})
|
|
133
|
+
expect(state.get().documentProjections).toEqual({
|
|
134
|
+
[docHandle.documentId]: {[hash1]: projection1},
|
|
135
|
+
})
|
|
81
136
|
|
|
137
|
+
const unsubscribe2 = projectionState2.subscribe(vi.fn()) // Should use ID 4
|
|
82
138
|
expect(state.get().subscriptions).toEqual({
|
|
83
|
-
|
|
139
|
+
[docHandle.documentId]: {
|
|
140
|
+
[hash1]: {testSubId_2: true},
|
|
141
|
+
[hash2]: {testSubId_3: true},
|
|
142
|
+
},
|
|
84
143
|
})
|
|
85
144
|
expect(state.get().documentProjections).toEqual({
|
|
86
|
-
|
|
145
|
+
[docHandle.documentId]: {
|
|
146
|
+
[hash1]: projection1,
|
|
147
|
+
[hash2]: projection2,
|
|
148
|
+
},
|
|
87
149
|
})
|
|
88
150
|
|
|
89
|
-
|
|
151
|
+
const unsubscribe3 = projectionState1.subscribe(vi.fn()) // Should use ID 5
|
|
90
152
|
expect(state.get().subscriptions).toEqual({
|
|
91
|
-
|
|
153
|
+
[docHandle.documentId]: {
|
|
154
|
+
[hash1]: {testSubId_2: true, testSubId_4: true},
|
|
155
|
+
[hash2]: {testSubId_3: true},
|
|
156
|
+
},
|
|
92
157
|
})
|
|
93
158
|
|
|
94
|
-
|
|
95
|
-
expect(state.get().
|
|
96
|
-
|
|
159
|
+
// projections state should remain the same, even with multiple subscribers
|
|
160
|
+
expect(state.get().documentProjections).toEqual({
|
|
161
|
+
[docHandle.documentId]: {
|
|
162
|
+
[hash1]: projection1,
|
|
163
|
+
[hash2]: projection2,
|
|
164
|
+
},
|
|
165
|
+
})
|
|
97
166
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
167
|
+
// --- Test Unsubscribe ---
|
|
168
|
+
unsubscribe1() // Unsubscribes ID 3
|
|
169
|
+
expect(state.get().subscriptions[docHandle.documentId]?.[hash1]).toEqual({
|
|
170
|
+
testSubId_2: true,
|
|
171
|
+
testSubId_4: true,
|
|
172
|
+
})
|
|
173
|
+
vi.advanceTimersByTime(PROJECTION_STATE_CLEAR_DELAY)
|
|
174
|
+
expect(state.get().subscriptions[docHandle.documentId]?.[hash1]).toEqual({testSubId_4: true})
|
|
175
|
+
expect(state.get().documentProjections[docHandle.documentId]?.[hash1]).toEqual(projection1)
|
|
176
|
+
|
|
177
|
+
unsubscribe3() // Unsubscribes ID 5
|
|
178
|
+
vi.advanceTimersByTime(PROJECTION_STATE_CLEAR_DELAY)
|
|
179
|
+
expect(state.get().subscriptions[docHandle.documentId]?.[hash1]).toBeUndefined()
|
|
180
|
+
expect(state.get().documentProjections[docHandle.documentId]?.[hash1]).toBeUndefined()
|
|
181
|
+
expect(state.get().subscriptions[docHandle.documentId]?.[hash2]).toEqual({testSubId_3: true})
|
|
182
|
+
expect(state.get().documentProjections[docHandle.documentId]?.[hash2]).toEqual(projection2)
|
|
183
|
+
|
|
184
|
+
unsubscribe2() // Unsubscribes ID 4
|
|
185
|
+
vi.advanceTimersByTime(PROJECTION_STATE_CLEAR_DELAY)
|
|
186
|
+
expect(state.get().subscriptions[docHandle.documentId]).toBeUndefined()
|
|
187
|
+
expect(state.get().documentProjections[docHandle.documentId]).toBeUndefined()
|
|
188
|
+
})
|
|
102
189
|
|
|
103
|
-
|
|
190
|
+
it('resets isPending to false for the specific projection on final unsubscribe for that projection', () => {
|
|
191
|
+
const projectionState = getProjectionState(instance, {projection: projection1, ...docHandle})
|
|
192
|
+
const initialData = {name: 'Initial Name'}
|
|
193
|
+
const hash = hashString(projection1)
|
|
104
194
|
|
|
105
|
-
|
|
106
|
-
|
|
195
|
+
state.set('presetValueToPending', (prev: ProjectionStoreState) => ({
|
|
196
|
+
values: {
|
|
197
|
+
...prev.values,
|
|
198
|
+
[docHandle.documentId]: {
|
|
199
|
+
...prev.values[docHandle.documentId],
|
|
200
|
+
[hash]: {data: initialData, isPending: true},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
}))
|
|
107
204
|
|
|
108
|
-
|
|
205
|
+
const unsubscribe1 = projectionState.subscribe(vi.fn()) // Should use ID 2
|
|
206
|
+
const unsubscribe2 = projectionState.subscribe(vi.fn()) // Should use ID 3
|
|
109
207
|
|
|
110
|
-
|
|
111
|
-
|
|
208
|
+
expect(state.get().values[docHandle.documentId]?.[hash]).toEqual({
|
|
209
|
+
data: initialData,
|
|
210
|
+
isPending: true,
|
|
211
|
+
})
|
|
112
212
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
expect(state.get().values[
|
|
116
|
-
|
|
213
|
+
unsubscribe1() // Unsubscribes ID 2
|
|
214
|
+
vi.advanceTimersByTime(PROJECTION_STATE_CLEAR_DELAY)
|
|
215
|
+
expect(state.get().values[docHandle.documentId]?.[hash]).toEqual({
|
|
216
|
+
data: initialData,
|
|
217
|
+
isPending: true,
|
|
218
|
+
})
|
|
219
|
+
expect(Object.keys(state.get().subscriptions[docHandle.documentId]?.[hash] ?? {}).length).toBe(
|
|
220
|
+
1,
|
|
221
|
+
)
|
|
222
|
+
expect(state.get().subscriptions[docHandle.documentId]?.[hash]).toEqual({testSubId_3: true})
|
|
223
|
+
|
|
224
|
+
unsubscribe2() // Unsubscribes ID 3
|
|
225
|
+
expect(state.get().values[docHandle.documentId]?.[hash]).toEqual({
|
|
226
|
+
data: initialData,
|
|
227
|
+
isPending: true,
|
|
228
|
+
})
|
|
229
|
+
vi.advanceTimersByTime(PROJECTION_STATE_CLEAR_DELAY)
|
|
117
230
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
231
|
+
// NOW the pending state should be reset for this specific projection
|
|
232
|
+
expect(state.get().values[docHandle.documentId]?.[hash]).toEqual({
|
|
233
|
+
data: initialData,
|
|
234
|
+
isPending: false,
|
|
235
|
+
})
|
|
236
|
+
expect(state.get().subscriptions[docHandle.documentId]?.[hash]).toBeUndefined()
|
|
237
|
+
expect(state.get().documentProjections[docHandle.documentId]?.[hash]).toBeUndefined()
|
|
122
238
|
})
|
|
123
239
|
})
|
|
@@ -1,41 +1,35 @@
|
|
|
1
1
|
import {omit} from 'lodash-es'
|
|
2
2
|
|
|
3
|
-
import {type DocumentHandle} from '../
|
|
4
|
-
import {
|
|
5
|
-
import {type
|
|
6
|
-
import {createStateSourceAction, type StateSource} from '../resources/createStateSourceAction'
|
|
7
|
-
import {getPublishedId, insecureRandomId} from '../utils/ids'
|
|
3
|
+
import {type DocumentHandle} from '../config/sanityConfig'
|
|
4
|
+
import {bindActionByDataset} from '../store/createActionBinder'
|
|
5
|
+
import {type SanityInstance} from '../store/createSanityInstance'
|
|
8
6
|
import {
|
|
9
|
-
|
|
10
|
-
type
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
-
} from '
|
|
14
|
-
import {
|
|
7
|
+
createStateSourceAction,
|
|
8
|
+
type SelectorContext,
|
|
9
|
+
type StateSource,
|
|
10
|
+
} from '../store/createStateSourceAction'
|
|
11
|
+
import {hashString} from '../utils/hashString'
|
|
12
|
+
import {getPublishedId, insecureRandomId} from '../utils/ids'
|
|
13
|
+
import {projectionStore} from './projectionStore'
|
|
14
|
+
import {type ProjectionStoreState, type ProjectionValuePending, type ValidProjection} from './types'
|
|
15
|
+
import {PROJECTION_STATE_CLEAR_DELAY, STABLE_EMPTY_PROJECTION, validateProjection} from './util'
|
|
15
16
|
|
|
16
|
-
interface GetProjectionStateOptions {
|
|
17
|
-
document: DocumentHandle
|
|
17
|
+
interface GetProjectionStateOptions extends DocumentHandle {
|
|
18
18
|
projection: ValidProjection
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const getProjectStateSourceAction = createStateSourceAction(
|
|
22
|
-
projectionStore,
|
|
23
|
-
(state, {document}: GetProjectionStateOptions): ProjectionValuePending<object> =>
|
|
24
|
-
state.values[document._id] ?? STABLE_EMPTY_PROJECTION,
|
|
25
|
-
)
|
|
26
|
-
|
|
27
21
|
/**
|
|
28
22
|
* @beta
|
|
29
23
|
*/
|
|
30
24
|
export function getProjectionState<TResult extends object>(
|
|
31
|
-
instance: SanityInstance
|
|
25
|
+
instance: SanityInstance,
|
|
32
26
|
options: GetProjectionStateOptions,
|
|
33
27
|
): StateSource<ProjectionValuePending<TResult>>
|
|
34
28
|
/**
|
|
35
29
|
* @beta
|
|
36
30
|
*/
|
|
37
31
|
export function getProjectionState(
|
|
38
|
-
instance: SanityInstance
|
|
32
|
+
instance: SanityInstance,
|
|
39
33
|
options: GetProjectionStateOptions,
|
|
40
34
|
): StateSource<ProjectionValuePending<Record<string, unknown>>>
|
|
41
35
|
/**
|
|
@@ -50,56 +44,92 @@ export function getProjectionState(
|
|
|
50
44
|
/**
|
|
51
45
|
* @beta
|
|
52
46
|
*/
|
|
53
|
-
export const _getProjectionState =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
export const _getProjectionState = bindActionByDataset(
|
|
48
|
+
projectionStore,
|
|
49
|
+
createStateSourceAction({
|
|
50
|
+
selector: (
|
|
51
|
+
{state}: SelectorContext<ProjectionStoreState>,
|
|
52
|
+
options: GetProjectionStateOptions,
|
|
53
|
+
): ProjectionValuePending<object> => {
|
|
54
|
+
const documentId = getPublishedId(options.documentId)
|
|
55
|
+
const projectionHash = hashString(options.projection)
|
|
56
|
+
return state.values[documentId]?.[projectionHash] ?? STABLE_EMPTY_PROJECTION
|
|
57
|
+
},
|
|
58
|
+
onSubscribe: ({state}, {projection, ...docHandle}: GetProjectionStateOptions) => {
|
|
59
|
+
const subscriptionId = insecureRandomId()
|
|
60
|
+
const documentId = getPublishedId(docHandle.documentId)
|
|
61
|
+
const validProjection = validateProjection(projection)
|
|
62
|
+
const projectionHash = hashString(validProjection)
|
|
66
63
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
state.set('addSubscription', (prev) => ({
|
|
65
|
+
documentProjections: {
|
|
66
|
+
...prev.documentProjections,
|
|
67
|
+
[documentId]: {
|
|
68
|
+
...prev.documentProjections[documentId],
|
|
69
|
+
[projectionHash]: validProjection,
|
|
71
70
|
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
},
|
|
72
|
+
subscriptions: {
|
|
73
|
+
...prev.subscriptions,
|
|
74
|
+
[documentId]: {
|
|
75
|
+
...prev.subscriptions[documentId],
|
|
76
|
+
[projectionHash]: {
|
|
77
|
+
...prev.subscriptions[documentId]?.[projectionHash],
|
|
76
78
|
[subscriptionId]: true,
|
|
77
79
|
},
|
|
78
80
|
},
|
|
79
|
-
}
|
|
81
|
+
},
|
|
82
|
+
}))
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
return () => {
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
state.set('removeSubscription', (prev): Partial<ProjectionStoreState> => {
|
|
87
|
+
const documentSubscriptionsForHash = omit(
|
|
88
|
+
prev.subscriptions[documentId]?.[projectionHash],
|
|
89
|
+
subscriptionId,
|
|
90
|
+
)
|
|
91
|
+
const hasSubscribersForProjection = !!Object.keys(documentSubscriptionsForHash).length
|
|
82
92
|
|
|
83
|
-
|
|
84
|
-
|
|
93
|
+
const nextSubscriptions = {...prev.subscriptions}
|
|
94
|
+
const nextDocumentProjections = {...prev.documentProjections}
|
|
95
|
+
const nextValues = {...prev.values}
|
|
85
96
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
// clean up the subscription and documentProjection if there are no subscribers
|
|
98
|
+
if (!hasSubscribersForProjection) {
|
|
99
|
+
delete nextSubscriptions[documentId]![projectionHash]
|
|
100
|
+
delete nextDocumentProjections[documentId]![projectionHash]
|
|
101
|
+
|
|
102
|
+
const currentProjectionValue = prev.values[documentId]?.[projectionHash]
|
|
103
|
+
if (currentProjectionValue && nextValues[documentId]) {
|
|
104
|
+
nextValues[documentId]![projectionHash] = {
|
|
105
|
+
data: currentProjectionValue.data,
|
|
106
|
+
isPending: false,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
if (nextSubscriptions[documentId]) {
|
|
111
|
+
nextSubscriptions[documentId]![projectionHash] = documentSubscriptionsForHash
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const hasAnySubscribersForDocument = Object.values(
|
|
116
|
+
nextSubscriptions[documentId] ?? {},
|
|
117
|
+
).some((subs) => Object.keys(subs).length > 0)
|
|
118
|
+
|
|
119
|
+
if (!hasAnySubscribersForDocument) {
|
|
120
|
+
delete nextSubscriptions[documentId]
|
|
121
|
+
delete nextDocumentProjections[documentId]
|
|
122
|
+
// Keep nextValues[documentId] as cache
|
|
123
|
+
}
|
|
91
124
|
|
|
92
125
|
return {
|
|
93
|
-
subscriptions:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
values: hasSubscribers
|
|
97
|
-
? prev.values
|
|
98
|
-
: {...prev.values, [documentId]: {data: projectionValue, isPending: false}},
|
|
126
|
+
subscriptions: nextSubscriptions,
|
|
127
|
+
documentProjections: nextDocumentProjections,
|
|
128
|
+
values: nextValues,
|
|
99
129
|
}
|
|
100
130
|
})
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
131
|
+
}, PROJECTION_STATE_CLEAR_DELAY)
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
}),
|
|
135
|
+
)
|