@sanity/sdk 3.0.0-rc.0 → 3.0.0-rc.2
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.
- package/README.md +12 -0
- package/dist/_exports/_internal.d.ts +163 -0
- package/dist/_exports/_internal.d.ts.map +1 -0
- package/dist/_exports/_internal.js +332 -0
- package/dist/_exports/_internal.js.map +1 -0
- package/dist/_exports/agent.d.ts +65 -0
- package/dist/_exports/agent.d.ts.map +1 -0
- package/dist/_exports/agent.js +71 -0
- package/dist/_exports/agent.js.map +1 -0
- package/dist/_exports/comlink.d.ts +118 -0
- package/dist/_exports/comlink.d.ts.map +1 -0
- package/dist/_exports/comlink.js +69 -0
- package/dist/_exports/comlink.js.map +1 -0
- package/dist/authStore-CtE9bLHW.js +754 -0
- package/dist/authStore-CtE9bLHW.js.map +1 -0
- package/dist/authStore-DKFX8fn7.d.ts +3179 -0
- package/dist/authStore-DKFX8fn7.d.ts.map +1 -0
- package/dist/clientStore-BtE2-q2i.js +90 -0
- package/dist/clientStore-BtE2-q2i.js.map +1 -0
- package/dist/createSanityInstance-DUI1bGIg.d.ts +278 -0
- package/dist/createSanityInstance-DUI1bGIg.d.ts.map +1 -0
- package/dist/createStateSourceAction-BOKtqFNm.js +300 -0
- package/dist/createStateSourceAction-BOKtqFNm.js.map +1 -0
- package/dist/getEnv-D5RGkQLu.js +8 -0
- package/dist/getEnv-D5RGkQLu.js.map +1 -0
- package/dist/getNodeState-CR3__B2D.js +67 -0
- package/dist/getNodeState-CR3__B2D.js.map +1 -0
- package/dist/index.d.ts +3 -2332
- package/dist/index.js +4171 -4332
- package/dist/index.js.map +1 -1
- package/dist/logger-DAtJb5RQ.js +173 -0
- package/dist/logger-DAtJb5RQ.js.map +1 -0
- package/dist/object-CxXlSVg7.js +83 -0
- package/dist/object-CxXlSVg7.js.map +1 -0
- package/dist/queryStore-DPf9sN3V.js +1346 -0
- package/dist/queryStore-DPf9sN3V.js.map +1 -0
- package/dist/rolldown-runtime-vyAXikos.js +9 -0
- package/dist/setCleanupTimeout-BiM6q1KF.js +31 -0
- package/dist/setCleanupTimeout-BiM6q1KF.js.map +1 -0
- package/dist/telemetryManager-Dq_qCkeI.js +127 -0
- package/dist/telemetryManager-Dq_qCkeI.js.map +1 -0
- package/dist/version-BChjsi4u.js +9 -0
- package/dist/version-BChjsi4u.js.map +1 -0
- package/package.json +47 -52
- package/src/_exports/_internal.ts +27 -0
- package/src/_exports/agent.ts +19 -0
- package/src/_exports/comlink.ts +16 -0
- package/src/_exports/index.ts +125 -64
- package/src/access/checkPermissions.test.ts +55 -0
- package/src/access/checkPermissions.ts +54 -0
- package/src/agent/agentActions.test.ts +16 -60
- package/src/agent/agentActions.ts +25 -38
- package/src/applications/applications.test-d.ts +88 -0
- package/src/applications/applications.test.ts +130 -0
- package/src/applications/applications.ts +367 -0
- package/src/auth/authLogger.ts +30 -0
- package/src/auth/authStore.test.ts +194 -13
- package/src/auth/authStore.ts +58 -26
- package/src/auth/getOrganizationVerificationState.test.ts +18 -17
- package/src/auth/getOrganizationVerificationState.ts +4 -4
- package/src/auth/handleAuthCallback.test.ts +35 -1
- package/src/auth/handleAuthCallback.ts +25 -6
- package/src/auth/logout.test.ts +75 -2
- package/src/auth/logout.ts +23 -4
- package/src/auth/refreshStampedToken.test.ts +219 -469
- package/src/auth/refreshStampedToken.ts +79 -195
- package/src/auth/studioAuth.ts +1 -1
- package/src/auth/studioModeAuth.ts +2 -1
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +146 -13
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +37 -6
- package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +2 -0
- package/src/client/clientStore.test.ts +62 -37
- package/src/client/clientStore.ts +45 -75
- package/src/client/liveEvents.test.ts +151 -0
- package/src/client/liveEvents.ts +107 -0
- package/src/comlink/controller/actions/destroyController.test.ts +4 -1
- package/src/comlink/controller/actions/getOrCreateChannel.test.ts +4 -1
- package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
- package/src/comlink/controller/actions/getOrCreateController.test.ts +4 -1
- package/src/comlink/controller/actions/releaseChannel.test.ts +1 -1
- package/src/comlink/controller/comlinkControllerStore.test.ts +4 -1
- package/src/comlink/node/actions/getOrCreateNode.test.ts +9 -3
- package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
- package/src/comlink/node/actions/releaseNode.test.ts +7 -4
- package/src/comlink/node/comlinkNodeStore.test.ts +2 -2
- package/src/comlink/node/getNodeState.test.ts +1 -1
- package/src/comlink/node/getNodeState.ts +2 -1
- package/src/comments/addonDatasetStore.test.ts +215 -0
- package/src/comments/addonDatasetStore.ts +249 -0
- package/src/comments/buildCommentThreads.test.ts +83 -0
- package/src/comments/buildCommentThreads.ts +54 -0
- package/src/comments/commentActions.test.ts +540 -0
- package/src/comments/commentActions.ts +555 -0
- package/src/comments/commentFieldPath.test.ts +40 -0
- package/src/comments/commentFieldPath.ts +44 -0
- package/src/comments/commentsConstants.ts +80 -0
- package/src/comments/commentsStore.test.ts +419 -0
- package/src/comments/commentsStore.ts +483 -0
- package/src/comments/normalizeComment.test.ts +116 -0
- package/src/comments/normalizeComment.ts +45 -0
- package/src/comments/observeComments.test.ts +193 -0
- package/src/comments/observeComments.ts +113 -0
- package/src/comments/reducers.test.ts +277 -0
- package/src/comments/reducers.ts +316 -0
- package/src/comments/types.ts +249 -0
- package/src/comments/weakenReferences.test.ts +47 -0
- package/src/comments/weakenReferences.ts +29 -0
- package/src/config/__tests__/handles.test.ts +18 -12
- package/src/config/handles.ts +25 -7
- package/src/config/sanityConfig.ts +40 -82
- package/src/datasets/datasets.test.ts +2 -2
- package/src/datasets/datasets.ts +10 -4
- package/src/document/actions.test.ts +115 -33
- package/src/document/actions.ts +212 -15
- package/src/document/applyDocumentActions.test.ts +35 -13
- package/src/document/applyDocumentActions.ts +30 -9
- package/src/document/documentConstants.ts +15 -0
- package/src/document/documentStore.concurrency.test.ts +1306 -0
- package/src/document/documentStore.test.ts +753 -273
- package/src/document/documentStore.ts +197 -75
- package/src/document/events.test.ts +57 -2
- package/src/document/events.ts +82 -26
- package/src/document/listen.test.ts +56 -0
- package/src/document/listen.ts +74 -21
- package/src/document/listenerEventOperators.test.ts +311 -0
- package/src/document/listenerEventOperators.ts +217 -0
- package/src/document/patchOperations.test.ts +76 -2
- package/src/document/patchOperations.ts +60 -1
- package/src/document/permissions.test.ts +107 -60
- package/src/document/permissions.ts +44 -19
- package/src/document/processActions/create.ts +138 -0
- package/src/document/processActions/delete.ts +100 -0
- package/src/document/processActions/discard.ts +63 -0
- package/src/document/processActions/edit.ts +154 -0
- package/src/document/processActions/processActions.ts +218 -0
- package/src/document/processActions/publish.ts +120 -0
- package/src/document/processActions/releaseArchive.ts +77 -0
- package/src/document/processActions/releaseCreate.ts +59 -0
- package/src/document/processActions/releaseDelete.ts +65 -0
- package/src/document/processActions/releaseEdit.ts +37 -0
- package/src/document/processActions/releasePublish.ts +45 -0
- package/src/document/processActions/releaseSchedule.ts +87 -0
- package/src/document/processActions/releaseUtil.ts +31 -0
- package/src/document/processActions/shared.ts +308 -0
- package/src/document/processActions/unpublish.ts +85 -0
- package/src/document/processActions.test.ts +1129 -121
- package/src/document/reducers.test.ts +772 -64
- package/src/document/reducers.ts +205 -51
- package/src/document/resourceRules.test.ts +178 -0
- package/src/document/resourceRules.ts +117 -0
- package/src/document/sharedListener.test.ts +16 -14
- package/src/document/sharedListener.ts +24 -8
- package/src/document/util.test.ts +46 -0
- package/src/document/util.ts +50 -0
- package/src/favorites/favorites.test.ts +5 -6
- package/src/installations/installations.test-d.ts +73 -0
- package/src/installations/installations.test.ts +62 -0
- package/src/installations/installations.ts +206 -0
- package/src/organization/organization.test-d.ts +36 -0
- package/src/organization/organization.test.ts +138 -0
- package/src/organization/organization.ts +138 -0
- package/src/organizations/organizations.test-d.ts +56 -0
- package/src/organizations/organizations.test.ts +150 -0
- package/src/organizations/organizations.ts +107 -0
- package/src/presence/bifurTransport.test.ts +201 -9
- package/src/presence/bifurTransport.ts +98 -14
- package/src/presence/paths.test.ts +70 -0
- package/src/presence/paths.ts +41 -0
- package/src/presence/presenceStore.test.ts +915 -24
- package/src/presence/presenceStore.ts +576 -104
- package/src/presence/types.ts +175 -3
- package/src/preview/previewProjectionUtils.test.ts +9 -22
- package/src/preview/previewProjectionUtils.ts +7 -3
- package/src/preview/types.ts +0 -15
- package/src/project/project.test-d.ts +37 -0
- package/src/project/project.test.ts +157 -12
- package/src/project/project.ts +138 -25
- package/src/projection/getProjectionState.test.ts +18 -20
- package/src/projection/getProjectionState.ts +23 -3
- package/src/projection/projectionQuery.ts +5 -5
- package/src/projection/projectionStore.test.ts +1 -9
- package/src/projection/resolveProjection.test.ts +1 -2
- package/src/projection/resolveProjection.ts +16 -0
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
- package/src/projection/subscribeToStateAndFetchBatches.ts +32 -23
- package/src/projects/projects.test-d.ts +32 -0
- package/src/projects/projects.test.ts +108 -42
- package/src/projects/projects.ts +63 -27
- package/src/query/queryStore.test.ts +259 -123
- package/src/query/queryStore.ts +85 -69
- package/src/query/reducers.test.ts +0 -42
- package/src/query/reducers.ts +2 -18
- package/src/releases/getPerspectiveState.test.ts +17 -23
- package/src/releases/getPerspectiveState.ts +8 -20
- package/src/releases/observeReleases.test.ts +127 -0
- package/src/releases/observeReleases.ts +102 -0
- package/src/releases/releasesStore.test.ts +100 -53
- package/src/releases/releasesStore.ts +69 -44
- package/src/releases/utils/sortReleases.test.ts +38 -2
- package/src/releases/utils/sortReleases.ts +15 -10
- package/src/store/createActionBinder.test.ts +109 -90
- package/src/store/createActionBinder.ts +36 -28
- package/src/store/createSanityInstance.test.ts +15 -13
- package/src/store/createSanityInstance.ts +14 -2
- package/src/store/createStateSourceAction.test.ts +64 -2
- package/src/store/createStateSourceAction.ts +38 -43
- package/src/store/createStoreInstance.test.ts +4 -2
- package/src/store/fetcherStore.test-d.ts +121 -0
- package/src/store/fetcherStore.test.ts +299 -0
- package/src/store/fetcherStore.ts +801 -0
- package/src/telemetry/environment.test.ts +119 -0
- package/src/telemetry/environment.ts +92 -0
- package/src/telemetry/events.ts +42 -0
- package/src/telemetry/initTelemetry.test.ts +432 -0
- package/src/telemetry/initTelemetry.ts +218 -0
- package/src/telemetry/telemetryManager.test.ts +327 -0
- package/src/telemetry/telemetryManager.ts +199 -0
- package/src/users/reducers.test.ts +3 -1
- package/src/users/reducers.ts +5 -6
- package/src/users/types.ts +4 -4
- package/src/users/usersStore.test.ts +16 -12
- package/src/users/usersStore.ts +5 -1
- package/src/utils/buildQuery.test.ts +14 -0
- package/src/utils/buildQuery.ts +17 -0
- package/src/utils/createFetcherStore.test.ts +7 -5
- package/src/utils/createFetcherStore.ts +8 -5
- package/src/utils/getStagingApiHost.test.ts +21 -0
- package/src/utils/getStagingApiHost.ts +14 -0
- package/src/utils/ids.test.ts +1 -29
- package/src/utils/ids.ts +0 -10
- package/src/utils/includeShape.ts +36 -0
- package/src/utils/isImportError.test.ts +72 -0
- package/src/utils/isImportError.ts +34 -0
- package/src/utils/logger.test.ts +12 -0
- package/src/utils/logger.ts +8 -3
- package/src/utils/object.test.ts +95 -0
- package/src/utils/object.ts +142 -0
- package/src/utils/setCleanupTimeout.ts +37 -0
- package/src/document/processActions.ts +0 -707
- package/src/utils/defineIntent.test.ts +0 -477
- package/src/utils/defineIntent.ts +0 -244
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {createClient, type SanityClient} from '@sanity/client'
|
|
2
2
|
import {Subject} from 'rxjs'
|
|
3
|
-
import {
|
|
3
|
+
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
4
4
|
|
|
5
5
|
import {getAuthMethodState, getTokenState} from '../auth/authStore'
|
|
6
6
|
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
@@ -33,7 +33,10 @@ beforeEach(() => {
|
|
|
33
33
|
vi.mocked(createClient).mockImplementation(
|
|
34
34
|
(clientConfig) => ({config: () => clientConfig}) as SanityClient,
|
|
35
35
|
)
|
|
36
|
-
instance = createSanityInstance(
|
|
36
|
+
instance = createSanityInstance({
|
|
37
|
+
projectId: 'test-project',
|
|
38
|
+
dataset: 'test-dataset',
|
|
39
|
+
})
|
|
37
40
|
})
|
|
38
41
|
|
|
39
42
|
afterEach(() => {
|
|
@@ -42,6 +45,43 @@ afterEach(() => {
|
|
|
42
45
|
|
|
43
46
|
describe('clientStore', () => {
|
|
44
47
|
describe('getClient', () => {
|
|
48
|
+
it('should create a client with default configuration', () => {
|
|
49
|
+
const client = getClient(instance, {apiVersion: '2024-11-12'})
|
|
50
|
+
|
|
51
|
+
const defaultConfiguration = {
|
|
52
|
+
useCdn: false,
|
|
53
|
+
ignoreBrowserTokenWarning: true,
|
|
54
|
+
allowReconfigure: false,
|
|
55
|
+
requestTagPrefix: 'sanity.sdk',
|
|
56
|
+
projectId: 'test-project',
|
|
57
|
+
dataset: 'test-dataset',
|
|
58
|
+
token: 'initial-token',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
expect(vi.mocked(createClient)).toHaveBeenCalledWith({
|
|
62
|
+
...defaultConfiguration,
|
|
63
|
+
apiVersion: '2024-11-12',
|
|
64
|
+
})
|
|
65
|
+
expect(client.config()).toEqual({
|
|
66
|
+
...defaultConfiguration,
|
|
67
|
+
apiVersion: '2024-11-12',
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should pass staging apiHost when __SANITY_STAGING__ is true and no explicit apiHost', () => {
|
|
72
|
+
vi.stubGlobal('__SANITY_STAGING__', true)
|
|
73
|
+
|
|
74
|
+
getClient(instance, {apiVersion: '2024-11-12'})
|
|
75
|
+
|
|
76
|
+
expect(vi.mocked(createClient)).toHaveBeenCalledWith(
|
|
77
|
+
expect.objectContaining({
|
|
78
|
+
apiHost: 'https://api.sanity.work',
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
vi.unstubAllGlobals()
|
|
83
|
+
})
|
|
84
|
+
|
|
45
85
|
it('should throw when using disallowed configuration keys', () => {
|
|
46
86
|
expect(() =>
|
|
47
87
|
getClient(instance, {
|
|
@@ -150,8 +190,8 @@ describe('clientStore', () => {
|
|
|
150
190
|
})
|
|
151
191
|
})
|
|
152
192
|
|
|
153
|
-
describe('
|
|
154
|
-
it('should create
|
|
193
|
+
describe('resource handling', () => {
|
|
194
|
+
it('should create resource when media library resource is provided and be projectless', () => {
|
|
155
195
|
const client = getClient(instance, {
|
|
156
196
|
apiVersion: '2024-11-12',
|
|
157
197
|
resource: {mediaLibraryId: 'media-lib-123'},
|
|
@@ -159,8 +199,8 @@ describe('clientStore', () => {
|
|
|
159
199
|
|
|
160
200
|
expect(vi.mocked(createClient)).toHaveBeenCalledWith(
|
|
161
201
|
expect.objectContaining({
|
|
162
|
-
'
|
|
163
|
-
|
|
202
|
+
resource: {type: 'media-library', id: 'media-lib-123'},
|
|
203
|
+
apiVersion: '2024-11-12',
|
|
164
204
|
}),
|
|
165
205
|
)
|
|
166
206
|
// Client should be projectless - no projectId/dataset in config
|
|
@@ -168,12 +208,12 @@ describe('clientStore', () => {
|
|
|
168
208
|
expect(client.config()).not.toHaveProperty('dataset')
|
|
169
209
|
expect(client.config()).toEqual(
|
|
170
210
|
expect.objectContaining({
|
|
171
|
-
|
|
211
|
+
resource: {type: 'media-library', id: 'media-lib-123'},
|
|
172
212
|
}),
|
|
173
213
|
)
|
|
174
214
|
})
|
|
175
215
|
|
|
176
|
-
it('should create resource when canvas
|
|
216
|
+
it('should create resource when canvas resource is provided and be projectless', () => {
|
|
177
217
|
const client = getClient(instance, {
|
|
178
218
|
apiVersion: '2024-11-12',
|
|
179
219
|
resource: {canvasId: 'canvas-123'},
|
|
@@ -181,8 +221,8 @@ describe('clientStore', () => {
|
|
|
181
221
|
|
|
182
222
|
expect(vi.mocked(createClient)).toHaveBeenCalledWith(
|
|
183
223
|
expect.objectContaining({
|
|
184
|
-
|
|
185
|
-
|
|
224
|
+
resource: {type: 'canvas', id: 'canvas-123'},
|
|
225
|
+
apiVersion: '2024-11-12',
|
|
186
226
|
}),
|
|
187
227
|
)
|
|
188
228
|
// Client should be projectless - no projectId/dataset in config
|
|
@@ -190,47 +230,32 @@ describe('clientStore', () => {
|
|
|
190
230
|
expect(client.config()).not.toHaveProperty('dataset')
|
|
191
231
|
expect(client.config()).toEqual(
|
|
192
232
|
expect.objectContaining({
|
|
193
|
-
|
|
233
|
+
resource: {type: 'canvas', id: 'canvas-123'},
|
|
194
234
|
}),
|
|
195
235
|
)
|
|
196
236
|
})
|
|
197
237
|
|
|
198
|
-
|
|
199
|
-
it.skip('should create projectless client when source is provided, ignoring instance config', () => {
|
|
238
|
+
it('should transform dataset resource to project-based config for now', () => {
|
|
200
239
|
const client = getClient(instance, {
|
|
201
240
|
apiVersion: '2024-11-12',
|
|
202
241
|
resource: {projectId: 'source-project', dataset: 'source-dataset'},
|
|
203
242
|
})
|
|
204
243
|
|
|
205
|
-
|
|
206
|
-
expect(client.config()).not.toHaveProperty('projectId')
|
|
207
|
-
expect(client.config()).not.toHaveProperty('dataset')
|
|
208
|
-
expect(client.config()).toEqual(
|
|
244
|
+
expect(vi.mocked(createClient)).toHaveBeenCalledWith(
|
|
209
245
|
expect.objectContaining({
|
|
210
|
-
|
|
246
|
+
projectId: 'source-project',
|
|
247
|
+
dataset: 'source-dataset',
|
|
211
248
|
}),
|
|
212
249
|
)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
apiVersion: '2024-11-12',
|
|
219
|
-
resource: {mediaLibraryId: 'media-lib-123'},
|
|
220
|
-
projectId: 'explicit-project',
|
|
221
|
-
dataset: 'explicit-dataset',
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
225
|
-
'Both resource and explicit projectId/dataset are provided. The resource will be used and projectId/dataset will be ignored.',
|
|
250
|
+
expect(client.config()).toEqual(
|
|
251
|
+
expect.objectContaining({
|
|
252
|
+
projectId: 'source-project',
|
|
253
|
+
dataset: 'source-dataset',
|
|
254
|
+
}),
|
|
226
255
|
)
|
|
227
|
-
// Client should still be projectless despite explicit projectId/dataset
|
|
228
|
-
expect(client.config()).not.toHaveProperty('projectId')
|
|
229
|
-
expect(client.config()).not.toHaveProperty('dataset')
|
|
230
|
-
consoleSpy.mockRestore()
|
|
231
256
|
})
|
|
232
257
|
|
|
233
|
-
it('should create different clients for different
|
|
258
|
+
it('should create different clients for different resources', () => {
|
|
234
259
|
const client1 = getClient(instance, {
|
|
235
260
|
apiVersion: '2024-11-12',
|
|
236
261
|
resource: {projectId: 'source-project', dataset: 'source-dataset'},
|
|
@@ -250,7 +275,7 @@ describe('clientStore', () => {
|
|
|
250
275
|
expect(vi.mocked(createClient)).toHaveBeenCalledTimes(3)
|
|
251
276
|
})
|
|
252
277
|
|
|
253
|
-
it('should reuse clients with identical
|
|
278
|
+
it('should reuse clients with identical resource configurations', () => {
|
|
254
279
|
const client1 = getClient(instance, {
|
|
255
280
|
apiVersion: '2024-11-12',
|
|
256
281
|
resource: {projectId: 'source-project', dataset: 'source-dataset'},
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type ClientConfig,
|
|
3
|
-
type ClientPerspective,
|
|
4
|
-
createClient,
|
|
5
|
-
type SanityClient,
|
|
6
|
-
} from '@sanity/client'
|
|
7
|
-
import {pick} from 'lodash-es'
|
|
1
|
+
import {type ClientConfig, createClient, type SanityClient} from '@sanity/client'
|
|
8
2
|
|
|
9
3
|
import {getAuthMethodState, getTokenState} from '../auth/authStore'
|
|
10
4
|
import {
|
|
@@ -16,6 +10,8 @@ import {
|
|
|
16
10
|
import {bindActionGlobally} from '../store/createActionBinder'
|
|
17
11
|
import {createStateSourceAction} from '../store/createStateSourceAction'
|
|
18
12
|
import {defineStore, type StoreContext} from '../store/defineStore'
|
|
13
|
+
import {getStagingApiHost} from '../utils/getStagingApiHost'
|
|
14
|
+
import {pickProperties} from '../utils/object'
|
|
19
15
|
|
|
20
16
|
const DEFAULT_API_VERSION = '2024-11-12'
|
|
21
17
|
const DEFAULT_REQUEST_TAG_PREFIX = 'sanity.sdk'
|
|
@@ -35,31 +31,30 @@ type AllowedClientConfigKey =
|
|
|
35
31
|
| 'useProjectHostname'
|
|
36
32
|
|
|
37
33
|
const allowedKeys = Object.keys({
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
'resource': null,
|
|
34
|
+
apiHost: null,
|
|
35
|
+
useCdn: null,
|
|
36
|
+
token: null,
|
|
37
|
+
perspective: null,
|
|
38
|
+
proxy: null,
|
|
39
|
+
withCredentials: null,
|
|
40
|
+
timeout: null,
|
|
41
|
+
maxRetries: null,
|
|
42
|
+
dataset: null,
|
|
43
|
+
projectId: null,
|
|
44
|
+
scope: null,
|
|
45
|
+
apiVersion: null,
|
|
46
|
+
requestTagPrefix: null,
|
|
47
|
+
useProjectHostname: null,
|
|
48
|
+
resource: null,
|
|
54
49
|
} satisfies Record<keyof ClientOptions, null>) as (keyof ClientOptions)[]
|
|
55
50
|
|
|
56
|
-
const DEFAULT_CLIENT_CONFIG = {
|
|
51
|
+
const DEFAULT_CLIENT_CONFIG: ClientConfig = {
|
|
57
52
|
apiVersion: DEFAULT_API_VERSION,
|
|
58
53
|
useCdn: false,
|
|
59
54
|
ignoreBrowserTokenWarning: true,
|
|
60
55
|
allowReconfigure: false,
|
|
61
56
|
requestTagPrefix: DEFAULT_REQUEST_TAG_PREFIX,
|
|
62
|
-
}
|
|
57
|
+
}
|
|
63
58
|
|
|
64
59
|
/**
|
|
65
60
|
* States tracked by the client store
|
|
@@ -71,11 +66,6 @@ export interface ClientStoreState {
|
|
|
71
66
|
authMethod?: 'localstorage' | 'cookie'
|
|
72
67
|
}
|
|
73
68
|
|
|
74
|
-
interface ClientResource {
|
|
75
|
-
type: 'dataset' | 'media-library' | 'canvas'
|
|
76
|
-
id: string
|
|
77
|
-
}
|
|
78
|
-
|
|
79
69
|
/**
|
|
80
70
|
* Options used when retrieving a client instance from the client store.
|
|
81
71
|
*
|
|
@@ -92,34 +82,23 @@ interface ClientResource {
|
|
|
92
82
|
*
|
|
93
83
|
* @public
|
|
94
84
|
*/
|
|
95
|
-
export interface ClientOptions extends
|
|
96
|
-
Pick<ClientConfig, AllowedClientConfigKey>,
|
|
97
|
-
'resource'
|
|
98
|
-
> {
|
|
99
|
-
/**
|
|
100
|
-
* Narrows the inherited `perspective` to exclude stackable perspectives,
|
|
101
|
-
* which are not supported by the SDK.
|
|
102
|
-
*/
|
|
103
|
-
'perspective'?: Exclude<ClientPerspective, readonly unknown[]>
|
|
85
|
+
export interface ClientOptions extends Pick<ClientConfig, AllowedClientConfigKey> {
|
|
104
86
|
/**
|
|
105
87
|
* An optional flag to choose between the default client (typically project-level)
|
|
106
88
|
* and the global client ('global'). When set to `'global'`, the global client
|
|
107
89
|
* is used.
|
|
108
90
|
*/
|
|
109
|
-
|
|
91
|
+
scope?: 'default' | 'global'
|
|
110
92
|
/**
|
|
111
93
|
* A required string indicating the API version for the client.
|
|
112
94
|
*/
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @internal
|
|
116
|
-
*/
|
|
117
|
-
'~experimental_resource'?: ClientConfig['~experimental_resource']
|
|
95
|
+
apiVersion: string
|
|
118
96
|
|
|
119
97
|
/**
|
|
120
98
|
* @internal
|
|
99
|
+
* The SDK resource to use for the client -- this will get transformed into a ClientConfig resource.
|
|
121
100
|
*/
|
|
122
|
-
|
|
101
|
+
resource?: DocumentResource
|
|
123
102
|
}
|
|
124
103
|
|
|
125
104
|
const clientStore = defineStore<ClientStoreState>({
|
|
@@ -156,7 +135,13 @@ const listenToAuthMethod = ({instance, state}: StoreContext<ClientStoreState>) =
|
|
|
156
135
|
})
|
|
157
136
|
}
|
|
158
137
|
|
|
159
|
-
|
|
138
|
+
type ClientInstanceCacheKeyInput = ClientConfig &
|
|
139
|
+
Partial<Pick<ClientOptions, 'scope'>> & {
|
|
140
|
+
apiVersion: string
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const getClientConfigKey = (options: ClientInstanceCacheKeyInput) =>
|
|
144
|
+
JSON.stringify(pickProperties(options, allowedKeys))
|
|
160
145
|
|
|
161
146
|
/**
|
|
162
147
|
* Retrieves a Sanity client instance configured with the provided options.
|
|
@@ -193,10 +178,10 @@ export const getClient = bindActionGlobally(
|
|
|
193
178
|
|
|
194
179
|
const tokenFromState = state.get().token
|
|
195
180
|
const {clients, authMethod} = state.get()
|
|
181
|
+
let projectId = options.projectId ?? instance.config.projectId
|
|
182
|
+
let dataset = options.dataset ?? instance.config.dataset
|
|
196
183
|
|
|
197
|
-
let resource:
|
|
198
|
-
let projectId: string | undefined = options.projectId
|
|
199
|
-
let dataset: string | undefined = options.dataset
|
|
184
|
+
let resource: ClientConfig['resource'] | undefined
|
|
200
185
|
|
|
201
186
|
if (options.resource) {
|
|
202
187
|
if (isMediaLibraryResource(options.resource)) {
|
|
@@ -204,43 +189,31 @@ export const getClient = bindActionGlobally(
|
|
|
204
189
|
} else if (isCanvasResource(options.resource)) {
|
|
205
190
|
resource = {type: 'canvas', id: options.resource.canvasId}
|
|
206
191
|
} else if (isDatasetResource(options.resource)) {
|
|
192
|
+
// use project-based routes for datasets to avoid existing CORS and Studio auth cookie issues
|
|
207
193
|
projectId = options.resource.projectId
|
|
208
194
|
dataset = options.resource.dataset
|
|
209
195
|
}
|
|
210
|
-
// temporarily excluding dataset resource as a resource for now. Many of the global API endpoints require vX api version.
|
|
211
|
-
// When ready, remove the above check and uncomment the below check.
|
|
212
|
-
// } else if (isDatasetResource(options.resource)) {
|
|
213
|
-
// resource = {type: 'dataset', id: `${options.resource.projectId}.${options.resource.dataset}`}
|
|
214
196
|
}
|
|
215
197
|
|
|
216
|
-
const apiHost = options.apiHost ?? instance.config.auth?.apiHost
|
|
198
|
+
const apiHost = options.apiHost ?? instance.config.auth?.apiHost ?? getStagingApiHost()
|
|
217
199
|
|
|
218
|
-
const effectiveOptions = {
|
|
200
|
+
const effectiveOptions: ClientConfig & {apiVersion: string} = {
|
|
219
201
|
...DEFAULT_CLIENT_CONFIG,
|
|
220
202
|
...((options.scope === 'global' || !projectId || resource) && {useProjectHostname: false}),
|
|
221
203
|
token: authMethod === 'cookie' ? undefined : (tokenFromState ?? undefined),
|
|
222
204
|
...options,
|
|
223
205
|
...(projectId && {projectId}),
|
|
224
206
|
...(dataset && {dataset}),
|
|
207
|
+
...(resource ? {resource} : {resource: undefined}),
|
|
225
208
|
...(apiHost && {apiHost}),
|
|
226
|
-
|
|
227
|
-
} as ClientOptions
|
|
209
|
+
}
|
|
228
210
|
|
|
229
|
-
// When a
|
|
211
|
+
// When a resource is provided, don't use projectId/dataset - the client should be "projectless"
|
|
230
212
|
// The client code itself will ignore the non-resource config, so we do this to prevent confusing the user.
|
|
231
213
|
// (ref: https://github.com/sanity-io/client/blob/5c23f81f5ab93a53f5b22b39845c867988508d84/src/data/dataMethods.ts#L691)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
// eslint-disable-next-line no-console
|
|
236
|
-
console.warn(
|
|
237
|
-
'Both resource and explicit projectId/dataset are provided. The resource will be used and projectId/dataset will be ignored.',
|
|
238
|
-
)
|
|
239
|
-
}
|
|
240
|
-
if (resource) {
|
|
241
|
-
delete effectiveOptions.projectId
|
|
242
|
-
delete effectiveOptions.dataset
|
|
243
|
-
}
|
|
214
|
+
if (resource) {
|
|
215
|
+
delete effectiveOptions.projectId
|
|
216
|
+
delete effectiveOptions.dataset
|
|
244
217
|
}
|
|
245
218
|
|
|
246
219
|
if (effectiveOptions.token === null || typeof effectiveOptions.token === 'undefined') {
|
|
@@ -252,14 +225,11 @@ export const getClient = bindActionGlobally(
|
|
|
252
225
|
delete effectiveOptions.withCredentials
|
|
253
226
|
}
|
|
254
227
|
|
|
255
|
-
// Don't pass our DocumentResource to createClient - we've already derived projectId/dataset or ~experimental_resource
|
|
256
|
-
const {resource: _omitResource, ...clientConfig} = effectiveOptions
|
|
257
|
-
|
|
258
228
|
const key = getClientConfigKey(effectiveOptions)
|
|
259
229
|
|
|
260
230
|
if (clients[key]) return clients[key]
|
|
261
231
|
|
|
262
|
-
const client = createClient(
|
|
232
|
+
const client = createClient(effectiveOptions)
|
|
263
233
|
state.set('addClient', (prev) => ({clients: {...prev.clients, [key]: client}}))
|
|
264
234
|
|
|
265
235
|
return client
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConnectionFailedError,
|
|
3
|
+
CorsOriginError,
|
|
4
|
+
DisconnectError,
|
|
5
|
+
type LiveEvent,
|
|
6
|
+
type LiveEventMessage,
|
|
7
|
+
type SanityClient,
|
|
8
|
+
} from '@sanity/client'
|
|
9
|
+
import {of, Subject} from 'rxjs'
|
|
10
|
+
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
11
|
+
|
|
12
|
+
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
13
|
+
import {type StateSource} from '../store/createStateSourceAction'
|
|
14
|
+
import {getClientState} from './clientStore'
|
|
15
|
+
import {LIVE_EVENTS_RETRY_DELAY, observeLiveEvents} from './liveEvents'
|
|
16
|
+
|
|
17
|
+
vi.mock('./clientStore', () => ({getClientState: vi.fn()}))
|
|
18
|
+
|
|
19
|
+
describe('observeLiveEvents', () => {
|
|
20
|
+
let instance: SanityInstance
|
|
21
|
+
let liveEventSubjects: Subject<LiveEvent>[]
|
|
22
|
+
let events: ReturnType<typeof vi.fn>
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks()
|
|
26
|
+
instance = createSanityInstance({projectId: 'test', dataset: 'test'})
|
|
27
|
+
|
|
28
|
+
liveEventSubjects = []
|
|
29
|
+
events = vi.fn().mockImplementation(() => {
|
|
30
|
+
const subject = new Subject<LiveEvent>()
|
|
31
|
+
liveEventSubjects.push(subject)
|
|
32
|
+
return subject
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
vi.mocked(getClientState).mockReturnValue({
|
|
36
|
+
observable: of({
|
|
37
|
+
config: vi.fn().mockReturnValue({token: 'token'}),
|
|
38
|
+
live: {events},
|
|
39
|
+
} as unknown as SanityClient),
|
|
40
|
+
} as StateSource<SanityClient>)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
instance.dispose()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('emits only message events', () => {
|
|
48
|
+
const messages: LiveEventMessage[] = []
|
|
49
|
+
const subscription = observeLiveEvents(instance, {onCorsError: vi.fn()}).subscribe((message) =>
|
|
50
|
+
messages.push(message),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
liveEventSubjects[0].next({type: 'welcome'} as LiveEvent)
|
|
54
|
+
liveEventSubjects[0].next({type: 'message', id: 'e1', tags: ['s1:a']} as LiveEvent)
|
|
55
|
+
|
|
56
|
+
expect(messages).toEqual([{type: 'message', id: 'e1', tags: ['s1:a']}])
|
|
57
|
+
subscription.unsubscribe()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('reports CORS errors through onCorsError and completes without erroring', () => {
|
|
61
|
+
const onCorsError = vi.fn()
|
|
62
|
+
const error = vi.fn()
|
|
63
|
+
const complete = vi.fn()
|
|
64
|
+
observeLiveEvents(instance, {onCorsError}).subscribe({error, complete})
|
|
65
|
+
|
|
66
|
+
const corsError = new CorsOriginError({projectId: 'test'})
|
|
67
|
+
liveEventSubjects[0].error(corsError)
|
|
68
|
+
|
|
69
|
+
expect(onCorsError).toHaveBeenCalledWith(corsError)
|
|
70
|
+
expect(error).not.toHaveBeenCalled()
|
|
71
|
+
expect(complete).toHaveBeenCalled()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('completes without retrying on DisconnectError', async () => {
|
|
75
|
+
vi.useFakeTimers()
|
|
76
|
+
try {
|
|
77
|
+
const error = vi.fn()
|
|
78
|
+
const complete = vi.fn()
|
|
79
|
+
observeLiveEvents(instance, {onCorsError: vi.fn()}).subscribe({error, complete})
|
|
80
|
+
|
|
81
|
+
liveEventSubjects[0].error(new DisconnectError('Server disconnected client'))
|
|
82
|
+
await vi.advanceTimersByTimeAsync(LIVE_EVENTS_RETRY_DELAY * 5)
|
|
83
|
+
|
|
84
|
+
expect(error).not.toHaveBeenCalled()
|
|
85
|
+
expect(complete).toHaveBeenCalled()
|
|
86
|
+
expect(events).toHaveBeenCalledTimes(1)
|
|
87
|
+
} finally {
|
|
88
|
+
vi.useRealTimers()
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('completes without retrying on a 4xx connection rejection', async () => {
|
|
93
|
+
vi.useFakeTimers()
|
|
94
|
+
try {
|
|
95
|
+
const error = vi.fn()
|
|
96
|
+
const complete = vi.fn()
|
|
97
|
+
observeLiveEvents(instance, {onCorsError: vi.fn()}).subscribe({error, complete})
|
|
98
|
+
|
|
99
|
+
// The server rejected the connection with a 401 (e.g. expired token) —
|
|
100
|
+
// it will keep rejecting, so retrying would reconnect once per second
|
|
101
|
+
// forever
|
|
102
|
+
liveEventSubjects[0].error(
|
|
103
|
+
new ConnectionFailedError('EventSource connection failed', {status: 401}),
|
|
104
|
+
)
|
|
105
|
+
await vi.advanceTimersByTimeAsync(LIVE_EVENTS_RETRY_DELAY * 5)
|
|
106
|
+
|
|
107
|
+
expect(error).not.toHaveBeenCalled()
|
|
108
|
+
expect(complete).toHaveBeenCalled()
|
|
109
|
+
expect(events).toHaveBeenCalledTimes(1)
|
|
110
|
+
} finally {
|
|
111
|
+
vi.useRealTimers()
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('retries a connection failure without a status (transient network failure)', async () => {
|
|
116
|
+
vi.useFakeTimers()
|
|
117
|
+
try {
|
|
118
|
+
const error = vi.fn()
|
|
119
|
+
const subscription = observeLiveEvents(instance, {onCorsError: vi.fn()}).subscribe({error})
|
|
120
|
+
|
|
121
|
+
// No status means the failure could be transient (native EventSource
|
|
122
|
+
// exposes no status) — reconnecting is correct here
|
|
123
|
+
liveEventSubjects[0].error(new ConnectionFailedError('EventSource connection failed'))
|
|
124
|
+
await vi.advanceTimersByTimeAsync(LIVE_EVENTS_RETRY_DELAY)
|
|
125
|
+
|
|
126
|
+
expect(events).toHaveBeenCalledTimes(2)
|
|
127
|
+
expect(error).not.toHaveBeenCalled()
|
|
128
|
+
subscription.unsubscribe()
|
|
129
|
+
} finally {
|
|
130
|
+
vi.useRealTimers()
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('retries the connection after server-initiated errors', async () => {
|
|
135
|
+
vi.useFakeTimers()
|
|
136
|
+
try {
|
|
137
|
+
const error = vi.fn()
|
|
138
|
+
const subscription = observeLiveEvents(instance, {onCorsError: vi.fn()}).subscribe({error})
|
|
139
|
+
expect(events).toHaveBeenCalledTimes(1)
|
|
140
|
+
|
|
141
|
+
liveEventSubjects[0].error(new Error('ChannelError'))
|
|
142
|
+
await vi.advanceTimersByTimeAsync(LIVE_EVENTS_RETRY_DELAY)
|
|
143
|
+
|
|
144
|
+
expect(events).toHaveBeenCalledTimes(2)
|
|
145
|
+
expect(error).not.toHaveBeenCalled()
|
|
146
|
+
subscription.unsubscribe()
|
|
147
|
+
} finally {
|
|
148
|
+
vi.useRealTimers()
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConnectionFailedError,
|
|
3
|
+
CorsOriginError,
|
|
4
|
+
DisconnectError,
|
|
5
|
+
type LiveEventMessage,
|
|
6
|
+
} from '@sanity/client'
|
|
7
|
+
import {catchError, defer, EMPTY, filter, type Observable, retry, switchMap} from 'rxjs'
|
|
8
|
+
|
|
9
|
+
import {type DocumentResource} from '../config/sanityConfig'
|
|
10
|
+
import {type SanityInstance} from '../store/createSanityInstance'
|
|
11
|
+
import {getClientState} from './clientStore'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* All live-events subscriptions must use identical connection options
|
|
15
|
+
* (API version, request tag, `includeDrafts`): `@sanity/client` caches the
|
|
16
|
+
* underlying EventSource per URL + options, so divergent options would
|
|
17
|
+
* silently split what should be a single shared connection.
|
|
18
|
+
*/
|
|
19
|
+
const LIVE_EVENTS_API_VERSION = 'v2025-05-06'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Delay before re-establishing the live events connection after a
|
|
23
|
+
* server-initiated error (e.g. ChannelError, MessageError). Live errors are
|
|
24
|
+
* retried rather than surfaced because an errored live connection would
|
|
25
|
+
* otherwise poison the consuming store. Plain connection drops never reach
|
|
26
|
+
* this — they are reconnected inside `@sanity/client`.
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export const LIVE_EVENTS_RETRY_DELAY = 1000
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Options for {@link observeLiveEvents}.
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
export interface ObserveLiveEventsOptions {
|
|
37
|
+
/** Resource to scope the underlying client and live connection to. */
|
|
38
|
+
resource?: DocumentResource
|
|
39
|
+
/**
|
|
40
|
+
* Called when the connection fails due to a CORS misconfiguration. The
|
|
41
|
+
* error is swallowed (the stream never errors; it idles until a new client
|
|
42
|
+
* is emitted) so consumers can surface it as store state instead of a
|
|
43
|
+
* stream error.
|
|
44
|
+
*/
|
|
45
|
+
onCorsError: (error: unknown) => void
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Emits Live Content API messages for a dataset so consumers can invalidate
|
|
50
|
+
* fetched data via sync tags.
|
|
51
|
+
*
|
|
52
|
+
* All consumers observe the same underlying EventSource connection:
|
|
53
|
+
* `@sanity/client` caches the live-events stream per URL and connection
|
|
54
|
+
* options, and every subscription made through this function uses the same
|
|
55
|
+
* API version and request tag.
|
|
56
|
+
*
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
export function observeLiveEvents(
|
|
60
|
+
instance: SanityInstance,
|
|
61
|
+
{resource, onCorsError}: ObserveLiveEventsOptions,
|
|
62
|
+
): Observable<LiveEventMessage> {
|
|
63
|
+
return getClientState(instance, {
|
|
64
|
+
apiVersion: LIVE_EVENTS_API_VERSION,
|
|
65
|
+
resource,
|
|
66
|
+
}).observable.pipe(
|
|
67
|
+
switchMap((client) =>
|
|
68
|
+
defer(() =>
|
|
69
|
+
client.live.events({includeDrafts: !!client.config().token, tag: 'live-events'}),
|
|
70
|
+
).pipe(
|
|
71
|
+
catchError((error) => {
|
|
72
|
+
if (error instanceof CorsOriginError) {
|
|
73
|
+
// Swallow CORS errors without bubbling up so that they can be
|
|
74
|
+
// handled by the consumer (e.g. shown via the Cors Error component)
|
|
75
|
+
onCorsError(error)
|
|
76
|
+
return EMPTY
|
|
77
|
+
}
|
|
78
|
+
if (error instanceof DisconnectError) {
|
|
79
|
+
// The server explicitly told this client to stop reconnecting —
|
|
80
|
+
// end live updates without erroring (consumers keep serving data,
|
|
81
|
+
// they just stop receiving live invalidation)
|
|
82
|
+
return EMPTY
|
|
83
|
+
}
|
|
84
|
+
if (
|
|
85
|
+
error instanceof ConnectionFailedError &&
|
|
86
|
+
typeof error.status === 'number' &&
|
|
87
|
+
error.status >= 400 &&
|
|
88
|
+
error.status < 500
|
|
89
|
+
) {
|
|
90
|
+
// The server rejected the connection with a 4xx (e.g. a 401 from
|
|
91
|
+
// an expired token) — it will keep rejecting, so retrying would
|
|
92
|
+
// reconnect once per second forever. End live updates like a
|
|
93
|
+
// DisconnectError. A ConnectionFailedError without a status stays
|
|
94
|
+
// retryable: it may be a transient network failure.
|
|
95
|
+
return EMPTY
|
|
96
|
+
}
|
|
97
|
+
throw error
|
|
98
|
+
}),
|
|
99
|
+
// Server-initiated live errors (e.g. ChannelError, MessageError) are
|
|
100
|
+
// retried, not surfaced. Dropped connections are already reconnected
|
|
101
|
+
// inside @sanity/client.
|
|
102
|
+
retry({delay: LIVE_EVENTS_RETRY_DELAY}),
|
|
103
|
+
),
|
|
104
|
+
),
|
|
105
|
+
filter((e): e is LiveEventMessage => e.type === 'message'),
|
|
106
|
+
)
|
|
107
|
+
}
|
|
@@ -7,7 +7,10 @@ import {type ComlinkControllerState} from '../comlinkControllerStore'
|
|
|
7
7
|
import {destroyController} from './destroyController'
|
|
8
8
|
|
|
9
9
|
describe('destroyController', () => {
|
|
10
|
-
const instance = createSanityInstance(
|
|
10
|
+
const instance = createSanityInstance({
|
|
11
|
+
projectId: 'test-project-id',
|
|
12
|
+
dataset: 'test-dataset',
|
|
13
|
+
})
|
|
11
14
|
let state: ReturnType<typeof createStoreState<ComlinkControllerState>>
|
|
12
15
|
let mockController: {destroy: ReturnType<typeof vi.fn>}
|
|
13
16
|
|
|
@@ -13,7 +13,10 @@ const channelConfig = {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
describe('getOrCreateChannel', () => {
|
|
16
|
-
const instance = createSanityInstance(
|
|
16
|
+
const instance = createSanityInstance({
|
|
17
|
+
projectId: 'test-project-id',
|
|
18
|
+
dataset: 'test-dataset',
|
|
19
|
+
})
|
|
17
20
|
let state: ReturnType<typeof createStoreState<ComlinkControllerState>>
|
|
18
21
|
let mockController: Partial<Controller>
|
|
19
22
|
let mockChannel: Partial<ChannelInstance<FrameMessage, WindowMessage>>
|