@sanity/sdk 3.0.0-rc.0 → 3.0.0-rc.1

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 (197) hide show
  1. package/dist/_chunks-dts/createGroqSearchFilter.d.ts +1147 -0
  2. package/dist/_chunks-dts/createGroqSearchFilter.d.ts.map +1 -0
  3. package/dist/_chunks-es/_internal.js +134 -0
  4. package/dist/_chunks-es/_internal.js.map +1 -0
  5. package/dist/_chunks-es/createGroqSearchFilter.js +1864 -0
  6. package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -0
  7. package/dist/_chunks-es/telemetryManager.js +93 -0
  8. package/dist/_chunks-es/telemetryManager.js.map +1 -0
  9. package/dist/_exports/_internal.d.ts +95 -0
  10. package/dist/_exports/_internal.d.ts.map +1 -0
  11. package/dist/_exports/_internal.js +24 -0
  12. package/dist/_exports/_internal.js.map +1 -0
  13. package/dist/index.d.ts +1079 -1076
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +1967 -2302
  16. package/dist/index.js.map +1 -1
  17. package/package.json +48 -47
  18. package/src/_exports/_internal.ts +26 -0
  19. package/src/_exports/index.ts +120 -11
  20. package/src/access/checkPermissions.test.ts +55 -0
  21. package/src/access/checkPermissions.ts +54 -0
  22. package/src/agent/agentActions.test.ts +16 -60
  23. package/src/agent/agentActions.ts +25 -38
  24. package/src/applications/applications.test-d.ts +83 -0
  25. package/src/applications/applications.test.ts +130 -0
  26. package/src/applications/applications.ts +364 -0
  27. package/src/applications/userApplications.test.ts +126 -0
  28. package/src/applications/userApplications.ts +261 -0
  29. package/src/auth/authLogger.ts +30 -0
  30. package/src/auth/authMode.test.ts +25 -0
  31. package/src/auth/authMode.ts +6 -3
  32. package/src/auth/authStore.test.ts +194 -13
  33. package/src/auth/authStore.ts +59 -26
  34. package/src/auth/getOrganizationVerificationState.test.ts +18 -17
  35. package/src/auth/getOrganizationVerificationState.ts +4 -4
  36. package/src/auth/handleAuthCallback.test.ts +35 -1
  37. package/src/auth/handleAuthCallback.ts +25 -6
  38. package/src/auth/logout.test.ts +75 -2
  39. package/src/auth/logout.ts +23 -4
  40. package/src/auth/refreshStampedToken.test.ts +219 -469
  41. package/src/auth/refreshStampedToken.ts +79 -195
  42. package/src/auth/studioAuth.ts +1 -1
  43. package/src/auth/studioModeAuth.ts +2 -1
  44. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +146 -13
  45. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +37 -6
  46. package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +2 -0
  47. package/src/client/clientStore.test.ts +62 -37
  48. package/src/client/clientStore.ts +45 -75
  49. package/src/client/liveEvents.test.ts +151 -0
  50. package/src/client/liveEvents.ts +107 -0
  51. package/src/comlink/controller/actions/destroyController.test.ts +4 -1
  52. package/src/comlink/controller/actions/getOrCreateChannel.test.ts +4 -1
  53. package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
  54. package/src/comlink/controller/actions/getOrCreateController.test.ts +4 -1
  55. package/src/comlink/controller/actions/releaseChannel.test.ts +1 -1
  56. package/src/comlink/controller/comlinkControllerStore.test.ts +4 -1
  57. package/src/comlink/node/actions/getOrCreateNode.test.ts +9 -3
  58. package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
  59. package/src/comlink/node/actions/releaseNode.test.ts +7 -4
  60. package/src/comlink/node/comlinkNodeStore.test.ts +2 -2
  61. package/src/comlink/node/getNodeState.test.ts +1 -1
  62. package/src/comlink/node/getNodeState.ts +2 -1
  63. package/src/config/__tests__/handles.test.ts +18 -12
  64. package/src/config/handles.ts +25 -7
  65. package/src/config/sanityConfig.ts +109 -79
  66. package/src/datasets/datasets.test.ts +2 -2
  67. package/src/datasets/datasets.ts +10 -4
  68. package/src/document/actions.test.ts +115 -33
  69. package/src/document/actions.ts +212 -15
  70. package/src/document/applyDocumentActions.test.ts +38 -12
  71. package/src/document/applyDocumentActions.ts +30 -9
  72. package/src/document/documentConstants.ts +7 -0
  73. package/src/document/documentStore.concurrency.test.ts +1306 -0
  74. package/src/document/documentStore.test.ts +673 -278
  75. package/src/document/documentStore.ts +162 -71
  76. package/src/document/events.test.ts +57 -2
  77. package/src/document/events.ts +82 -26
  78. package/src/document/listen.test.ts +56 -0
  79. package/src/document/listen.ts +74 -21
  80. package/src/document/listenerEventOperators.test.ts +311 -0
  81. package/src/document/listenerEventOperators.ts +217 -0
  82. package/src/document/patchOperations.test.ts +49 -2
  83. package/src/document/patchOperations.ts +50 -0
  84. package/src/document/permissions.test.ts +105 -60
  85. package/src/document/permissions.ts +44 -19
  86. package/src/document/processActions/create.ts +138 -0
  87. package/src/document/processActions/delete.ts +100 -0
  88. package/src/document/processActions/discard.ts +63 -0
  89. package/src/document/processActions/edit.ts +154 -0
  90. package/src/document/processActions/processActions.ts +218 -0
  91. package/src/document/processActions/publish.ts +120 -0
  92. package/src/document/processActions/releaseArchive.ts +77 -0
  93. package/src/document/processActions/releaseCreate.ts +59 -0
  94. package/src/document/processActions/releaseDelete.ts +65 -0
  95. package/src/document/processActions/releaseEdit.ts +37 -0
  96. package/src/document/processActions/releasePublish.ts +45 -0
  97. package/src/document/processActions/releaseSchedule.ts +87 -0
  98. package/src/document/processActions/releaseUtil.ts +31 -0
  99. package/src/document/processActions/shared.ts +308 -0
  100. package/src/document/processActions/unpublish.ts +85 -0
  101. package/src/document/processActions.test.ts +1129 -121
  102. package/src/document/reducers.test.ts +639 -64
  103. package/src/document/reducers.ts +187 -48
  104. package/src/document/resourceRules.test.ts +178 -0
  105. package/src/document/resourceRules.ts +117 -0
  106. package/src/document/sharedListener.test.ts +16 -14
  107. package/src/document/sharedListener.ts +24 -8
  108. package/src/document/util.ts +14 -0
  109. package/src/favorites/favorites.test.ts +5 -6
  110. package/src/installations/installations.test-d.ts +68 -0
  111. package/src/installations/installations.test.ts +62 -0
  112. package/src/installations/installations.ts +202 -0
  113. package/src/organization/organization.test-d.ts +36 -0
  114. package/src/organization/organization.test.ts +138 -0
  115. package/src/organization/organization.ts +138 -0
  116. package/src/organizations/organizations.test-d.ts +56 -0
  117. package/src/organizations/organizations.test.ts +150 -0
  118. package/src/organizations/organizations.ts +107 -0
  119. package/src/presence/bifurTransport.test.ts +46 -6
  120. package/src/presence/bifurTransport.ts +19 -2
  121. package/src/presence/presenceStore.test.ts +94 -23
  122. package/src/presence/presenceStore.ts +85 -29
  123. package/src/preview/getPreviewState.test.ts +137 -0
  124. package/src/preview/getPreviewState.ts +69 -0
  125. package/src/preview/previewProjectionUtils.test.ts +9 -22
  126. package/src/preview/previewProjectionUtils.ts +7 -3
  127. package/src/preview/resolvePreview.test.ts +64 -0
  128. package/src/preview/resolvePreview.ts +43 -0
  129. package/src/preview/types.ts +10 -0
  130. package/src/project/project.test-d.ts +37 -0
  131. package/src/project/project.test.ts +157 -12
  132. package/src/project/project.ts +138 -25
  133. package/src/projection/getProjectionState.test.ts +18 -20
  134. package/src/projection/getProjectionState.ts +23 -3
  135. package/src/projection/projectionQuery.ts +5 -5
  136. package/src/projection/projectionStore.test.ts +1 -1
  137. package/src/projection/resolveProjection.test.ts +1 -2
  138. package/src/projection/resolveProjection.ts +16 -0
  139. package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
  140. package/src/projection/subscribeToStateAndFetchBatches.ts +32 -23
  141. package/src/projection/types.ts +8 -0
  142. package/src/projects/projects.test-d.ts +32 -0
  143. package/src/projects/projects.test.ts +108 -42
  144. package/src/projects/projects.ts +63 -27
  145. package/src/query/queryStore.test.ts +259 -123
  146. package/src/query/queryStore.ts +78 -63
  147. package/src/query/reducers.test.ts +0 -42
  148. package/src/query/reducers.ts +2 -18
  149. package/src/releases/getPerspectiveState.test.ts +17 -23
  150. package/src/releases/getPerspectiveState.ts +8 -20
  151. package/src/releases/observeReleases.test.ts +127 -0
  152. package/src/releases/observeReleases.ts +102 -0
  153. package/src/releases/releasesStore.test.ts +100 -53
  154. package/src/releases/releasesStore.ts +69 -44
  155. package/src/releases/utils/sortReleases.test.ts +38 -2
  156. package/src/releases/utils/sortReleases.ts +15 -10
  157. package/src/store/createActionBinder.test.ts +87 -90
  158. package/src/store/createActionBinder.ts +79 -25
  159. package/src/store/createSanityInstance.test.ts +83 -13
  160. package/src/store/createSanityInstance.ts +77 -2
  161. package/src/store/createStateSourceAction.test.ts +64 -2
  162. package/src/store/createStateSourceAction.ts +39 -44
  163. package/src/store/createStoreInstance.test.ts +4 -2
  164. package/src/store/fetcherStore.test-d.ts +121 -0
  165. package/src/store/fetcherStore.test.ts +299 -0
  166. package/src/store/fetcherStore.ts +801 -0
  167. package/src/telemetry/environment.test.ts +119 -0
  168. package/src/telemetry/environment.ts +92 -0
  169. package/src/telemetry/events.ts +42 -0
  170. package/src/telemetry/initTelemetry.test.ts +432 -0
  171. package/src/telemetry/initTelemetry.ts +218 -0
  172. package/src/telemetry/telemetryManager.test.ts +327 -0
  173. package/src/telemetry/telemetryManager.ts +199 -0
  174. package/src/users/reducers.test.ts +6 -1
  175. package/src/users/reducers.ts +5 -6
  176. package/src/users/types.ts +4 -4
  177. package/src/users/usersStore.test.ts +16 -12
  178. package/src/users/usersStore.ts +5 -1
  179. package/src/utils/buildQuery.test.ts +14 -0
  180. package/src/utils/buildQuery.ts +17 -0
  181. package/src/utils/createFetcherStore.test.ts +7 -5
  182. package/src/utils/createFetcherStore.ts +8 -5
  183. package/src/utils/getStagingApiHost.test.ts +21 -0
  184. package/src/utils/getStagingApiHost.ts +14 -0
  185. package/src/utils/ids.test.ts +1 -29
  186. package/src/utils/ids.ts +0 -10
  187. package/src/utils/includeShape.ts +36 -0
  188. package/src/utils/isImportError.test.ts +72 -0
  189. package/src/utils/isImportError.ts +34 -0
  190. package/src/utils/logger.test.ts +12 -0
  191. package/src/utils/logger.ts +8 -3
  192. package/src/utils/object.test.ts +95 -0
  193. package/src/utils/object.ts +142 -0
  194. package/src/utils/setCleanupTimeout.ts +37 -0
  195. package/src/document/processActions.ts +0 -707
  196. package/src/utils/defineIntent.test.ts +0 -477
  197. package/src/utils/defineIntent.ts +0 -244
