@sanity/sdk 0.0.0-alpha.21 → 0.0.0-alpha.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/index.d.ts +428 -325
  2. package/dist/index.js +1618 -1553
  3. package/dist/index.js.map +1 -1
  4. package/package.json +6 -7
  5. package/src/_exports/index.ts +31 -30
  6. package/src/auth/authStore.test.ts +149 -104
  7. package/src/auth/authStore.ts +51 -100
  8. package/src/auth/handleAuthCallback.test.ts +67 -34
  9. package/src/auth/handleAuthCallback.ts +8 -7
  10. package/src/auth/logout.test.ts +61 -29
  11. package/src/auth/logout.ts +26 -28
  12. package/src/auth/refreshStampedToken.test.ts +9 -9
  13. package/src/auth/refreshStampedToken.ts +62 -56
  14. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +5 -5
  15. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +45 -47
  16. package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +4 -5
  17. package/src/auth/subscribeToStorageEventsAndSetToken.ts +22 -24
  18. package/src/client/clientStore.test.ts +131 -67
  19. package/src/client/clientStore.ts +117 -116
  20. package/src/comlink/controller/actions/destroyController.test.ts +38 -13
  21. package/src/comlink/controller/actions/destroyController.ts +11 -15
  22. package/src/comlink/controller/actions/getOrCreateChannel.test.ts +56 -27
  23. package/src/comlink/controller/actions/getOrCreateChannel.ts +37 -35
  24. package/src/comlink/controller/actions/getOrCreateController.test.ts +27 -16
  25. package/src/comlink/controller/actions/getOrCreateController.ts +23 -22
  26. package/src/comlink/controller/actions/releaseChannel.test.ts +37 -13
  27. package/src/comlink/controller/actions/releaseChannel.ts +22 -21
  28. package/src/comlink/controller/comlinkControllerStore.test.ts +65 -36
  29. package/src/comlink/controller/comlinkControllerStore.ts +44 -5
  30. package/src/comlink/node/actions/getOrCreateNode.test.ts +31 -15
  31. package/src/comlink/node/actions/getOrCreateNode.ts +30 -29
  32. package/src/comlink/node/actions/releaseNode.test.ts +75 -55
  33. package/src/comlink/node/actions/releaseNode.ts +19 -21
  34. package/src/comlink/node/comlinkNodeStore.test.ts +6 -11
  35. package/src/comlink/node/comlinkNodeStore.ts +22 -5
  36. package/src/config/authConfig.ts +79 -0
  37. package/src/config/sanityConfig.ts +48 -0
  38. package/src/datasets/datasets.test.ts +2 -2
  39. package/src/datasets/datasets.ts +18 -5
  40. package/src/document/actions.test.ts +22 -10
  41. package/src/document/actions.ts +44 -56
  42. package/src/document/applyDocumentActions.test.ts +96 -36
  43. package/src/document/applyDocumentActions.ts +140 -99
  44. package/src/document/documentStore.test.ts +103 -155
  45. package/src/document/documentStore.ts +247 -237
  46. package/src/document/listen.ts +56 -55
  47. package/src/document/patchOperations.ts +0 -43
  48. package/src/document/permissions.test.ts +25 -12
  49. package/src/document/permissions.ts +11 -4
  50. package/src/document/processActions.test.ts +41 -8
  51. package/src/document/reducers.test.ts +87 -16
  52. package/src/document/reducers.ts +2 -2
  53. package/src/document/sharedListener.test.ts +34 -16
  54. package/src/document/sharedListener.ts +33 -11
  55. package/src/preview/getPreviewState.test.ts +40 -39
  56. package/src/preview/getPreviewState.ts +68 -56
  57. package/src/preview/previewConstants.ts +43 -0
  58. package/src/preview/previewQuery.test.ts +1 -1
  59. package/src/preview/previewQuery.ts +4 -5
  60. package/src/preview/previewStore.test.ts +13 -58
  61. package/src/preview/previewStore.ts +7 -21
  62. package/src/preview/resolvePreview.test.ts +33 -104
  63. package/src/preview/resolvePreview.ts +11 -21
  64. package/src/preview/subscribeToStateAndFetchBatches.test.ts +96 -97
  65. package/src/preview/subscribeToStateAndFetchBatches.ts +85 -81
  66. package/src/preview/util.ts +1 -0
  67. package/src/project/project.test.ts +3 -3
  68. package/src/project/project.ts +28 -5
  69. package/src/projection/getProjectionState.test.ts +69 -49
  70. package/src/projection/getProjectionState.ts +42 -50
  71. package/src/projection/projectionQuery.ts +1 -1
  72. package/src/projection/projectionStore.test.ts +13 -51
  73. package/src/projection/projectionStore.ts +6 -18
  74. package/src/projection/resolveProjection.test.ts +32 -127
  75. package/src/projection/resolveProjection.ts +15 -28
  76. package/src/projection/subscribeToStateAndFetchBatches.test.ts +105 -90
  77. package/src/projection/subscribeToStateAndFetchBatches.ts +94 -81
  78. package/src/projection/util.ts +2 -0
  79. package/src/projects/projects.test.ts +13 -4
  80. package/src/projects/projects.ts +6 -1
  81. package/src/query/queryStore.test.ts +10 -47
  82. package/src/query/queryStore.ts +151 -133
  83. package/src/query/queryStoreConstants.ts +2 -0
  84. package/src/store/createActionBinder.test.ts +153 -0
  85. package/src/store/createActionBinder.ts +176 -0
  86. package/src/store/createSanityInstance.test.ts +84 -0
  87. package/src/store/createSanityInstance.ts +124 -0
  88. package/src/store/createStateSourceAction.test.ts +196 -0
  89. package/src/store/createStateSourceAction.ts +260 -0
  90. package/src/store/createStoreInstance.test.ts +81 -0
  91. package/src/store/createStoreInstance.ts +80 -0
  92. package/src/store/createStoreState.test.ts +85 -0
  93. package/src/store/createStoreState.ts +92 -0
  94. package/src/store/defineStore.test.ts +18 -0
  95. package/src/store/defineStore.ts +81 -0
  96. package/src/users/reducers.test.ts +318 -0
  97. package/src/users/reducers.ts +88 -0
  98. package/src/users/types.ts +46 -4
  99. package/src/users/usersConstants.ts +4 -0
  100. package/src/users/usersStore.test.ts +350 -223
  101. package/src/users/usersStore.ts +285 -149
  102. package/src/utils/createFetcherStore.test.ts +6 -7
  103. package/src/utils/createFetcherStore.ts +150 -153
  104. package/src/{common/util.test.ts → utils/hashString.test.ts} +1 -1
  105. package/src/auth/fetchLoginUrls.test.ts +0 -163
  106. package/src/auth/fetchLoginUrls.ts +0 -74
  107. package/src/common/createLiveEventSubscriber.test.ts +0 -121
  108. package/src/common/createLiveEventSubscriber.ts +0 -55
  109. package/src/common/types.ts +0 -4
  110. package/src/instance/identity.test.ts +0 -46
  111. package/src/instance/identity.ts +0 -29
  112. package/src/instance/sanityInstance.test.ts +0 -77
  113. package/src/instance/sanityInstance.ts +0 -57
  114. package/src/instance/types.ts +0 -37
  115. package/src/preview/getPreviewProjection.ts +0 -45
  116. package/src/resources/README.md +0 -370
  117. package/src/resources/createAction.test.ts +0 -101
  118. package/src/resources/createAction.ts +0 -44
  119. package/src/resources/createResource.test.ts +0 -112
  120. package/src/resources/createResource.ts +0 -102
  121. package/src/resources/createStateSourceAction.test.ts +0 -114
  122. package/src/resources/createStateSourceAction.ts +0 -83
  123. package/src/resources/createStore.test.ts +0 -67
  124. package/src/resources/createStore.ts +0 -46
  125. package/src/store/createStore.test.ts +0 -108
  126. package/src/store/createStore.ts +0 -106
  127. /package/src/{common/util.ts → utils/hashString.ts} +0 -0
