@sanity/sdk 2.4.0 → 2.5.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 (47) hide show
  1. package/dist/index.d.ts +35 -90
  2. package/dist/index.js +237 -111
  3. package/dist/index.js.map +1 -1
  4. package/package.json +9 -8
  5. package/src/auth/authStore.test.ts +13 -13
  6. package/src/auth/refreshStampedToken.test.ts +16 -16
  7. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +6 -6
  8. package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +4 -4
  9. package/src/comlink/controller/actions/destroyController.test.ts +2 -2
  10. package/src/comlink/controller/actions/getOrCreateChannel.test.ts +6 -6
  11. package/src/comlink/controller/actions/getOrCreateController.test.ts +5 -5
  12. package/src/comlink/controller/actions/getOrCreateController.ts +1 -1
  13. package/src/comlink/controller/actions/releaseChannel.test.ts +3 -2
  14. package/src/comlink/controller/comlinkControllerStore.test.ts +4 -4
  15. package/src/comlink/node/actions/getOrCreateNode.test.ts +7 -7
  16. package/src/comlink/node/actions/releaseNode.test.ts +2 -2
  17. package/src/comlink/node/comlinkNodeStore.test.ts +4 -3
  18. package/src/config/sanityConfig.ts +8 -3
  19. package/src/document/actions.ts +11 -7
  20. package/src/document/applyDocumentActions.test.ts +9 -6
  21. package/src/document/applyDocumentActions.ts +9 -49
  22. package/src/document/documentStore.test.ts +128 -115
  23. package/src/document/documentStore.ts +40 -10
  24. package/src/document/permissions.test.ts +9 -9
  25. package/src/document/permissions.ts +17 -7
  26. package/src/document/processActions.test.ts +248 -0
  27. package/src/document/processActions.ts +173 -0
  28. package/src/document/reducers.ts +13 -6
  29. package/src/presence/presenceStore.ts +13 -7
  30. package/src/preview/previewStore.test.ts +10 -2
  31. package/src/preview/previewStore.ts +2 -1
  32. package/src/preview/subscribeToStateAndFetchBatches.test.ts +8 -5
  33. package/src/preview/subscribeToStateAndFetchBatches.ts +9 -3
  34. package/src/projection/projectionStore.test.ts +18 -2
  35. package/src/projection/projectionStore.ts +2 -1
  36. package/src/projection/subscribeToStateAndFetchBatches.test.ts +6 -5
  37. package/src/projection/subscribeToStateAndFetchBatches.ts +9 -3
  38. package/src/releases/getPerspectiveState.ts +2 -2
  39. package/src/releases/releasesStore.ts +10 -4
  40. package/src/store/createActionBinder.test.ts +8 -6
  41. package/src/store/createActionBinder.ts +44 -29
  42. package/src/store/createStateSourceAction.test.ts +12 -11
  43. package/src/store/createStateSourceAction.ts +6 -6
  44. package/src/store/createStoreInstance.test.ts +29 -16
  45. package/src/store/createStoreInstance.ts +6 -5
  46. package/src/store/defineStore.test.ts +1 -1
  47. package/src/store/defineStore.ts +12 -7
