@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
package/src/auth/authStore.ts
CHANGED
|
@@ -2,8 +2,10 @@ import {type ClientConfig, createClient, type SanityClient} from '@sanity/client
|
|
|
2
2
|
import {type CurrentUser} from '@sanity/types'
|
|
3
3
|
import {type Subscription} from 'rxjs'
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import {type AuthConfig, type AuthProvider} from '../config/authConfig'
|
|
6
|
+
import {bindActionGlobally} from '../store/createActionBinder'
|
|
7
|
+
import {createStateSourceAction} from '../store/createStateSourceAction'
|
|
8
|
+
import {defineStore} from '../store/defineStore'
|
|
7
9
|
import {AuthStateType} from './authStateType'
|
|
8
10
|
import {refreshStampedToken} from './refreshStampedToken'
|
|
9
11
|
import {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser'
|
|
@@ -45,84 +47,6 @@ export type LoggingInAuthState = {type: AuthStateType.LOGGING_IN; isExchangingTo
|
|
|
45
47
|
*/
|
|
46
48
|
export type ErrorAuthState = {type: AuthStateType.ERROR; error: unknown}
|
|
47
49
|
|
|
48
|
-
/**
|
|
49
|
-
* Configuration for an authentication provider
|
|
50
|
-
* @public
|
|
51
|
-
*/
|
|
52
|
-
export interface AuthProvider {
|
|
53
|
-
/**
|
|
54
|
-
* Unique identifier for the auth provider (e.g., 'google', 'github')
|
|
55
|
-
*/
|
|
56
|
-
name: string
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Display name for the auth provider in the UI
|
|
60
|
-
*/
|
|
61
|
-
title: string
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Complete authentication URL including callback and token parameters
|
|
65
|
-
*/
|
|
66
|
-
url: string
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Optional URL for direct sign-up flow
|
|
70
|
-
*/
|
|
71
|
-
signUpUrl?: string
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Configuration options for creating an auth store.
|
|
76
|
-
*
|
|
77
|
-
* @public
|
|
78
|
-
*/
|
|
79
|
-
export interface AuthConfig {
|
|
80
|
-
/**
|
|
81
|
-
* The initial location href to use when handling auth callbacks.
|
|
82
|
-
* Defaults to the current window location if available.
|
|
83
|
-
*/
|
|
84
|
-
initialLocationHref?: string
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Factory function to create a SanityClient instance.
|
|
88
|
-
* Defaults to the standard Sanity client factory if not provided.
|
|
89
|
-
*/
|
|
90
|
-
clientFactory?: (config: ClientConfig) => SanityClient
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Custom authentication providers to use instead of or in addition to the default ones.
|
|
94
|
-
* Can be an array of providers or a function that takes the default providers and returns
|
|
95
|
-
* a modified array or a Promise resolving to one.
|
|
96
|
-
*/
|
|
97
|
-
providers?: AuthProvider[] | ((prev: AuthProvider[]) => AuthProvider[] | Promise<AuthProvider[]>)
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* The API hostname for requests. Usually leave this undefined, but it can be set
|
|
101
|
-
* if using a custom domain or CNAME for the API endpoint.
|
|
102
|
-
*/
|
|
103
|
-
apiHost?: string
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Storage implementation to persist authentication state.
|
|
107
|
-
* Defaults to `localStorage` if available.
|
|
108
|
-
*/
|
|
109
|
-
storageArea?: Storage
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* A callback URL for your application.
|
|
113
|
-
* If none is provided, the auth API will redirect back to the current location (`location.href`).
|
|
114
|
-
* When handling callbacks, this URL's pathname is checked to ensure it matches the callback.
|
|
115
|
-
*/
|
|
116
|
-
callbackUrl?: string
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* A static authentication token to use instead of handling the OAuth flow.
|
|
120
|
-
* When provided, the auth store will remain in a logged-in state with this token,
|
|
121
|
-
* ignoring any storage or callback handling.
|
|
122
|
-
*/
|
|
123
|
-
token?: string
|
|
124
|
-
}
|
|
125
|
-
|
|
126
50
|
/**
|
|
127
51
|
* Represents the various states the authentication can be in.
|
|
128
52
|
*
|
|
@@ -149,13 +73,14 @@ export interface AuthStoreState {
|
|
|
149
73
|
storageKey: string
|
|
150
74
|
storageArea: Storage | undefined
|
|
151
75
|
apiHost: string | undefined
|
|
76
|
+
loginUrl: string
|
|
152
77
|
callbackUrl: string | undefined
|
|
153
78
|
providedToken: string | undefined
|
|
154
79
|
}
|
|
155
80
|
dashboardContext?: DashboardContext
|
|
156
81
|
}
|
|
157
82
|
|
|
158
|
-
export const authStore =
|
|
83
|
+
export const authStore = defineStore<AuthStoreState>({
|
|
159
84
|
name: 'Auth',
|
|
160
85
|
getInitialState(instance) {
|
|
161
86
|
const {
|
|
@@ -170,6 +95,20 @@ export const authStore = createResource<AuthStoreState>({
|
|
|
170
95
|
|
|
171
96
|
const storageKey = `__sanity_auth_token`
|
|
172
97
|
|
|
98
|
+
// This login URL will only be used for local development
|
|
99
|
+
let loginDomain = 'https://www.sanity.io'
|
|
100
|
+
try {
|
|
101
|
+
if (apiHost && new URL(apiHost).hostname.endsWith('.sanity.work')) {
|
|
102
|
+
loginDomain = 'https://www.sanity.work'
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
/* empty */
|
|
106
|
+
}
|
|
107
|
+
const loginUrl = new URL('/login', loginDomain)
|
|
108
|
+
loginUrl.searchParams.set('origin', initialLocationHref)
|
|
109
|
+
loginUrl.searchParams.set('type', 'stampedToken') // Token must be stamped to have an sid passed back
|
|
110
|
+
loginUrl.searchParams.set('withSid', 'true')
|
|
111
|
+
|
|
173
112
|
let authState: AuthState
|
|
174
113
|
|
|
175
114
|
const token = getTokenFromStorage(storageArea, storageKey)
|
|
@@ -188,6 +127,7 @@ export const authStore = createResource<AuthStoreState>({
|
|
|
188
127
|
authState,
|
|
189
128
|
options: {
|
|
190
129
|
apiHost,
|
|
130
|
+
loginUrl: loginUrl.toString(),
|
|
191
131
|
callbackUrl,
|
|
192
132
|
customProviders,
|
|
193
133
|
providedToken,
|
|
@@ -198,22 +138,23 @@ export const authStore = createResource<AuthStoreState>({
|
|
|
198
138
|
},
|
|
199
139
|
}
|
|
200
140
|
},
|
|
201
|
-
initialize() {
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
141
|
+
initialize(context) {
|
|
142
|
+
const subscriptions: Subscription[] = []
|
|
143
|
+
subscriptions.push(subscribeToStateAndFetchCurrentUser(context))
|
|
144
|
+
|
|
145
|
+
if (context.state.get().options?.storageArea) {
|
|
146
|
+
subscriptions.push(subscribeToStorageEventsAndSetToken(context))
|
|
206
147
|
}
|
|
207
|
-
|
|
148
|
+
|
|
208
149
|
if (!tokenRefresherRunning) {
|
|
209
150
|
tokenRefresherRunning = true
|
|
210
|
-
|
|
151
|
+
subscriptions.push(refreshStampedToken(context))
|
|
211
152
|
}
|
|
212
153
|
|
|
213
154
|
return () => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
155
|
+
for (const subscription of subscriptions) {
|
|
156
|
+
subscription.unsubscribe()
|
|
157
|
+
}
|
|
217
158
|
}
|
|
218
159
|
},
|
|
219
160
|
})
|
|
@@ -221,33 +162,43 @@ export const authStore = createResource<AuthStoreState>({
|
|
|
221
162
|
/**
|
|
222
163
|
* @public
|
|
223
164
|
*/
|
|
224
|
-
export const getCurrentUserState =
|
|
225
|
-
|
|
165
|
+
export const getCurrentUserState = bindActionGlobally(
|
|
166
|
+
authStore,
|
|
167
|
+
createStateSourceAction(({state: {authState}}) =>
|
|
168
|
+
authState.type === AuthStateType.LOGGED_IN ? authState.currentUser : null,
|
|
169
|
+
),
|
|
226
170
|
)
|
|
227
171
|
|
|
228
172
|
/**
|
|
229
173
|
* @public
|
|
230
174
|
*/
|
|
231
|
-
export const getTokenState =
|
|
232
|
-
|
|
175
|
+
export const getTokenState = bindActionGlobally(
|
|
176
|
+
authStore,
|
|
177
|
+
createStateSourceAction(({state: {authState}}) =>
|
|
178
|
+
authState.type === AuthStateType.LOGGED_IN ? authState.token : null,
|
|
179
|
+
),
|
|
233
180
|
)
|
|
181
|
+
|
|
234
182
|
/**
|
|
235
183
|
* @public
|
|
236
184
|
*/
|
|
237
|
-
export const
|
|
185
|
+
export const getLoginUrlState = bindActionGlobally(
|
|
238
186
|
authStore,
|
|
239
|
-
({
|
|
187
|
+
createStateSourceAction(({state: {options}}) => options.loginUrl),
|
|
240
188
|
)
|
|
241
189
|
|
|
242
190
|
/**
|
|
243
191
|
* @public
|
|
244
192
|
*/
|
|
245
|
-
export const getAuthState =
|
|
193
|
+
export const getAuthState = bindActionGlobally(
|
|
194
|
+
authStore,
|
|
195
|
+
createStateSourceAction(({state: {authState}}) => authState),
|
|
196
|
+
)
|
|
246
197
|
|
|
247
198
|
/**
|
|
248
199
|
* @public
|
|
249
200
|
*/
|
|
250
|
-
export const getDashboardOrganizationId =
|
|
201
|
+
export const getDashboardOrganizationId = bindActionGlobally(
|
|
251
202
|
authStore,
|
|
252
|
-
({dashboardContext}) => dashboardContext?.orgId,
|
|
203
|
+
createStateSourceAction(({state: {dashboardContext}}) => dashboardContext?.orgId),
|
|
253
204
|
)
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import {NEVER} from 'rxjs'
|
|
1
2
|
import {beforeEach, describe, it} from 'vitest'
|
|
2
3
|
|
|
3
|
-
import {createSanityInstance} from '../
|
|
4
|
-
import {createResourceState} from '../resources/createResource'
|
|
4
|
+
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
5
5
|
import {AuthStateType} from './authStateType'
|
|
6
|
-
import {
|
|
6
|
+
import {getAuthState} from './authStore'
|
|
7
7
|
import {handleAuthCallback} from './handleAuthCallback'
|
|
8
|
+
import {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser'
|
|
9
|
+
import {subscribeToStorageEventsAndSetToken} from './subscribeToStorageEventsAndSetToken'
|
|
8
10
|
import {getAuthCode, getTokenFromStorage} from './utils'
|
|
9
11
|
|
|
10
12
|
vi.mock('./utils', async (importOriginal) => {
|
|
@@ -12,20 +14,31 @@ vi.mock('./utils', async (importOriginal) => {
|
|
|
12
14
|
return {...original, getTokenFromStorage: vi.fn(), getAuthCode: vi.fn()}
|
|
13
15
|
})
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
vi.mock('./subscribeToStateAndFetchCurrentUser')
|
|
18
|
+
vi.mock('./subscribeToStorageEventsAndSetToken')
|
|
19
|
+
|
|
20
|
+
let instance: SanityInstance | undefined
|
|
21
|
+
|
|
22
|
+
describe('handleCallback', () => {
|
|
16
23
|
beforeEach(() => {
|
|
17
24
|
vi.clearAllMocks()
|
|
25
|
+
vi.mocked(subscribeToStateAndFetchCurrentUser).mockImplementation(() => NEVER.subscribe())
|
|
26
|
+
vi.mocked(subscribeToStorageEventsAndSetToken).mockImplementation(() => NEVER.subscribe())
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
instance?.dispose()
|
|
18
31
|
})
|
|
19
32
|
|
|
20
33
|
it('trades the auth code for a token, sets the state to logged in, and sets the token in storage', async () => {
|
|
21
|
-
const
|
|
22
|
-
const clientFactory = vi.fn().mockReturnValue({request
|
|
34
|
+
const request = vi.fn().mockResolvedValue({token: 'new-token'})
|
|
35
|
+
const clientFactory = vi.fn().mockReturnValue({request})
|
|
23
36
|
const setItem = vi.fn()
|
|
24
37
|
vi.mocked(getTokenFromStorage).mockReturnValue(null)
|
|
25
38
|
const authCode = 'auth-code'
|
|
26
39
|
vi.mocked(getAuthCode).mockReturnValue(authCode)
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
instance = createSanityInstance({
|
|
29
42
|
projectId: 'p',
|
|
30
43
|
dataset: 'd',
|
|
31
44
|
auth: {
|
|
@@ -34,15 +47,16 @@ describe('handleAuthCallback', () => {
|
|
|
34
47
|
},
|
|
35
48
|
})
|
|
36
49
|
|
|
37
|
-
const
|
|
38
|
-
|
|
50
|
+
const authState = getAuthState(instance)
|
|
51
|
+
|
|
52
|
+
expect(authState.getCurrent()).toMatchObject({isExchangingToken: false})
|
|
39
53
|
|
|
40
54
|
const resultPromise = handleAuthCallback(
|
|
41
|
-
|
|
55
|
+
instance,
|
|
42
56
|
'https://example.com/callback?foo=bar#withSid=code',
|
|
43
57
|
)
|
|
44
58
|
|
|
45
|
-
expect(
|
|
59
|
+
expect(authState.getCurrent()).toMatchObject({isExchangingToken: true})
|
|
46
60
|
const result = await resultPromise
|
|
47
61
|
|
|
48
62
|
expect(result).toBe('https://example.com/callback?foo=bar')
|
|
@@ -51,13 +65,13 @@ describe('handleAuthCallback', () => {
|
|
|
51
65
|
requestTagPrefix: 'sanity.sdk.auth',
|
|
52
66
|
useProjectHostname: false,
|
|
53
67
|
})
|
|
54
|
-
expect(
|
|
68
|
+
expect(request).toHaveBeenLastCalledWith({
|
|
55
69
|
method: 'GET',
|
|
56
70
|
query: {sid: authCode},
|
|
57
71
|
tag: 'fetch-token',
|
|
58
72
|
uri: '/auth/fetch',
|
|
59
73
|
})
|
|
60
|
-
expect(setItem).toHaveBeenCalledWith(
|
|
74
|
+
expect(setItem).toHaveBeenCalledWith('__sanity_auth_token', '{"token":"new-token"}')
|
|
61
75
|
})
|
|
62
76
|
|
|
63
77
|
it('returns early if there is a provided token', async () => {
|
|
@@ -68,7 +82,7 @@ describe('handleAuthCallback', () => {
|
|
|
68
82
|
const authCode = 'auth-code'
|
|
69
83
|
vi.mocked(getAuthCode).mockReturnValue(authCode)
|
|
70
84
|
|
|
71
|
-
|
|
85
|
+
instance = createSanityInstance({
|
|
72
86
|
projectId: 'p',
|
|
73
87
|
dataset: 'd',
|
|
74
88
|
auth: {
|
|
@@ -78,9 +92,8 @@ describe('handleAuthCallback', () => {
|
|
|
78
92
|
},
|
|
79
93
|
})
|
|
80
94
|
|
|
81
|
-
const state = createResourceState(authStore.getInitialState(instance))
|
|
82
95
|
const result = await handleAuthCallback(
|
|
83
|
-
|
|
96
|
+
instance,
|
|
84
97
|
'https://example.com/callback?foo=bar#withSid=code',
|
|
85
98
|
)
|
|
86
99
|
|
|
@@ -90,37 +103,58 @@ describe('handleAuthCallback', () => {
|
|
|
90
103
|
})
|
|
91
104
|
|
|
92
105
|
it('returns early if already exchanging the the token', async () => {
|
|
93
|
-
|
|
106
|
+
let resolveRequest!: (value: {token: string; label: string}) => void
|
|
107
|
+
const requestPromise = new Promise<{token: string; label: string}>((resolve) => {
|
|
108
|
+
resolveRequest = resolve
|
|
109
|
+
})
|
|
110
|
+
vi.mocked(getAuthCode).mockReturnValue('code')
|
|
111
|
+
const request = vi.fn().mockReturnValue(requestPromise)
|
|
112
|
+
const clientFactory = vi.fn().mockReturnValue({request})
|
|
94
113
|
const setItem = vi.fn()
|
|
95
114
|
|
|
96
|
-
|
|
115
|
+
instance = createSanityInstance({
|
|
97
116
|
projectId: 'p',
|
|
98
117
|
dataset: 'd',
|
|
99
118
|
auth: {
|
|
100
119
|
clientFactory,
|
|
101
|
-
storageArea: {setItem
|
|
120
|
+
storageArea: {setItem: setItem as Storage['setItem']} as Storage,
|
|
102
121
|
},
|
|
103
122
|
})
|
|
104
123
|
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
124
|
+
const authState = getAuthState(instance)
|
|
125
|
+
expect(authState.getCurrent()).toMatchObject({type: AuthStateType.LOGGING_IN})
|
|
126
|
+
|
|
127
|
+
const locationHref = 'https://example.com/callback?foo=bar#withSid=code'
|
|
128
|
+
const originalResultPromise = handleAuthCallback(instance, locationHref)
|
|
129
|
+
|
|
130
|
+
expect(authState.getCurrent()).toMatchObject({
|
|
131
|
+
type: AuthStateType.LOGGING_IN,
|
|
132
|
+
isExchangingToken: true,
|
|
108
133
|
})
|
|
109
|
-
const result = await handleAuthCallback(
|
|
110
|
-
{instance, state},
|
|
111
|
-
'https://example.com/callback?foo=bar#withSid=code',
|
|
112
|
-
)
|
|
113
134
|
|
|
114
|
-
|
|
135
|
+
// ensures mock calls are reset to zero
|
|
136
|
+
clientFactory.mockClear()
|
|
137
|
+
setItem.mockClear()
|
|
138
|
+
|
|
139
|
+
// notice how this resolves first
|
|
140
|
+
const earlyResult = await handleAuthCallback(instance, locationHref)
|
|
141
|
+
expect(earlyResult).toBe(false)
|
|
142
|
+
|
|
115
143
|
expect(clientFactory).not.toHaveBeenCalled()
|
|
116
144
|
expect(setItem).not.toHaveBeenCalled()
|
|
145
|
+
|
|
146
|
+
// this will resolve
|
|
147
|
+
resolveRequest({token: 'token', label: 'label'})
|
|
148
|
+
expect(await originalResultPromise).toBe('https://example.com/callback?foo=bar')
|
|
149
|
+
|
|
150
|
+
// expect(result).toBe(false)
|
|
117
151
|
})
|
|
118
152
|
|
|
119
153
|
it('returns early if there is no auth code present', async () => {
|
|
120
154
|
const clientFactory = vi.fn()
|
|
121
155
|
const setItem = vi.fn()
|
|
122
156
|
|
|
123
|
-
|
|
157
|
+
instance = createSanityInstance({
|
|
124
158
|
projectId: 'p',
|
|
125
159
|
dataset: 'd',
|
|
126
160
|
auth: {
|
|
@@ -130,9 +164,8 @@ describe('handleAuthCallback', () => {
|
|
|
130
164
|
})
|
|
131
165
|
vi.mocked(getAuthCode).mockReturnValue(null)
|
|
132
166
|
|
|
133
|
-
const state = createResourceState(authStore.getInitialState(instance))
|
|
134
167
|
const result = await handleAuthCallback(
|
|
135
|
-
|
|
168
|
+
instance,
|
|
136
169
|
'https://example.com/callback?foo=bar#withSid=code',
|
|
137
170
|
)
|
|
138
171
|
|
|
@@ -150,7 +183,7 @@ describe('handleAuthCallback', () => {
|
|
|
150
183
|
const authCode = 'auth-code'
|
|
151
184
|
vi.mocked(getAuthCode).mockReturnValue(authCode)
|
|
152
185
|
|
|
153
|
-
|
|
186
|
+
instance = createSanityInstance({
|
|
154
187
|
projectId: 'p',
|
|
155
188
|
dataset: 'd',
|
|
156
189
|
auth: {
|
|
@@ -159,14 +192,14 @@ describe('handleAuthCallback', () => {
|
|
|
159
192
|
},
|
|
160
193
|
})
|
|
161
194
|
|
|
162
|
-
const
|
|
195
|
+
const authState = getAuthState(instance)
|
|
163
196
|
const result = await handleAuthCallback(
|
|
164
|
-
|
|
197
|
+
instance,
|
|
165
198
|
'https://example.com/callback?foo=bar#withSid=code',
|
|
166
199
|
)
|
|
167
200
|
|
|
168
201
|
expect(result).toBe(false)
|
|
169
|
-
expect(
|
|
202
|
+
expect(authState.getCurrent()).toMatchObject({type: AuthStateType.ERROR, error})
|
|
170
203
|
|
|
171
204
|
expect(clientFactory).toHaveBeenCalledWith({
|
|
172
205
|
apiVersion: '2021-06-07',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {bindActionGlobally} from '../store/createActionBinder'
|
|
2
2
|
import {DEFAULT_API_VERSION, REQUEST_TAG_PREFIX} from './authConstants'
|
|
3
3
|
import {AuthStateType} from './authStateType'
|
|
4
4
|
import {authStore, type AuthStoreState, type DashboardContext} from './authStore'
|
|
@@ -7,11 +7,12 @@ import {getAuthCode, getDefaultLocation} from './utils'
|
|
|
7
7
|
/**
|
|
8
8
|
* @public
|
|
9
9
|
*/
|
|
10
|
-
export const handleAuthCallback =
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export const handleAuthCallback = bindActionGlobally(
|
|
11
|
+
authStore,
|
|
12
|
+
async ({state}, locationHref: string = getDefaultLocation()) => {
|
|
13
|
+
const {providedToken, callbackUrl, clientFactory, apiHost, storageArea, storageKey} =
|
|
14
|
+
state.get().options
|
|
13
15
|
|
|
14
|
-
return async function (locationHref: string = getDefaultLocation()) {
|
|
15
16
|
// If a token is provided, no need to handle callback
|
|
16
17
|
if (providedToken) return false
|
|
17
18
|
|
|
@@ -69,5 +70,5 @@ export const handleAuthCallback = createAction(authStore, ({state}) => {
|
|
|
69
70
|
state.set('exchangeSessionForTokenError', {authState: {type: AuthStateType.ERROR, error}})
|
|
70
71
|
return false
|
|
71
72
|
}
|
|
72
|
-
}
|
|
73
|
-
|
|
73
|
+
},
|
|
74
|
+
)
|
package/src/auth/logout.test.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import {NEVER} from 'rxjs'
|
|
1
2
|
import {describe, it} from 'vitest'
|
|
2
3
|
|
|
3
|
-
import {createSanityInstance} from '../
|
|
4
|
-
import {createResourceState} from '../resources/createResource'
|
|
4
|
+
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
5
5
|
import {AuthStateType} from './authStateType'
|
|
6
|
-
import {
|
|
6
|
+
import {getAuthState} from './authStore'
|
|
7
7
|
import {logout} from './logout'
|
|
8
|
+
import {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser'
|
|
9
|
+
import {subscribeToStorageEventsAndSetToken} from './subscribeToStorageEventsAndSetToken'
|
|
8
10
|
import {getTokenFromStorage} from './utils'
|
|
9
11
|
|
|
10
12
|
vi.mock('./utils', async (importOriginal) => {
|
|
@@ -12,28 +14,43 @@ vi.mock('./utils', async (importOriginal) => {
|
|
|
12
14
|
return {...original, getTokenFromStorage: vi.fn()}
|
|
13
15
|
})
|
|
14
16
|
|
|
17
|
+
vi.mock('./subscribeToStateAndFetchCurrentUser')
|
|
18
|
+
vi.mock('./subscribeToStorageEventsAndSetToken')
|
|
19
|
+
|
|
20
|
+
let instance: SanityInstance | undefined
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
vi.mocked(subscribeToStateAndFetchCurrentUser).mockImplementation(() => NEVER.subscribe())
|
|
24
|
+
vi.mocked(subscribeToStorageEventsAndSetToken).mockImplementation(() => NEVER.subscribe())
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
instance?.dispose()
|
|
29
|
+
})
|
|
30
|
+
|
|
15
31
|
describe('logout', () => {
|
|
16
32
|
it("calls '/auth/logout', sets the state to logged out, and removes any storage items", async () => {
|
|
17
33
|
vi.mocked(getTokenFromStorage).mockReturnValue('token')
|
|
18
34
|
const mockRequest = vi.fn().mockResolvedValue(undefined)
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
35
|
+
const clientFactory = vi.fn().mockReturnValue({request: mockRequest})
|
|
36
|
+
const removeItem = vi.fn() as Storage['removeItem']
|
|
37
|
+
|
|
38
|
+
instance = createSanityInstance({
|
|
23
39
|
projectId: 'p',
|
|
24
40
|
dataset: 'd',
|
|
25
41
|
auth: {
|
|
26
42
|
clientFactory,
|
|
27
|
-
storageArea: {removeItem} as
|
|
43
|
+
storageArea: {removeItem} as Storage,
|
|
28
44
|
},
|
|
29
45
|
})
|
|
30
46
|
|
|
31
|
-
const
|
|
47
|
+
const authState = getAuthState(instance)
|
|
48
|
+
expect(authState.getCurrent()).toMatchObject({type: AuthStateType.LOGGED_IN})
|
|
32
49
|
|
|
33
|
-
const logoutPromise = logout(
|
|
34
|
-
expect(
|
|
50
|
+
const logoutPromise = logout(instance)
|
|
51
|
+
expect(authState.getCurrent()).toMatchObject({isDestroyingSession: true})
|
|
35
52
|
await logoutPromise
|
|
36
|
-
expect(
|
|
53
|
+
expect(authState.getCurrent()).toMatchObject({isDestroyingSession: false})
|
|
37
54
|
|
|
38
55
|
expect(clientFactory).toHaveBeenCalledWith({
|
|
39
56
|
apiVersion: '2021-06-07',
|
|
@@ -42,50 +59,65 @@ describe('logout', () => {
|
|
|
42
59
|
useProjectHostname: false,
|
|
43
60
|
})
|
|
44
61
|
expect(mockRequest).toHaveBeenCalledWith({method: 'POST', uri: '/auth/logout'})
|
|
45
|
-
expect(removeItem).toHaveBeenCalledWith(
|
|
62
|
+
expect(removeItem).toHaveBeenCalledWith('__sanity_auth_token')
|
|
46
63
|
})
|
|
47
64
|
|
|
48
65
|
it('returns early if there is a provided token', async () => {
|
|
49
66
|
const clientFactory = vi.fn()
|
|
50
|
-
const removeItem = vi.fn()
|
|
51
|
-
|
|
67
|
+
const removeItem = vi.fn() as Storage['removeItem']
|
|
68
|
+
instance = createSanityInstance({
|
|
52
69
|
projectId: 'p',
|
|
53
70
|
dataset: 'd',
|
|
54
71
|
auth: {
|
|
55
72
|
token: 'provided-token',
|
|
56
73
|
clientFactory,
|
|
57
|
-
storageArea: {removeItem} as
|
|
74
|
+
storageArea: {removeItem} as Storage,
|
|
58
75
|
},
|
|
59
76
|
})
|
|
60
77
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
await logout({state, instance})
|
|
78
|
+
await logout(instance)
|
|
64
79
|
|
|
65
80
|
expect(clientFactory).not.toHaveBeenCalled()
|
|
66
81
|
expect(removeItem).not.toHaveBeenCalled()
|
|
67
82
|
})
|
|
68
83
|
|
|
69
84
|
it('returns early if the session is already destroying', async () => {
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
85
|
+
let resolveRequest!: () => void
|
|
86
|
+
const requestPromise = new Promise<void>((resolve) => {
|
|
87
|
+
resolveRequest = resolve
|
|
88
|
+
})
|
|
89
|
+
const request = vi.fn().mockReturnValue(requestPromise)
|
|
90
|
+
const clientFactory = vi.fn().mockReturnValue({request})
|
|
91
|
+
const removeItem = vi.fn() as Storage['removeItem']
|
|
92
|
+
vi.mocked(getTokenFromStorage).mockReturnValue('token')
|
|
93
|
+
|
|
94
|
+
instance = createSanityInstance({
|
|
73
95
|
projectId: 'p',
|
|
74
96
|
dataset: 'd',
|
|
75
97
|
auth: {
|
|
76
|
-
token: 'provided-token',
|
|
77
98
|
clientFactory,
|
|
78
|
-
storageArea: {removeItem} as
|
|
99
|
+
storageArea: {removeItem} as Storage,
|
|
79
100
|
},
|
|
80
101
|
})
|
|
81
102
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
103
|
+
const authState = getAuthState(instance)
|
|
104
|
+
expect(authState.getCurrent()).toMatchObject({type: AuthStateType.LOGGED_IN})
|
|
105
|
+
|
|
106
|
+
const originalLogout = logout(instance)
|
|
107
|
+
expect(authState.getCurrent()).toMatchObject({isDestroyingSession: true})
|
|
108
|
+
|
|
109
|
+
// reset counts
|
|
110
|
+
clientFactory.mockClear()
|
|
111
|
+
vi.mocked(removeItem).mockClear()
|
|
112
|
+
|
|
113
|
+
await logout(instance) // should early return
|
|
86
114
|
|
|
87
|
-
await logout({state, instance})
|
|
88
115
|
expect(clientFactory).not.toHaveBeenCalled()
|
|
89
116
|
expect(removeItem).not.toHaveBeenCalled()
|
|
117
|
+
|
|
118
|
+
resolveRequest()
|
|
119
|
+
|
|
120
|
+
await originalLogout
|
|
121
|
+
expect(removeItem).toHaveBeenCalledOnce()
|
|
90
122
|
})
|
|
91
123
|
})
|