@sanity/sdk 2.8.0 → 2.9.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 (92) hide show
  1. package/dist/_chunks-dts/utils.d.ts +2396 -0
  2. package/dist/_chunks-es/_internal.js +129 -0
  3. package/dist/_chunks-es/_internal.js.map +1 -0
  4. package/dist/_chunks-es/createGroqSearchFilter.js +1460 -0
  5. package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -0
  6. package/dist/_chunks-es/telemetryManager.js +87 -0
  7. package/dist/_chunks-es/telemetryManager.js.map +1 -0
  8. package/dist/_chunks-es/version.js +7 -0
  9. package/dist/_chunks-es/version.js.map +1 -0
  10. package/dist/_exports/_internal.d.ts +64 -0
  11. package/dist/_exports/_internal.js +20 -0
  12. package/dist/_exports/_internal.js.map +1 -0
  13. package/dist/index.d.ts +2 -2343
  14. package/dist/index.js +383 -1777
  15. package/dist/index.js.map +1 -1
  16. package/package.json +11 -4
  17. package/src/_exports/_internal.ts +14 -0
  18. package/src/_exports/index.ts +10 -1
  19. package/src/auth/authStore.test.ts +150 -1
  20. package/src/auth/authStore.ts +11 -11
  21. package/src/auth/dashboardAuth.ts +2 -2
  22. package/src/auth/handleAuthCallback.ts +9 -3
  23. package/src/auth/logout.test.ts +1 -1
  24. package/src/auth/logout.ts +1 -1
  25. package/src/auth/refreshStampedToken.test.ts +118 -1
  26. package/src/auth/refreshStampedToken.ts +3 -2
  27. package/src/auth/standaloneAuth.ts +9 -3
  28. package/src/auth/studioAuth.ts +34 -7
  29. package/src/auth/studioModeAuth.ts +2 -1
  30. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +10 -2
  31. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +5 -1
  32. package/src/auth/subscribeToStorageEventsAndSetToken.ts +2 -2
  33. package/src/auth/utils.ts +33 -0
  34. package/src/client/clientStore.test.ts +14 -0
  35. package/src/client/clientStore.ts +2 -1
  36. package/src/comlink/node/getNodeState.ts +2 -1
  37. package/src/config/sanityConfig.ts +6 -0
  38. package/src/document/actions.ts +18 -11
  39. package/src/document/applyDocumentActions.test.ts +7 -6
  40. package/src/document/applyDocumentActions.ts +10 -4
  41. package/src/document/documentStore.test.ts +536 -188
  42. package/src/document/documentStore.ts +142 -76
  43. package/src/document/events.ts +7 -2
  44. package/src/document/permissions.test.ts +18 -16
  45. package/src/document/permissions.ts +35 -11
  46. package/src/document/processActions.test.ts +359 -32
  47. package/src/document/processActions.ts +104 -76
  48. package/src/document/reducers.test.ts +117 -29
  49. package/src/document/reducers.ts +43 -36
  50. package/src/document/sharedListener.ts +16 -6
  51. package/src/document/util.ts +14 -0
  52. package/src/favorites/favorites.test.ts +9 -2
  53. package/src/presence/bifurTransport.ts +6 -1
  54. package/src/preview/getPreviewState.test.ts +115 -98
  55. package/src/preview/getPreviewState.ts +38 -60
  56. package/src/preview/previewProjectionUtils.test.ts +179 -0
  57. package/src/preview/previewProjectionUtils.ts +93 -0
  58. package/src/preview/resolvePreview.test.ts +42 -25
  59. package/src/preview/resolvePreview.ts +29 -10
  60. package/src/preview/{previewStore.ts → types.ts} +8 -17
  61. package/src/projection/getProjectionState.test.ts +16 -16
  62. package/src/projection/getProjectionState.ts +2 -1
  63. package/src/projection/projectionQuery.ts +2 -3
  64. package/src/projection/types.ts +1 -1
  65. package/src/query/queryStore.ts +2 -1
  66. package/src/releases/getPerspectiveState.ts +7 -6
  67. package/src/releases/releasesStore.test.ts +20 -5
  68. package/src/releases/releasesStore.ts +20 -8
  69. package/src/store/createStateSourceAction.test.ts +62 -0
  70. package/src/store/createStateSourceAction.ts +34 -39
  71. package/src/telemetry/__telemetry__/sdk.telemetry.ts +42 -0
  72. package/src/telemetry/devMode.test.ts +52 -0
  73. package/src/telemetry/devMode.ts +40 -0
  74. package/src/telemetry/initTelemetry.test.ts +225 -0
  75. package/src/telemetry/initTelemetry.ts +205 -0
  76. package/src/telemetry/telemetryManager.test.ts +263 -0
  77. package/src/telemetry/telemetryManager.ts +187 -0
  78. package/src/users/usersStore.test.ts +1 -0
  79. package/src/users/usersStore.ts +5 -1
  80. package/src/utils/createFetcherStore.test.ts +6 -4
  81. package/src/utils/createFetcherStore.ts +2 -1
  82. package/src/utils/getStagingApiHost.test.ts +21 -0
  83. package/src/utils/getStagingApiHost.ts +14 -0
  84. package/src/utils/ids.test.ts +1 -29
  85. package/src/utils/ids.ts +0 -10
  86. package/src/utils/setCleanupTimeout.ts +24 -0
  87. package/src/preview/previewQuery.test.ts +0 -236
  88. package/src/preview/previewQuery.ts +0 -153
  89. package/src/preview/previewStore.test.ts +0 -36
  90. package/src/preview/subscribeToStateAndFetchBatches.test.ts +0 -221
  91. package/src/preview/subscribeToStateAndFetchBatches.ts +0 -112
  92. package/src/preview/util.ts +0 -13