@@ -0,0 +1,299 @@
1
+ import {NEVER, of, startWith, Subject, throwError} from 'rxjs'
2
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
3
+
4
+ import {createSanityInstance, type SanityInstance} from './createSanityInstance'
5
+ import {defineFetcher, defineMutation, type Fetcher} from './fetcherStore'
6
+
7
+ const flush = () => new Promise((resolve) => setTimeout(resolve, 0))
8
+
9
+ describe('fetcherStore', () => {
10
+ let instance: SanityInstance
11
+
12
+ beforeEach(() => {
13
+ instance = createSanityInstance({projectId: 'p', dataset: 'd'})
14
+ vi.useRealTimers()
15
+ })
16
+
17
+ afterEach(() => {
18
+ instance.dispose()
19
+ })
20
+
21
+ it('fetches on subscribe and exposes a success snapshot', async () => {
22
+ const fetcher = defineFetcher<[number], string>({
23
+ name: 'basic',
24
+ getKey: (_i, id) => `${id}`,
25
+ fetch: () => (id) => of(`data-${id}`),
26
+ })
27
+
28
+ const data = await fetcher.resolveState(instance, 1)
29
+ expect(data).toBe('data-1')
30
+ expect(fetcher.getState(instance, 1).getCurrent()).toMatchObject({
31
+ status: 'success',
32
+ data: 'data-1',
33
+ isFetching: false,
34
+ })
35
+ })
36
+
37
+ it('serves fresh data without refetching, and revalidates once stale', async () => {
38
+ const fetchSpy = vi.fn((id: number) => of(`v-${id}`))
39
+ const fetcher = defineFetcher<[number], string>({
40
+ name: 'swr',
41
+ staleTime: 10_000,
42
+ getKey: (_i, id) => `${id}`,
43
+ fetch: () => fetchSpy,
44
+ })
45
+
46
+ const first = fetcher.getState(instance, 1)
47
+ const unsub1 = first.subscribe()
48
+ await vi.waitFor(() => expect(first.getCurrent().status).toBe('success'))
49
+ expect(fetchSpy).toHaveBeenCalledTimes(1)
50
+ unsub1()
51
+
52
+ // Re-subscribe within staleTime — fresh, so no refetch.
53
+ const second = fetcher.getState(instance, 1)
54
+ const unsub2 = second.subscribe()
55
+ await flush()
56
+ expect(fetchSpy).toHaveBeenCalledTimes(1)
57
+ unsub2()
58
+
59
+ // Invalidation forces staleness — an active subscriber revalidates.
60
+ const third = fetcher.getState(instance, 1)
61
+ const unsub3 = third.subscribe()
62
+ await flush()
63
+ fetcher.invalidate(instance, 1)
64
+ await vi.waitFor(() => expect(fetchSpy).toHaveBeenCalledTimes(2))
65
+ unsub3()
66
+ })
67
+
68
+ it('invalidate refetches an actively-subscribed entry', async () => {
69
+ let n = 0
70
+ const fetcher = defineFetcher<[], number>({
71
+ name: 'invalidate',
72
+ getKey: () => 'k',
73
+ fetch: () => () => of(++n),
74
+ })
75
+
76
+ const source = fetcher.getState(instance)
77
+ const unsub = source.subscribe()
78
+ await vi.waitFor(() => expect(source.getCurrent().data).toBe(1))
79
+
80
+ fetcher.invalidate(instance)
81
+ await vi.waitFor(() => expect(source.getCurrent().data).toBe(2))
82
+ unsub()
83
+ })
84
+
85
+ it('refetch bypasses staleness and resolves with fresh data', async () => {
86
+ let n = 0
87
+ const fetcher = defineFetcher<[], number>({
88
+ name: 'refetch',
89
+ staleTime: 60_000,
90
+ getKey: () => 'k',
91
+ fetch: () => () => of(++n),
92
+ })
93
+
94
+ expect(await fetcher.resolveState(instance)).toBe(1)
95
+ expect(await fetcher.refetch(instance)).toBe(2)
96
+ })
97
+
98
+ it('surfaces an initial failure as an error snapshot, background failure on the success arm', async () => {
99
+ let attempt = 0
100
+ const fetcher = defineFetcher<[], string>({
101
+ name: 'errors',
102
+ getKey: () => 'k',
103
+ fetch: () => () => (attempt++ === 0 ? throwError(() => new Error('boom')) : of('recovered')),
104
+ })
105
+
106
+ await expect(fetcher.resolveState(instance)).rejects.toThrow('boom')
107
+ expect(fetcher.getState(instance).getCurrent().status).toBe('error')
108
+
109
+ // Now succeed, then fail a background revalidation: data stays, error attaches.
110
+ await fetcher.refetch(instance)
111
+ expect(fetcher.getState(instance).getCurrent()).toMatchObject({
112
+ status: 'success',
113
+ data: 'recovered',
114
+ })
115
+ })
116
+
117
+ it('pre-seeds an entry via initialData without fetching when the seed is fresh', async () => {
118
+ const fetchSpy = vi.fn(() => of('fetched'))
119
+ const fetcher = defineFetcher<[], string>({
120
+ name: 'seeded',
121
+ staleTime: 10_000,
122
+ getKey: () => 'k',
123
+ fetch: () => fetchSpy,
124
+ initialData: () => ({data: 'seeded', dataUpdatedAt: Date.now()}),
125
+ })
126
+
127
+ const source = fetcher.getState(instance)
128
+ const unsub = source.subscribe()
129
+ await flush()
130
+
131
+ expect(source.getCurrent()).toMatchObject({status: 'success', data: 'seeded'})
132
+ expect(fetchSpy).not.toHaveBeenCalled()
133
+ unsub()
134
+ })
135
+
136
+ it('setData writes optimistically and can be undone', () => {
137
+ const fetcher = defineFetcher<[], string>({
138
+ name: 'setdata',
139
+ getKey: () => 'k',
140
+ fetch: () => () => NEVER,
141
+ })
142
+
143
+ const {undo} = fetcher.setData(instance, [], 'written')
144
+ expect(fetcher.getState(instance).getCurrent().data).toBe('written')
145
+
146
+ undo()
147
+ expect(fetcher.getState(instance).getCurrent().status).toBe('pending')
148
+ })
149
+
150
+ it('garbage-collects an entry written without a subscriber after gcTime', async () => {
151
+ const fetcher = defineFetcher<[], string>({
152
+ name: 'gc-orphan',
153
+ gcTime: 5,
154
+ getKey: () => 'k',
155
+ fetch: () => () => NEVER,
156
+ })
157
+
158
+ // No subscriber ever holds this entry (as an optimistic mutation write would),
159
+ // so only setData's own GC scheduling can clean it up.
160
+ fetcher.setData(instance, [], 'written')
161
+ expect(fetcher.getState(instance).getCurrent().data).toBe('written')
162
+
163
+ await vi.waitFor(() => expect(fetcher.getState(instance).getCurrent().status).toBe('pending'))
164
+ })
165
+
166
+ it('rolls back optimistic writes when a mutation fails', async () => {
167
+ const fetcher = defineFetcher<[], string>({
168
+ name: 'mutate-rollback',
169
+ getKey: () => 'k',
170
+ fetch: () => () => NEVER,
171
+ })
172
+ fetcher.setData(instance, [], 'server')
173
+
174
+ const failing = defineMutation<string, never>({
175
+ name: 'rename-fail',
176
+ mutationFn: () => () => throwError(() => new Error('rejected')),
177
+ onMutate: (write, value) => write(fetcher, [], value),
178
+ })
179
+
180
+ await expect(failing(instance, 'optimistic')).rejects.toThrow('rejected')
181
+ expect(fetcher.getState(instance).getCurrent().data).toBe('server')
182
+ })
183
+
184
+ it('reconciles tagged entries after a successful mutation', async () => {
185
+ let n = 0
186
+ const list = defineFetcher<[], number>({
187
+ name: 'list',
188
+ getKey: () => 'all',
189
+ tags: [{type: 'thing', id: 'LIST'}],
190
+ fetch: () => () => of(++n),
191
+ })
192
+
193
+ const source = list.getState(instance)
194
+ const unsub = source.subscribe()
195
+ await vi.waitFor(() => expect(source.getCurrent().data).toBe(1))
196
+
197
+ const create = defineMutation<void, {ok: true}>({
198
+ name: 'create-thing',
199
+ mutationFn: () => () => of({ok: true}),
200
+ invalidates: [{type: 'thing', id: 'LIST'}],
201
+ })
202
+
203
+ const {invalidated} = await create(instance, undefined)
204
+ await invalidated
205
+ expect(source.getCurrent().data).toBe(2)
206
+ unsub()
207
+ })
208
+
209
+ it('discards a revalidation result that lands after an optimistic write', async () => {
210
+ const server = new Subject<string>()
211
+ const fetcher: Fetcher<[], string> = defineFetcher<[], string>({
212
+ name: 'version-guard',
213
+ staleTime: 0,
214
+ getKey: () => 'k',
215
+ fetch: () => () => server.pipe(startWith('initial')),
216
+ })
217
+
218
+ const source = fetcher.getState(instance)
219
+ const unsub = source.subscribe()
220
+ await vi.waitFor(() => expect(source.getCurrent().data).toBe('initial'))
221
+
222
+ // Force a refetch (in flight, no value yet), then write optimistically.
223
+ const refetching = fetcher.refetch(instance)
224
+ fetcher.setData(instance, [], 'optimistic')
225
+ expect(source.getCurrent().data).toBe('optimistic')
226
+
227
+ // Late server value must NOT clobber the optimistic write.
228
+ server.next('late-server')
229
+ await refetching
230
+ expect(source.getCurrent().data).toBe('optimistic')
231
+ unsub()
232
+ })
233
+
234
+ it('settles a pre-empted in-flight fetch when a forced refetch replaces it', async () => {
235
+ const server = new Subject<string>()
236
+ const fetcher = defineFetcher<[], string>({
237
+ name: 'preempt',
238
+ getKey: () => 'k',
239
+ fetch: () => () => server,
240
+ })
241
+
242
+ // First refetch is in flight with no value yet; a second forced refetch
243
+ // pre-empts it. Both promises must settle rather than hang.
244
+ const first = fetcher.refetch(instance)
245
+ const second = fetcher.refetch(instance)
246
+
247
+ server.next('data')
248
+ server.complete()
249
+
250
+ await expect(first).resolves.toBe('data')
251
+ await expect(second).resolves.toBe('data')
252
+ })
253
+
254
+ it('clears isFetching when a discarded revalidation runs on a non-completing source', async () => {
255
+ const server = new Subject<string>()
256
+ const fetcher = defineFetcher<[], string>({
257
+ name: 'version-guard-fetching',
258
+ staleTime: 0,
259
+ getKey: () => 'k',
260
+ fetch: () => () => server,
261
+ })
262
+
263
+ const source = fetcher.getState(instance)
264
+ const unsub = source.subscribe()
265
+ await vi.waitFor(() => expect(source.getCurrent().isFetching).toBe(true))
266
+
267
+ // Optimistic write bumps the version while the fetch is still in flight.
268
+ fetcher.setData(instance, [], 'optimistic')
269
+
270
+ // The late server value is discarded (version guard) — and because the source
271
+ // never completes, isFetching must be cleared here, not left stuck true.
272
+ server.next('late')
273
+ await flush()
274
+ expect(source.getCurrent()).toMatchObject({data: 'optimistic', isFetching: false})
275
+ unsub()
276
+ })
277
+
278
+ it('polls on refetchInterval while an entry has subscribers', async () => {
279
+ let n = 0
280
+ const fetcher = defineFetcher<[], number>({
281
+ name: 'polling',
282
+ staleTime: 60_000,
283
+ refetchInterval: 10,
284
+ getKey: () => 'k',
285
+ fetch: () => () => of(++n),
286
+ })
287
+
288
+ const source = fetcher.getState(instance)
289
+ const unsub = source.subscribe()
290
+ await vi.waitFor(() => expect((source.getCurrent().data ?? 0) >= 1).toBe(true))
291
+
292
+ // Fresh per staleTime, so any further increase can only come from the interval.
293
+ const before = source.getCurrent().data ?? 0
294
+ await vi.waitFor(() => expect((source.getCurrent().data ?? 0) > before).toBe(true), {
295
+ timeout: 500,
296
+ })
297
+ unsub()
298
+ })
299
+ })