@sanity/sdk 2.8.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.
Files changed (99) hide show
  1. package/dist/index.d.ts +228 -239
  2. package/dist/index.js +287 -454
  3. package/dist/index.js.map +1 -1
  4. package/package.json +4 -4
  5. package/src/_exports/index.ts +16 -17
  6. package/src/agent/agentActions.test.ts +60 -16
  7. package/src/agent/agentActions.ts +29 -20
  8. package/src/auth/authMode.test.ts +0 -25
  9. package/src/auth/authMode.ts +3 -6
  10. package/src/auth/authStore.test.ts +129 -66
  11. package/src/auth/authStore.ts +9 -11
  12. package/src/auth/dashboardAuth.ts +2 -2
  13. package/src/auth/getOrganizationVerificationState.test.ts +10 -11
  14. package/src/auth/handleAuthCallback.test.ts +0 -12
  15. package/src/auth/handleAuthCallback.ts +9 -3
  16. package/src/auth/logout.test.ts +0 -6
  17. package/src/auth/refreshStampedToken.test.ts +121 -17
  18. package/src/auth/standaloneAuth.ts +9 -3
  19. package/src/auth/studioAuth.ts +35 -8
  20. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +9 -3
  21. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +1 -1
  22. package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +0 -2
  23. package/src/auth/subscribeToStorageEventsAndSetToken.ts +2 -2
  24. package/src/auth/utils.ts +33 -0
  25. package/src/client/clientStore.test.ts +14 -61
  26. package/src/client/clientStore.ts +52 -28
  27. package/src/comlink/controller/actions/destroyController.test.ts +1 -4
  28. package/src/comlink/controller/actions/getOrCreateChannel.test.ts +1 -4
  29. package/src/comlink/controller/actions/getOrCreateController.test.ts +1 -4
  30. package/src/comlink/controller/actions/releaseChannel.test.ts +1 -1
  31. package/src/comlink/controller/comlinkControllerStore.test.ts +1 -4
  32. package/src/comlink/node/actions/getOrCreateNode.test.ts +1 -4
  33. package/src/comlink/node/actions/releaseNode.test.ts +1 -4
  34. package/src/comlink/node/comlinkNodeStore.test.ts +2 -2
  35. package/src/comlink/node/getNodeState.test.ts +1 -1
  36. package/src/config/__tests__/handles.test.ts +12 -18
  37. package/src/config/handles.ts +7 -25
  38. package/src/config/sanityConfig.ts +99 -52
  39. package/src/datasets/datasets.test.ts +2 -2
  40. package/src/datasets/datasets.ts +4 -10
  41. package/src/document/actions.test.ts +33 -4
  42. package/src/document/actions.ts +3 -10
  43. package/src/document/applyDocumentActions.test.ts +17 -18
  44. package/src/document/applyDocumentActions.ts +9 -12
  45. package/src/document/documentStore.test.ts +303 -133
  46. package/src/document/documentStore.ts +70 -61
  47. package/src/document/permissions.test.ts +44 -8
  48. package/src/document/processActions.test.ts +77 -7
  49. package/src/document/reducers.test.ts +35 -3
  50. package/src/document/sharedListener.test.ts +13 -13
  51. package/src/document/sharedListener.ts +8 -3
  52. package/src/favorites/favorites.test.ts +10 -2
  53. package/src/presence/presenceStore.test.ts +34 -9
  54. package/src/presence/presenceStore.ts +29 -13
  55. package/src/preview/previewProjectionUtils.test.ts +192 -0
  56. package/src/preview/previewProjectionUtils.ts +88 -0
  57. package/src/preview/{previewStore.ts → types.ts} +6 -25
  58. package/src/project/project.test.ts +1 -1
  59. package/src/project/project.ts +14 -20
  60. package/src/projection/getProjectionState.test.ts +4 -2
  61. package/src/projection/getProjectionState.ts +2 -21
  62. package/src/projection/projectionQuery.ts +2 -3
  63. package/src/projection/projectionStore.test.ts +3 -3
  64. package/src/projection/resolveProjection.test.ts +2 -1
  65. package/src/projection/resolveProjection.ts +2 -18
  66. package/src/projection/subscribeToStateAndFetchBatches.test.ts +2 -2
  67. package/src/projection/subscribeToStateAndFetchBatches.ts +23 -36
  68. package/src/projection/types.ts +1 -9
  69. package/src/projects/projects.test.ts +1 -1
  70. package/src/query/queryStore.test.ts +86 -28
  71. package/src/query/queryStore.ts +23 -38
  72. package/src/releases/getPerspectiveState.test.ts +14 -13
  73. package/src/releases/getPerspectiveState.ts +6 -6
  74. package/src/releases/releasesStore.test.ts +21 -6
  75. package/src/releases/releasesStore.ts +18 -8
  76. package/src/store/createActionBinder.test.ts +114 -111
  77. package/src/store/createActionBinder.ts +52 -101
  78. package/src/store/createSanityInstance.test.ts +13 -83
  79. package/src/store/createSanityInstance.ts +2 -78
  80. package/src/store/createStateSourceAction.test.ts +2 -2
  81. package/src/store/createStateSourceAction.ts +5 -5
  82. package/src/store/createStoreInstance.test.ts +2 -4
  83. package/src/users/reducers.test.ts +1 -6
  84. package/src/users/reducers.ts +2 -2
  85. package/src/users/types.ts +4 -4
  86. package/src/users/usersStore.test.ts +12 -15
  87. package/src/utils/createFetcherStore.test.ts +1 -1
  88. package/src/utils/logger.test.ts +0 -12
  89. package/src/utils/logger.ts +3 -8
  90. package/src/preview/getPreviewState.test.ts +0 -120
  91. package/src/preview/getPreviewState.ts +0 -91
  92. package/src/preview/previewQuery.test.ts +0 -236
  93. package/src/preview/previewQuery.ts +0 -153
  94. package/src/preview/previewStore.test.ts +0 -36
  95. package/src/preview/resolvePreview.test.ts +0 -47
  96. package/src/preview/resolvePreview.ts +0 -20
  97. package/src/preview/subscribeToStateAndFetchBatches.test.ts +0 -221
  98. package/src/preview/subscribeToStateAndFetchBatches.ts +0 -112
  99. 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 {getAuthCode, getTokenFromLocation, getTokenFromStorage} from './utils'
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
- dataset: 'd',
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 (without studioMode flag)', () => {
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 like studioMode
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({projectId: 'p', dataset: 'd'})
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({projectId: 'p', dataset: 'd'})
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({projectId: 'p', dataset: 'd'})
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({projectId: 'p', dataset: 'd'})
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
  })
@@ -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
- // This state update structure should trigger listeners in clientStore
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: {type: AuthStateType.LOGGED_IN, token: providedToken, currentUser: null},
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 (only config.projectId is used)
35
+ // Mock instance with studio.projectId
36
36
  const mockInstance = {
37
- config: {projectId: 'proj-1', dataset: 'd'},
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.config.projectId is missing', () => {
90
+ it('should emit {error: null} if instance has no default source projectId', () => {
91
91
  const instanceWithoutProjectId = {
92
- config: {projectId: undefined, dataset: 'd'},
93
- // Add other required properties if SanityInstance type needs them
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 {getAuthCode, getCleanedUrl, getDefaultLocation, getTokenFromLocation} from './utils'
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: {type: AuthStateType.LOGGED_IN, token: tokenFromUrl, currentUser: null},
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: {type: AuthStateType.LOGGED_IN, token, currentUser: null}})
86
+ state.set('setToken', {authState: createLoggedInAuthState(token, null)})
81
87
 
82
88
  return cleanedUrl
83
89
  } catch (error) {
@@ -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,