Skip to content

Commit 9a3e862

Browse files
committed
feat: support using the same instance for different datasets
This introduces the concept of a "key" for a store. The main goal here is to enable the support for us using a single SanityInstance across all the stores, but still have separate stores per projectId/dataset. This is accomplished by saying that each store is determined by a "key" which can be dynamically built from the options. We first invoke the key logic and then dispatches to the right store. For now have projectId/dataset keys, but in the future we expect this to be per-resource. This also means that it's possible to use a single SanityInstance for working with multiple projectId/dataset. If you pass in an explicit projectId/dataset then it will no longer care about the configuration on the SanityInstance.
1 parent 8b5e9b8 commit 9a3e862

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+313
-206
lines changed

packages/core/src/auth/authStore.test.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ describe('authStore', () => {
9292
},
9393
})
9494

95-
const {options, dashboardContext} = authStore.getInitialState(instance)
95+
const {options, dashboardContext} = authStore.getInitialState(instance, null)
9696

9797
expect(options.apiHost).toBe(apiHost)
9898
expect(options.callbackUrl).toBe(callbackUrl)
@@ -114,7 +114,7 @@ describe('authStore', () => {
114114
auth: {initialLocationHref},
115115
})
116116

117-
const {dashboardContext, authState} = authStore.getInitialState(instance)
117+
const {dashboardContext, authState} = authStore.getInitialState(instance, null)
118118
expect(dashboardContext).toEqual(context)
119119
expect(authState.type).toBe(AuthStateType.LOGGED_OUT)
120120
})
@@ -129,7 +129,7 @@ describe('authStore', () => {
129129
auth: {initialLocationHref},
130130
})
131131

132-
const {dashboardContext, authState} = authStore.getInitialState(instance)
132+
const {dashboardContext, authState} = authStore.getInitialState(instance, null)
133133
expect(dashboardContext).toEqual(expectedContext)
134134
expect(authState.type).toBe(AuthStateType.LOGGED_OUT)
135135
})
@@ -143,7 +143,7 @@ describe('authStore', () => {
143143
auth: {initialLocationHref},
144144
})
145145

146-
const {dashboardContext, authState} = authStore.getInitialState(instance)
146+
const {dashboardContext, authState} = authStore.getInitialState(instance, null)
147147
expect(dashboardContext).toStrictEqual({})
148148
expect(authState.type).toBe(AuthStateType.LOGGED_OUT)
149149
expect(errorSpy).toHaveBeenCalledWith(
@@ -166,7 +166,7 @@ describe('authStore', () => {
166166
},
167167
})
168168

169-
const {authState, dashboardContext} = authStore.getInitialState(instance)
169+
const {authState, dashboardContext} = authStore.getInitialState(instance, null)
170170
expect(authState).toMatchObject({type: AuthStateType.LOGGED_IN, token})
171171
expect(dashboardContext).toEqual(context)
172172
})
@@ -182,7 +182,7 @@ describe('authStore', () => {
182182

183183
vi.mocked(getAuthCode).mockReturnValue('auth-code')
184184

185-
const {authState, dashboardContext} = authStore.getInitialState(instance)
185+
const {authState, dashboardContext} = authStore.getInitialState(instance, null)
186186
expect(authState).toMatchObject({type: AuthStateType.LOGGING_IN})
187187
expect(dashboardContext).toEqual(context)
188188
})
@@ -197,7 +197,7 @@ describe('authStore', () => {
197197
vi.mocked(getAuthCode).mockReturnValue(null)
198198
vi.mocked(getTokenFromStorage).mockReturnValue(storageToken)
199199

200-
const {authState, dashboardContext} = authStore.getInitialState(instance)
200+
const {authState, dashboardContext} = authStore.getInitialState(instance, null)
201201
expect(authState).toMatchObject({type: AuthStateType.LOGGED_IN, token: storageToken})
202202
expect(dashboardContext).toStrictEqual({})
203203
})
@@ -215,7 +215,7 @@ describe('authStore', () => {
215215
vi.mocked(getAuthCode).mockReturnValue(null)
216216
vi.mocked(getTokenFromStorage).mockReturnValue(storageToken)
217217

218-
const {authState, dashboardContext} = authStore.getInitialState(instance)
218+
const {authState, dashboardContext} = authStore.getInitialState(instance, null)
219219
expect(authState).toMatchObject({type: AuthStateType.LOGGED_OUT})
220220
expect(dashboardContext).toEqual(context)
221221
})
@@ -229,7 +229,7 @@ describe('authStore', () => {
229229
vi.mocked(getAuthCode).mockReturnValue(null)
230230
vi.mocked(getTokenFromStorage).mockReturnValue(null)
231231

232-
const {authState, dashboardContext} = authStore.getInitialState(instance)
232+
const {authState, dashboardContext} = authStore.getInitialState(instance, null)
233233
expect(authState).toMatchObject({type: AuthStateType.LOGGED_OUT})
234234
expect(dashboardContext).toStrictEqual({})
235235
})
@@ -251,7 +251,7 @@ describe('authStore', () => {
251251
auth: {storageArea: mockStorage}, // Provide mock storage
252252
})
253253

