@sanity/sdk 2.9.0 → 2.11.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 (73) hide show
  1. package/dist/_chunks-dts/utils.d.ts +295 -69
  2. package/dist/_chunks-es/_internal.js +3 -14
  3. package/dist/_chunks-es/_internal.js.map +1 -1
  4. package/dist/_chunks-es/createGroqSearchFilter.js +129 -59
  5. package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -1
  6. package/dist/_chunks-es/version.js +1 -1
  7. package/dist/_exports/_internal.d.ts +16 -2
  8. package/dist/_exports/_internal.js +3 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.js +275 -149
  11. package/dist/index.js.map +1 -1
  12. package/package.json +11 -15
  13. package/src/_exports/_internal.ts +1 -0
  14. package/src/_exports/index.ts +33 -2
  15. package/src/agent/agentActions.ts +21 -25
  16. package/src/client/clientStore.test.ts +24 -60
  17. package/src/client/clientStore.ts +49 -56
  18. package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
  19. package/src/comlink/node/actions/getOrCreateNode.test.ts +5 -2
  20. package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
  21. package/src/comlink/node/actions/releaseNode.test.ts +3 -3
  22. package/src/config/sanityConfig.ts +72 -13
  23. package/src/document/applyDocumentActions.test.ts +7 -7
  24. package/src/document/applyDocumentActions.ts +5 -5
  25. package/src/document/documentStore.test.ts +68 -62
  26. package/src/document/documentStore.ts +33 -38
  27. package/src/document/processActions.ts +2 -2
  28. package/src/document/reducers.ts +4 -4
  29. package/src/document/sharedListener.ts +5 -7
  30. package/src/organization/organization.test-d.ts +102 -0
  31. package/src/organization/organization.test.ts +138 -0
  32. package/src/organization/organization.ts +166 -0
  33. package/src/organizations/organizations.test-d.ts +77 -0
  34. package/src/organizations/organizations.test.ts +150 -0
  35. package/src/organizations/organizations.ts +132 -0
  36. package/src/presence/bifurTransport.test.ts +46 -6
  37. package/src/presence/bifurTransport.ts +13 -1
  38. package/src/presence/presenceStore.test.ts +101 -5
  39. package/src/presence/presenceStore.ts +96 -24
  40. package/src/preview/getPreviewState.ts +1 -1
  41. package/src/preview/previewProjectionUtils.test.ts +4 -4
  42. package/src/preview/previewProjectionUtils.ts +6 -7
  43. package/src/preview/resolvePreview.ts +5 -1
  44. package/src/project/project.test-d.ts +93 -0
  45. package/src/project/project.test.ts +108 -10
  46. package/src/project/project.ts +152 -26
  47. package/src/projection/getProjectionState.ts +4 -4
  48. package/src/projection/projectionStore.test.ts +2 -2
  49. package/src/projection/resolveProjection.ts +2 -2
  50. package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
  51. package/src/projection/subscribeToStateAndFetchBatches.ts +11 -15
  52. package/src/projects/projects.test-d.ts +38 -0
  53. package/src/projects/projects.test.ts +104 -38
  54. package/src/projects/projects.ts +74 -14
  55. package/src/query/queryStore.test.ts +12 -12
  56. package/src/query/queryStore.ts +10 -11
  57. package/src/query/reducers.ts +3 -3
  58. package/src/releases/getPerspectiveState.ts +5 -5
  59. package/src/releases/releasesStore.test.ts +6 -6
  60. package/src/releases/releasesStore.ts +9 -9
  61. package/src/store/createActionBinder.test.ts +31 -31
  62. package/src/store/createActionBinder.ts +43 -38
  63. package/src/store/createSanityInstance.ts +5 -6
  64. package/src/telemetry/devMode.test.ts +8 -0
  65. package/src/telemetry/devMode.ts +10 -9
  66. package/src/telemetry/initTelemetry.test.ts +0 -17
  67. package/src/telemetry/initTelemetry.ts +2 -12
  68. package/src/users/reducers.ts +3 -4
  69. package/src/utils/createFetcherStore.ts +6 -4
  70. package/src/utils/isImportError.test.ts +72 -0
  71. package/src/utils/isImportError.ts +34 -0
  72. package/src/utils/object.test.ts +95 -0
  73. package/src/utils/object.ts +142 -0
@@ -1,35 +1,95 @@
1
1
  import {switchMap} from 'rxjs'
2
2
 
3
3
  import {getClientState} from '../client/clientStore'
