@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
|
@@ -14,15 +14,14 @@ import {
|
|
|
14
14
|
tap,
|
|
15
15
|
} from 'rxjs/operators'
|
|
16
16
|
|
|
17
|
-
import {type
|
|
17
|
+
import {bindActionGlobally, type BoundStoreAction} from '../store/createActionBinder'
|
|
18
|
+
import {type SanityInstance} from '../store/createSanityInstance'
|
|
18
19
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} from '../
|
|
24
|
-
import {createResource} from '../resources/createResource'
|
|
25
|
-
import {createStateSourceAction, type StateSource} from '../resources/createStateSourceAction'
|
|
20
|
+
createStateSourceAction,
|
|
21
|
+
type SelectorContext,
|
|
22
|
+
type StateSource,
|
|
23
|
+
} from '../store/createStateSourceAction'
|
|
24
|
+
import {defineStore, type StoreContext} from '../store/defineStore'
|
|
26
25
|
import {insecureRandomId} from '../utils/ids'
|
|
27
26
|
|
|
28
27
|
interface CreateFetcherStoreOptions<TParams extends unknown[], TData> {
|
|
@@ -38,7 +37,7 @@ interface CreateFetcherStoreOptions<TParams extends unknown[], TData> {
|
|
|
38
37
|
* The function used to convert the params into keys that state related to
|
|
39
38
|
* those params will be stored.
|
|
40
39
|
*/
|
|
41
|
-
getKey: (...params: TParams) => string
|
|
40
|
+
getKey: (instance: SanityInstance, ...params: TParams) => string
|
|
42
41
|
/**
|
|
43
42
|
* Delay in ms before clearing state after the last subscription is removed.
|
|
44
43
|
* This results in react components suspending again due to no previous state
|
|
@@ -53,6 +52,7 @@ interface CreateFetcherStoreOptions<TParams extends unknown[], TData> {
|
|
|
53
52
|
|
|
54
53
|
interface StoreEntry<TParams extends unknown[], TData> {
|
|
55
54
|
params: TParams
|
|
55
|
+
instance: SanityInstance
|
|
56
56
|
key: string
|
|
57
57
|
data?: TData
|
|
58
58
|
error?: unknown
|
|
@@ -74,12 +74,12 @@ export interface FetcherStoreState<TParams extends unknown[], TData> {
|
|
|
74
74
|
* @public
|
|
75
75
|
*/
|
|
76
76
|
export interface FetcherStore<TParams extends unknown[], TData> {
|
|
77
|
-
getState:
|
|
77
|
+
getState: BoundStoreAction<
|
|
78
78
|
FetcherStoreState<TParams, TData>,
|
|
79
79
|
TParams,
|
|
80
80
|
StateSource<TData | undefined>
|
|
81
81
|
>
|
|
82
|
-
resolveState:
|
|
82
|
+
resolveState: BoundStoreAction<FetcherStoreState<TParams, TData>, TParams, Promise<TData>>
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
@@ -108,13 +108,13 @@ export function createFetcherStore<TParams extends unknown[], TData>({
|
|
|
108
108
|
fetchThrottleInternal = 1000,
|
|
109
109
|
stateExpirationDelay = 5000,
|
|
110
110
|
}: CreateFetcherStoreOptions<TParams, TData>): FetcherStore<TParams, TData> {
|
|
111
|
-
const store =
|
|
111
|
+
const store = defineStore<FetcherStoreState<TParams, TData>>({
|
|
112
112
|
name,
|
|
113
113
|
getInitialState: () => ({
|
|
114
114
|
stateByParams: {},
|
|
115
115
|
}),
|
|
116
|
-
initialize() {
|
|
117
|
-
const subscription = subscribeToSubscriptionsAndFetch(
|
|
116
|
+
initialize: (context) => {
|
|
117
|
+
const subscription = subscribeToSubscriptionsAndFetch(context)
|
|
118
118
|
return () => subscription.unsubscribe()
|
|
119
119
|
},
|
|
120
120
|
})
|
|
@@ -125,160 +125,157 @@ export function createFetcherStore<TParams extends unknown[], TData>({
|
|
|
125
125
|
* and if enough time has elapsed since the last fetch, we update the timestamp
|
|
126
126
|
* and call the factory function for that key.
|
|
127
127
|
*/
|
|
128
|
-
const subscribeToSubscriptionsAndFetch =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
const subscribeToSubscriptionsAndFetch = ({
|
|
129
|
+
state,
|
|
130
|
+
}: StoreContext<FetcherStoreState<TParams, TData>>) => {
|
|
131
|
+
return state.observable
|
|
132
|
+
.pipe(
|
|
133
|
+
// Map the state to an array of [serialized, entry] pairs.
|
|
134
|
+
switchMap((s: FetcherStoreState<TParams, TData>) => {
|
|
135
|
+
const entries = Object.entries(s.stateByParams)
|
|
136
|
+
return entries.length > 0 ? from(entries) : EMPTY
|
|
137
|
+
}),
|
|
138
|
+
// Group by the serialized key.
|
|
139
|
+
groupBy(([key]) => key),
|
|
140
|
+
mergeMap((group$) =>
|
|
141
|
+
group$.pipe(
|
|
142
|
+
// Emit an initial value for pairwise comparisons.
|
|
143
|
+
startWith<[string, StoreEntry<TParams, TData> | undefined]>([group$.key, undefined]),
|
|
144
|
+
pairwise(),
|
|
145
|
+
// Trigger only when the subscriptions array grows.
|
|
146
|
+
filter(([[, prevEntry], [, currEntry]]) => {
|
|
147
|
+
const prevSubs = prevEntry?.subscriptions ?? []
|
|
148
|
+
const currSubs = currEntry?.subscriptions ?? []
|
|
149
|
+
return currSubs.length > prevSubs.length
|
|
150
|
+
}),
|
|
151
|
+
map(([, [, currEntry]]) => currEntry),
|
|
132
152
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return entries.length > 0 ? from(entries) : EMPTY
|
|
153
|
+
// Only trigger if we haven't fetched recently.
|
|
154
|
+
filter((entry) => {
|
|
155
|
+
const lastFetch = entry?.lastFetchInitiatedAt
|
|
156
|
+
if (!lastFetch) return true
|
|
157
|
+
return Date.now() - new Date(lastFetch).getTime() >= fetchThrottleInternal
|
|
139
158
|
}),
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
group$.pipe(
|
|
144
|
-
// Emit an initial value for pairwise comparisons.
|
|
145
|
-
startWith<[string, StoreEntry<TParams, TData> | undefined]>([
|
|
146
|
-
group$.key,
|
|
147
|
-
undefined,
|
|
148
|
-
]),
|
|
149
|
-
pairwise(),
|
|
150
|
-
// Trigger only when the subscriptions array grows.
|
|
151
|
-
filter(([[, prevEntry], [, currEntry]]) => {
|
|
152
|
-
const prevSubs = prevEntry?.subscriptions ?? []
|
|
153
|
-
const currSubs = currEntry?.subscriptions ?? []
|
|
154
|
-
return currSubs.length > prevSubs.length
|
|
155
|
-
}),
|
|
156
|
-
map(([, [, currEntry]]) => currEntry),
|
|
159
|
+
switchMap((entry) => {
|
|
160
|
+
// Retrieve params from the entry
|
|
161
|
+
if (!entry) return EMPTY
|
|
157
162
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
// Record that a fetch is being initiated.
|
|
164
|
+
state.set('setLastFetchInitiatedAt', (prev: FetcherStoreState<TParams, TData>) => ({
|
|
165
|
+
stateByParams: {
|
|
166
|
+
...prev.stateByParams,
|
|
167
|
+
[entry.key]: {
|
|
168
|
+
...entry,
|
|
169
|
+
...prev.stateByParams[entry.key],
|
|
170
|
+
lastFetchInitiatedAt: new Date().toISOString(),
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
}))
|
|
167
174
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
const factoryFn = getObservable(entry.instance)
|
|
176
|
+
return factoryFn(...entry.params).pipe(
|
|
177
|
+
// the `createStateSourceAction` util requires the update
|
|
178
|
+
// to
|
|
179
|
+
delay(0, asapScheduler),
|
|
180
|
+
tap((data: TData) =>
|
|
181
|
+
state.set('setData', (prev: FetcherStoreState<TParams, TData>) => ({
|
|
182
|
+
stateByParams: {
|
|
183
|
+
...prev.stateByParams,
|
|
184
|
+
[entry.key]: {
|
|
185
|
+
...omit(entry, 'error'),
|
|
186
|
+
...omit(prev.stateByParams[entry.key], 'error'),
|
|
187
|
+
data,
|
|
179
188
|
},
|
|
180
|
-
}
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
stateByParams
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
data,
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
})),
|
|
198
|
-
),
|
|
199
|
-
catchError((error) => {
|
|
200
|
-
state.set('setError', (prev) => ({
|
|
201
|
-
stateByParams: {
|
|
202
|
-
...prev.stateByParams,
|
|
203
|
-
[entry.key]: {
|
|
204
|
-
...entry,
|
|
205
|
-
...prev.stateByParams[entry.key],
|
|
206
|
-
error,
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
}))
|
|
189
|
+
},
|
|
190
|
+
})),
|
|
191
|
+
),
|
|
192
|
+
catchError((error) => {
|
|
193
|
+
state.set('setError', (prev) => ({
|
|
194
|
+
stateByParams: {
|
|
195
|
+
...prev.stateByParams,
|
|
196
|
+
[entry.key]: {
|
|
197
|
+
...entry,
|
|
198
|
+
...prev.stateByParams[entry.key],
|
|
199
|
+
error,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
}))
|
|
210
203
|
|
|
211
|
-
|
|
212
|
-
}),
|
|
213
|
-
)
|
|
204
|
+
return EMPTY
|
|
214
205
|
}),
|
|
215
|
-
)
|
|
216
|
-
),
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
206
|
+
)
|
|
207
|
+
}),
|
|
208
|
+
),
|
|
209
|
+
),
|
|
210
|
+
)
|
|
211
|
+
.subscribe({
|
|
212
|
+
error: (error) => state.set('setError', {error}),
|
|
213
|
+
})
|
|
214
|
+
}
|
|
224
215
|
|
|
225
|
-
const getState =
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
216
|
+
const getState = bindActionGlobally(
|
|
217
|
+
store,
|
|
218
|
+
createStateSourceAction({
|
|
219
|
+
selector: (
|
|
220
|
+
{
|
|
221
|
+
instance,
|
|
222
|
+
state: {stateByParams, error},
|
|
223
|
+
}: SelectorContext<FetcherStoreState<TParams, TData>>,
|
|
224
|
+
...params: TParams
|
|
225
|
+
) => {
|
|
226
|
+
if (error) throw error
|
|
227
|
+
const key = getKey(instance, ...params)
|
|
228
|
+
const entry = stateByParams[key]
|
|
229
|
+
if (entry?.error) throw entry.error
|
|
230
|
+
return entry?.data
|
|
231
|
+
},
|
|
232
|
+
onSubscribe: ({instance, state}, ...params: TParams) => {
|
|
233
|
+
const subscriptionId = insecureRandomId()
|
|
234
|
+
const key = getKey(instance, ...params)
|
|
236
235
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
236
|
+
state.set('addSubscription', (prev: FetcherStoreState<TParams, TData>) => ({
|
|
237
|
+
stateByParams: {
|
|
238
|
+
...prev.stateByParams,
|
|
239
|
+
[key]: {
|
|
240
|
+
...prev.stateByParams[key],
|
|
241
|
+
instance,
|
|
242
|
+
key,
|
|
243
|
+
params: prev.stateByParams[key]?.params || params,
|
|
244
|
+
subscriptions: [...(prev.stateByParams[key]?.subscriptions || []), subscriptionId],
|
|
245
|
+
},
|
|
245
246
|
},
|
|
246
|
-
}
|
|
247
|
-
}))
|
|
247
|
+
}))
|
|
248
248
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
249
|
+
return () => {
|
|
250
|
+
setTimeout(() => {
|
|
251
|
+
state.set('removeSubscription', (prev: FetcherStoreState<TParams, TData>) => {
|
|
252
|
+
const entry = prev.stateByParams[key]
|
|
253
|
+
if (!entry) return prev
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
const newSubs = (entry.subscriptions || []).filter((id) => id !== subscriptionId)
|
|
256
|
+
if (newSubs.length === 0) {
|
|
257
|
+
return {stateByParams: omit(prev.stateByParams, key)}
|
|
258
|
+
}
|
|
259
259
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
260
|
+
return {
|
|
261
|
+
stateByParams: {
|
|
262
|
+
...prev.stateByParams,
|
|
263
|
+
[key]: {
|
|
264
|
+
...entry,
|
|
265
|
+
subscriptions: newSubs,
|
|
266
|
+
},
|
|
266
267
|
},
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
})
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
},
|
|
273
|
-
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
}, stateExpirationDelay)
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
}),
|
|
274
|
+
)
|
|
274
275
|
|
|
275
|
-
const resolveState =
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
getState(this, ...params).observable.pipe(first((i) => i !== undefined)),
|
|
279
|
-
)
|
|
280
|
-
}
|
|
281
|
-
})
|
|
276
|
+
const resolveState = bindActionGlobally(store, ({instance}, ...params: TParams) =>
|
|
277
|
+
firstValueFrom(getState(instance, ...params).observable.pipe(first((i) => i !== undefined))),
|
|
278
|
+
)
|
|
282
279
|
|
|
283
280
|
return {getState, resolveState}
|
|
284
281
|
}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import {createSanityInstance} from '../instance/sanityInstance'
|
|
2
|
-
import {createResourceState} from '../resources/createResource'
|
|
3
|
-
import {authStore} from './authStore'
|
|
4
|
-
import {fetchLoginUrls} from './fetchLoginUrls'
|
|
5
|
-
|
|
6
|
-
describe('fetchLoginUrls', () => {
|
|
7
|
-
it('returns providers with updated URLs', async () => {
|
|
8
|
-
const mockRequest = vi.fn().mockResolvedValue({
|
|
9
|
-
providers: [
|
|
10
|
-
{title: 'Provider A', url: 'https://auth.example.com/a'},
|
|
11
|
-
{title: 'Provider B', url: 'https://auth.example.com/b'},
|
|
12
|
-
],
|
|
13
|
-
})
|
|
14
|
-
const clientFactory = vi.fn().mockReturnValue({request: mockRequest})
|
|
15
|
-
const instance = createSanityInstance({
|
|
16
|
-
projectId: 'p',
|
|
17
|
-
dataset: 'd',
|
|
18
|
-
auth: {clientFactory},
|
|
19
|
-
})
|
|
20
|
-
const state = createResourceState(authStore.getInitialState(instance))
|
|
21
|
-
const providers = await fetchLoginUrls({instance, state})
|
|
22
|
-
|
|
23
|
-
expect(providers.length).toBe(2)
|
|
24
|
-
expect(providers[0].url).toContain('withSid=true')
|
|
25
|
-
expect(providers[1].url).toContain('withSid=true')
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('caches the providers and early returns', async () => {
|
|
29
|
-
const clientFactory = vi.fn()
|
|
30
|
-
const instance = createSanityInstance({
|
|
31
|
-
projectId: 'p',
|
|
32
|
-
dataset: 'd',
|
|
33
|
-
auth: {clientFactory},
|
|
34
|
-
})
|
|
35
|
-
const state = createResourceState(authStore.getInitialState(instance))
|
|
36
|
-
|
|
37
|
-
const provider = {
|
|
38
|
-
name: 'cached-provided',
|
|
39
|
-
title: 'cached provider',
|
|
40
|
-
url: 'https://auth.example.com#withSid=true',
|
|
41
|
-
}
|
|
42
|
-
state.set('setInitialProviders', {providers: [provider]})
|
|
43
|
-
|
|
44
|
-
const providers = await fetchLoginUrls({instance, state})
|
|
45
|
-
|
|
46
|
-
expect(providers.length).toBe(1)
|
|
47
|
-
expect(providers[0].url).toContain('https://auth.example.com#withSid=true')
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('handles providers as a static array and merges/replaces accordingly', async () => {
|
|
51
|
-
const mockRequest = vi.fn().mockResolvedValue({
|
|
52
|
-
providers: [
|
|
53
|
-
{title: 'Provider A', name: 'provider-a', url: 'https://auth.example.com/a'},
|
|
54
|
-
{title: 'Provider B', name: 'provider-b', url: 'https://auth.example.com/b'},
|
|
55
|
-
],
|
|
56
|
-
})
|
|
57
|
-
const clientFactory = vi.fn().mockReturnValue({request: mockRequest})
|
|
58
|
-
const instance = createSanityInstance({
|
|
59
|
-
projectId: 'p',
|
|
60
|
-
dataset: 'd',
|
|
61
|
-
auth: {
|
|
62
|
-
clientFactory,
|
|
63
|
-
providers: [
|
|
64
|
-
{
|
|
65
|
-
title: 'Custom Provider B',
|
|
66
|
-
name: 'custom-provider-b',
|
|
67
|
-
url: 'https://auth.example.com/b',
|
|
68
|
-
},
|
|
69
|
-
{title: 'Provider C', name: 'provider-c', url: 'https://auth.example.com/c'},
|
|
70
|
-
],
|
|
71
|
-
},
|
|
72
|
-
})
|
|
73
|
-
const state = createResourceState(authStore.getInitialState(instance))
|
|
74
|
-
const providers = await fetchLoginUrls({instance, state})
|
|
75
|
-
|
|
76
|
-
expect(providers.find((p) => p.title === 'Provider A')).toBeTruthy()
|
|
77
|
-
expect(providers.find((p) => p.title === 'Custom Provider B')).toBeTruthy()
|
|
78
|
-
expect(providers.find((p) => p.title === 'Provider C')).toBeTruthy()
|
|
79
|
-
expect(providers.find((p) => p.title === 'Provider B')).toBeFalsy()
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('allows custom provider function modification', async () => {
|
|
83
|
-
const mockRequest = vi.fn().mockResolvedValue({
|
|
84
|
-
providers: [{title: 'Provider A', url: 'https://auth.example.com/a'}],
|
|
85
|
-
})
|
|
86
|
-
const clientFactory = vi.fn().mockReturnValue({request: mockRequest})
|
|
87
|
-
const instance = createSanityInstance({
|
|
88
|
-
projectId: 'p',
|
|
89
|
-
dataset: 'd',
|
|
90
|
-
auth: {
|
|
91
|
-
clientFactory,
|
|
92
|
-
providers: (defaults) => defaults.map((p) => ({...p, title: 'Modified ' + p.title})),
|
|
93
|
-
},
|
|
94
|
-
})
|
|
95
|
-
const state = createResourceState(authStore.getInitialState(instance))
|
|
96
|
-
const providers = await fetchLoginUrls({instance, state})
|
|
97
|
-
|
|
98
|
-
expect(providers[0].title).toBe('Modified Provider A')
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('uses default providers if none are specified', async () => {
|
|
102
|
-
const mockRequest = vi.fn().mockResolvedValue({
|
|
103
|
-
providers: [{title: 'Provider A', url: 'https://auth.example.com/a'}],
|
|
104
|
-
})
|
|
105
|
-
const clientFactory = vi.fn().mockReturnValue({request: mockRequest})
|
|
106
|
-
const instance = createSanityInstance({
|
|
107
|
-
projectId: 'p',
|
|
108
|
-
dataset: 'd',
|
|
109
|
-
auth: {
|
|
110
|
-
clientFactory,
|
|
111
|
-
},
|
|
112
|
-
})
|
|
113
|
-
const state = createResourceState(authStore.getInitialState(instance))
|
|
114
|
-
const providers = await fetchLoginUrls({instance, state})
|
|
115
|
-
|
|
116
|
-
expect(providers.length).toBe(1)
|
|
117
|
-
expect(providers[0].title).toBe('Provider A')
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it('includes callbackUrl in provider URLs if set', async () => {
|
|
121
|
-
const mockRequest = vi.fn().mockResolvedValue({
|
|
122
|
-
providers: [{title: 'Provider A', url: 'https://auth.example.com/a'}],
|
|
123
|
-
})
|
|
124
|
-
const clientFactory = vi.fn().mockReturnValue({request: mockRequest})
|
|
125
|
-
const instance = createSanityInstance({
|
|
126
|
-
projectId: 'p',
|
|
127
|
-
dataset: 'd',
|
|
128
|
-
auth: {
|
|
129
|
-
clientFactory,
|
|
130
|
-
callbackUrl: 'http://localhost/callback',
|
|
131
|
-
},
|
|
132
|
-
})
|
|
133
|
-
const state = createResourceState(authStore.getInitialState(instance))
|
|
134
|
-
const providers = await fetchLoginUrls({instance, state})
|
|
135
|
-
expect(providers[0].url).toContain('origin=http%3A%2F%2Flocalhost%2Fcallback')
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
it('should allow async custom provider function', async () => {
|
|
139
|
-
const mockRequest = vi.fn().mockResolvedValue({
|
|
140
|
-
providers: [{title: 'Provider A', url: 'https://auth.example.com/a'}],
|
|
141
|
-
})
|
|
142
|
-
const clientFactory = vi.fn().mockReturnValue({request: mockRequest})
|
|
143
|
-
const instance = createSanityInstance({
|
|
144
|
-
projectId: 'p',
|
|
145
|
-
dataset: 'd',
|
|
146
|
-
auth: {
|
|
147
|
-
clientFactory,
|
|
148
|
-
providers: async (defaults) => {
|
|
149
|
-
await new Promise((r) => setTimeout(r, 10))
|
|
150
|
-
return defaults.concat([
|
|
151
|
-
{title: 'Provider C', name: 'provider-c', url: 'https://auth.example.com/c'},
|
|
152
|
-
])
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
})
|
|
156
|
-
const state = createResourceState(authStore.getInitialState(instance))
|
|
157
|
-
|
|
158
|
-
const providers = await fetchLoginUrls({instance, state})
|
|
159
|
-
|
|
160
|
-
expect(providers.length).toBe(2)
|
|
161
|
-
expect(providers.some((p) => p.title === 'Provider C')).toBe(true)
|
|
162
|
-
})
|
|
163
|
-
})
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import {type AuthProvider} from '@sanity/client'
|
|
2
|
-
|
|
3
|
-
import {createAction} from '../resources/createAction'
|
|
4
|
-
import {DEFAULT_API_VERSION, REQUEST_TAG_PREFIX} from './authConstants'
|
|
5
|
-
import {authStore} from './authStore'
|
|
6
|
-
import {getDefaultLocation} from './utils'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @public
|
|
10
|
-
*/
|
|
11
|
-
export const fetchLoginUrls = createAction(authStore, ({state}) => {
|
|
12
|
-
const {callbackUrl, clientFactory, apiHost, customProviders} = state.get().options
|
|
13
|
-
const client = clientFactory({
|
|
14
|
-
apiVersion: DEFAULT_API_VERSION,
|
|
15
|
-
requestTagPrefix: REQUEST_TAG_PREFIX,
|
|
16
|
-
useProjectHostname: false,
|
|
17
|
-
...(apiHost && {apiHost}),
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
return async function () {
|
|
21
|
-
const cachedProviders = state.get().providers
|
|
22
|
-
if (cachedProviders) return cachedProviders
|
|
23
|
-
|
|
24
|
-
const {providers: defaultProviders} = await client.request<{providers: AuthProvider[]}>({
|
|
25
|
-
uri: '/auth/providers',
|
|
26
|
-
tag: 'fetch-providers',
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
let providers: AuthProvider[]
|
|
30
|
-
|
|
31
|
-
if (typeof customProviders === 'function') {
|
|
32
|
-
providers = await customProviders(defaultProviders)
|
|
33
|
-
} else if (!customProviders?.length) {
|
|
34
|
-
providers = defaultProviders
|
|
35
|
-
} else {
|
|
36
|
-
const customProviderUrls = new Set(customProviders.map((p) => p.url))
|
|
37
|
-
providers = defaultProviders
|
|
38
|
-
.filter((official) => !customProviderUrls.has(official.url))
|
|
39
|
-
.concat(customProviders)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const configuredProviders = providers.map((provider) => {
|
|
43
|
-
const url = new URL(provider.url)
|
|
44
|
-
const origin = new URL(
|
|
45
|
-
callbackUrl
|
|
46
|
-
? new URL(callbackUrl, new URL(getDefaultLocation()).origin).toString()
|
|
47
|
-
: getDefaultLocation(),
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
// `getDefaultLocation()` may be populated with an `sid` from a previous
|
|
51
|
-
// failed login attempt and should be omitted from the next login URL
|
|
52
|
-
const hashParams = new URLSearchParams(origin.hash.slice(1))
|
|
53
|
-
hashParams.delete('sid')
|
|
54
|
-
origin.hash = hashParams.toString()
|
|
55
|
-
origin.searchParams.delete('sid')
|
|
56
|
-
origin.searchParams.delete('url')
|
|
57
|
-
|
|
58
|
-
// similarly, the origin may be populated with an `error` query param if
|
|
59
|
-
// the auth provider redirects back to the application. this should also
|
|
60
|
-
// be omitted from the origin sent
|
|
61
|
-
origin.searchParams.delete('error')
|
|
62
|
-
|
|
63
|
-
url.searchParams.set('origin', origin.toString())
|
|
64
|
-
url.searchParams.set('withSid', 'true')
|
|
65
|
-
url.searchParams.set('type', 'stampedToken')
|
|
66
|
-
|
|
67
|
-
return {...provider, url: url.toString()}
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
state.set('fetchedLoginUrls', {providers: configuredProviders})
|
|
71
|
-
|
|
72
|
-
return configuredProviders
|
|
73
|
-
}
|
|
74
|
-
})
|