@sanity/sdk 2.7.0 → 3.0.0-rc.0
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 +228 -239
- package/dist/index.js +287 -454
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/_exports/index.ts +16 -17
- package/src/agent/agentActions.test.ts +60 -16
- package/src/agent/agentActions.ts +29 -20
- package/src/auth/authMode.test.ts +0 -25
- package/src/auth/authMode.ts +3 -6
- package/src/auth/authStore.test.ts +129 -66
- package/src/auth/authStore.ts +9 -11
- package/src/auth/dashboardAuth.ts +2 -2
- package/src/auth/getOrganizationVerificationState.test.ts +10 -11
- package/src/auth/handleAuthCallback.test.ts +0 -12
- package/src/auth/handleAuthCallback.ts +9 -3
- package/src/auth/logout.test.ts +0 -6
- package/src/auth/refreshStampedToken.test.ts +121 -17
- package/src/auth/standaloneAuth.ts +9 -3
- package/src/auth/studioAuth.ts +35 -8
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +9 -3
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +1 -1
- package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +0 -2
- package/src/auth/subscribeToStorageEventsAndSetToken.ts +2 -2
- package/src/auth/utils.ts +33 -0
- package/src/client/clientStore.test.ts +14 -61
- package/src/client/clientStore.ts +52 -28
- package/src/comlink/controller/actions/destroyController.test.ts +1 -4
- package/src/comlink/controller/actions/getOrCreateChannel.test.ts +1 -4
- package/src/comlink/controller/actions/getOrCreateController.test.ts +1 -4
- package/src/comlink/controller/actions/releaseChannel.test.ts +1 -1
- package/src/comlink/controller/comlinkControllerStore.test.ts +1 -4
- package/src/comlink/node/actions/getOrCreateNode.test.ts +1 -4
- package/src/comlink/node/actions/releaseNode.test.ts +1 -4
- package/src/comlink/node/comlinkNodeStore.test.ts +2 -2
- package/src/comlink/node/getNodeState.test.ts +1 -1
- package/src/config/__tests__/handles.test.ts +12 -18
- package/src/config/handles.ts +7 -25
- package/src/config/sanityConfig.ts +99 -52
- package/src/datasets/datasets.test.ts +2 -2
- package/src/datasets/datasets.ts +4 -10
- package/src/document/actions.test.ts +33 -4
- package/src/document/actions.ts +3 -10
- package/src/document/applyDocumentActions.test.ts +17 -18
- package/src/document/applyDocumentActions.ts +9 -12
- package/src/document/documentStore.test.ts +303 -133
- package/src/document/documentStore.ts +70 -61
- package/src/document/permissions.test.ts +44 -8
- package/src/document/processActions.test.ts +77 -7
- package/src/document/reducers.test.ts +35 -3
- package/src/document/sharedListener.test.ts +13 -13
- package/src/document/sharedListener.ts +8 -3
- package/src/favorites/favorites.test.ts +10 -2
- package/src/presence/presenceStore.test.ts +34 -9
- package/src/presence/presenceStore.ts +29 -13
- package/src/preview/previewProjectionUtils.test.ts +192 -0
- package/src/preview/previewProjectionUtils.ts +88 -0
- package/src/preview/{previewStore.ts → types.ts} +6 -25
- package/src/project/project.test.ts +1 -1
- package/src/project/project.ts +14 -20
- package/src/projection/getProjectionState.test.ts +4 -2
- package/src/projection/getProjectionState.ts +2 -21
- package/src/projection/projectionQuery.ts +2 -3
- package/src/projection/projectionStore.test.ts +3 -3
- package/src/projection/resolveProjection.test.ts +2 -1
- package/src/projection/resolveProjection.ts +2 -18
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +2 -2
- package/src/projection/subscribeToStateAndFetchBatches.ts +23 -36
- package/src/projection/types.ts +1 -9
- package/src/projects/projects.test.ts +1 -1
- package/src/query/queryStore.test.ts +86 -28
- package/src/query/queryStore.ts +23 -38
- package/src/releases/getPerspectiveState.test.ts +14 -13
- package/src/releases/getPerspectiveState.ts +6 -6
- package/src/releases/releasesStore.test.ts +21 -6
- package/src/releases/releasesStore.ts +18 -8
- package/src/store/createActionBinder.test.ts +114 -111
- package/src/store/createActionBinder.ts +52 -101
- package/src/store/createSanityInstance.test.ts +13 -83
- package/src/store/createSanityInstance.ts +2 -78
- package/src/store/createStateSourceAction.test.ts +2 -2
- package/src/store/createStateSourceAction.ts +5 -5
- package/src/store/createStoreInstance.test.ts +2 -4
- package/src/users/reducers.test.ts +1 -6
- package/src/users/reducers.ts +2 -2
- package/src/users/types.ts +4 -4
- package/src/users/usersStore.test.ts +12 -15
- package/src/utils/createFetcherStore.test.ts +1 -1
- package/src/utils/logger.test.ts +0 -12
- package/src/utils/logger.ts +3 -8
- package/src/preview/getPreviewState.test.ts +0 -120
- package/src/preview/getPreviewState.ts +0 -91
- package/src/preview/previewQuery.test.ts +0 -236
- package/src/preview/previewQuery.ts +0 -153
- package/src/preview/previewStore.test.ts +0 -36
- package/src/preview/resolvePreview.test.ts +0 -47
- package/src/preview/resolvePreview.ts +0 -20
- package/src/preview/subscribeToStateAndFetchBatches.test.ts +0 -221
- package/src/preview/subscribeToStateAndFetchBatches.ts +0 -112
- package/src/preview/util.ts +0 -13
|
@@ -7,6 +7,7 @@ import {createSanityInstance} from '../store/createSanityInstance'
|
|
|
7
7
|
import {AuthStateType} from './authStateType'
|
|
8
8
|
import {
|
|
9
9
|
authStore,
|
|
10
|
+
getAuthMethodState,
|
|
10
11
|
getAuthState,
|
|
11
12
|
getCurrentUserState,
|
|
12
13
|
getDashboardOrganizationId,
|
|
@@ -17,7 +18,12 @@ import {handleAuthCallback} from './handleAuthCallback'
|
|
|
17
18
|
import {checkForCookieAuth, getStudioTokenFromLocalStorage} from './studioModeAuth'
|
|
18
19
|
import {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser'
|
|
19
20
|
import {subscribeToStorageEventsAndSetToken} from './subscribeToStorageEventsAndSetToken'
|
|
20
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
createLoggedInAuthState,
|
|
23
|
+
getAuthCode,
|
|
24
|
+
getTokenFromLocation,
|
|
25
|
+
getTokenFromStorage,
|
|
26
|
+
} from './utils'
|
|
21
27
|
|
|
22
28
|
vi.mock('./utils', async (importOriginal) => {
|
|
23
29
|
const original = await importOriginal<typeof import('./utils')>()
|
|
@@ -79,8 +85,6 @@ describe('authStore', () => {
|
|
|
79
85
|
const storageArea = {} as Storage
|
|
80
86
|
|
|
81
87
|
instance = createSanityInstance({
|
|
82
|
-
projectId: 'p',
|
|
83
|
-
dataset: 'd',
|
|
84
88
|
auth: {
|
|
85
89
|
apiHost,
|
|
86
90
|
callbackUrl,
|
|
@@ -109,8 +113,6 @@ describe('authStore', () => {
|
|
|
109
113
|
const context = {mode: 'test', env: 'staging', orgId: 'abc'}
|
|
110
114
|
const initialLocationHref = `https://example.com/?_context=${encodeURIComponent(JSON.stringify(context))}`
|
|
111
115
|
instance = createSanityInstance({
|
|
112
|
-
projectId: 'p',
|
|
113
|
-
dataset: 'd',
|
|
114
116
|
auth: {initialLocationHref},
|
|
115
117
|
})
|
|
116
118
|
|
|
@@ -124,8 +126,6 @@ describe('authStore', () => {
|
|
|
124
126
|
const expectedContext = {mode: 'test', env: 'staging', orgId: 'abc'}
|
|
125
127
|
const initialLocationHref = `https://example.com/?_context=${encodeURIComponent(JSON.stringify(context))}`
|
|
126
128
|
instance = createSanityInstance({
|
|
127
|
-
projectId: 'p',
|
|
128
|
-
dataset: 'd',
|
|
129
129
|
auth: {initialLocationHref},
|
|
130
130
|
})
|
|
131
131
|
|
|
@@ -138,8 +138,6 @@ describe('authStore', () => {
|
|
|
138
138
|
const initialLocationHref = `https://example.com/?_context=invalid-json`
|
|
139
139
|
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
140
140
|
instance = createSanityInstance({
|
|
141
|
-
projectId: 'p',
|
|
142
|
-
dataset: 'd',
|
|
143
141
|
auth: {initialLocationHref},
|
|
144
142
|
})
|
|
145
143
|
|
|
@@ -156,8 +154,6 @@ describe('authStore', () => {
|
|
|
156
154
|
it('rejects array _context and falls back to standalone mode', () => {
|
|
157
155
|
const initialLocationHref = `https://example.com/?_context=${encodeURIComponent(JSON.stringify(['a', 'b']))}`
|
|
158
156
|
instance = createSanityInstance({
|
|
159
|
-
projectId: 'p',
|
|
160
|
-
dataset: 'd',
|
|
161
157
|
auth: {initialLocationHref},
|
|
162
158
|
})
|
|
163
159
|
|
|
@@ -170,8 +166,6 @@ describe('authStore', () => {
|
|
|
170
166
|
it('rejects empty object _context and falls back to standalone mode', () => {
|
|
171
167
|
const initialLocationHref = `https://example.com/?_context=${encodeURIComponent(JSON.stringify({}))}`
|
|
172
168
|
instance = createSanityInstance({
|
|
173
|
-
projectId: 'p',
|
|
174
|
-
dataset: 'd',
|
|
175
169
|
auth: {initialLocationHref},
|
|
176
170
|
})
|
|
177
171
|
|
|
@@ -186,8 +180,6 @@ describe('authStore', () => {
|
|
|
186
180
|
const context = {mode: 'dashboard'}
|
|
187
181
|
const initialLocationHref = `https://example.com/?_context=${encodeURIComponent(JSON.stringify(context))}`
|
|
188
182
|
instance = createSanityInstance({
|
|
189
|
-
projectId: 'p',
|
|
190
|
-
dataset: 'd',
|
|
191
183
|
auth: {
|
|
192
184
|
token,
|
|
193
185
|
initialLocationHref,
|
|
@@ -203,8 +195,6 @@ describe('authStore', () => {
|
|
|
203
195
|
const context = {orgId: 'org1'}
|
|
204
196
|
const initialLocationHref = `https://example.com/?_context=${encodeURIComponent(JSON.stringify(context))}`
|
|
205
197
|
instance = createSanityInstance({
|
|
206
|
-
projectId: 'p',
|
|
207
|
-
dataset: 'd',
|
|
208
198
|
auth: {initialLocationHref},
|
|
209
199
|
})
|
|
210
200
|
|
|
@@ -217,10 +207,7 @@ describe('authStore', () => {
|
|
|
217
207
|
|
|
218
208
|
it('sets to logged in from storage token when NOT in dashboard', () => {
|
|
219
209
|
const storageToken = 'storage-token'
|
|
220
|
-
instance = createSanityInstance(
|
|
221
|
-
projectId: 'p',
|
|
222
|
-
dataset: 'd',
|
|
223
|
-
})
|
|
210
|
+
instance = createSanityInstance()
|
|
224
211
|
|
|
225
212
|
vi.mocked(getAuthCode).mockReturnValue(null)
|
|
226
213
|
vi.mocked(getTokenFromStorage).mockReturnValue(storageToken)
|
|
@@ -235,8 +222,6 @@ describe('authStore', () => {
|
|
|
235
222
|
const context = {mode: 'dashboard'}
|
|
236
223
|
const initialLocationHref = `https://example.com/?_context=${encodeURIComponent(JSON.stringify(context))}`
|
|
237
224
|
instance = createSanityInstance({
|
|
238
|
-
projectId: 'p',
|
|
239
|
-
dataset: 'd',
|
|
240
225
|
auth: {initialLocationHref},
|
|
241
226
|
})
|
|
242
227
|
|
|
@@ -249,10 +234,7 @@ describe('authStore', () => {
|
|
|
249
234
|
})
|
|
250
235
|
|
|
251
236
|
it('sets the state to logged out when no token, code, or context', () => {
|
|
252
|
-
instance = createSanityInstance(
|
|
253
|
-
projectId: 'p',
|
|
254
|
-
dataset: 'd',
|
|
255
|
-
})
|
|
237
|
+
instance = createSanityInstance()
|
|
256
238
|
|
|
257
239
|
vi.mocked(getAuthCode).mockReturnValue(null)
|
|
258
240
|
vi.mocked(getTokenFromStorage).mockReturnValue(null)
|
|
@@ -274,10 +256,8 @@ describe('authStore', () => {
|
|
|
274
256
|
vi.mocked(getStudioTokenFromLocalStorage).mockReturnValue(studioToken)
|
|
275
257
|
|
|
276
258
|
instance = createSanityInstance({
|
|
277
|
-
projectId,
|
|
278
|
-
|
|
279
|
-
studio: {},
|
|
280
|
-
auth: {storageArea: mockStorage}, // Provide mock storage
|
|
259
|
+
studio: {projectId},
|
|
260
|
+
auth: {storageArea: mockStorage},
|
|
281
261
|
})
|
|
282
262
|
|
|
283
263
|
const {authState, options} = authStore.getInitialState(instance, null)
|
|
@@ -298,9 +278,7 @@ describe('authStore', () => {
|
|
|
298
278
|
vi.mocked(checkForCookieAuth).mockResolvedValue(true)
|
|
299
279
|
|
|
300
280
|
instance = createSanityInstance({
|
|
301
|
-
projectId,
|
|
302
|
-
dataset: 'd',
|
|
303
|
-
studio: {},
|
|
281
|
+
studio: {projectId},
|
|
304
282
|
auth: {storageArea: mockStorage},
|
|
305
283
|
})
|
|
306
284
|
|
|
@@ -321,8 +299,6 @@ describe('authStore', () => {
|
|
|
321
299
|
}
|
|
322
300
|
|
|
323
301
|
instance = createSanityInstance({
|
|
324
|
-
projectId: 'studio-project',
|
|
325
|
-
dataset: 'production',
|
|
326
302
|
studio: {
|
|
327
303
|
auth: {token: mockTokenSource},
|
|
328
304
|
},
|
|
@@ -334,15 +310,13 @@ describe('authStore', () => {
|
|
|
334
310
|
expect(getStudioTokenFromLocalStorage).not.toHaveBeenCalled()
|
|
335
311
|
})
|
|
336
312
|
|
|
337
|
-
it('resolves to studio mode when studio config is provided
|
|
313
|
+
it('resolves to studio mode when studio config is provided', () => {
|
|
338
314
|
instance = createSanityInstance({
|
|
339
|
-
projectId: 'studio-project',
|
|
340
|
-
dataset: 'production',
|
|
341
315
|
studio: {},
|
|
342
316
|
})
|
|
343
317
|
|
|
344
318
|
const {authState} = authStore.getInitialState(instance, null)
|
|
345
|
-
// Without tokenSource, falls back to localStorage discovery
|
|
319
|
+
// Without tokenSource, falls back to localStorage discovery
|
|
346
320
|
expect(authState.type).toBe(AuthStateType.LOGGED_OUT)
|
|
347
321
|
expect(getStudioTokenFromLocalStorage).toHaveBeenCalled()
|
|
348
322
|
})
|
|
@@ -353,8 +327,6 @@ describe('authStore', () => {
|
|
|
353
327
|
const mockTokenSource = {subscribe: mockSubscribe}
|
|
354
328
|
|
|
355
329
|
instance = createSanityInstance({
|
|
356
|
-
projectId: 'studio-project',
|
|
357
|
-
dataset: 'production',
|
|
358
330
|
studio: {
|
|
359
331
|
auth: {token: mockTokenSource},
|
|
360
332
|
},
|
|
@@ -368,14 +340,62 @@ describe('authStore', () => {
|
|
|
368
340
|
expect(checkForCookieAuth).not.toHaveBeenCalled()
|
|
369
341
|
})
|
|
370
342
|
|
|
343
|
+
it('treats null token as cookie auth when studio reports authenticated', () => {
|
|
344
|
+
let tokenObserver!: {next: (token: string | null) => void}
|
|
345
|
+
const mockSubscribe = vi.fn((observer: {next: (token: string | null) => void}) => {
|
|
346
|
+
tokenObserver = observer
|
|
347
|
+
return {unsubscribe: vi.fn()}
|
|
348
|
+
})
|
|
349
|
+
const mockTokenSource = {subscribe: mockSubscribe}
|
|
350
|
+
|
|
351
|
+
instance = createSanityInstance({
|
|
352
|
+
studio: {
|
|
353
|
+
authenticated: true,
|
|
354
|
+
auth: {token: mockTokenSource},
|
|
355
|
+
},
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
getAuthState(instance)
|
|
359
|
+
tokenObserver.next(null)
|
|
360
|
+
|
|
361
|
+
expect(getAuthState(instance).getCurrent()).toMatchObject({
|
|
362
|
+
type: AuthStateType.LOGGED_IN,
|
|
363
|
+
token: '',
|
|
364
|
+
})
|
|
365
|
+
expect(getAuthMethodState(instance).getCurrent()).toBe('cookie')
|
|
366
|
+
expect(checkForCookieAuth).not.toHaveBeenCalled()
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
it('sets logged out when token is null and studio is not authenticated', () => {
|
|
370
|
+
let tokenObserver!: {next: (token: string | null) => void}
|
|
371
|
+
const mockSubscribe = vi.fn((observer: {next: (token: string | null) => void}) => {
|
|
372
|
+
tokenObserver = observer
|
|
373
|
+
return {unsubscribe: vi.fn()}
|
|
374
|
+
})
|
|
375
|
+
const mockTokenSource = {subscribe: mockSubscribe}
|
|
376
|
+
|
|
377
|
+
instance = createSanityInstance({
|
|
378
|
+
studio: {
|
|
379
|
+
authenticated: false,
|
|
380
|
+
auth: {token: mockTokenSource},
|
|
381
|
+
},
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
getAuthState(instance)
|
|
385
|
+
tokenObserver.next(null)
|
|
386
|
+
|
|
387
|
+
expect(getAuthState(instance).getCurrent()).toMatchObject({
|
|
388
|
+
type: AuthStateType.LOGGED_OUT,
|
|
389
|
+
})
|
|
390
|
+
expect(getAuthMethodState(instance).getCurrent()).toBeUndefined()
|
|
391
|
+
expect(checkForCookieAuth).not.toHaveBeenCalled()
|
|
392
|
+
})
|
|
393
|
+
|
|
371
394
|
it('falls back to default auth (storage token) when studio mode is disabled', () => {
|
|
372
395
|
const storageToken = 'regular-storage-token'
|
|
373
396
|
vi.mocked(getTokenFromStorage).mockReturnValue(storageToken)
|
|
374
397
|
|
|
375
|
-
instance = createSanityInstance(
|
|
376
|
-
projectId: 'p',
|
|
377
|
-
dataset: 'd',
|
|
378
|
-
})
|
|
398
|
+
instance = createSanityInstance()
|
|
379
399
|
|
|
380
400
|
const {authState, options} = authStore.getInitialState(instance, null)
|
|
381
401
|
expect(getStudioTokenFromLocalStorage).not.toHaveBeenCalled()
|
|
@@ -387,8 +407,6 @@ describe('authStore', () => {
|
|
|
387
407
|
it('sets to logging in if getTokenFromLocation returns a token', () => {
|
|
388
408
|
const initialLocationHref = 'https://example.com/#token=hash-token'
|
|
389
409
|
instance = createSanityInstance({
|
|
390
|
-
projectId: 'p',
|
|
391
|
-
dataset: 'd',
|
|
392
410
|
auth: {initialLocationHref},
|
|
393
411
|
})
|
|
394
412
|
|
|
@@ -443,8 +461,6 @@ describe('authStore', () => {
|
|
|
443
461
|
|
|
444
462
|
it('subscribes to state and storage events and unsubscribes on dispose', () => {
|
|
445
463
|
instance = createSanityInstance({
|
|
446
|
-
projectId: 'p',
|
|
447
|
-
dataset: 'd',
|
|
448
464
|
auth: {storageArea: mockLocalStorage},
|
|
449
465
|
})
|
|
450
466
|
|
|
@@ -469,8 +485,6 @@ describe('authStore', () => {
|
|
|
469
485
|
|
|
470
486
|
it('does not subscribe to storage events when not using storage area', () => {
|
|
471
487
|
instance = createSanityInstance({
|
|
472
|
-
projectId: 'p',
|
|
473
|
-
dataset: 'd',
|
|
474
488
|
auth: {storageArea: undefined},
|
|
475
489
|
})
|
|
476
490
|
|
|
@@ -511,7 +525,7 @@ describe('authStore', () => {
|
|
|
511
525
|
return NEVER.subscribe()
|
|
512
526
|
})
|
|
513
527
|
|
|
514
|
-
instance = createSanityInstance(
|
|
528
|
+
instance = createSanityInstance()
|
|
515
529
|
|
|
516
530
|
const {getCurrent} = getCurrentUserState(instance)
|
|
517
531
|
|
|
@@ -520,7 +534,7 @@ describe('authStore', () => {
|
|
|
520
534
|
})
|
|
521
535
|
|
|
522
536
|
it('returns null otherwise', () => {
|
|
523
|
-
instance = createSanityInstance(
|
|
537
|
+
instance = createSanityInstance()
|
|
524
538
|
const {getCurrent} = getCurrentUserState(instance)
|
|
525
539
|
expect(getCurrent()).toBe(null)
|
|
526
540
|
})
|
|
@@ -536,8 +550,6 @@ describe('authStore', () => {
|
|
|
536
550
|
it('returns the token if logged in', () => {
|
|
537
551
|
const token = 'hard-coded-token'
|
|
538
552
|
instance = createSanityInstance({
|
|
539
|
-
projectId: 'p',
|
|
540
|
-
dataset: 'd',
|
|
541
553
|
auth: {token},
|
|
542
554
|
})
|
|
543
555
|
const tokenState = getTokenState(instance)
|
|
@@ -548,7 +560,7 @@ describe('authStore', () => {
|
|
|
548
560
|
})
|
|
549
561
|
|
|
550
562
|
it('returns null otherwise', () => {
|
|
551
|
-
instance = createSanityInstance(
|
|
563
|
+
instance = createSanityInstance()
|
|
552
564
|
|
|
553
565
|
const tokenState = getTokenState(instance)
|
|
554
566
|
expect(tokenState.getCurrent()).toBe(null)
|
|
@@ -566,7 +578,7 @@ describe('authStore', () => {
|
|
|
566
578
|
})
|
|
567
579
|
|
|
568
580
|
it('returns the default login url', () => {
|
|
569
|
-
instance = createSanityInstance(
|
|
581
|
+
instance = createSanityInstance()
|
|
570
582
|
|
|
571
583
|
const loginUrlState = getLoginUrlState(instance)
|
|
572
584
|
expect(loginUrlState.getCurrent()).toBe(
|
|
@@ -584,8 +596,6 @@ describe('authStore', () => {
|
|
|
584
596
|
|
|
585
597
|
it('returns the current state in `authState`', () => {
|
|
586
598
|
instance = createSanityInstance({
|
|
587
|
-
projectId: 'p',
|
|
588
|
-
dataset: 'd',
|
|
589
599
|
auth: {token: 'hard-coded-token'},
|
|
590
600
|
})
|
|
591
601
|
|
|
@@ -616,8 +626,6 @@ describe('authStore', () => {
|
|
|
616
626
|
const context = {orgId: 'initial-org-id'}
|
|
617
627
|
const initialLocationHref = `https://example.com/?_context=${encodeURIComponent(JSON.stringify(context))}`
|
|
618
628
|
instance = createSanityInstance({
|
|
619
|
-
projectId: 'p',
|
|
620
|
-
dataset: 'd',
|
|
621
629
|
auth: {initialLocationHref},
|
|
622
630
|
})
|
|
623
631
|
|
|
@@ -634,8 +642,6 @@ describe('authStore', () => {
|
|
|
634
642
|
const authCode = 'test-auth-code'
|
|
635
643
|
|
|
636
644
|
instance = createSanityInstance({
|
|
637
|
-
projectId: 'p',
|
|
638
|
-
dataset: 'd',
|
|
639
645
|
auth: {
|
|
640
646
|
clientFactory: () => mockClient as unknown as SanityClient,
|
|
641
647
|
initialLocationHref, // Set initial context
|
|
@@ -664,8 +670,6 @@ describe('authStore', () => {
|
|
|
664
670
|
const context = {mode: 'test'} // No orgId
|
|
665
671
|
const initialLocationHref = `https://example.com/?_context=${encodeURIComponent(JSON.stringify(context))}`
|
|
666
672
|
instance = createSanityInstance({
|
|
667
|
-
projectId: 'p',
|
|
668
|
-
dataset: 'd',
|
|
669
673
|
auth: {initialLocationHref},
|
|
670
674
|
})
|
|
671
675
|
|
|
@@ -673,4 +677,63 @@ describe('authStore', () => {
|
|
|
673
677
|
expect(organizationId.getCurrent()).toBeUndefined()
|
|
674
678
|
})
|
|
675
679
|
})
|
|
680
|
+
|
|
681
|
+
describe('createLoggedInAuthState', () => {
|
|
682
|
+
beforeEach(() => {
|
|
683
|
+
vi.useFakeTimers()
|
|
684
|
+
vi.setSystemTime(new Date('2026-01-01T00:00:00.000Z'))
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
afterEach(() => {
|
|
688
|
+
vi.useRealTimers()
|
|
689
|
+
})
|
|
690
|
+
|
|
691
|
+
it('sets lastTokenRefresh for stamped tokens', () => {
|
|
692
|
+
const state = createLoggedInAuthState('sk-stamped-token-st123', null)
|
|
693
|
+
|
|
694
|
+
expect(state).toEqual({
|
|
695
|
+
type: AuthStateType.LOGGED_IN,
|
|
696
|
+
token: 'sk-stamped-token-st123',
|
|
697
|
+
currentUser: null,
|
|
698
|
+
lastTokenRefresh: Date.now(),
|
|
699
|
+
})
|
|
700
|
+
})
|
|
701
|
+
|
|
702
|
+
it('does not set lastTokenRefresh for non-stamped tokens', () => {
|
|
703
|
+
const state = createLoggedInAuthState('sk-regular-token', null)
|
|
704
|
+
|
|
705
|
+
expect(state).toEqual({
|
|
706
|
+
type: AuthStateType.LOGGED_IN,
|
|
707
|
+
token: 'sk-regular-token',
|
|
708
|
+
currentUser: null,
|
|
709
|
+
})
|
|
710
|
+
expect(state.lastTokenRefresh).toBeUndefined()
|
|
711
|
+
})
|
|
712
|
+
|
|
713
|
+
it('preserves an existing lastTokenRefresh when provided', () => {
|
|
714
|
+
const existingTimestamp = Date.now() - 5000
|
|
715
|
+
const state = createLoggedInAuthState('sk-stamped-token-st123', null, existingTimestamp)
|
|
716
|
+
|
|
717
|
+
expect(state.lastTokenRefresh).toBe(existingTimestamp)
|
|
718
|
+
})
|
|
719
|
+
|
|
720
|
+
it('passes through the currentUser', () => {
|
|
721
|
+
const user = {id: 'user-1', name: 'Test'} as CurrentUser
|
|
722
|
+
const state = createLoggedInAuthState('sk-stamped-token-st123', user)
|
|
723
|
+
|
|
724
|
+
expect(state.currentUser).toBe(user)
|
|
725
|
+
})
|
|
726
|
+
|
|
727
|
+
it('handles null currentUser', () => {
|
|
728
|
+
const state = createLoggedInAuthState('sk-stamped-token-st123', null)
|
|
729
|
+
|
|
730
|
+
expect(state.currentUser).toBeNull()
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
it('does not set lastTokenRefresh when explicitly undefined for non-stamped token', () => {
|
|
734
|
+
const state = createLoggedInAuthState('sk-regular-token', null, undefined)
|
|
735
|
+
|
|
736
|
+
expect(state.lastTokenRefresh).toBeUndefined()
|
|
737
|
+
})
|
|
738
|
+
})
|
|
676
739
|
})
|
package/src/auth/authStore.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {type AuthStrategyOptions} from './authStrategy'
|
|
|
11
11
|
import {getDashboardInitialState, initializeDashboardAuth} from './dashboardAuth'
|
|
12
12
|
import {getStandaloneInitialState, initializeStandaloneAuth} from './standaloneAuth'
|
|
13
13
|
import {getStudioInitialState, initializeStudioAuth} from './studioAuth'
|
|
14
|
-
import {getCleanedUrl, getDefaultLocation} from './utils'
|
|
14
|
+
import {createLoggedInAuthState, getCleanedUrl, getDefaultLocation} from './utils'
|
|
15
15
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
// Public types
|
|
@@ -132,7 +132,7 @@ export const authStore = defineStore<AuthStoreState>({
|
|
|
132
132
|
|
|
133
133
|
const strategyOptions: AuthStrategyOptions = {
|
|
134
134
|
authConfig,
|
|
135
|
-
projectId: instance.config.projectId,
|
|
135
|
+
projectId: instance.config.studio?.projectId,
|
|
136
136
|
initialLocationHref,
|
|
137
137
|
clientFactory,
|
|
138
138
|
tokenSource: instance.config.studio?.auth?.token,
|
|
@@ -274,16 +274,14 @@ export const setAuthToken = bindActionGlobally(authStore, ({state}, token: strin
|
|
|
274
274
|
if (token) {
|
|
275
275
|
// Update state only if the new token is different or currently logged out
|
|
276
276
|
if (currentAuthState.type !== AuthStateType.LOGGED_IN || currentAuthState.token !== token) {
|
|
277
|
-
|
|
277
|
+
const currentUser =
|
|
278
|
+
currentAuthState.type === AuthStateType.LOGGED_IN ? currentAuthState.currentUser : null
|
|
279
|
+
const preservedLastTokenRefresh =
|
|
280
|
+
currentAuthState.type === AuthStateType.LOGGED_IN
|
|
281
|
+
? currentAuthState.lastTokenRefresh
|
|
282
|
+
: undefined
|
|
278
283
|
state.set('setToken', {
|
|
279
|
-
authState:
|
|
280
|
-
type: AuthStateType.LOGGED_IN,
|
|
281
|
-
token: token,
|
|
282
|
-
// Keep existing user or set to null? Setting to null forces refetch.
|
|
283
|
-
// Keep existing user to avoid unnecessary refetches if user data is still valid.
|
|
284
|
-
currentUser:
|
|
285
|
-
currentAuthState.type === AuthStateType.LOGGED_IN ? currentAuthState.currentUser : null,
|
|
286
|
-
},
|
|
284
|
+
authState: createLoggedInAuthState(token, currentUser, preservedLastTokenRefresh),
|
|
287
285
|
})
|
|
288
286
|
}
|
|
289
287
|
} else {
|
|
@@ -7,7 +7,7 @@ import {type AuthStoreState, type DashboardContext} from './authStore'
|
|
|
7
7
|
import {type AuthStrategyOptions, type AuthStrategyResult} from './authStrategy'
|
|
8
8
|
import {refreshStampedToken} from './refreshStampedToken'
|
|
9
9
|
import {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser'
|
|
10
|
-
import {getAuthCode, getTokenFromLocation} from './utils'
|
|
10
|
+
import {createLoggedInAuthState, getAuthCode, getTokenFromLocation} from './utils'
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Parses the dashboard context from a location href's `_context` URL parameter.
|
|
@@ -66,7 +66,7 @@ export function getDashboardInitialState(options: AuthStrategyOptions): AuthStra
|
|
|
66
66
|
// Provided token always wins, even in dashboard
|
|
67
67
|
if (providedToken) {
|
|
68
68
|
return {
|
|
69
|
-
authState:
|
|
69
|
+
authState: createLoggedInAuthState(providedToken, null),
|
|
70
70
|
storageKey,
|
|
71
71
|
storageArea,
|
|
72
72
|
authMethod: undefined,
|
|
@@ -32,10 +32,10 @@ vi.mock('../project/organizationVerification', async (importOriginal) => {
|
|
|
32
32
|
describe('observeOrganizationVerificationState', () => {
|
|
33
33
|
let testScheduler: TestScheduler
|
|
34
34
|
|
|
35
|
-
// Mock instance
|
|
35
|
+
// Mock instance with studio.projectId
|
|
36
36
|
const mockInstance = {
|
|
37
|
-
config: {projectId: 'proj-1'
|
|
38
|
-
} as SanityInstance
|
|
37
|
+
config: {studio: {projectId: 'proj-1'}},
|
|
38
|
+
} as unknown as SanityInstance
|
|
39
39
|
|
|
40
40
|
beforeEach(() => {
|
|
41
41
|
testScheduler = new TestScheduler((actual, expected) => {
|
|
@@ -80,18 +80,17 @@ describe('observeOrganizationVerificationState', () => {
|
|
|
80
80
|
const expectedValues = {a: {error: null}}
|
|
81
81
|
|
|
82
82
|
const result$ = observeOrganizationVerificationState(mockInstance, [
|
|
83
|
-
mockInstance.config.projectId!,
|
|
83
|
+
mockInstance.config.studio!.projectId!,
|
|
84
84
|
])
|
|
85
85
|
expectObservable(result$).toBe(expectedMarble, expectedValues)
|
|
86
86
|
})
|
|
87
87
|
expect(compareProjectOrganization).not.toHaveBeenCalled()
|
|
88
88
|
})
|
|
89
89
|
|
|
90
|
-
it('should emit {error: null} if instance
|
|
90
|
+
it('should emit {error: null} if instance has no default source projectId', () => {
|
|
91
91
|
const instanceWithoutProjectId = {
|
|
92
|
-
config: {
|
|
93
|
-
|
|
94
|
-
} as SanityInstance
|
|
92
|
+
config: {auth: {}},
|
|
93
|
+
} as unknown as SanityInstance
|
|
95
94
|
|
|
96
95
|
testScheduler.run(({hot, expectObservable}) => {
|
|
97
96
|
// Dashboard org ID doesn't matter much here, but provide one
|
|
@@ -139,7 +138,7 @@ describe('observeOrganizationVerificationState', () => {
|
|
|
139
138
|
const expectedValues = {r: {error: null}} // Expect null error
|
|
140
139
|
|
|
141
140
|
const result$ = observeOrganizationVerificationState(mockInstance, [
|
|
142
|
-
mockInstance.config.projectId!,
|
|
141
|
+
mockInstance.config.studio!.projectId!,
|
|
143
142
|
])
|
|
144
143
|
expectObservable(result$).toBe(expectedMarble, expectedValues)
|
|
145
144
|
})
|
|
@@ -163,7 +162,7 @@ describe('observeOrganizationVerificationState', () => {
|
|
|
163
162
|
const expectedValues = {r: comparisonResult}
|
|
164
163
|
|
|
165
164
|
const result$ = observeOrganizationVerificationState(mockInstance, [
|
|
166
|
-
mockInstance.config.projectId!,
|
|
165
|
+
mockInstance.config.studio!.projectId!,
|
|
167
166
|
])
|
|
168
167
|
expectObservable(result$).toBe(expectedMarble, expectedValues)
|
|
169
168
|
})
|
|
@@ -187,7 +186,7 @@ describe('observeOrganizationVerificationState', () => {
|
|
|
187
186
|
const expectedValues = {r: comparisonResult}
|
|
188
187
|
|
|
189
188
|
const result$ = observeOrganizationVerificationState(mockInstance, [
|
|
190
|
-
mockInstance.config.projectId!,
|
|
189
|
+
mockInstance.config.studio!.projectId!,
|
|
191
190
|
])
|
|
192
191
|
expectObservable(result$).toBe(expectedMarble, expectedValues)
|
|
193
192
|
})
|
|
@@ -44,8 +44,6 @@ describe('handleCallback', () => {
|
|
|
44
44
|
vi.mocked(getAuthCode).mockReturnValue(authCode)
|
|
45
45
|
|
|
46
46
|
instance = createSanityInstance({
|
|
47
|
-
projectId: 'p',
|
|
48
|
-
dataset: 'd',
|
|
49
47
|
auth: {
|
|
50
48
|
clientFactory,
|
|
51
49
|
storageArea: {setItem} as unknown as Storage,
|
|
@@ -89,8 +87,6 @@ describe('handleCallback', () => {
|
|
|
89
87
|
vi.mocked(getAuthCode).mockReturnValue(authCode)
|
|
90
88
|
|
|
91
89
|
instance = createSanityInstance({
|
|
92
|
-
projectId: 'p',
|
|
93
|
-
dataset: 'd',
|
|
94
90
|
auth: {
|
|
95
91
|
clientFactory,
|
|
96
92
|
storageArea: {setItem} as unknown as Storage,
|
|
@@ -119,8 +115,6 @@ describe('handleCallback', () => {
|
|
|
119
115
|
const setItem = vi.fn()
|
|
120
116
|
|
|
121
117
|
instance = createSanityInstance({
|
|
122
|
-
projectId: 'p',
|
|
123
|
-
dataset: 'd',
|
|
124
118
|
auth: {
|
|
125
119
|
clientFactory,
|
|
126
120
|
storageArea: {setItem: setItem as Storage['setItem']} as Storage,
|
|
@@ -161,8 +155,6 @@ describe('handleCallback', () => {
|
|
|
161
155
|
const setItem = vi.fn()
|
|
162
156
|
|
|
163
157
|
instance = createSanityInstance({
|
|
164
|
-
projectId: 'p',
|
|
165
|
-
dataset: 'd',
|
|
166
158
|
auth: {
|
|
167
159
|
clientFactory,
|
|
168
160
|
storageArea: {setItem} as unknown as Storage,
|
|
@@ -190,8 +182,6 @@ describe('handleCallback', () => {
|
|
|
190
182
|
vi.mocked(getAuthCode).mockReturnValue(authCode)
|
|
191
183
|
|
|
192
184
|
instance = createSanityInstance({
|
|
193
|
-
projectId: 'p',
|
|
194
|
-
dataset: 'd',
|
|
195
185
|
auth: {
|
|
196
186
|
clientFactory,
|
|
197
187
|
storageArea: {setItem} as unknown as Storage,
|
|
@@ -229,8 +219,6 @@ describe('handleCallback', () => {
|
|
|
229
219
|
vi.mocked(getTokenFromLocation).mockReturnValue('hash-token')
|
|
230
220
|
|
|
231
221
|
instance = createSanityInstance({
|
|
232
|
-
projectId: 'p',
|
|
233
|
-
dataset: 'd',
|
|
234
222
|
auth: {
|
|
235
223
|
clientFactory,
|
|
236
224
|
storageArea: {setItem} as unknown as Storage,
|
|
@@ -2,7 +2,13 @@ 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'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
createLoggedInAuthState,
|
|
7
|
+
getAuthCode,
|
|
8
|
+
getCleanedUrl,
|
|
9
|
+
getDefaultLocation,
|
|
10
|
+
getTokenFromLocation,
|
|
11
|
+
} from './utils'
|
|
6
12
|
|
|
7
13
|
/**
|
|
8
14
|
* @public
|
|
@@ -27,7 +33,7 @@ export const handleAuthCallback = bindActionGlobally(
|
|
|
27
33
|
const tokenFromUrl = getTokenFromLocation(locationHref)
|
|
28
34
|
if (tokenFromUrl) {
|
|
29
35
|
state.set('setTokenFromUrl', {
|
|
30
|
-
authState:
|
|
36
|
+
authState: createLoggedInAuthState(tokenFromUrl, null),
|
|
31
37
|
})
|
|
32
38
|
return cleanedUrl
|
|
33
39
|
}
|
|
@@ -77,7 +83,7 @@ export const handleAuthCallback = bindActionGlobally(
|
|
|
77
83
|
})
|
|
78
84
|
|
|
79
85
|
storageArea?.setItem(storageKey, JSON.stringify({token}))
|
|
80
|
-
state.set('setToken', {authState:
|
|
86
|
+
state.set('setToken', {authState: createLoggedInAuthState(token, null)})
|
|
81
87
|
|
|
82
88
|
return cleanedUrl
|
|
83
89
|
} catch (error) {
|
package/src/auth/logout.test.ts
CHANGED
|
@@ -36,8 +36,6 @@ describe('logout', () => {
|
|
|
36
36
|
const removeItem = vi.fn() as Storage['removeItem']
|
|
37
37
|
|
|
38
38
|
instance = createSanityInstance({
|
|
39
|
-
projectId: 'p',
|
|
40
|
-
dataset: 'd',
|
|
41
39
|
auth: {
|
|
42
40
|
clientFactory,
|
|
43
41
|
storageArea: {removeItem} as Storage,
|
|
@@ -67,8 +65,6 @@ describe('logout', () => {
|
|
|
67
65
|
const clientFactory = vi.fn()
|
|
68
66
|
const removeItem = vi.fn() as Storage['removeItem']
|
|
69
67
|
instance = createSanityInstance({
|
|
70
|
-
projectId: 'p',
|
|
71
|
-
dataset: 'd',
|
|
72
68
|
auth: {
|
|
73
69
|
token: 'provided-token',
|
|
74
70
|
clientFactory,
|
|
@@ -93,8 +89,6 @@ describe('logout', () => {
|
|
|
93
89
|
vi.mocked(getTokenFromStorage).mockReturnValue('token')
|
|
94
90
|
|
|
95
91
|
instance = createSanityInstance({
|
|
96
|
-
projectId: 'p',
|
|
97
|
-
dataset: 'd',
|
|
98
92
|
auth: {
|
|
99
93
|
clientFactory,
|
|
100
94
|
storageArea: {removeItem} as Storage,
|