4
+ import {type Project} from '../project/project'
5
+ import {type SanityInstance} from '../store/createSanityInstance'
6
+ import {type StateSource} from '../store/createStateSourceAction'
4
7
  import {createFetcherStore} from '../utils/createFetcherStore'
5
8
 
6
9
  const API_VERSION = 'v2025-02-19'
7
10
 
11
+ /** @public */
12
+ export interface ProjectsOptions<
13
+ IncludeMembers extends boolean = false,
14
+ IncludeFeatures extends boolean = true,
15
+ > {
16
+ organizationId?: string
17
+ includeMembers?: IncludeMembers
18
+ includeFeatures?: IncludeFeatures
19
+ onlyExplicitMembership?: boolean
20
+ }
21
+
22
+ function normalizeProjectsOptions(options?: ProjectsOptions<boolean, boolean>) {
23
+ return {
24
+ organizationId: options?.organizationId,
25
+ includeMembers: options?.includeMembers ?? false,
26
+ includeFeatures: options?.includeFeatures ?? true,
27
+ onlyExplicitMembership: options?.onlyExplicitMembership ?? false,
28
+ }
29
+ }
30
+
31
+ /** @internal */
32
+ export function getProjectsCacheKey(
33
+ _instance: SanityInstance,
34
+ options?: ProjectsOptions<boolean, boolean>,
35
+ ): string {
36
+ const {organizationId, includeMembers, includeFeatures, onlyExplicitMembership} =
37
+ normalizeProjectsOptions(options)
38
+ const orgKey = organizationId ? `:org:${organizationId}` : ''
39
+ const membersKey = includeMembers ? ':members' : ''
40
+ const featuresKey = includeFeatures ? ':features' : ''
41
+ const explicitKey = onlyExplicitMembership ? ':explicit' : ''
42
+ return `projects${orgKey}${membersKey}${featuresKey}${explicitKey}`
43
+ }
44
+
8
45
  const projects = createFetcherStore({
9
46
  name: 'Projects',
10
- getKey: (_instance, options?: {organizationId?: string; includeMembers?: boolean}) => {
11
- const orgKey = options?.organizationId ? `:org:${options.organizationId}` : ''
12
- const membersKey = options?.includeMembers === false ? ':no-members' : ''
13
- return `projects${orgKey}${membersKey}`
14
- },
15
- fetcher: (instance) => (options?: {organizationId?: string; includeMembers?: boolean}) =>
47
+ getKey: getProjectsCacheKey,
48
+ fetcher: (instance) => (options?: ProjectsOptions<boolean, boolean>) =>
16
49
  getClientState(instance, {
17
50
  apiVersion: API_VERSION,
18
51
  scope: 'global',
19
- requestTagPrefix: 'sanity.sdk.projects',
20
52
  }).observable.pipe(
21
53
  switchMap((client) => {
22
- const organizationId = options?.organizationId
23
- return client.observable.projects.list({
24
- // client method has a type that expects false | undefined
25
- includeMembers: !options?.includeMembers ? false : undefined,
26
- organizationId,
54
+ const normalized = normalizeProjectsOptions(options)
55
+ const query = Object.fromEntries(
56
+ Object.entries(normalized)
57
+ .filter(([, value]) => value !== undefined)
58
+ .map(([key, value]) => [key, String(value)]),
59
+ )
60
+
61
+ return client.observable.request({
62
+ uri: '/projects',
63
+ query,
64
+ tag: 'projects.get',
27
65
  })
28
66
  }),
29
67
  ),
30
68
  })
31
69
 
70
+ /**
71
+ * Public signature for the projects state source. The conditional generics
72
+ * cannot flow through `BoundStoreAction`, so we declare the signature here
73
+ * and assign the (already-correct) runtime function to it.
74
+ */
75
+ type GetProjectsState = <
76
+ IncludeMembers extends boolean = false,
77
+ IncludeFeatures extends boolean = true,
78
+ >(
79
+ instance: SanityInstance,
80
+ options?: ProjectsOptions<IncludeMembers, IncludeFeatures>,
81
+ ) => StateSource<Project<IncludeMembers, IncludeFeatures>[] | undefined>
82
+
83
+ type ResolveProjects = <
84
+ IncludeMembers extends boolean = false,
85
+ IncludeFeatures extends boolean = true,
86
+ >(
87
+ instance: SanityInstance,
88
+ options?: ProjectsOptions<IncludeMembers, IncludeFeatures>,
89
+ ) => Promise<Project<IncludeMembers, IncludeFeatures>[]>
90
+
32
91
  /** @public */
33
- export const getProjectsState = projects.getState
92
+ export const getProjectsState: GetProjectsState = projects.getState
93
+
34
94
  /** @public */
35
- export const resolveProjects = projects.resolveState
95
+ export const resolveProjects: ResolveProjects = projects.resolveState
@@ -3,7 +3,7 @@ import {delay, filter, firstValueFrom, Observable, of, Subject} from 'rxjs'
3
3
  import {beforeEach, describe, expect, it, vi} from 'vitest'
4
4
 
5
5
  import {getClientState} from '../client/clientStore'
6
- import {isCanvasSource} from '../config/sanityConfig'
6
+ import {isCanvasResource} from '../config/sanityConfig'
7
7
  import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
8
8
  import {type StateSource} from '../store/createStateSourceAction'
9
9
  import {getQueryState, resolveQuery} from './queryStore'
@@ -448,21 +448,21 @@ describe('queryStore', () => {
448
448
  base.dispose()
449
449
  })
450
450
 
451
- it('uses source from params when passed in query options (listenForNewSubscribersAndFetch)', async () => {
451
+ it('uses resource from params when passed in query options (listenForNewSubscribersAndFetch)', async () => {
452
452
  const query = '*[_type == "movie"]'
453
453
  const mediaLibrarySource = {mediaLibraryId: 'ml123'}
454
454
 
455
- const state = getQueryState(instance, {query, source: mediaLibrarySource})
455
+ const state = getQueryState(instance, {query, resource: mediaLibrarySource})
456
456
  const unsubscribe = state.subscribe()
457
457
 
458
458
  await firstValueFrom(state.observable.pipe(filter((i) => i !== undefined)))
459
459
 
460
- // Verify getClientState was called with the source from params in listenForNewSubscribersAndFetch
461
- // This call includes projectId, dataset, and source
460
+ // Verify getClientState was called with the resource from params in listenForNewSubscribersAndFetch
461
+ // This call includes projectId, dataset, and resource
462
462
  expect(getClientState).toHaveBeenCalledWith(
463
463
  instance,
464
464
  expect.objectContaining({
465
- source: expect.objectContaining({
465
+ resource: expect.objectContaining({
466
466
  mediaLibraryId: 'ml123',
467
467
  }),
468
468
  }),
@@ -471,22 +471,22 @@ describe('queryStore', () => {
471
471
  unsubscribe()
472
472
  })
473
473
 
474
- it('uses source from store context key when not a dataset source (listenToLiveClientAndSetLastLiveEventIds)', async () => {
474
+ it('uses resource from store context key when not a dataset resource (listenToLiveClientAndSetLastLiveEventIds)', async () => {
475
475
  const query = '*[_type == "movie"]'
476
476
  const canvasSource = {canvasId: 'canvas456'}
477
477
 
478
- const state = getQueryState(instance, {query, source: canvasSource})
478
+ const state = getQueryState(instance, {query, resource: canvasSource})
479
479
  const unsubscribe = state.subscribe()
480
480
 
481
481
  await firstValueFrom(state.observable.pipe(filter((i) => i !== undefined)))
482
482
 
483
- // Verify getClientState was called with the canvas source for live events
484
- // The source is extracted from the store key and passed when it's not a dataset source
485
- // This call only has apiVersion and source (no projectId/dataset)
483
+ // Verify getClientState was called with the canvas resource for live events
484
+ // The resource is extracted from the store key and passed when it's not a dataset resource
485
+ // This call only has apiVersion and resource (no projectId/dataset)
486
486
  const calls = vi.mocked(getClientState).mock.calls
487
487
  const liveClientCall = calls.find(
488
488
  ([_instance, options]) =>
489
- isCanvasSource(options.source!) && options.source.canvasId === 'canvas456',
489
+ isCanvasResource(options.resource!) && options.resource.canvasId === 'canvas456',
490
490
  )
491
491
  expect(liveClientCall).toBeDefined()
492
492
 
@@ -24,7 +24,7 @@ import {
24
24
  } from 'rxjs'
25
25
 
26
26
  import {getClientState} from '../client/clientStore'
27
- import {type DatasetHandle, isDatasetSource} from '../config/sanityConfig'
27
+ import {type DatasetHandle} from '../config/sanityConfig'
28
28
  /*
29
29
  * Although this is an import dependency cycle, it is not a logical cycle:
30
30
  * 1. queryStore uses getPerspectiveState when resolving release perspectives
@@ -35,7 +35,7 @@ import {type DatasetHandle, isDatasetSource} from '../config/sanityConfig'
35
35
  // eslint-disable-next-line import/no-cycle
36
36
  import {getPerspectiveState} from '../releases/getPerspectiveState'
37
37
  import {isReleasePerspective} from '../releases/utils/isReleasePerspective'
38
- import {bindActionBySource, type BoundSourceKey} from '../store/createActionBinder'
38
+ import {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'
39
39
  import {type SanityInstance} from '../store/createSanityInstance'
40
40
  import {
41
41
  createStateSourceAction,
@@ -117,7 +117,7 @@ function normalizeOptionsWithPerspective(
117
117
  }
118
118
  }
119
119
 
120
- const queryStore = defineStore<QueryStoreState, BoundSourceKey>({
120
+ const queryStore = defineStore<QueryStoreState, BoundResourceKey>({
121
121
  name: 'QueryStore',
122
122
  getInitialState: () => ({queries: {}}),
123
123
  initialize(context) {
@@ -173,7 +173,7 @@ const listenForNewSubscribersAndFetch = ({state, instance}: StoreContext<QuerySt
173
173
  projectId,
174
174
  dataset,
175
175
  tag,
176
- source,
176
+ resource,
177
177
  perspective: perspectiveFromOptions,
178
178
  ...restOptions
179
179
  } = parseQueryKey(group$.key)
@@ -190,7 +190,7 @@ const listenForNewSubscribersAndFetch = ({state, instance}: StoreContext<QuerySt
190
190
  apiVersion: QUERY_STORE_API_VERSION,
191
191
  projectId,
192
192
  dataset,
193
- source,
193
+ resource,
194
194
  }).observable
195
195
 
196
196
  return combineLatest({
@@ -226,12 +226,11 @@ const listenForNewSubscribersAndFetch = ({state, instance}: StoreContext<QuerySt
226
226
  const listenToLiveClientAndSetLastLiveEventIds = ({
227
227
  state,
228
228
  instance,
229
- key: {source},
230
- }: StoreContext<QueryStoreState, BoundSourceKey>) => {
229
+ key: {resource},
230
+ }: StoreContext<QueryStoreState, BoundResourceKey>) => {
231
231
  const liveMessages$ = getClientState(instance, {
232
232
  apiVersion: QUERY_STORE_API_VERSION,
233
- // temporary guard here until we're ready for everything to be queried via global api
234
- ...(source && !isDatasetSource(source) ? {source} : {}),
233
+ resource,
235
234
  }).observable.pipe(
236
235
  switchMap((client) =>
237
236
  defer(() =>
@@ -316,7 +315,7 @@ export function getQueryState(
316
315
  ): ReturnType<typeof _getQueryState> {
317
316
  return _getQueryState(...args)
318
317
  }
319
- const _getQueryState = bindActionBySource(
318
+ const _getQueryState = bindActionByResource(
320
319
  queryStore,
321
320
  createStateSourceAction({
322
321
  selector: ({state, instance}: SelectorContext<QueryStoreState>, options: QueryOptions) => {
@@ -375,7 +374,7 @@ export function resolveQuery<TData>(
375
374
  export function resolveQuery(...args: Parameters<typeof _resolveQuery>): Promise<unknown> {
376
375
  return _resolveQuery(...args)
377
376
  }
378
- const _resolveQuery = bindActionBySource(
377
+ const _resolveQuery = bindActionByResource(
379
378
  queryStore,
380
379
  ({state, instance}, {signal, ...options}: ResolveQueryOptions) => {
381
380
  const normalized = normalizeOptionsWithPerspective(instance, options)
@@ -1,4 +1,4 @@
1
- import {omit} from 'lodash-es'
1
+ import {omitProperty} from '../utils/object'
2
2
 
3
3
  interface QueryState {
4
4
  syncTags?: string[]
@@ -54,7 +54,7 @@ export const removeSubscriber =
54
54
  const prevQuery = prev.queries[key]
55
55
  if (!prevQuery) return prev
56
56
  const subscribers = prevQuery.subscribers.filter((id) => id !== subscriptionId)
57
- if (!subscribers.length) return {...prev, queries: omit(prev.queries, key)}
57
+ if (!subscribers.length) return {...prev, queries: omitProperty(prev.queries, key)}
58
58
  return {...prev, queries: {...prev.queries, [key]: {...prevQuery, subscribers}}}
59
59
  }
60
60
 
@@ -64,7 +64,7 @@ export const cancelQuery =
64
64
  const prevQuery = prev.queries[key]
65
65
  if (!prevQuery) return prev
66
66
  if (prevQuery.subscribers.length) return prev
67
- return {...prev, queries: omit(prev.queries, key)}
67
+ return {...prev, queries: omitProperty(prev.queries, key)}
68
68
  }
69
69
 
70
70
  export const initializeQuery =
@@ -1,7 +1,7 @@
1
1
  import {createSelector} from 'reselect'
2
2
 
3
- import {type DocumentSource, type PerspectiveHandle} from '../config/sanityConfig'
4
- import {bindActionBySource, type BoundStoreAction} from '../store/createActionBinder'
3
+ import {type DocumentResource, type PerspectiveHandle} from '../config/sanityConfig'
4
+ import {bindActionByResource, type BoundStoreAction} from '../store/createActionBinder'
5
5
  import {createStateSourceAction, type SelectorContext} from '../store/createStateSourceAction'
6
6
  /*
7
7
  * Although this is an import dependency cycle, it is not a logical cycle:
@@ -26,7 +26,7 @@ const selectActiveReleases = (context: SelectorContext<ReleasesStoreState>) =>
26
26
  context.state.activeReleases
27
27
  const selectOptions = (
28
28
  _context: SelectorContext<ReleasesStoreState>,
29
- options: PerspectiveHandle & {projectId?: string; dataset?: string; source?: DocumentSource},
29
+ options: PerspectiveHandle & {projectId?: string; dataset?: string; resource?: DocumentResource},
30
30
  ) => options
31
31
 
32
32
  const memoizedOptionsSelector = createSelector(
@@ -105,11 +105,11 @@ let _boundGetPerspectiveState: BoundGetPerspectiveState | undefined
105
105
  */
106
106
  export const getPerspectiveState: BoundGetPerspectiveState = (instance, ...rest) => {
107
107
  if (!_boundGetPerspectiveState) {
108
- _boundGetPerspectiveState = bindActionBySource(
108
+ _boundGetPerspectiveState = bindActionByResource(
109
109
  releasesStore,
110
110
  _getPerspectiveStateSelector,
111
111
  ) as BoundGetPerspectiveState
112
112
  }
113
- // bindActionBySource keyFn destructures { source } from the first param, so pass {} when no options
113
+ // bindActionByResource keyFn destructures { resource } from the first param, so pass {} when no options
114
114
  return _boundGetPerspectiveState(instance, ...(rest.length ? rest : [{}]))
115
115
  }
@@ -39,8 +39,8 @@ describe('releasesStore', () => {
39
39
  expect.objectContaining({
40
40
  query: 'releases::all()',
41
41
  perspective: 'raw',
42
- source: undefined,
43
42
  tag: 'releases',
43
+ resource: {dataset: 'test', projectId: 'test'},
44
44
  }),
45
45
  )
46
46
  })
@@ -73,7 +73,7 @@ describe('releasesStore', () => {
73
73
  } as ReleaseDocument,
74
74
  ]
75
75
 
76
- const state = getActiveReleasesState(instance, {source: {projectId: 'test', dataset: 'test'}})
76
+ const state = getActiveReleasesState(instance, {resource: {projectId: 'test', dataset: 'test'}})
77
77
 
78
78
  const [observer] = subscriber.mock.lastCall!
79
79
 
@@ -92,7 +92,7 @@ describe('releasesStore', () => {
92
92
  observable: releasesSubject.asObservable(),
93
93
  } as StateSource<ReleaseDocument[] | undefined>)
94
94
 
95
- const state = getActiveReleasesState(instance, {source: {projectId: 'test', dataset: 'test'}})
95
+ const state = getActiveReleasesState(instance, {resource: {projectId: 'test', dataset: 'test'}})
96
96
 
97
97
  // Initial state should be default
98
98
  expect(state.getCurrent()).toBeUndefined() // Default initial state
@@ -139,7 +139,7 @@ describe('releasesStore', () => {
139
139
  observable: of([]),
140
140
  } as StateSource<ReleaseDocument[] | undefined>)
141
141
 
142
- const state = getActiveReleasesState(instance, {source: {projectId: 'test', dataset: 'test'}})
142
+ const state = getActiveReleasesState(instance, {resource: {projectId: 'test', dataset: 'test'}})
143
143
 
144
144
  await new Promise((resolve) => setTimeout(resolve, 0))
145
145
 
@@ -153,7 +153,7 @@ describe('releasesStore', () => {
153
153
  getCurrent: () => null as unknown as ReleaseDocument[] | undefined,
154
154
  observable: of(null as unknown as ReleaseDocument[] | undefined),
155
155
  } as StateSource<ReleaseDocument[] | undefined>)
156
- const state = getActiveReleasesState(instance, {source: {projectId: 'test', dataset: 'test'}})
156
+ const state = getActiveReleasesState(instance, {resource: {projectId: 'test', dataset: 'test'}})
157
157
  await new Promise((resolve) => setTimeout(resolve, 0))
158
158
  expect(state.getCurrent()).toEqual([])
159
159
 
@@ -175,7 +175,7 @@ describe('releasesStore', () => {
175
175
  observable: subject.asObservable(),
176
176
  } as StateSource<ReleaseDocument[] | undefined>)
177
177
 
178
- const state = getActiveReleasesState(instance, {source: {projectId: 'test', dataset: 'test'}})
178
+ const state = getActiveReleasesState(instance, {resource: {projectId: 'test', dataset: 'test'}})
179
179
 
180
180
  subject.error(new Error('Query failed'))
181
181
 
@@ -1,7 +1,7 @@
1
1
  import {type SanityDocument} from '@sanity/types'
2
2
  import {map} from 'rxjs'
3
3
 
4
- import {type DocumentSource, isDatasetSource} from '../config/sanityConfig'
4
+ import {type DocumentResource} from '../config/sanityConfig'
5
5
  /*
6
6
  * Although this is an import dependency cycle, it is not a logical cycle:
7
7
  * 1. releasesStore uses queryStore as a data source
@@ -11,7 +11,7 @@ import {type DocumentSource, isDatasetSource} from '../config/sanityConfig'
11
11
  */
12
12
  // eslint-disable-next-line import/no-cycle
13
13
  import {getQueryState} from '../query/queryStore'
14
- import {bindActionBySource, type BoundSourceKey} from '../store/createActionBinder'
14
+ import {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'
15
15
  import {type SanityInstance} from '../store/createSanityInstance'
16
16
  import {createStateSourceAction, type StateSource} from '../store/createStateSourceAction'
17
17
  import {defineStore, type StoreContext} from '../store/defineStore'
@@ -41,7 +41,7 @@ export interface ReleasesStoreState {
41
41
  error?: unknown
42
42
  }
43
43
 
44
- export const releasesStore = defineStore<ReleasesStoreState, BoundSourceKey>({
44
+ export const releasesStore = defineStore<ReleasesStoreState, BoundResourceKey>({
45
45
  name: 'Releases',
46
46
  getInitialState: (): ReleasesStoreState => ({
47
47
  activeReleases: undefined,
@@ -56,7 +56,7 @@ export const releasesStore = defineStore<ReleasesStoreState, BoundSourceKey>({
56
56
  * Get the active releases from the store.
57
57
  * @internal
58
58
  */
59
- const _getActiveReleasesState = bindActionBySource(
59
+ const _getActiveReleasesState = bindActionByResource(
60
60
  releasesStore,
61
61
  createStateSourceAction({
62
62
  selector: ({state}, _?) => state.activeReleases,
@@ -69,9 +69,9 @@ const _getActiveReleasesState = bindActionBySource(
69
69
  */
70
70
  export const getActiveReleasesState = (
71
71
  instance: SanityInstance,
72
- options?: {source?: DocumentSource},
72
+ options?: {resource?: DocumentResource},
73
73
  ): StateSource<ReleaseDocument[] | undefined> =>
74
- // bindActionBySource keyFn destructures { source } from the first param, so pass {} when no options
74
+ // bindActionByResource keyFn destructures { resource } from the first param, so pass {} when no options
75
75
  _getActiveReleasesState(instance, options ?? {})
76
76
 
77
77
  const RELEASES_QUERY = 'releases::all()'
@@ -79,12 +79,12 @@ const RELEASES_QUERY = 'releases::all()'
79
79
  const subscribeToReleases = ({
80
80
  instance,
81
81
  state,
82
- key: {source},
83
- }: StoreContext<ReleasesStoreState, BoundSourceKey>) => {
82
+ key: {resource},
83
+ }: StoreContext<ReleasesStoreState, BoundResourceKey>) => {
84
84
  const {observable: releases$} = getQueryState<ReleaseDocument[]>(instance, {
85
85
  query: RELEASES_QUERY,
86
86
  perspective: 'raw',
87
- source: source && !isDatasetSource(source) ? source : undefined,
87
+ resource,
88
88
  tag: 'releases',
89
89
  })
90
90
  return releases$
@@ -1,10 +1,10 @@
1
1
  import {beforeEach, describe, expect, it, vi} from 'vitest'
2
2
 
3
- import {type DocumentSource} from '../config/sanityConfig'
3
+ import {type DocumentResource} from '../config/sanityConfig'
4
4
  import {
5
5
  bindActionByDataset,
6
- bindActionBySource,
7
- bindActionBySourceAndPerspective,
6
+ bindActionByResource,
7
+ bindActionByResourceAndPerspective,
8
8
  bindActionGlobally,
9
9
  createActionBinder,
10
10
  } from './createActionBinder'
@@ -161,28 +161,28 @@ describe('bindActionGlobally', () => {
161
161
  })
162
162
  })
163
163
 
164
- describe('bindActionBySource', () => {
165
- it('should throw an error when provided an invalid source', () => {
164
+ describe('bindActionByResource', () => {
165
+ it('should throw an error when provided an invalid resource', () => {
166
166
  const storeDefinition = {
167
167
  name: 'SourceStore',
168
168
  getInitialState: () => ({counter: 0}),
169
169
  }
170
170
  const action = vi.fn((_context) => 'success')
171
- const boundAction = bindActionBySource(storeDefinition, action)
171
+ const boundAction = bindActionByResource(storeDefinition, action)
172
172
  const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
173
173
 
174
174
  expect(() =>
175
- boundAction(instance, {source: {invalid: 'source'} as unknown as DocumentSource}),
176
- ).toThrow('Received invalid source:')
175
+ boundAction(instance, {resource: {invalid: 'resource'} as unknown as DocumentResource}),
176
+ ).toThrow('Received invalid resource:')
177
177
  })
178
178
 
179
- it('should throw an error when no source provided and projectId/dataset are missing', () => {
179
+ it('should throw an error when no resource provided and projectId/dataset are missing', () => {
180
180
  const storeDefinition = {
181
181
  name: 'SourceStore',
182
182
  getInitialState: () => ({counter: 0}),
183
183
  }
184
184
  const action = vi.fn((_context) => 'success')
185
- const boundAction = bindActionBySource(storeDefinition, action)
185
+ const boundAction = bindActionByResource(storeDefinition, action)
186
186
  const instance = createSanityInstance({projectId: '', dataset: ''})
187
187
 
188
188
  expect(() => boundAction(instance, {})).toThrow(
@@ -190,47 +190,47 @@ describe('bindActionBySource', () => {
190
190
  )
191
191
  })
192
192
 
193
- it('should work correctly with a valid dataset source', () => {
193
+ it('should work correctly with a valid dataset resource', () => {
194
194
  const storeDefinition = {
195
195
  name: 'SourceStore',
196
196
  getInitialState: () => ({counter: 0}),
197
197
  }
198
198
  const action = vi.fn((_context) => 'success')
199
- const boundAction = bindActionBySource(storeDefinition, action)
199
+ const boundAction = bindActionByResource(storeDefinition, action)
200
200
  const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
201
201
 
202
202
  const result = boundAction(instance, {
203
- source: {projectId: 'proj2', dataset: 'ds2'},
203
+ resource: {projectId: 'proj2', dataset: 'ds2'},
204
204
  })
205
205
  expect(result).toBe('success')
206
206
  })
207
207
  })
208
208
 
209
- describe('bindActionBySourceAndPerspective', () => {
210
- it('should throw an error when provided an invalid source', () => {
209
+ describe('bindActionByResourceAndPerspective', () => {
210
+ it('should throw an error when provided an invalid resource', () => {
211
211
  const storeDefinition = {
212
212
  name: 'PerspectiveStore',
213
213
  getInitialState: () => ({counter: 0}),
214
214
  }
215
215
  const action = vi.fn((_context) => 'success')
216
- const boundAction = bindActionBySourceAndPerspective(storeDefinition, action)
216
+ const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
217
217
  const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
218
218
 
219
219
  expect(() =>
220
220
  boundAction(instance, {
221
- source: {invalid: 'source'} as unknown as DocumentSource,
221
+ resource: {invalid: 'resource'} as unknown as DocumentResource,
222
222
  perspective: 'drafts',
223
223
  }),
224
- ).toThrow('Received invalid source:')
224
+ ).toThrow('Received invalid resource:')
225
225
  })
226
226
 
227
- it('should throw an error when no source provided and projectId/dataset are missing', () => {
227
+ it('should throw an error when no resource provided and projectId/dataset are missing', () => {
228
228
  const storeDefinition = {
229
229
  name: 'PerspectiveStore',
230
230
  getInitialState: () => ({counter: 0}),
231
231
  }
232
232
  const action = vi.fn((_context) => 'success')
233
- const boundAction = bindActionBySourceAndPerspective(storeDefinition, action)
233
+ const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
234
234
  const instance = createSanityInstance({projectId: '', dataset: ''})
235
235
 
236
236
  expect(() => boundAction(instance, {perspective: 'drafts'})).toThrow(
@@ -238,33 +238,33 @@ describe('bindActionBySourceAndPerspective', () => {
238
238
  )
239
239
  })
240
240
 
241
- it('should work correctly with a valid dataset source and explicit perspective', () => {
241
+ it('should work correctly with a valid dataset resource and explicit perspective', () => {
242
242
  const storeDefinition = {
243
243
  name: 'PerspectiveStore',
244
244
  getInitialState: () => ({counter: 0}),
245
245
  }
246
246
  const action = vi.fn((_context) => 'success')
247
- const boundAction = bindActionBySourceAndPerspective(storeDefinition, action)
247
+ const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
248
248
  const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
249
249
 
250
250
  const result = boundAction(instance, {
251
- source: {projectId: 'proj2', dataset: 'ds2'},
251
+ resource: {projectId: 'proj2', dataset: 'ds2'},
252
252
  perspective: 'drafts',
253
253
  })
254
254
  expect(result).toBe('success')
255
255
  })
256
256
 
257
- it('should work correctly with valid dataset source and no perspective (falls back to drafts)', () => {
257
+ it('should work correctly with valid dataset resource and no perspective (falls back to drafts)', () => {
258
258
  const storeDefinition = {
259
259
  name: 'PerspectiveStore',
260
260
  getInitialState: () => ({counter: 0}),
261
261
  }
262
262
  const action = vi.fn((_context) => 'success')
263
- const boundAction = bindActionBySourceAndPerspective(storeDefinition, action)
263
+ const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
264
264
  const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
265
265
 
266
266
  const result = boundAction(instance, {
267
- source: {projectId: 'proj1', dataset: 'ds1'},
267
+ resource: {projectId: 'proj1', dataset: 'ds1'},
268
268
  })
269
269
  expect(result).toBe('success')
270
270
  })
@@ -275,7 +275,7 @@ describe('bindActionBySourceAndPerspective', () => {
275
275
  getInitialState: () => ({counter: 0}),
276
276
  }
277
277
  const action = vi.fn((context) => context.key)
278
- const boundAction = bindActionBySourceAndPerspective(storeDefinition, action)
278
+ const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
279
279
  const instance = createSanityInstance({
280
280
  projectId: 'proj1',
281
281
  dataset: 'ds1',
@@ -300,7 +300,7 @@ describe('bindActionBySourceAndPerspective', () => {
300
300
  context.state.counter += increment
301
301
  return context.state.counter
302
302
  })
303
- const boundAction = bindActionBySourceAndPerspective(storeDefinition, action)
303
+ const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
304
304
  // Use unique project/dataset so we don't reuse stores from other tests
305
305
  const instance = createSanityInstance({
306
306
  projectId: 'perspective-isolation',
@@ -322,7 +322,7 @@ describe('bindActionBySourceAndPerspective', () => {
322
322
  getInitialState: () => ({counter: 0}),
323
323
  }
324
324
  const action = vi.fn((_context) => 'success')
325
- const boundAction = bindActionBySourceAndPerspective(storeDefinition, action)
325
+ const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
326
326
  const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'})
327
327
 
328
328
  const result = boundAction(instance, {
@@ -339,7 +339,7 @@ describe('bindActionBySourceAndPerspective', () => {
339
339
  )
340
340
  })
341
341
 
342
- it('should reuse same store when same source and perspective are used', () => {
342
+ it('should reuse same store when same resource and perspective are used', () => {
343
343
  const storeDefinition = {
344
344
  name: 'PerspectiveStore',
345
345
  getInitialState: () => ({counter: 0}),
@@ -348,7 +348,7 @@ describe('bindActionBySourceAndPerspective', () => {
348
348
  context.state.counter += increment
349
349
  return context.state.counter
350
350
  })
351
- const boundAction = bindActionBySourceAndPerspective(storeDefinition, action)
351
+ const boundAction = bindActionByResourceAndPerspective(storeDefinition, action)
352
352
  // Use unique project/dataset so we don't reuse stores from other tests
353
353
  const instance = createSanityInstance({
354
354
  projectId: 'perspective-reuse',