@@ -21,7 +21,7 @@ describe('createStateSourceAction', () => {
21
21
  it('should create a source that provides current state through getCurrent', () => {
22
22
  const selector = vi.fn(({state: s}: SelectorContext<CountStoreState>) => s.count)
23
23
  const action = createStateSourceAction(selector)
24
- const source = action({state, instance})
24
+ const source = action({state, instance, key: null})
25
25
 
26
26
  expect(source.getCurrent()).toBe(0)
27
27
  state.set('test', {count: 5})
@@ -33,7 +33,7 @@ describe('createStateSourceAction', () => {
33
33
  const source = createStateSourceAction({
34
34
  selector: ({state: s}: SelectorContext<CountStoreState>) => s.count,
35
35
  isEqual: (a, b) => a === b,
36
- })({state, instance})
36
+ })({state, instance, key: null})
37
37
 
38
38
  const unsubscribe = source.subscribe(onStoreChanged)
39
39
 
@@ -53,11 +53,11 @@ describe('createStateSourceAction', () => {
53
53
  const source = createStateSourceAction({
54
54
  selector: ({state: s}: SelectorContext<CountStoreState>) => s.items,
55
55
  onSubscribe,
56
- })({state, instance})
56
+ })({state, instance, key: null})
57
57
 
58
58
  const unsubscribe = source.subscribe()
59
59
  expect(onSubscribe).toHaveBeenCalledWith(
60
- expect.objectContaining({state, instance}),
60
+ expect.objectContaining({state, instance, key: null}),
61
61
  // No params in this case
62
62
  )
63
63
 
@@ -68,7 +68,7 @@ describe('createStateSourceAction', () => {
68
68
  const action = createStateSourceAction({
69
69
  selector: ({state: s}: SelectorContext<CountStoreState>, index: number) => s.items[index],
70
70
  })
71
- const source = action({state, instance}, 0)
71
+ const source = action({state, instance, key: null}, 0)
72
72
 
73
73
  state.set('add', {items: ['first']})
74
74
  expect(source.getCurrent()).toBe('first')
@@ -80,7 +80,7 @@ describe('createStateSourceAction', () => {
80
80
  selector: () => {
81
81
  throw error
82
82
  },
83
- })({state, instance})
83
+ })({state, instance, key: null})
84
84
 
85
85
  const errorHandler = vi.fn()
86
86
  source.observable.subscribe({error: errorHandler})
@@ -94,7 +94,7 @@ describe('createStateSourceAction', () => {
94
94
  const source = createStateSourceAction({
95
95
  selector: ({state: s}: SelectorContext<CountStoreState>) => s.items.map((i) => i.length),
96
96
  isEqual,
97
- })({state, instance})
97
+ })({state, instance, key: null})
98
98
 
99
99
  const onChange = vi.fn()
100
100
  source.subscribe(onChange)
@@ -112,7 +112,7 @@ describe('createStateSourceAction', () => {
112
112
  const source = createStateSourceAction({
113
113
  selector: ({state: s}: SelectorContext<CountStoreState>) => s.count,
114
114
  onSubscribe: () => cleanup,
115
- })({state, instance})
115
+ })({state, instance, key: null})
116
116
 
117
117
  const unsubscribe = source.subscribe()
118
118
  unsubscribe()
@@ -125,6 +125,7 @@ describe('createStateSourceAction', () => {
125
125
  )({
126
126
  state,
127
127
  instance,
128
+ key: null,
128
129
  })
129
130
 
130
131
  const subscriber1 = vi.fn()
@@ -144,7 +145,7 @@ describe('createStateSourceAction', () => {
144
145
 
145
146
  it('should cache selector context per state object', () => {
146
147
  const selector = vi.fn(({state: s}: SelectorContext<CountStoreState>) => s.count)
147
- const source = createStateSourceAction(selector)({state, instance})
148
+ const source = createStateSourceAction(selector)({state, instance, key: null})
148
149
 
149
150
  // Initial call creates context
150
151
  expect(source.getCurrent()).toBe(0)
@@ -181,10 +182,10 @@ describe('createStateSourceAction', () => {
181
182
  const secondInstance = createSanityInstance({projectId: 'test2', dataset: 'test2'})
182
183
  const selector = vi.fn(({state: s}: SelectorContext<CountStoreState>) => s.count)
183
184
 
184
- const source1 = createStateSourceAction(selector)({state, instance})
185
+ const source1 = createStateSourceAction(selector)({state, instance, key: null})
185
186
  source1.getCurrent()
186
187
 
187
- const source2 = createStateSourceAction(selector)({state, instance: secondInstance})
188
+ const source2 = createStateSourceAction(selector)({state, instance: secondInstance, key: null})
188
189
  source2.getCurrent()
189
190
 
190
191
  const context1 = selector.mock.calls[0][0]
@@ -89,7 +89,7 @@ export type Selector<TState, TParams extends unknown[], TReturn> = (
89
89
  /**
90
90
  * Configuration options for creating a state source action
91
91
  */
92
- interface StateSourceOptions<TState, TParams extends unknown[], TReturn> {
92
+ interface StateSourceOptions<TState, TParams extends unknown[], TReturn, TKey> {
93
93
  /**
94
94
  * Selector function that derives the desired value from store state
95
95
  *
@@ -106,7 +106,7 @@ interface StateSourceOptions<TState, TParams extends unknown[], TReturn> {
106
106
  * @param params - Action parameters provided during invocation
107
107
  * @returns Optional cleanup function called when subscription ends
108
108
  */
109
- onSubscribe?: (context: StoreContext<TState>, ...params: TParams) => void | (() => void)
109
+ onSubscribe?: (context: StoreContext<TState, TKey>, ...params: TParams) => void | (() => void)
110
110
 
111
111
  /**
112
112
  * Equality function to prevent unnecessary updates
@@ -168,9 +168,9 @@ interface StateSourceOptions<TState, TParams extends unknown[], TReturn> {
168
168
  * })
169
169
  * ```
170
170
  */
171
- export function createStateSourceAction<TState, TParams extends unknown[], TReturn>(
172
- options: Selector<TState, TParams, TReturn> | StateSourceOptions<TState, TParams, TReturn>,
173
- ): StoreAction<TState, TParams, StateSource<TReturn>> {
171
+ export function createStateSourceAction<TState, TParams extends unknown[], TReturn, TKey = unknown>(
172
+ options: Selector<TState, TParams, TReturn> | StateSourceOptions<TState, TParams, TReturn, TKey>,
173
+ ): StoreAction<TState, TParams, StateSource<TReturn>, TKey> {
174
174
  const selector = typeof options === 'function' ? options : options.selector
175
175
  const subscribeHandler = options && 'onSubscribe' in options ? options.onSubscribe : undefined
176
176
  const isEqual = options && 'isEqual' in options ? (options.isEqual ?? Object.is) : Object.is
@@ -184,7 +184,7 @@ export function createStateSourceAction<TState, TParams extends unknown[], TRetu
184
184
  * @param context - Store context providing access to state and instance
185
185
  * @param params - Parameters provided when invoking the bound action
186
186
  */
187
- function stateSourceAction(context: StoreContext<TState>, ...params: TParams) {
187
+ function stateSourceAction(context: StoreContext<TState, TKey>, ...params: TParams) {
188
188
  const {state, instance} = context
189
189
 
190
190
  const getCurrent = () => {
@@ -24,36 +24,45 @@ describe('createStoreInstance', () => {
24
24
  }
25
25
 
26
26
  it('should create store instance with initial state', () => {
27
- const store = createStoreInstance(instance, storeDef)
27
+ const store = createStoreInstance(instance, {name: 'store'}, storeDef)
28
28
  expect(store.state).toBeDefined()
29
29
  })
30
30
 
31
31
  it('should call getInitialState with Sanity instance', () => {
32
32
  const getInitialState = vi.fn(() => ({count: 0}))
33
- createStoreInstance(instance, {...storeDef, getInitialState})
34
- expect(getInitialState).toHaveBeenCalledWith(instance)
33
+ createStoreInstance(instance, {name: 'store'}, {...storeDef, getInitialState})
34
+ expect(getInitialState).toHaveBeenCalledWith(instance, {name: 'store'})
35
35
  })
36
36
 
37
37
  it('should call initialize function with context', () => {
38
38
  const initialize = vi.fn()
39
39
 
40
- const store = createStoreInstance(instance, {
41
- ...storeDef,
42
- initialize,
43
- })
40
+ const store = createStoreInstance(
41
+ instance,
42
+ {name: 'store'},
43
+ {
44
+ ...storeDef,
45
+ initialize,
46
+ },
47
+ )
44
48
  expect(initialize).toHaveBeenCalledWith({
45
49
  state: store.state,
46
50
  instance,
51
+ key: {name: 'store'},
47
52
  })
48
53
  })
49
54
 
50
55
  it('should handle store disposal with cleanup function', () => {
51
56
  const disposeMock = vi.fn()
52
57
 
53
- const store = createStoreInstance(instance, {
54
- ...storeDef,
55
- initialize: () => disposeMock,
56
- })
58
+ const store = createStoreInstance(
59
+ instance,
60
+ {name: 'store'},
61
+ {
62
+ ...storeDef,
63
+ initialize: () => disposeMock,
64
+ },
65
+ )
57
66
  store.dispose()
58
67
 
59
68
  expect(disposeMock).toHaveBeenCalledTimes(1)
@@ -61,7 +70,7 @@ describe('createStoreInstance', () => {
61
70
  })
62
71
 
63
72
  it('should handle disposal without initialize function', () => {
64
- const store = createStoreInstance(instance, storeDef)
73
+ const store = createStoreInstance(instance, {name: 'store'}, storeDef)
65
74
  store.dispose()
66
75
  expect(store.isDisposed()).toBe(true)
67
76
  })
@@ -69,10 +78,14 @@ describe('createStoreInstance', () => {
69
78
  it('should prevent multiple disposals', () => {
70
79
  const disposeMock = vi.fn()
71
80
 
72
- const store = createStoreInstance(instance, {
73
- ...storeDef,
74
- initialize: () => disposeMock,
75
- })
81
+ const store = createStoreInstance(
82
+ instance,
83
+ {name: 'store'},
84
+ {
85
+ ...storeDef,
86
+ initialize: () => disposeMock,
87
+ },
88
+ )
76
89
  store.dispose()
77
90
  store.dispose()
78
91
 
@@ -57,15 +57,16 @@ export interface StoreInstance<TState> {
57
57
  * instance.dispose()
58
58
  * ```
59
59
  */
60
- export function createStoreInstance<TState>(
60
+ export function createStoreInstance<TState, TKey extends {name: string}>(
61
61
  instance: SanityInstance,
62
- {name, getInitialState, initialize}: StoreDefinition<TState>,
62
+ key: TKey,
63
+ {name, getInitialState, initialize}: StoreDefinition<TState, TKey>,
63
64
  ): StoreInstance<TState> {
64
- const state = createStoreState(getInitialState(instance), {
65
+ const state = createStoreState(getInitialState(instance, key), {
65
66
  enabled: !!getEnv('DEV'),
66
- name: `${name}-${instance.config.projectId}.${instance.config.dataset}`,
67
+ name: `${name}-${key.name}`,
67
68
  })
68
- const dispose = initialize?.({state, instance})
69
+ const dispose = initialize?.({state, instance, key})
69
70
  const disposed = {current: false}
70
71
 
71
72
  return {
@@ -13,6 +13,6 @@ describe('defineStore', () => {
13
13
  const result = defineStore(storeDef)
14
14
  expect(result).toBe(storeDef)
15
15
  expect(result.name).toBe('TestStore')
16
- expect(result.getInitialState({} as SanityInstance)).toBe(42)
16
+ expect(result.getInitialState({} as SanityInstance, null)).toBe(42)
17
17
  })
18
18
  })
@@ -4,7 +4,7 @@ import {type StoreState} from './createStoreState'
4
4
  /**
5
5
  * Context object provided to store initialization functions
6
6
  */
7
- export interface StoreContext<TState> {
7
+ export interface StoreContext<TState, TKey = unknown> {
8
8
  /**
9
9
  * Sanity instance associated with this store
10
10
  *
@@ -20,6 +20,11 @@ export interface StoreContext<TState> {
20
20
  * Contains methods for getting/setting state and observing changes
21
21
  */
22
22
  state: StoreState<TState>
23
+
24
+ /**
25
+ * The key used to instantiate the store.
26
+ */
27
+ key: TKey
23
28
  }
24
29
 
25
30
  /**
@@ -29,7 +34,7 @@ export interface StoreContext<TState> {
29
34
  * Stores are isolated state containers that can be associated with Sanity instances.
30
35
  * Each store definition creates a separate state instance per composite key.
31
36
  */
32
- export interface StoreDefinition<TState> {
37
+ export interface StoreDefinition<TState, TKey = unknown> {
33
38
  /**
34
39
  * Unique name for the store
35
40
  *
@@ -47,7 +52,7 @@ export interface StoreDefinition<TState> {
47
52
  * Called when a new store instance is created. Can use Sanity instance
48
53
  * configuration to determine initial state.
49
54
  */
50
- getInitialState: (instance: SanityInstance) => TState
55
+ getInitialState: (instance: SanityInstance, key: TKey) => TState
51
56
 
52
57
  /**
53
58
  * Optional initialization function
@@ -65,7 +70,7 @@ export interface StoreDefinition<TState> {
65
70
  * - Cancel pending operations
66
71
  * - Dispose external connections
67
72
  */
68
- initialize?: (context: StoreContext<TState>) => (() => void) | undefined
73
+ initialize?: (context: StoreContext<TState, TKey>) => (() => void) | undefined
69
74
  }
70
75
 
71
76
  /**
@@ -74,8 +79,8 @@ export interface StoreDefinition<TState> {
74
79
  * @param storeDefinition - Configuration object defining the store
75
80
  * @returns The finalized store definition
76
81
  */
77
- export function defineStore<TState>(
78
- storeDefinition: StoreDefinition<TState>,
79
- ): StoreDefinition<TState> {
82
+ export function defineStore<TState, TKey = unknown>(
83
+ storeDefinition: StoreDefinition<TState, TKey>,
84
+ ): StoreDefinition<TState, TKey> {
80
85
  return storeDefinition
81
86
  }