@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.
- package/dist/_chunks-dts/utils.d.ts +295 -69
- package/dist/_chunks-es/_internal.js +3 -14
- package/dist/_chunks-es/_internal.js.map +1 -1
- package/dist/_chunks-es/createGroqSearchFilter.js +129 -59
- package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -1
- package/dist/_chunks-es/version.js +1 -1
- package/dist/_exports/_internal.d.ts +16 -2
- package/dist/_exports/_internal.js +3 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +275 -149
- package/dist/index.js.map +1 -1
- package/package.json +11 -15
- package/src/_exports/_internal.ts +1 -0
- package/src/_exports/index.ts +33 -2
- package/src/agent/agentActions.ts +21 -25
- package/src/client/clientStore.test.ts +24 -60
- package/src/client/clientStore.ts +49 -56
- package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
- package/src/comlink/node/actions/getOrCreateNode.test.ts +5 -2
- package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
- package/src/comlink/node/actions/releaseNode.test.ts +3 -3
- package/src/config/sanityConfig.ts +72 -13
- package/src/document/applyDocumentActions.test.ts +7 -7
- package/src/document/applyDocumentActions.ts +5 -5
- package/src/document/documentStore.test.ts +68 -62
- package/src/document/documentStore.ts +33 -38
- package/src/document/processActions.ts +2 -2
- package/src/document/reducers.ts +4 -4
- package/src/document/sharedListener.ts +5 -7
- package/src/organization/organization.test-d.ts +102 -0
- package/src/organization/organization.test.ts +138 -0
- package/src/organization/organization.ts +166 -0
- package/src/organizations/organizations.test-d.ts +77 -0
- package/src/organizations/organizations.test.ts +150 -0
- package/src/organizations/organizations.ts +132 -0
- package/src/presence/bifurTransport.test.ts +46 -6
- package/src/presence/bifurTransport.ts +13 -1
- package/src/presence/presenceStore.test.ts +101 -5
- package/src/presence/presenceStore.ts +96 -24
- package/src/preview/getPreviewState.ts +1 -1
- package/src/preview/previewProjectionUtils.test.ts +4 -4
- package/src/preview/previewProjectionUtils.ts +6 -7
- package/src/preview/resolvePreview.ts +5 -1
- package/src/project/project.test-d.ts +93 -0
- package/src/project/project.test.ts +108 -10
- package/src/project/project.ts +152 -26
- package/src/projection/getProjectionState.ts +4 -4
- package/src/projection/projectionStore.test.ts +2 -2
- package/src/projection/resolveProjection.ts +2 -2
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
- package/src/projection/subscribeToStateAndFetchBatches.ts +11 -15
- package/src/projects/projects.test-d.ts +38 -0
- package/src/projects/projects.test.ts +104 -38
- package/src/projects/projects.ts +74 -14
- package/src/query/queryStore.test.ts +12 -12
- package/src/query/queryStore.ts +10 -11
- package/src/query/reducers.ts +3 -3
- package/src/releases/getPerspectiveState.ts +5 -5
- package/src/releases/releasesStore.test.ts +6 -6
- package/src/releases/releasesStore.ts +9 -9
- package/src/store/createActionBinder.test.ts +31 -31
- package/src/store/createActionBinder.ts +43 -38
- package/src/store/createSanityInstance.ts +5 -6
- package/src/telemetry/devMode.test.ts +8 -0
- package/src/telemetry/devMode.ts +10 -9
- package/src/telemetry/initTelemetry.test.ts +0 -17
- package/src/telemetry/initTelemetry.ts +2 -12
- package/src/users/reducers.ts +3 -4
- package/src/utils/createFetcherStore.ts +6 -4
- package/src/utils/isImportError.test.ts +72 -0
- package/src/utils/isImportError.ts +34 -0
- package/src/utils/object.test.ts +95 -0
- package/src/utils/object.ts +142 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Sanity SDK",
|
|
6
6
|
"keywords": [
|
|
@@ -49,45 +49,41 @@
|
|
|
49
49
|
"prettier": "@sanity/prettier-config",
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@sanity/bifur-client": "^0.4.1",
|
|
52
|
-
"@sanity/client": "^7.
|
|
53
|
-
"@sanity/comlink": "^3.
|
|
52
|
+
"@sanity/client": "^7.22.0",
|
|
53
|
+
"@sanity/comlink": "^3.1.1",
|
|
54
54
|
"@sanity/diff-match-patch": "^3.2.0",
|
|
55
55
|
"@sanity/diff-patch": "^6.0.0",
|
|
56
56
|
"@sanity/id-utils": "^1.0.0",
|
|
57
57
|
"@sanity/image-url": "^2.0.3",
|
|
58
58
|
"@sanity/json-match": "^1.0.5",
|
|
59
|
-
"@sanity/message-protocol": "^0.
|
|
59
|
+
"@sanity/message-protocol": "^0.23.0",
|
|
60
60
|
"@sanity/mutate": "^0.16.1",
|
|
61
|
-
"@sanity/telemetry": "^1.
|
|
61
|
+
"@sanity/telemetry": "^1.1.0",
|
|
62
62
|
"@sanity/types": "^5.2.0",
|
|
63
63
|
"groq": "3.88.1-typegen-experimental.0",
|
|
64
|
-
"groq-js": "^1.
|
|
65
|
-
"lodash-es": "^4.17.21",
|
|
64
|
+
"groq-js": "^1.30.1",
|
|
66
65
|
"reselect": "^5.1.1",
|
|
67
66
|
"rxjs": "^7.8.2",
|
|
68
|
-
"zustand": "^5.0.
|
|
67
|
+
"zustand": "^5.0.12"
|
|
69
68
|
},
|
|
70
69
|
"devDependencies": {
|
|
71
70
|
"@sanity/browserslist-config": "^1.0.5",
|
|
72
71
|
"@sanity/pkg-utils": "^8.1.29",
|
|
73
72
|
"@sanity/prettier-config": "^1.0.6",
|
|
74
|
-
"@types/
|
|
75
|
-
"@vitest/coverage-v8": "
|
|
73
|
+
"@types/node": "^22.19.1",
|
|
74
|
+
"@vitest/coverage-v8": "4.1.5",
|
|
76
75
|
"eslint": "^9.22.0",
|
|
77
76
|
"prettier": "^3.7.3",
|
|
78
77
|
"rollup-plugin-visualizer": "^5.14.0",
|
|
79
78
|
"typescript": "^5.8.3",
|
|
80
79
|
"vite": "^7.0.0",
|
|
81
|
-
"vitest": "^
|
|
80
|
+
"vitest": "^4.1.4",
|
|
82
81
|
"@repo/config-eslint": "0.0.0",
|
|
83
|
-
"@repo/config-test": "0.0.1",
|
|
84
82
|
"@repo/package.bundle": "3.82.0",
|
|
83
|
+
"@repo/config-test": "0.0.1",
|
|
85
84
|
"@repo/package.config": "0.0.1",
|
|
86
85
|
"@repo/tsconfig": "0.0.1"
|
|
87
86
|
},
|
|
88
|
-
"engines": {
|
|
89
|
-
"node": ">=20.19"
|
|
90
|
-
},
|
|
91
87
|
"publishConfig": {
|
|
92
88
|
"access": "public"
|
|
93
89
|
},
|
|
@@ -12,3 +12,4 @@ export {getQueryKey, parseQueryKey} from '../query/queryStore' // only used for
|
|
|
12
12
|
export {getTelemetryManager, initTelemetry, trackHookMounted} from '../telemetry/initTelemetry'
|
|
13
13
|
export {getUsersKey, parseUsersKey} from '../users/reducers' // only used for memoizing in React, not needed for actual functionality
|
|
14
14
|
export {createGroqSearchFilter} from '../utils/createGroqSearchFilter'
|
|
15
|
+
export {isDeepEqual, pickProperties} from '../utils/object'
|
package/src/_exports/index.ts
CHANGED
|
@@ -86,15 +86,22 @@ export {
|
|
|
86
86
|
type LogNamespace,
|
|
87
87
|
} from '../config/loggingConfig'
|
|
88
88
|
export {
|
|
89
|
+
type CanvasResource,
|
|
89
90
|
type CanvasSource,
|
|
90
91
|
type DatasetHandle,
|
|
92
|
+
type DatasetResource,
|
|
91
93
|
type DatasetSource,
|
|
92
94
|
type DocumentHandle,
|
|
95
|
+
type DocumentResource,
|
|
93
96
|
type DocumentSource,
|
|
94
97
|
type DocumentTypeHandle,
|
|
98
|
+
isCanvasResource,
|
|
95
99
|
isCanvasSource,
|
|
100
|
+
isDatasetResource,
|
|
96
101
|
isDatasetSource,
|
|
102
|
+
isMediaLibraryResource,
|
|
97
103
|
isMediaLibrarySource,
|
|
104
|
+
type MediaLibraryResource,
|
|
98
105
|
type MediaLibrarySource,
|
|
99
106
|
type PerspectiveHandle,
|
|
100
107
|
type ProjectHandle,
|
|
@@ -150,6 +157,20 @@ export {type JsonMatch} from '../document/patchOperations'
|
|
|
150
157
|
export {type DocumentPermissionsResult, type PermissionDeniedReason} from '../document/permissions'
|
|
151
158
|
export type {FavoriteStatusResponse} from '../favorites/favorites'
|
|
152
159
|
export {getFavoritesState, resolveFavoritesState} from '../favorites/favorites'
|
|
160
|
+
export {
|
|
161
|
+
getOrganizationState,
|
|
162
|
+
type Organization,
|
|
163
|
+
type OrganizationBase,
|
|
164
|
+
type OrganizationMember,
|
|
165
|
+
type OrganizationOptions,
|
|
166
|
+
resolveOrganization,
|
|
167
|
+
} from '../organization/organization'
|
|
168
|
+
export {
|
|
169
|
+
getOrganizationsState,
|
|
170
|
+
type Organizations,
|
|
171
|
+
type OrganizationsOptions,
|
|
172
|
+
resolveOrganizations,
|
|
173
|
+
} from '../organizations/organizations'
|
|
153
174
|
export {getPresence} from '../presence/presenceStore'
|
|
154
175
|
export type {
|
|
155
176
|
DisconnectEvent,
|
|
@@ -171,11 +192,20 @@ export type {
|
|
|
171
192
|
ValuePending,
|
|
172
193
|
} from '../preview/types'
|
|
173
194
|
export {type OrgVerificationResult} from '../project/organizationVerification'
|
|
174
|
-
export {
|
|
195
|
+
export {
|
|
196
|
+
getProjectState,
|
|
197
|
+
type Project,
|
|
198
|
+
type ProjectBase,
|
|
199
|
+
type ProjectMember,
|
|
200
|
+
type ProjectMemberRole,
|
|
201
|
+
type ProjectMetadata,
|
|
202
|
+
type ProjectOptions,
|
|
203
|
+
resolveProject,
|
|
204
|
+
} from '../project/project'
|
|
175
205
|
export {getProjectionState} from '../projection/getProjectionState'
|
|
176
206
|
export {resolveProjection} from '../projection/resolveProjection'
|
|
177
207
|
export {type ProjectionValuePending, type ValidProjection} from '../projection/types'
|
|
178
|
-
export {getProjectsState, resolveProjects} from '../projects/projects'
|
|
208
|
+
export {getProjectsState, type ProjectsOptions, resolveProjects} from '../projects/projects'
|
|
179
209
|
export {
|
|
180
210
|
getQueryKey,
|
|
181
211
|
getQueryState,
|
|
@@ -212,6 +242,7 @@ export {type FetcherStore, type FetcherStoreState} from '../utils/createFetcherS
|
|
|
212
242
|
export {createGroqSearchFilter} from '../utils/createGroqSearchFilter'
|
|
213
243
|
export {defineIntent, type Intent, type IntentFilter} from '../utils/defineIntent'
|
|
214
244
|
export {getCorsErrorProjectId} from '../utils/getCorsErrorProjectId'
|
|
245
|
+
export {isImportError} from '../utils/isImportError'
|
|
215
246
|
export {CORE_SDK_VERSION} from '../version'
|
|
216
247
|
export {
|
|
217
248
|
getIndexForKey,
|
|
@@ -2,6 +2,7 @@ import {type SanityClient} from '@sanity/client'
|
|
|
2
2
|
import {from, Observable, switchMap} from 'rxjs'
|
|
3
3
|
|
|
4
4
|
import {getClientState} from '../client/clientStore'
|
|
5
|
+
import {type DocumentResource} from '../config/sanityConfig'
|
|
5
6
|
import {type SanityInstance} from '../store/createSanityInstance'
|
|
6
7
|
|
|
7
8
|
const API_VERSION = 'vX'
|
|
@@ -58,12 +59,11 @@ export type AgentPatchResult = Awaited<ReturnType<SanityClient['agent']['action'
|
|
|
58
59
|
export function agentGenerate(
|
|
59
60
|
instance: SanityInstance,
|
|
60
61
|
options: AgentGenerateOptions,
|
|
62
|
+
resource?: DocumentResource,
|
|
61
63
|
): AgentGenerateResult {
|
|
62
|
-
return getClientState(instance, {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
dataset: instance.config.dataset,
|
|
66
|
-
}).observable.pipe(switchMap((client) => client.observable.agent.action.generate(options)))
|
|
64
|
+
return getClientState(instance, {apiVersion: API_VERSION, resource}).observable.pipe(
|
|
65
|
+
switchMap((client) => client.observable.agent.action.generate(options)),
|
|
66
|
+
)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/**
|
|
@@ -76,12 +76,11 @@ export function agentGenerate(
|
|
|
76
76
|
export function agentTransform(
|
|
77
77
|
instance: SanityInstance,
|
|
78
78
|
options: AgentTransformOptions,
|
|
79
|
+
resource?: DocumentResource,
|
|
79
80
|
): AgentTransformResult {
|
|
80
|
-
return getClientState(instance, {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
dataset: instance.config.dataset,
|
|
84
|
-
}).observable.pipe(switchMap((client) => client.observable.agent.action.transform(options)))
|
|
81
|
+
return getClientState(instance, {apiVersion: API_VERSION, resource}).observable.pipe(
|
|
82
|
+
switchMap((client) => client.observable.agent.action.transform(options)),
|
|
83
|
+
)
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
/**
|
|
@@ -94,12 +93,11 @@ export function agentTransform(
|
|
|
94
93
|
export function agentTranslate(
|
|
95
94
|
instance: SanityInstance,
|
|
96
95
|
options: AgentTranslateOptions,
|
|
96
|
+
resource?: DocumentResource,
|
|
97
97
|
): AgentTranslateResult {
|
|
98
|
-
return getClientState(instance, {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
dataset: instance.config.dataset,
|
|
102
|
-
}).observable.pipe(switchMap((client) => client.observable.agent.action.translate(options)))
|
|
98
|
+
return getClientState(instance, {apiVersion: API_VERSION, resource}).observable.pipe(
|
|
99
|
+
switchMap((client) => client.observable.agent.action.translate(options)),
|
|
100
|
+
)
|
|
103
101
|
}
|
|
104
102
|
|
|
105
103
|
/**
|
|
@@ -112,12 +110,11 @@ export function agentTranslate(
|
|
|
112
110
|
export function agentPrompt(
|
|
113
111
|
instance: SanityInstance,
|
|
114
112
|
options: AgentPromptOptions,
|
|
113
|
+
resource?: DocumentResource,
|
|
115
114
|
): Observable<AgentPromptResult> {
|
|
116
|
-
return getClientState(instance, {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
dataset: instance.config.dataset,
|
|
120
|
-
}).observable.pipe(switchMap((client) => from(client.agent.action.prompt(options))))
|
|
115
|
+
return getClientState(instance, {apiVersion: API_VERSION, resource}).observable.pipe(
|
|
116
|
+
switchMap((client) => from(client.agent.action.prompt(options))),
|
|
117
|
+
)
|
|
121
118
|
}
|
|
122
119
|
|
|
123
120
|
/**
|
|
@@ -130,10 +127,9 @@ export function agentPrompt(
|
|
|
130
127
|
export function agentPatch(
|
|
131
128
|
instance: SanityInstance,
|
|
132
129
|
options: AgentPatchOptions,
|
|
130
|
+
resource?: DocumentResource,
|
|
133
131
|
): Observable<AgentPatchResult> {
|
|
134
|
-
return getClientState(instance, {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
dataset: instance.config.dataset,
|
|
138
|
-
}).observable.pipe(switchMap((client) => from(client.agent.action.patch(options))))
|
|
132
|
+
return getClientState(instance, {apiVersion: API_VERSION, resource}).observable.pipe(
|
|
133
|
+
switchMap((client) => from(client.agent.action.patch(options))),
|
|
134
|
+
)
|
|
139
135
|
}
|
|
@@ -190,17 +190,17 @@ describe('clientStore', () => {
|
|
|
190
190
|
})
|
|
191
191
|
})
|
|
192
192
|
|
|
193
|
-
describe('
|
|
194
|
-
it('should create
|
|
193
|
+
describe('resource handling', () => {
|
|
194
|
+
it('should create resource when media library resource is provided and be projectless', () => {
|
|
195
195
|
const client = getClient(instance, {
|
|
196
196
|
apiVersion: '2024-11-12',
|
|
197
|
-
|
|
197
|
+
resource: {mediaLibraryId: 'media-lib-123'},
|
|
198
198
|
})
|
|
199
199
|
|
|
200
200
|
expect(vi.mocked(createClient)).toHaveBeenCalledWith(
|
|
201
201
|
expect.objectContaining({
|
|
202
|
-
'
|
|
203
|
-
|
|
202
|
+
resource: {type: 'media-library', id: 'media-lib-123'},
|
|
203
|
+
apiVersion: '2024-11-12',
|
|
204
204
|
}),
|
|
205
205
|
)
|
|
206
206
|
// Client should be projectless - no projectId/dataset in config
|
|
@@ -208,21 +208,21 @@ describe('clientStore', () => {
|
|
|
208
208
|
expect(client.config()).not.toHaveProperty('dataset')
|
|
209
209
|
expect(client.config()).toEqual(
|
|
210
210
|
expect.objectContaining({
|
|
211
|
-
|
|
211
|
+
resource: {type: 'media-library', id: 'media-lib-123'},
|
|
212
212
|
}),
|
|
213
213
|
)
|
|
214
214
|
})
|
|
215
215
|
|
|
216
|
-
it('should create resource when
|
|
216
|
+
it('should create resource when canvas resource is provided and be projectless', () => {
|
|
217
217
|
const client = getClient(instance, {
|
|
218
218
|
apiVersion: '2024-11-12',
|
|
219
|
-
|
|
219
|
+
resource: {canvasId: 'canvas-123'},
|
|
220
220
|
})
|
|
221
221
|
|
|
222
222
|
expect(vi.mocked(createClient)).toHaveBeenCalledWith(
|
|
223
223
|
expect.objectContaining({
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
resource: {type: 'canvas', id: 'canvas-123'},
|
|
225
|
+
apiVersion: '2024-11-12',
|
|
226
226
|
}),
|
|
227
227
|
)
|
|
228
228
|
// Client should be projectless - no projectId/dataset in config
|
|
@@ -230,79 +230,43 @@ describe('clientStore', () => {
|
|
|
230
230
|
expect(client.config()).not.toHaveProperty('dataset')
|
|
231
231
|
expect(client.config()).toEqual(
|
|
232
232
|
expect.objectContaining({
|
|
233
|
-
|
|
233
|
+
resource: {type: 'canvas', id: 'canvas-123'},
|
|
234
234
|
}),
|
|
235
235
|
)
|
|
236
236
|
})
|
|
237
237
|
|
|
238
|
-
it('should
|
|
238
|
+
it('should transform dataset resource to project-based config for now', () => {
|
|
239
239
|
const client = getClient(instance, {
|
|
240
240
|
apiVersion: '2024-11-12',
|
|
241
|
-
|
|
241
|
+
resource: {projectId: 'source-project', dataset: 'source-dataset'},
|
|
242
242
|
})
|
|
243
243
|
|
|
244
244
|
expect(vi.mocked(createClient)).toHaveBeenCalledWith(
|
|
245
245
|
expect.objectContaining({
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
projectId: 'source-project',
|
|
247
|
+
dataset: 'source-dataset',
|
|
248
248
|
}),
|
|
249
249
|
)
|
|
250
|
-
// Client should be projectless - no projectId/dataset in config
|
|
251
|
-
expect(client.config()).not.toHaveProperty('projectId')
|
|
252
|
-
expect(client.config()).not.toHaveProperty('dataset')
|
|
253
|
-
expect(client.config()).toEqual(
|
|
254
|
-
expect.objectContaining({
|
|
255
|
-
'~experimental_resource': {type: 'canvas', id: 'canvas-123'},
|
|
256
|
-
}),
|
|
257
|
-
)
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
it('should create projectless client when source is provided, ignoring instance config', () => {
|
|
261
|
-
const client = getClient(instance, {
|
|
262
|
-
apiVersion: '2024-11-12',
|
|
263
|
-
source: {projectId: 'source-project', dataset: 'source-dataset'},
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
// Client should be projectless - source takes precedence, instance config is ignored
|
|
267
|
-
expect(client.config()).not.toHaveProperty('projectId')
|
|
268
|
-
expect(client.config()).not.toHaveProperty('dataset')
|
|
269
250
|
expect(client.config()).toEqual(
|
|
270
251
|
expect.objectContaining({
|
|
271
|
-
|
|
252
|
+
projectId: 'source-project',
|
|
253
|
+
dataset: 'source-dataset',
|
|
272
254
|
}),
|
|
273
255
|
)
|
|
274
256
|
})
|
|
275
257
|
|
|
276
|
-
it('should
|
|
277
|
-
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
278
|
-
const client = getClient(instance, {
|
|
279
|
-
apiVersion: '2024-11-12',
|
|
280
|
-
source: {projectId: 'source-project', dataset: 'source-dataset'},
|
|
281
|
-
projectId: 'explicit-project',
|
|
282
|
-
dataset: 'explicit-dataset',
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
286
|
-
'Both source and explicit projectId/dataset are provided. The source will be used and projectId/dataset will be ignored.',
|
|
287
|
-
)
|
|
288
|
-
// Client should still be projectless despite explicit projectId/dataset
|
|
289
|
-
expect(client.config()).not.toHaveProperty('projectId')
|
|
290
|
-
expect(client.config()).not.toHaveProperty('dataset')
|
|
291
|
-
consoleSpy.mockRestore()
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
it('should create different clients for different sources', () => {
|
|
258
|
+
it('should create different clients for different resources', () => {
|
|
295
259
|
const client1 = getClient(instance, {
|
|
296
260
|
apiVersion: '2024-11-12',
|
|
297
|
-
|
|
261
|
+
resource: {projectId: 'source-project', dataset: 'source-dataset'},
|
|
298
262
|
})
|
|
299
263
|
const client2 = getClient(instance, {
|
|
300
264
|
apiVersion: '2024-11-12',
|
|
301
|
-
|
|
265
|
+
resource: {mediaLibraryId: 'media-lib-123'},
|
|
302
266
|
})
|
|
303
267
|
const client3 = getClient(instance, {
|
|
304
268
|
apiVersion: '2024-11-12',
|
|
305
|
-
|
|
269
|
+
resource: {canvasId: 'canvas-123'},
|
|
306
270
|
})
|
|
307
271
|
|
|
308
272
|
expect(client1).not.toBe(client2)
|
|
@@ -311,14 +275,14 @@ describe('clientStore', () => {
|
|
|
311
275
|
expect(vi.mocked(createClient)).toHaveBeenCalledTimes(3)
|
|
312
276
|
})
|
|
313
277
|
|
|
314
|
-
it('should reuse clients with identical
|
|
278
|
+
it('should reuse clients with identical resource configurations', () => {
|
|
315
279
|
const client1 = getClient(instance, {
|
|
316
280
|
apiVersion: '2024-11-12',
|
|
317
|
-
|
|
281
|
+
resource: {projectId: 'source-project', dataset: 'source-dataset'},
|
|
318
282
|
})
|
|
319
283
|
const client2 = getClient(instance, {
|
|
320
284
|
apiVersion: '2024-11-12',
|
|
321
|
-
|
|
285
|
+
resource: {projectId: 'source-project', dataset: 'source-dataset'},
|
|
322
286
|
})
|
|
323
287
|
|
|
324
288
|
expect(client1).toBe(client2)
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {type ClientConfig, createClient, type SanityClient} from '@sanity/client'
|
|
2
|
-
import {pick} from 'lodash-es'
|
|
3
2
|
|
|
4
3
|
import {getAuthMethodState, getTokenState} from '../auth/authStore'
|
|
5
4
|
import {
|
|
6
|
-
type
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
type DocumentResource,
|
|
6
|
+
isCanvasResource,
|
|
7
|
+
isDatasetResource,
|
|
8
|
+
isMediaLibraryResource,
|
|
10
9
|
} from '../config/sanityConfig'
|
|
11
10
|
import {bindActionGlobally} from '../store/createActionBinder'
|
|
12
11
|
import {createStateSourceAction} from '../store/createStateSourceAction'
|
|
13
12
|
import {defineStore, type StoreContext} from '../store/defineStore'
|
|
14
13
|
import {getStagingApiHost} from '../utils/getStagingApiHost'
|
|
14
|
+
import {pickProperties} from '../utils/object'
|
|
15
15
|
|
|
16
16
|
const DEFAULT_API_VERSION = '2024-11-12'
|
|
17
17
|
const DEFAULT_REQUEST_TAG_PREFIX = 'sanity.sdk'
|
|
@@ -31,22 +31,21 @@ type AllowedClientConfigKey =
|
|
|
31
31
|
| 'useProjectHostname'
|
|
32
32
|
|
|
33
33
|
const allowedKeys = Object.keys({
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
'source': 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,
|
|
50
49
|
} satisfies Record<keyof ClientOptions, null>) as (keyof ClientOptions)[]
|
|
51
50
|
|
|
52
51
|
const DEFAULT_CLIENT_CONFIG: ClientConfig = {
|
|
@@ -67,11 +66,6 @@ export interface ClientStoreState {
|
|
|
67
66
|
authMethod?: 'localstorage' | 'cookie'
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
interface ClientResource {
|
|
71
|
-
type: 'dataset' | 'media-library' | 'canvas'
|
|
72
|
-
id: string
|
|
73
|
-
}
|
|
74
|
-
|
|
75
69
|
/**
|
|
76
70
|
* Options used when retrieving a client instance from the client store.
|
|
77
71
|
*
|
|
@@ -94,20 +88,17 @@ export interface ClientOptions extends Pick<ClientConfig, AllowedClientConfigKey
|
|
|
94
88
|
* and the global client ('global'). When set to `'global'`, the global client
|
|
95
89
|
* is used.
|
|
96
90
|
*/
|
|
97
|
-
|
|
91
|
+
scope?: 'default' | 'global'
|
|
98
92
|
/**
|
|
99
93
|
* A required string indicating the API version for the client.
|
|
100
94
|
*/
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* @internal
|
|
104
|
-
*/
|
|
105
|
-
'~experimental_resource'?: ClientConfig['~experimental_resource']
|
|
95
|
+
apiVersion: string
|
|
106
96
|
|
|
107
97
|
/**
|
|
108
98
|
* @internal
|
|
99
|
+
* The SDK resource to use for the client -- this will get transformed into a ClientConfig resource.
|
|
109
100
|
*/
|
|
110
|
-
|
|
101
|
+
resource?: DocumentResource
|
|
111
102
|
}
|
|
112
103
|
|
|
113
104
|
const clientStore = defineStore<ClientStoreState>({
|
|
@@ -144,7 +135,13 @@ const listenToAuthMethod = ({instance, state}: StoreContext<ClientStoreState>) =
|
|
|
144
135
|
})
|
|
145
136
|
}
|
|
146
137
|
|
|
147
|
-
|
|
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))
|
|
148
145
|
|
|
149
146
|
/**
|
|
150
147
|
* Retrieves a Sanity client instance configured with the provided options.
|
|
@@ -181,44 +178,40 @@ export const getClient = bindActionGlobally(
|
|
|
181
178
|
|
|
182
179
|
const tokenFromState = state.get().token
|
|
183
180
|
const {clients, authMethod} = state.get()
|
|
184
|
-
|
|
185
|
-
let
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
resource = {type: 'media-library', id: options.
|
|
192
|
-
} else if (
|
|
193
|
-
resource = {type: 'canvas', id: options.
|
|
181
|
+
let projectId = options.projectId ?? instance.config.projectId
|
|
182
|
+
let dataset = options.dataset ?? instance.config.dataset
|
|
183
|
+
|
|
184
|
+
let resource: ClientConfig['resource'] | undefined
|
|
185
|
+
|
|
186
|
+
if (options.resource) {
|
|
187
|
+
if (isMediaLibraryResource(options.resource)) {
|
|
188
|
+
resource = {type: 'media-library', id: options.resource.mediaLibraryId}
|
|
189
|
+
} else if (isCanvasResource(options.resource)) {
|
|
190
|
+
resource = {type: 'canvas', id: options.resource.canvasId}
|
|
191
|
+
} else if (isDatasetResource(options.resource)) {
|
|
192
|
+
// use project-based routes for datasets to avoid existing CORS and Studio auth cookie issues
|
|
193
|
+
projectId = options.resource.projectId
|
|
194
|
+
dataset = options.resource.dataset
|
|
194
195
|
}
|
|
195
196
|
}
|
|
196
197
|
|
|
197
|
-
const projectId = options.projectId ?? instance.config.projectId
|
|
198
|
-
const dataset = options.dataset ?? instance.config.dataset
|
|
199
198
|
const apiHost = options.apiHost ?? instance.config.auth?.apiHost ?? getStagingApiHost()
|
|
200
199
|
|
|
201
|
-
const effectiveOptions:
|
|
200
|
+
const effectiveOptions: ClientConfig & {apiVersion: string} = {
|
|
202
201
|
...DEFAULT_CLIENT_CONFIG,
|
|
203
202
|
...((options.scope === 'global' || !projectId || resource) && {useProjectHostname: false}),
|
|
204
203
|
token: authMethod === 'cookie' ? undefined : (tokenFromState ?? undefined),
|
|
205
204
|
...options,
|
|
206
205
|
...(projectId && {projectId}),
|
|
207
206
|
...(dataset && {dataset}),
|
|
207
|
+
...(resource ? {resource} : {resource: undefined}),
|
|
208
208
|
...(apiHost && {apiHost}),
|
|
209
|
-
...(resource && {'~experimental_resource': resource}),
|
|
210
209
|
}
|
|
211
210
|
|
|
212
|
-
// When a
|
|
213
|
-
// The client code itself will ignore the non-
|
|
211
|
+
// When a resource is provided, don't use projectId/dataset - the client should be "projectless"
|
|
212
|
+
// The client code itself will ignore the non-resource config, so we do this to prevent confusing the user.
|
|
214
213
|
// (ref: https://github.com/sanity-io/client/blob/5c23f81f5ab93a53f5b22b39845c867988508d84/src/data/dataMethods.ts#L691)
|
|
215
214
|
if (resource) {
|
|
216
|
-
if (options.projectId || options.dataset) {
|
|
217
|
-
// eslint-disable-next-line no-console
|
|
218
|
-
console.warn(
|
|
219
|
-
'Both source and explicit projectId/dataset are provided. The source will be used and projectId/dataset will be ignored.',
|
|
220
|
-
)
|
|
221
|
-
}
|
|
222
215
|
delete effectiveOptions.projectId
|
|
223
216
|
delete effectiveOptions.dataset
|
|
224
217
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {type ChannelInput, type ChannelInstance} from '@sanity/comlink'
|
|
2
|
-
import {isEqual} from 'lodash-es'
|
|
3
2
|
|
|
4
3
|
import {type StoreContext} from '../../../store/defineStore'
|
|
4
|
+
import {isDeepEqual} from '../../../utils/object'
|
|
5
5
|
import {type FrameMessage, type WindowMessage} from '../../types'
|
|
6
6
|
import {type ComlinkControllerState} from '../comlinkControllerStore'
|
|
7
7
|
|
|
@@ -25,7 +25,7 @@ export const getOrCreateChannel = (
|
|
|
25
25
|
|
|
26
26
|
// limit channels to one per name
|
|
27
27
|
if (existing) {
|
|
28
|
-
if (!
|
|
28
|
+
if (!isDeepEqual(existing.options, options)) {
|
|
29
29
|
throw new Error(`Channel "${options.name}" already exists with different options`)
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -27,14 +27,17 @@ describe('getOrCreateNode', () => {
|
|
|
27
27
|
dataset: 'test-dataset',
|
|
28
28
|
})
|
|
29
29
|
let state: ReturnType<typeof createStoreState<ComlinkNodeState>>
|
|
30
|
-
let mockNode:
|
|
30
|
+
let mockNode: {
|
|
31
31
|
start: ReturnType<typeof vi.fn>
|
|
32
32
|
stop: ReturnType<typeof vi.fn>
|
|
33
|
+
onStatus: ReturnType<typeof vi.fn>
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
beforeEach(() => {
|
|
36
37
|
mockNode = {start: vi.fn(), stop: vi.fn(), onStatus: vi.fn()}
|
|
37
|
-
vi.mocked(comlink.createNode).mockReturnValue(
|
|
38
|
+
vi.mocked(comlink.createNode).mockReturnValue(
|
|
39
|
+
mockNode as unknown as Node<WindowMessage, FrameMessage>,
|
|
40
|
+
)
|
|
38
41
|
state = createStoreState<ComlinkNodeState>({nodes: new Map(), subscriptions: new Map()})
|
|
39
42
|
vi.clearAllMocks()
|
|
40
43
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {createNode, type Node, type NodeInput} from '@sanity/comlink'
|
|
2
|
-
import {isEqual} from 'lodash-es'
|
|
3
2
|
|
|
4
3
|
import {type StoreContext} from '../../../store/defineStore'
|
|
4
|
+
import {isDeepEqual} from '../../../utils/object'
|
|
5
5
|
import {type FrameMessage, type WindowMessage} from '../../types'
|
|
6
6
|
import {type ComlinkNodeState} from '../comlinkNodeStore'
|
|
7
7
|
|
|
@@ -14,7 +14,7 @@ export const getOrCreateNode = (
|
|
|
14
14
|
|
|
15
15
|
// limit nodes to one per name
|
|
16
16
|
if (existing) {
|
|
17
|
-
if (!
|
|
17
|
+
if (!isDeepEqual(existing.options, options)) {
|
|
18
18
|
throw new Error(`Node "${options.name}" already exists with different options`)
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -15,7 +15,7 @@ const nodeConfig = {
|
|
|
15
15
|
describe('releaseNode', () => {
|
|
16
16
|
let instance: SanityInstance
|
|
17
17
|
let state: ReturnType<typeof createStoreState<ComlinkNodeState>>
|
|
18
|
-
let mockNode:
|
|
18
|
+
let mockNode: {
|
|
19
19
|
start: ReturnType<typeof vi.fn>
|
|
20
20
|
stop: ReturnType<typeof vi.fn>
|
|
21
21
|
onStatus: ReturnType<typeof vi.fn>
|
|
@@ -39,7 +39,7 @@ describe('releaseNode', () => {
|
|
|
39
39
|
// Set up a node in the state
|
|
40
40
|
const nodes = new Map()
|
|
41
41
|
nodes.set('test-node', {
|
|
42
|
-
node: mockNode as Node<WindowMessage, FrameMessage>,
|
|
42
|
+
node: mockNode as unknown as Node<WindowMessage, FrameMessage>,
|
|
43
43
|
options: nodeConfig,
|
|
44
44
|
})
|
|
45
45
|
state.set('setup', {nodes})
|
|
@@ -58,7 +58,7 @@ describe('releaseNode', () => {
|
|
|
58
58
|
const statusUnsub = vi.fn()
|
|
59
59
|
const nodes = new Map()
|
|
60
60
|
nodes.set('test-node', {
|
|
61
|
-
node: mockNode as Node<WindowMessage, FrameMessage>,
|
|
61
|
+
node: mockNode as unknown as Node<WindowMessage, FrameMessage>,
|
|
62
62
|
options: nodeConfig,
|
|
63
63
|
statusUnsub,
|
|
64
64
|
})
|