254-
const {authState, options} = authStore.getInitialState(instance)
254+
const {authState, options} = authStore.getInitialState(instance, null)
255255
expect(getStudioTokenFromLocalStorage).toHaveBeenCalledWith(mockStorage, projectId)
256256
expect(authState).toMatchObject({type: AuthStateType.LOGGED_IN, token: studioToken})
257257
expect(options.authMethod).toBe('localstorage')
@@ -277,7 +277,7 @@ describe('authStore', () => {
277277
})
278278

279279
// Initial state might be logged out before the async check completes
280-
const {authState: initialAuthState} = authStore.getInitialState(instance)
280+
const {authState: initialAuthState} = authStore.getInitialState(instance, null)
281281
expect(initialAuthState.type).toBe(AuthStateType.LOGGED_OUT) // Or potentially logging in depending on other factors
282282
expect(getStudioTokenFromLocalStorage).toHaveBeenCalledWith(mockStorage, projectId)
283283
expect(checkForCookieAuth).toHaveBeenCalledWith(projectId, expect.any(Function))
@@ -297,7 +297,7 @@ describe('authStore', () => {
297297
dataset: 'd',
298298
})
299299

300-
const {authState, options} = authStore.getInitialState(instance)
300+
const {authState, options} = authStore.getInitialState(instance, null)
301301
expect(getStudioTokenFromLocalStorage).not.toHaveBeenCalled()
302302
expect(checkForCookieAuth).not.toHaveBeenCalled()
303303
expect(getTokenFromStorage).toHaveBeenCalled()
@@ -315,7 +315,7 @@ describe('authStore', () => {
315315
vi.mocked(getAuthCode).mockReturnValue(null)
316316
vi.mocked(getTokenFromLocation).mockReturnValue('hash-token')
317317

318-
const {authState} = authStore.getInitialState(instance)
318+
const {authState} = authStore.getInitialState(instance, null)
319319
expect(authState).toMatchObject({
320320
type: AuthStateType.LOGGING_IN,
321321
isExchangingToken: false,

packages/core/src/auth/refreshStampedToken.test.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ describe('refreshStampedToken', () => {
122122
dataset: 'd',
123123
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
124124
})
125-
const initialState = authStore.getInitialState(instance)
125+
const initialState = authStore.getInitialState(instance, null)
126126
initialState.authState = {
127127
type: AuthStateType.LOGGED_IN,
128128
token: 'sk-initial-token-st123',
@@ -131,7 +131,7 @@ describe('refreshStampedToken', () => {
131131
initialState.dashboardContext = {mode: 'test'}
132132
const state = createStoreState(initialState)
133133

134-
const subscription = refreshStampedToken({state, instance})
134+
const subscription = refreshStampedToken({state, instance, key: null})
135135
subscriptions.push(subscription)
136136

137137
await vi.advanceTimersToNextTimerAsync()
@@ -168,7 +168,7 @@ describe('refreshStampedToken', () => {
168168
dataset: 'd',
169169
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
170170
})
171-
const initialState = authStore.getInitialState(instance)
171+
const initialState = authStore.getInitialState(instance, null)
172172
initialState.authState = {
173173
type: AuthStateType.LOGGED_IN,
174174
token: 'sk-initial-token-st123',
@@ -177,7 +177,7 @@ describe('refreshStampedToken', () => {
177177
initialState.dashboardContext = {mode: 'test'}
178178
const state = createStoreState(initialState)
179179

180-
const subscription = refreshStampedToken({state, instance})
180+
const subscription = refreshStampedToken({state, instance, key: null})
181181
subscriptions.push(subscription)
182182

183183
await vi.advanceTimersToNextTimerAsync()
@@ -202,7 +202,7 @@ describe('refreshStampedToken', () => {
202202
dataset: 'd',
203203
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
204204
})
205-
const initialState = authStore.getInitialState(instance)
205+
const initialState = authStore.getInitialState(instance, null)
206206
initialState.authState = {
207207
type: AuthStateType.LOGGED_IN,
208208
token: 'sk-initial-token-st123',
@@ -213,7 +213,7 @@ describe('refreshStampedToken', () => {
213213
let subscription: Subscription | undefined
214214
// We expect this NOT to throw, but accept we can't easily test the lock call or outcome
215215
expect(() => {
216-
subscription = refreshStampedToken({state, instance})
216+
subscription = refreshStampedToken({state, instance, key: null})
217217
subscriptions.push(subscription!)
218218
}).not.toThrow()
219219

@@ -253,15 +253,15 @@ describe('refreshStampedToken', () => {
253253
dataset: 'd',
254254
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
255255
})
256-
const initialState = authStore.getInitialState(instance)
256+
const initialState = authStore.getInitialState(instance, null)
257257
initialState.authState = {
258258
type: AuthStateType.LOGGED_IN,
259259
token: 'sk-initial-token-st123',
260260
currentUser: null,
261261
}
262262
const state = createStoreState(initialState)
263263

264-
const subscription = refreshStampedToken({state, instance})
264+
const subscription = refreshStampedToken({state, instance, key: null})
265265
subscriptions.push(subscription)
266266

267267
// DO NOT advance timers or yield here - focus on immediate observable logic
@@ -303,15 +303,15 @@ describe('refreshStampedToken', () => {
303303
dataset: 'd',
304304
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
305305
})
306-
const initialState = authStore.getInitialState(instance)
306+
const initialState = authStore.getInitialState(instance, null)
307307
initialState.authState = {
308308
type: AuthStateType.LOGGED_IN,
309309
token: 'sk-initial-token-st123',
310310
currentUser: null,
311311
}
312312
const state = createStoreState(initialState)
313313

314-
const subscription = refreshStampedToken({state, instance})
314+
const subscription = refreshStampedToken({state, instance, key: null})
315315
subscriptions.push(subscription)
316316

317317
// Advance timers to allow the async `performRefresh` to execute
@@ -349,7 +349,7 @@ describe('refreshStampedToken', () => {
349349
dataset: 'd',
350350
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
351351
})
352-
const initialState = authStore.getInitialState(instance)
352+
const initialState = authStore.getInitialState(instance, null)
353353
initialState.authState = {
354354
type: AuthStateType.LOGGED_IN,
355355
token: 'sk-initial-token-st123',
@@ -358,7 +358,7 @@ describe('refreshStampedToken', () => {
358358
initialState.dashboardContext = {mode: 'test'}
359359
const state = createStoreState(initialState)
360360

361-
const subscription = refreshStampedToken({state, instance})
361+
const subscription = refreshStampedToken({state, instance, key: null})
362362
subscriptions.push(subscription)
363363

364364
await vi.advanceTimersToNextTimerAsync()
@@ -378,14 +378,14 @@ describe('refreshStampedToken', () => {
378378
dataset: 'd',
379379
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
380380
})
381-
const initialState = authStore.getInitialState(instance)
381+
const initialState = authStore.getInitialState(instance, null)
382382
initialState.authState = {
383383
type: AuthStateType.LOGGED_OUT,
384384
isDestroyingSession: false,
385385
} as AuthState
386386
const state = createStoreState(initialState)
387387

388-
const subscription = refreshStampedToken({state, instance})
388+
const subscription = refreshStampedToken({state, instance, key: null})
389389
subscriptions.push(subscription)
390390

391391
await vi.advanceTimersByTimeAsync(0)
@@ -404,15 +404,15 @@ describe('refreshStampedToken', () => {
404404
dataset: 'd',
405405
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
406406
})
407-
const initialState = authStore.getInitialState(instance)
407+
const initialState = authStore.getInitialState(instance, null)
408408
initialState.authState = {
409409
type: AuthStateType.LOGGED_IN,
410410
token: 'sk-nonstamped-token',
411411
currentUser: null,
412412
}
413413
const state = createStoreState(initialState)
414414

415-
const subscription = refreshStampedToken({state, instance})
415+
const subscription = refreshStampedToken({state, instance, key: null})
416416
subscriptions.push(subscription)
417417

418418
await vi.advanceTimersByTimeAsync(0)

packages/core/src/auth/subscribeToStateAndFetchCurrentUser.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ describe('subscribeToStateAndFetchCurrentUser', () => {
2020
const clientFactory = vi.fn().mockReturnValue(mockClient)
2121
const instance = createSanityInstance({projectId: 'p', dataset: 'd', auth: {clientFactory}})
2222

23-
const state = createStoreState(authStore.getInitialState(instance))
24-
const subscription = subscribeToStateAndFetchCurrentUser({state, instance})
23+
const state = createStoreState(authStore.getInitialState(instance, null))
24+
const subscription = subscribeToStateAndFetchCurrentUser({state, instance, key: null})
2525

2626
expect(state.get()).toMatchObject({authState: {type: AuthStateType.LOGGED_OUT}})
2727

@@ -52,8 +52,8 @@ describe('subscribeToStateAndFetchCurrentUser', () => {
5252
const clientFactory = vi.fn().mockReturnValue(mockClient)
5353
const instance = createSanityInstance({projectId: 'p', dataset: 'd', auth: {clientFactory}})
5454

55-
const state = createStoreState(authStore.getInitialState(instance))
56-
const subscription = subscribeToStateAndFetchCurrentUser({state, instance})
55+
const state = createStoreState(authStore.getInitialState(instance, null))
56+
const subscription = subscribeToStateAndFetchCurrentUser({state, instance, key: null})
5757

5858
expect(state.get()).toMatchObject({authState: {type: AuthStateType.LOGGED_OUT}})
5959

@@ -88,8 +88,8 @@ describe('subscribeToStateAndFetchCurrentUser', () => {
8888
const clientFactory = vi.fn().mockReturnValue(mockClient)
8989
const instance = createSanityInstance({projectId: 'p', dataset: 'd', auth: {clientFactory}})
9090

91-
const state = createStoreState(authStore.getInitialState(instance))
92-
const subscription = subscribeToStateAndFetchCurrentUser({state, instance})
91+
const state = createStoreState(authStore.getInitialState(instance, null))
92+
const subscription = subscribeToStateAndFetchCurrentUser({state, instance, key: null})
9393

9494
expect(state.get()).toMatchObject({authState: {type: AuthStateType.LOGGED_OUT}})
9595

packages/core/src/auth/subscribeToStorageEventsAndSetToken.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ describe('subscribeToStorageEventsAndSetToken', () => {
3434
})
3535

3636
it('sets the state to logged in when a matching storage event returns a token', () => {
37-
const state = createStoreState(authStore.getInitialState(instance))
37+
const state = createStoreState(authStore.getInitialState(instance, null))
3838
const {storageKey} = state.get().options
39-
const subscription = subscribeToStorageEventsAndSetToken({state, instance})
39+
const subscription = subscribeToStorageEventsAndSetToken({state, instance, key: null})
4040

4141
expect(state.get()).toMatchObject({
4242
authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},
@@ -54,10 +54,10 @@ describe('subscribeToStorageEventsAndSetToken', () => {
5454

5555
it('sets the state to logged in when a matching storage event returns null', () => {
5656
vi.mocked(getTokenFromStorage).mockReturnValue('existing-token')
57-
const state = createStoreState(authStore.getInitialState(instance))
57+
const state = createStoreState(authStore.getInitialState(instance, null))
5858
const {storageKey} = state.get().options
5959

60-
const subscription = subscribeToStorageEventsAndSetToken({state, instance})
60+
const subscription = subscribeToStorageEventsAndSetToken({state, instance, key: null})
6161

6262
expect(state.get()).toMatchObject({
6363
authState: {type: AuthStateType.LOGGED_IN, token: 'existing-token', currentUser: null},

packages/core/src/comlink/controller/actions/destroyController.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe('destroyController', () => {
3939
})
4040

4141
// Execute action
42-
destroyController({state, instance})
42+
destroyController({state, instance, key: null})
4343

4444
// Verify controller was destroyed and state was cleared
4545
expect(mockController.destroy).toHaveBeenCalled()
@@ -49,7 +49,7 @@ describe('destroyController', () => {
4949

5050
it('should do nothing if no controller exists', () => {
5151
// State already has null controller, so just execute action
52-
expect(() => destroyController({state, instance})).not.toThrow()
52+
expect(() => destroyController({state, instance, key: null})).not.toThrow()
5353

5454
// State should remain unchanged
5555
expect(state.get().controller).toBeNull()

packages/core/src/comlink/controller/actions/getOrCreateChannel.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe('getOrCreateChannel', () => {
4949
it('should create a new channel using the controller', () => {
5050
const createChannelSpy = vi.spyOn(mockController, 'createChannel')
5151

52-
const channel = getOrCreateChannel({state, instance}, channelConfig)
52+
const channel = getOrCreateChannel({state, instance, key: null}, channelConfig)
5353

5454
expect(createChannelSpy).toHaveBeenCalledWith(channelConfig)
5555
expect(channel.on).toBeDefined()
@@ -70,17 +70,17 @@ describe('getOrCreateChannel', () => {
7070
channels: new Map(),
7171
})
7272

73-
expect(() => getOrCreateChannel({state, instance}, channelConfig)).toThrow(
73+
expect(() => getOrCreateChannel({state, instance, key: null}, channelConfig)).toThrow(
7474
'Controller must be initialized before using or creating channels',
7575
)
7676
})
7777

7878
it('should retrieve channel directly from store once created', () => {
79-
const createdChannel = getOrCreateChannel({state, instance}, channelConfig)
79+
const createdChannel = getOrCreateChannel({state, instance, key: null}, channelConfig)
8080
vi.clearAllMocks() // Clear call counts
8181

8282
// Retrieve channel again
83-
const retrievedChannel = getOrCreateChannel({state, instance}, channelConfig)
83+
const retrievedChannel = getOrCreateChannel({state, instance, key: null}, channelConfig)
8484
expect(retrievedChannel).toBeDefined()
8585
expect(retrievedChannel).toBe(createdChannel)
8686

@@ -95,10 +95,10 @@ describe('getOrCreateChannel', () => {
9595
})
9696

9797
it('should throw error when trying to create channel with different options', () => {
98-
getOrCreateChannel({state, instance}, channelConfig)
98+
getOrCreateChannel({state, instance, key: null}, channelConfig)
9999

100100
expect(() =>
101-
getOrCreateChannel({state, instance}, {...channelConfig, connectTo: 'window'}),
101+
getOrCreateChannel({state, instance, key: null}, {...channelConfig, connectTo: 'window'}),
102102
).toThrow('Channel "test" already exists with different options')
103103
})
104104
})

0 commit comments

Comments
 (0)