@@ -1,370 +0,0 @@
1
- # SDK Resource Management
2
-
3
- ## Resources
4
-
5
- In the SDK, we group together state and functions that concern that state into a unit called a **Resource**.
6
-
7
- To create a resource, call `createResource` with a `name` for the resource (this will be used as the key in the resource cache), a `getInitialState` function which takes in a `SanityInstance` and return the initial state, and an optional initialize function that can set up any subscriptions.
8
-
9
- ```ts
10
- // documentList.ts
11
- import {SyncTag} from '@sanity/client'
12
- import {createResource} from '../resources/createResource'
13
- import {subscribeToLiveContentAndSetLastLiveEventId} from './subscribeToLiveContentAndSetLastLiveEventId'
14
-
15
- // declare the state shape for this resource
16
- interface DocumentListState {
17
- results: DocumentHandle[]
18
- syncTags: SyncTag[]
19
- lastLiveEventId: string | null
20
- }
21
-
22
- export const documentList = createResource<DocumentListState>({
23
- name: 'documentList',
24
- getInitialState(instance) {
25
- // utilize the instance to set a different initial state
26
- // instance.config.someConfigOption
27
-
28
- return {results: [], syncTags: [], lastLiveEventId: null}
29
- },
30
- initialize() {
31
- // set up subscriptions
32
- const liveContentSubscription = subscribeToLiveContentAndSetLastLiveEventId(this)
33
-
34
- return () => {
35
- // teardown / cleanup
36
- // this function will be ran when `instance.dispose()` is called
37
- liveContentSubscription.unsubscribe()
38
- }
39
- },
40
- })
41
- ```
42
-
43
- ## Actions
44
-
45
- **Actions** are functions that are auto-magically bound to a resource by using `createAction`.
46
-
47
- ```ts
48
- // subscribeToLiveContentAndSetLastLiveEventId.ts
49
- import {createAction} from '../resources/createAction'
50
- import {getClientSource} from '../client/getClientSource'
51
- import {documentList} from './documentList'
52
-
53
- // use `createAction` to create a function that binds the resource's state
54
- export const subscribeToLiveContentAndSetLastLiveEventId = createAction(
55
- // provide the resource you'd like to bind this action to
56
- () => documentList,
57
- // provide the implementation of the function using the provided `state` or `instance`
58
- ({state, instance}) => {
59
- // return a function, the parameters here will be the parameters of the resulting action
60
- return function () {
61
- const client$ = getClientSource(instance).observable
62
- const liveMessage$ = client$.pipe(switchMap((client) => client.live.events()))
63
-
64
- return liveMessage$.subscribe((e) => {
65
- const {syncTags} = state.get()
66
- if (e.type === 'message' && e.tags.some((tag) => syncTags.includes(tag))) {
67
- state.set('setLastLiveEventId', {lastLiveEventId: e.id})
68
- }
69
- })
70
- }
71
- },
72
- )
73
- ```
74
-
75
- `createAction` returns a function that takes in either just an `instance` or an object with a instance and the initialized resource state `{instance, state}`.
76
-
77
- If the initialized state is not passed into the action, it will call `getOrCreateResource` to grab the existing resource state or to initialize the resource state if not already created. Take a look at the [implementation](./createAction.ts) to see this in action.
78
-
79
- ---
80
-
81
- When using a resource action from the same resource, the convention is to pass `this` which is bound to an object that contains the initialized state and the current instance.
82
-
83
- ```ts
84
- import {createAction} from '../resources/createAction'
85
- import {fooResource} from './fooResource'
86
-
87
- const privateFooAction = createAction(
88
- () => fooResource,
89
- ({state, instance}) => {
90
- return function (value: string) {
91
- // do something with state
92
- }
93
- },
94
- )
95
-
96
- export const publicFooAction = createAction(
97
- () => fooResource,
98
- ({state, instance}) => {
99
- return function () {
100
- const result = privateFooAction(this, 'some value')
101
- return result
102
- }
103
- },
104
- )
105
- ```
106
-
107
- When using a resource action from a different resource, the convention is to just pass in an instance and rely on `getOrCreateResource` to pull or create the state for the resource.
108
-
109
- ```ts
110
- import {createAction} from '../resources/createAction'
111
- import {barResource} from './barResource'
112
- import {publicFooAction} from '../foo/publicFooAction'
113
-
114
- export const barActionThatUsesFooAction = createAction(
115
- () => barResource,
116
- ({state, instance}) => {
117
- return function () {
118
- const result = publicFooAction(instance)
119
- return `from foo: ${result}`
120
- }
121
- },
122
- )
123
- ```
124
-
125
- `this` is also available to be used in the `initialize` function provided to `createResource`
126
-
127
- ```ts
128
- import {createResource} from '../resources/createResource'
129
- import {barActionThatUsesFooAction} from './barActionThatUsesFooAction'
130
-
131
- const barResource = createResource({
132
- name: 'bar',
133
- getInitialState: () => ({}),
134
- initialize() {
135
- const result = barActionThatUsesFooAction(this)
136
- // ...
137
-
138
- return () => {
139
- // ...
140
- }
141
- },
142
- })
143
- ```
144
-
145
- ## Stores
146
-
147
- By default, actions are global meaning that calling them with a sanity instance will result in global resource state available to all actions that use the same resource.
148
-
149
- There are certain cases like the document list where we do not want global state. This is where `createStore` comes in.
150
-
151
- ```ts
152
- interface TestState {
153
- value: number
154
- }
155
-
156
- const testResource = createResource<TestState>({
157
- name: 'test',
158
- getInitialState: () => ({value: 0}),
159
- initialize() {
160
- // set up subscriptions etc
161
- return () => {}
162
- },
163
- })
164
-
165
- const inc = createAction(
166
- () => testResource,
167
- ({state}) => {
168
- return function () {
169
- state.set('increment', (prevState) => ({value: prevState.value + 1}))
170
- }
171
- },
172
- )
173
-
174
- const set = createAction(
175
- () => testResource,
176
- ({state}) => {
177
- return function (value: number) {
178
- state.set('setValue', {value})
179
- }
180
- },
181
- )
182
-
183
- const get = createAction(
184
- () => testResource,
185
- ({state}) => {
186
- return function () {
187
- return state.get().value
188
- }
189
- },
190
- )
191
-
192
- export const createTestStore = createStore(testResource, {inc, set, get})
193
- ```
194
-
195
- Now `createTestStore` will return an object with the actions bound to it as methods.
196
-
197
- ```ts
198
- import {createTestStore} from './createTestStore'
199
-
200
- const testStore = createTestStore(instance)
201
-
202
- console.log(testStore.get()) // 0
203
- testStore.inc()
204
-
205
- console.log(testStore.get()) // 1
206
-
207
- // calls the clean up function returned in initialize
208
- testStore.dispose()
209
- ```
210
-
211
- ## Resource State
212
-
213
- All resource actions are provided with a `state` param of type `ResourceState` that contains a `get`, `set`, and `observable` properties.
214
-
215
- ```ts
216
- export type ResourceState<TState> = {
217
- get: () => TState
218
- set: (name: string, state: Partial<TState> | ((s: TState) => Partial<TState>)) => void
219
- observable: Observable<TState>
220
- }
221
- ```
222
-
223
- ```ts
224
- import {map, distinctUntilChanged} from 'rxjs'
225
- import {createAction} from '../resources/createAction'
226
- import {myResource} from './myResource'
227
-
228
- export const myAction = createAction(
229
- () => myResource,
230
- ({state, instance}) => {
231
- return function (value: string) {
232
- // call `state.get` to get the current resource state
233
- const currentState = state.get()
234
-
235
- // call `state.set` with an update label (for redux dev tools)
236
- // with either a new state value or a function that returns new state.
237
- // Note: state.set is powered by zustand which always shallowly
238
- // with the previous state in the store
239
- // https://zustand.docs.pmnd.rs/guides/updating-state
240
- state.set('stateUpdateLabel', (prev) => ({foo: value}))
241
-
242
- // you can also subscribe to internal state changes via `state.observable`
243
- const subscription = state.observable
244
- .pipe(
245
- map((s) => s.foo),
246
- distinctUntilChanged(),
247
- )
248
- .subscribe(console.log)
249
-
250
- return subscription
251
- }
252
- },
253
- )
254
- ```
255
-
256
- ## State Sources
257
-
258
- A `StateSource` represents a stream of changing values derived from the state of a resource.
259
-
260
- ```ts
261
- export interface StateSource<T> {
262
- // `getCurrent` allows the current state to be pulled without creating a subscription
263
- getCurrent: () => T
264
- // `subscribe` allows us to notify consumers when a new value is available.
265
- // note that this callback does not accept any parameters and the expectation
266
- // is for consumers to pull from `getCurrent`.
267
- subscribe: (onStoreChanged: () => void) => () => void
268
- // `observable` is provides combination of the above. on subscribe, this will
269
- // emit the current value from `getCurrent` and will update when the above
270
- // `subscribe` fires
271
- observable: Observable<T>
272
- }
273
- ```
274
-
275
- By convention, actions that return state sources should end with the `-State` suffix.
276
-
277
- ```ts
278
- // example usage in a hook
279
- import {getSchemaState} from '@sanity/sdk'
280
- import {useSanityInstance} from '@sanity/sdk-react'
281
-
282
- export function useSchema(): Schema {
283
- const instance = useSanityInstance()
284
- const {subscribe, getCurrent} = useMemo(() => getSchemaState(instance), [instance])
285
-
286
- return useSyncExternalStore(subscribe, getCurrent)
287
- }
288
- ```
289
-
290
- ```ts
291
- // example usage in an action
292
- import {getSchemaState} from '../schema/getSchemaState'
293
- import {createAction} from '../resources/createAction'
294
- import {myResource} from './myResource'
295
-
296
- export const myAction = createAction(
297
- () => myResource,
298
- ({state, instance}) => {
299
- return function () {
300
- const schema$ = getSchemaState(instance).observable
301
-
302
- // ...
303
- }
304
- },
305
- )
306
- ```
307
-
308
- Creating a state source involves creating a **selector** to pull the desired value from state and then using `createStateSourceAction`.
309
-
310
- ```ts
311
- import {schemaStore} from './schemaStore'
312
- import {createStateSourceAction} from '../resources/createStateSourceAction'
313
-
314
- export const getSchemaState = createStateSourceAction(
315
- () => schemaStore, // pick the resource
316
- (state) => state.schema, // write a selector
317
- )
318
- ```
319
-
320
- ## Selectors
321
-
322
- Selectors are functions that extract and compute derived data from the resource state. When used with `createStateSourceAction`, they help create efficient state sources that only update when their selected data changes.
323
-
324
- ```ts
325
- import {createSelector} from 'reselect'
326
- import {createStateSourceAction} from '../resources/createStateSourceAction'
327
-
328
- interface DocumentListState {
329
- options: DocumentListOptions
330
- lastLiveEventId?: string
331
- syncTags: SyncTag[]
332
- limit: number
333
- results: DocumentHandle[]
334
- isPending: boolean
335
- }
336
-
337
- const documentList = createResource<DocumentListState>({
338
- name: 'documentList',
339
- // ... resource implementation
340
- })
341
-
342
- const getDocumentListState = createStateSourceAction(
343
- () => documentList,
344
- // Basic selector that returns multiple state properties
345
- createSelector(
346
- [(state: DocumentListState) => state.results, (state: DocumentListState) => state.isPending],
347
- (results, isPending) => ({results, isPending}),
348
- ),
349
- )
350
- ```
351
-
352
- **When to Use Reselect**
353
-
354
- If your selector simply returns an existing object from state without creating a new derived value, you **don't need reselect**:
355
-
356
- ```ts
357
- // Simple selector that just returns an existing object - reselect not needed
358
- const getResults = createStateSourceAction(
359
- () => documentList,
360
- (state: DocumentListState) => state.results,
361
- )
362
- ```
363
-
364
- However, when computing derived data that creates new objects, arrays, or values, `reselect` provides several key benefits:
365
-
366
- 1. **Memoization**: The `createSelector` utility from reselect caches results and only recomputes when input selectors return new values, reducing unnecessary calculations.
367
- 2. **Reference Stability**: By memoizing results, reselect helps maintain stable references which is crucial for preventing infinite loops in state source subscriptions.
368
- 3. **Composition**: Input selectors can be either plain functions or other selectors, allowing you to build complex derivations while maintaining performance.
369
-
370
- Read more at [reselect.js.org](https://reselect.js.org/)
@@ -1,101 +0,0 @@
1
- import {describe, expect, it} from 'vitest'
2
-
3
- import {createSanityInstance} from '../instance/sanityInstance'
4
- import {type ActionContext, createAction, createInternalAction} from './createAction'
5
- import {createResource, createResourceState} from './createResource'
6
-
7
- const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
8
-
9
- interface TestState {
10
- value: number
11
- }
12
-
13
- const testResource = createResource<TestState>({
14
- name: 'test',
15
- getInitialState: () => ({value: 0}),
16
- initialize() {
17
- return () => {}
18
- },
19
- })
20
-
21
- describe('createAction', () => {
22
- it('should create an action that can access state and instance', () => {
23
- // Define an action that accesses state and instance
24
- const testAction = createAction(testResource, ({state, instance: {identity}}) => {
25
- return function () {
26
- state.set('increment', (prev) => ({value: prev.value + 1}))
27
- return identity.projectId
28
- }
29
- })
30
-
31
- // Call the action with instance
32
- const result = testAction(instance)
33
-
34
- // Verify that the state and instance are correctly passed into the action
35
- expect(result).toBe('test')
36
- expect(createResourceState({value: 0}).get()).toEqual({value: 0})
37
- })
38
-
39
- it('should correctly update the state using set', () => {
40
- const testAction = createAction(testResource, ({state, instance: {identity}}) => {
41
- return function () {
42
- state.set('increment', (prev) => ({value: prev.value + 1}))
43
- return identity.projectId
44
- }
45
- })
46
-
47
- const state = createResourceState({value: 1})
48
- const actionContext: ActionContext<TestState> = {instance, state}
49
-
50
- // call the action with state
51
- const projectId = testAction(actionContext)
52
- expect(projectId).toBe(instance.identity.projectId)
53
-
54
- // Verify that the state has been changed
55
- expect(state.get()).toEqual({value: 2})
56
- })
57
-
58
- it('should bind the action to a provided context and work without instance', () => {
59
- const testAction = createAction(testResource, ({state}) => {
60
- return function (value: number) {
61
- state.set('updateValue', {value: value})
62
- return state.get().value
63
- }
64
- })
65
-
66
- const mockState = createResourceState({value: 10})
67
- const result = testAction(
68
- {state: mockState, instance: instance} as ActionContext<TestState>,
69
- 20,
70
- )
71
-
72
- expect(result).toBe(20)
73
- expect(mockState.get()).toEqual({value: 20})
74
-
75
- const mockState2 = createResourceState({value: 10})
76
- const result2 = testAction({state: mockState2} as ActionContext<TestState>, 30)
77
- expect(result2).toBe(30)
78
- expect(mockState2.get()).toEqual({value: 30})
79
- })
80
- })
81
-
82
- describe('createInternalAction', () => {
83
- it('creates an action that requires state and instance', () => {
84
- // Define an action that accesses state and instance
85
- const testAction = createInternalAction<TestState, [], string>(
86
- ({state, instance: {identity}}) => {
87
- return function () {
88
- state.set('increment', (prev) => ({value: prev.value + 1}))
89
- return identity.projectId
90
- }
91
- },
92
- )
93
-
94
- const result = testAction({
95
- state: createResourceState({value: 10}),
96
- instance: instance,
97
- } as ActionContext<TestState>)
98
-
99
- expect(result).toBe('test')
100
- })
101
- })
@@ -1,44 +0,0 @@
1
- import {type SanityInstance} from '../instance/types'
2
- import {getOrCreateResource, type Resource, type ResourceState} from './createResource'
3
-
4
- /** @public */
5
- export interface ActionContext<TState> {
6
- instance: SanityInstance
7
- state: ResourceState<TState>
8
- }
9
-
10
- type ResourceActionDefinition<TState, TParams extends unknown[], TReturn> = (
11
- options: ActionContext<TState>,
12
- ) => (this: ActionContext<TState>, ...args: TParams) => TReturn
13
-
14
- /**
15
- * @public
16
- */
17
- export type ResourceAction<TState, TParams extends unknown[], TReturn> = (
18
- dependencies: SanityInstance | ActionContext<TState>,
19
- ...params: TParams
20
- ) => TReturn
21
-
22
- export function createAction<TState, TParams extends unknown[], TReturn>(
23
- resource: Resource<TState>,
24
- actionDefinition: ResourceActionDefinition<TState, TParams, TReturn>,
25
- ): ResourceAction<TState, TParams, TReturn> {
26
- return (dependencies: SanityInstance | ActionContext<TState>, ...args: TParams): TReturn => {
27
- const instance = 'state' in dependencies ? dependencies.instance : dependencies
28
- const {state} =
29
- 'state' in dependencies ? dependencies : getOrCreateResource(dependencies, resource)
30
- const actionContext = {instance, state}
31
- return actionDefinition(actionContext).bind(actionContext)(...args)
32
- }
33
- }
34
-
35
- /**
36
- * @internal
37
- */
38
- export function createInternalAction<TState, TParams extends unknown[], TReturn>(
39
- actionDefinition: ResourceActionDefinition<TState, TParams, TReturn>,
40
- ) {
41
- return (actionContext: ActionContext<TState>, ...args: TParams): TReturn => {
42
- return actionDefinition(actionContext).bind(actionContext)(...args)
43
- }
44
- }
@@ -1,112 +0,0 @@
1
- import {describe, expect, it, vi} from 'vitest'
2
-
3
- import {createSanityInstance} from '../instance/sanityInstance'
4
- import {
5
- createResource,
6
- disposeResources,
7
- getOrCreateResource,
8
- initializeResource,
9
- type Resource,
10
- } from './createResource'
11
-
12
- describe('createResource', () => {
13
- const instance = createSanityInstance({projectId: 'test', dataset: 'test'})
14
-
15
- interface TestState {
16
- value: number
17
- }
18
-
19
- const testResource: Resource<TestState> = createResource<TestState>({
20
- name: 'test',
21
- getInitialState: () => ({value: 0}),
22
- initialize: vi.fn(),
23
- })
24
-
25
- it('should return the same state from getOrCreateResource', () => {
26
- const resource1 = getOrCreateResource(instance, testResource)
27
- const resource2 = getOrCreateResource(instance, testResource)
28
- expect(resource1).toBe(resource2)
29
- })
30
-
31
- it('should initialize a resource with initial state', () => {
32
- const initialState = {value: 1}
33
- const resource = createResource<TestState>({
34
- name: 'test',
35
- getInitialState: () => initialState,
36
- })
37
-
38
- const {state} = initializeResource(instance, resource)
39
- expect(state.get()).toEqual(initialState)
40
- })
41
-
42
- it('should invoke the init function on initialization', () => {
43
- const resource = createResource<TestState>({
44
- name: 'test',
45
- getInitialState: () => ({value: 0}),
46
- initialize: vi.fn(),
47
- })
48
-
49
- initializeResource(instance, resource)
50
- expect(resource.initialize).toHaveBeenCalledTimes(1)
51
- })
52
-
53
- it('should update the state using set method', () => {
54
- const resource = createResource<TestState>({
55
- name: 'test',
56
- getInitialState: () => ({value: 0}),
57
- })
58
-
59
- const {state} = initializeResource(instance, resource)
60
- state.set('increment', (prevState) => ({value: prevState.value + 1}))
61
- expect(state.get()).toEqual({value: 1})
62
- })
63
-
64
- it('should create an observable from the state', () => {
65
- const resource = createResource<TestState>({
66
- name: 'test',
67
- getInitialState: () => ({value: 0}),
68
- })
69
- const {state} = initializeResource(instance, resource)
70
- const next = vi.fn()
71
- state.observable.subscribe(next)
72
- state.set('increment', (prevState) => ({value: prevState.value + 1}))
73
- expect(next).toHaveBeenCalledTimes(2) // inital and updated
74
- expect(next).toHaveBeenLastCalledWith({value: 1})
75
- })
76
-
77
- it('should return a disposable object with a dispose function', () => {
78
- const resource = createResource<TestState>({
79
- name: 'test',
80
- getInitialState: () => ({value: 0}),
81
- initialize: vi.fn(() => vi.fn()),
82
- })
83
-
84
- const {dispose} = initializeResource(instance, resource)
85
- dispose()
86
- expect(resource.initialize).toHaveReturnedWith(expect.any(Function))
87
- expect(resource.initialize).toHaveBeenCalledTimes(1)
88
- })
89
-
90
- it('should not throw when no dispose function is supplied', () => {
91
- const resource = createResource<TestState>({
92
- name: 'test',
93
- getInitialState: () => ({value: 0}),
94
- })
95
- const {dispose} = initializeResource(instance, resource)
96
- expect(() => dispose()).not.toThrowError()
97
- })
98
-
99
- it('should clear resource caqche on disposeResources', () => {
100
- const resource = createResource<TestState>({
101
- name: 'test',
102
- getInitialState: () => ({value: 0}),
103
- })
104
-
105
- getOrCreateResource(instance, resource)
106
- disposeResources(instance.identity)
107
-
108
- const {state} = initializeResource(instance, resource)
109
-
110
- expect(state.get()).toEqual({value: 0})
111
- })
112
- })