@@ -1,221 +0,0 @@
1
- import {NEVER, Observable, type Observer} from 'rxjs'
2
- import {describe, expect, it, vi} from 'vitest'
3
-
4
- import {getQueryState, resolveQuery} from '../query/queryStore'
5
- import {type BoundDatasetKey} from '../store/createActionBinder'
6
- import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
7
- import {type StateSource} from '../store/createStateSourceAction'
8
- import {createStoreState, type StoreState} from '../store/createStoreState'
9
- import {type PreviewQueryResult, type PreviewStoreState} from './previewStore'
10
- import {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'
11
- import {PREVIEW_PERSPECTIVE, PREVIEW_TAG} from './util'
12
-
13
- vi.mock('../query/queryStore')
14
-
15
- describe('subscribeToStateAndFetchBatches', () => {
16
- let instance: SanityInstance
17
- let state: StoreState<PreviewStoreState>
18
- let key: BoundDatasetKey
19
-
20
- beforeEach(() => {
21
- vi.clearAllMocks()
22
- instance = createSanityInstance({projectId: 'test', dataset: 'test'})
23
- state = createStoreState<PreviewStoreState>({
24
- subscriptions: {},
25
- values: {},
26
- })
27
- key = {name: 'test.test', projectId: 'test', dataset: 'test'}
28
-
29
- vi.mocked(getQueryState).mockReturnValue({
30
- getCurrent: () => undefined,
31
- observable: NEVER as Observable<PreviewQueryResult[] | undefined>,
32
- } as StateSource<PreviewQueryResult[] | undefined>)
33
-
34
- vi.mocked(resolveQuery).mockResolvedValue(undefined)
35
- })
36
-
37
- afterEach(() => {
38
- instance.dispose()
39
- })
40
-
41
- it('batches rapid subscription changes into single requests', async () => {
42
- const subscription = subscribeToStateAndFetchBatches({instance, state, key})
43
-
44
- // Add multiple subscriptions rapidly
45
- state.set('addSubscription1', {
46
- subscriptions: {doc1: {sub1: true}},
47
- })
48
-
49
- state.set('addSubscription2', (prev) => ({
50
- subscriptions: {...prev.subscriptions, doc2: {sub2: true}},
51
- }))
52
-
53
- // Wait for debounce
54
- await new Promise((resolve) => setTimeout(resolve, 100))
55
-
56
- expect(getQueryState).toHaveBeenCalledTimes(1)
57
- expect(getQueryState).toHaveBeenCalledWith(
58
- instance,
59
- expect.objectContaining({
60
- params: expect.objectContaining({
61
- __ids_71322c7a: ['doc1', 'drafts.doc1', 'doc2', 'drafts.doc2'],
62
- }),
63
- perspective: PREVIEW_PERSPECTIVE,
64
- tag: PREVIEW_TAG,
65
- query: expect.any(String),
66
- }),
67
- )
68
-
69
- subscription.unsubscribe()
70
- })
71
-
72
- it('processes query results and updates state with resolved values', async () => {
73
- const teardown = vi.fn()
74
- const subscriber = vi
75
- .fn<(observer: Observer<PreviewQueryResult[] | undefined>) => () => void>()
76
- .mockReturnValue(teardown)
77
-
78
- vi.mocked(getQueryState).mockReturnValue({
79
- getCurrent: () => undefined,
80
- observable: new Observable(subscriber),
81
- } as StateSource<PreviewQueryResult[] | undefined>)
82
-
83
- const subscription = subscribeToStateAndFetchBatches({instance, state, key})
84
-
85
- expect(subscriber).not.toHaveBeenCalled()
86
-
87
- // Add a subscription
88
- state.set('addSubscription', {
89
- subscriptions: {doc1: {sub1: true}},
90
- })
91
-
92
- expect(subscriber).not.toHaveBeenCalled()
93
-
94
- // Wait for debounce
95
- await new Promise((resolve) => setTimeout(resolve, 100))
96
-
97
- expect(subscriber).toHaveBeenCalled()
98
- expect(teardown).not.toHaveBeenCalled()
99
-
100
- const [observer] = subscriber.mock.lastCall!
101
-
102
- const timestamp = new Date().toISOString()
103
-
104
- observer.next([
105
- {
106
- _id: 'doc1',
107
- _type: 'test',
108
- _updatedAt: timestamp,
109
- titleCandidates: {title: 'Test Document'},
110
- subtitleCandidates: {description: 'Test Description'},
111
- },
112
- ])
113
-
114
- const {values} = state.get()
115
- expect(values['doc1']).toEqual({
116
- isPending: false,
117
- data: expect.objectContaining({
118
- title: 'Test Document',
119
- }),
120
- })
121
-
122
- subscription.unsubscribe()
123
- expect(teardown).toHaveBeenCalled()
124
- })
125
-
126
- it('handles new subscriptions optimistically with pending states', async () => {
127
- state.set('initializeValues', {
128
- values: {doc1: {data: {title: 'Doc 1'}, isPending: false}},
129
- subscriptions: {doc1: {sub1: true}},
130
- })
131
-
132
- const subscription = subscribeToStateAndFetchBatches({instance, state, key})
133
-
134
- // Add a subscription for a document already in the batch
135
- state.set('addSubscriptionAlreadyInBatch', (prev) => ({
136
- subscriptions: {doc1: {sub1: true, ...prev.subscriptions['doc1'], sub2: true}},
137
- }))
138
-
139
- // this isn't a new subscription so it isn't pending by design.
140
- // the pending state is intended to only appear for new documents
141
- expect(state.get().values['doc1']).toEqual({data: {title: 'Doc 1'}, isPending: false})
142
-
143
- expect(state.get().values['doc2']).toBeUndefined()
144
-
145
- state.set('addSubscriptionNotInBatch', {
146
- subscriptions: {doc2: {sub1: true}},
147
- })
148
-
149
- await new Promise((resolve) => setTimeout(resolve, 100))
150
-
151
- expect(state.get().values['doc2']).toEqual({data: null, isPending: true})
152
-
153
- subscription.unsubscribe()
154
- })
155
-
156
- it('cancels and restarts fetches when subscription set changes', async () => {
157
- const abortSpy = vi.spyOn(AbortController.prototype, 'abort')
158
- const subscription = subscribeToStateAndFetchBatches({instance, state, key})
159
-
160
- // Add initial subscription
161
- state.set('addSubscription1', {
162
- subscriptions: {doc1: {sub1: true}},
163
- })
164
-
165
- await new Promise((resolve) => setTimeout(resolve, 100))
166
-
167
- // Add another subscription before first fetch completes
168
- state.set('addSubscription2', (prev) => ({
169
- subscriptions: {...prev.subscriptions, doc2: {sub2: true}},
170
- }))
171
-
172
- await new Promise((resolve) => setTimeout(resolve, 100))
173
-
174
- expect(getQueryState).toHaveBeenCalledTimes(2)
175
- expect(abortSpy).toHaveBeenCalled()
176
-
177
- subscription.unsubscribe()
178
- })
179
-
180
- it('processes and applies fetch results correctly', async () => {
181
- const subscriber = vi.fn<(observer: Observer<PreviewQueryResult[] | undefined>) => () => void>()
182
-
183
- vi.mocked(getQueryState).mockReturnValue({
184
- getCurrent: () => undefined,
185
- observable: new Observable(subscriber),
186
- } as StateSource<PreviewQueryResult[] | undefined>)
187
-
188
- const subscription = subscribeToStateAndFetchBatches({instance, state, key})
189
-
190
- // Add a subscription
191
- state.set('addSubscription', {
192
- subscriptions: {doc1: {sub1: true}},
193
- })
194
-
195
- await new Promise((resolve) => setTimeout(resolve, 100))
196
-
197
- expect(subscriber).toHaveBeenCalled()
198
- const [observer] = subscriber.mock.lastCall!
199
-
200
- // Emit fetch results
201
- observer.next([
202
- {
203
- _id: 'doc1',
204
- _type: 'test',
205
- _updatedAt: '2024-01-01T00:00:00Z',
206
- titleCandidates: {title: 'Test Document'},
207
- subtitleCandidates: {description: 'Test Description'},
208
- },
209
- ])
210
-
211
- // Check that the state was updated
212
- expect(state.get().values['doc1']).toEqual({
213
- data: expect.objectContaining({
214
- title: 'Test Document',
215
- }),
216
- isPending: false,
217
- })
218
-
219
- subscription.unsubscribe()
220
- })
221
- })
@@ -1,112 +0,0 @@
1
- import {
2
- debounceTime,
3
- defer,
4
- distinctUntilChanged,
5
- EMPTY,
6
- filter,
7
- from,
8
- map,
9
- Observable,
10
- pairwise,
11
- startWith,
12
- Subscription,
13
- switchMap,
14
- tap,
15
- } from 'rxjs'
16
-
17
- import {getQueryState, resolveQuery} from '../query/queryStore'
18
- import {type BoundDatasetKey} from '../store/createActionBinder'
19
- import {type StoreContext} from '../store/defineStore'
20
- import {createPreviewQuery, processPreviewQuery} from './previewQuery'
21
- import {type PreviewQueryResult, type PreviewStoreState} from './previewStore'
22
- import {PREVIEW_PERSPECTIVE, PREVIEW_TAG} from './util'
23
-
24
- const BATCH_DEBOUNCE_TIME = 50
25
-
26
- const isSetEqual = <T>(a: Set<T>, b: Set<T>) =>
27
- a.size === b.size && Array.from(a).every((i) => b.has(i))
28
-
29
- export const subscribeToStateAndFetchBatches = ({
30
- state,
31
- instance,
32
- key: {projectId, dataset},
33
- }: StoreContext<PreviewStoreState, BoundDatasetKey>): Subscription => {
34
- const newSubscriberIds$ = state.observable.pipe(
35
- map(({subscriptions}) => new Set(Object.keys(subscriptions))),
36
- distinctUntilChanged(isSetEqual),
37
- debounceTime(BATCH_DEBOUNCE_TIME),
38
- startWith(new Set<string>()),
39
- pairwise(),
40
- tap(([prevIds, currIds]) => {
41
- // for all new subscriptions, set their values to pending
42
- const newIds = [...currIds].filter((element) => !prevIds.has(element))
43
- state.set('updatingPending', (prev) => {
44
- const pendingValues = newIds.reduce<PreviewStoreState['values']>((acc, id) => {
45
- const prevValue = prev.values[id]
46
- const value = prevValue?.data ? prevValue.data : null
47
- acc[id] = {data: value, isPending: true}
48
- return acc
49
- }, {})
50
- return {values: {...prev.values, ...pendingValues}}
51
- })
52
- }),
53
- map(([, ids]) => ids),
54
- distinctUntilChanged(isSetEqual),
55
- )
56
-
57
- return newSubscriberIds$
58
- .pipe(
59
- switchMap((ids) => {
60
- if (!ids.size) return EMPTY
61
- const {query, params} = createPreviewQuery(ids)
62
- const controller = new AbortController()
63
- return new Observable<PreviewQueryResult[]>((observer) => {
64
- const {getCurrent, observable} = getQueryState<PreviewQueryResult[]>(instance, {
65
- query,
66
- params,
67
- tag: PREVIEW_TAG,
68
- perspective: PREVIEW_PERSPECTIVE,
69
- projectId,
70
- dataset,
71
- })
72
- const source$ = defer(() => {
73
- if (getCurrent() === undefined) {
74
- return from(
75
- resolveQuery<PreviewQueryResult[]>(instance, {
76
- query,
77
- params,
78
- tag: PREVIEW_TAG,
79
- perspective: PREVIEW_PERSPECTIVE,
80
- signal: controller.signal,
81
- projectId,
82
- dataset,
83
- }),
84
- ).pipe(switchMap(() => observable))
85
- }
86
- return observable
87
- }).pipe(filter((result) => result !== undefined))
88
- const subscription = source$.subscribe(observer)
89
- return () => {
90
- if (!controller.signal.aborted) {
91
- controller.abort()
92
- }
93
-
94
- subscription.unsubscribe()
95
- }
96
- }).pipe(map((data) => ({data, ids})))
97
- }),
98
- map(({ids, data}) => ({
99
- values: processPreviewQuery({
100
- projectId,
101
- dataset,
102
- ids,
103
- results: data,
104
- }),
105
- })),
106
- )
107
- .subscribe({
108
- next: ({values}) => {
109
- state.set('updateResult', (prev) => ({values: {...prev.values, ...values}}))
110
- },
111
- })
112
- }
@@ -1,13 +0,0 @@
1
- import {getEnv} from '../utils/getEnv'
2
- import {type PreviewValue, type ValuePending} from './previewStore'
3
-
4
- export const PREVIEW_TAG = 'preview'
5
- export const PREVIEW_PERSPECTIVE = 'raw'
6
- export const STABLE_EMPTY_PREVIEW: ValuePending<PreviewValue> = {data: null, isPending: false}
7
- export const STABLE_ERROR_PREVIEW: ValuePending<PreviewValue> = {
8
- data: {
9
- title: 'Preview Error',
10
- ...(!!getEnv('DEV') && {subtitle: 'Check the console for more details'}),
11
- },
12
- isPending: false,
13
- }