@sanity/sdk 3.0.0-rc.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +12 -0
  2. package/dist/_exports/_internal.d.ts +71 -3
  3. package/dist/_exports/_internal.d.ts.map +1 -1
  4. package/dist/_exports/_internal.js +332 -24
  5. package/dist/_exports/_internal.js.map +1 -1
  6. package/dist/_exports/agent.d.ts +65 -0
  7. package/dist/_exports/agent.d.ts.map +1 -0
  8. package/dist/_exports/agent.js +71 -0
  9. package/dist/_exports/agent.js.map +1 -0
  10. package/dist/_exports/comlink.d.ts +118 -0
  11. package/dist/_exports/comlink.d.ts.map +1 -0
  12. package/dist/_exports/comlink.js +69 -0
  13. package/dist/_exports/comlink.js.map +1 -0
  14. package/dist/authStore-C0o1NOxW.d.ts +3165 -0
  15. package/dist/authStore-C0o1NOxW.d.ts.map +1 -0
  16. package/dist/authStore-CtE9bLHW.js +754 -0
  17. package/dist/authStore-CtE9bLHW.js.map +1 -0
  18. package/dist/clientStore-BtE2-q2i.js +90 -0
  19. package/dist/clientStore-BtE2-q2i.js.map +1 -0
  20. package/dist/createSanityInstance-DUI1bGIg.d.ts +278 -0
  21. package/dist/createSanityInstance-DUI1bGIg.d.ts.map +1 -0
  22. package/dist/createStateSourceAction-BOKtqFNm.js +300 -0
  23. package/dist/createStateSourceAction-BOKtqFNm.js.map +1 -0
  24. package/dist/getEnv-D5RGkQLu.js +8 -0
  25. package/dist/getEnv-D5RGkQLu.js.map +1 -0
  26. package/dist/getNodeState-CR3__B2D.js +67 -0
  27. package/dist/getNodeState-CR3__B2D.js.map +1 -0
  28. package/dist/index.d.ts +3 -2335
  29. package/dist/index.js +4048 -3878
  30. package/dist/index.js.map +1 -1
  31. package/dist/logger-DAtJb5RQ.js +173 -0
  32. package/dist/logger-DAtJb5RQ.js.map +1 -0
  33. package/dist/object-CxXlSVg7.js +83 -0
  34. package/dist/object-CxXlSVg7.js.map +1 -0
  35. package/dist/queryStore-C7yMHPUI.js +1346 -0
  36. package/dist/queryStore-C7yMHPUI.js.map +1 -0
  37. package/dist/rolldown-runtime-vyAXikos.js +9 -0
  38. package/dist/setCleanupTimeout-BiM6q1KF.js +31 -0
  39. package/dist/setCleanupTimeout-BiM6q1KF.js.map +1 -0
  40. package/dist/telemetryManager-UIEiMtC0.js +127 -0
  41. package/dist/telemetryManager-UIEiMtC0.js.map +1 -0
  42. package/dist/version-D8RP_Q59.js +9 -0
  43. package/dist/version-D8RP_Q59.js.map +1 -0
  44. package/package.json +11 -17
  45. package/src/_exports/_internal.ts +1 -0
  46. package/src/_exports/agent.ts +19 -0
  47. package/src/_exports/comlink.ts +16 -0
  48. package/src/_exports/index.ts +45 -90
  49. package/src/applications/applications.test-d.ts +5 -0
  50. package/src/applications/applications.ts +5 -2
  51. package/src/auth/authMode.test.ts +0 -25
  52. package/src/auth/authMode.ts +3 -6
  53. package/src/auth/authStore.test.ts +1 -1
  54. package/src/auth/authStore.ts +0 -1
  55. package/src/comments/addonDatasetStore.test.ts +215 -0
  56. package/src/comments/addonDatasetStore.ts +249 -0
  57. package/src/comments/buildCommentThreads.test.ts +83 -0
  58. package/src/comments/buildCommentThreads.ts +54 -0
  59. package/src/comments/commentActions.test.ts +540 -0
  60. package/src/comments/commentActions.ts +555 -0
  61. package/src/comments/commentFieldPath.test.ts +40 -0
  62. package/src/comments/commentFieldPath.ts +44 -0
  63. package/src/comments/commentsConstants.ts +80 -0
  64. package/src/comments/commentsStore.test.ts +419 -0
  65. package/src/comments/commentsStore.ts +483 -0
  66. package/src/comments/normalizeComment.test.ts +116 -0
  67. package/src/comments/normalizeComment.ts +45 -0
  68. package/src/comments/observeComments.test.ts +193 -0
  69. package/src/comments/observeComments.ts +113 -0
  70. package/src/comments/reducers.test.ts +277 -0
  71. package/src/comments/reducers.ts +316 -0
  72. package/src/comments/types.ts +249 -0
  73. package/src/comments/weakenReferences.test.ts +47 -0
  74. package/src/comments/weakenReferences.ts +29 -0
  75. package/src/config/sanityConfig.ts +3 -75
  76. package/src/datasets/datasets.test.ts +44 -9
  77. package/src/datasets/datasets.ts +24 -23
  78. package/src/document/applyDocumentActions.test.ts +8 -12
  79. package/src/document/documentConstants.ts +17 -0
  80. package/src/document/documentStore.test.ts +173 -19
  81. package/src/document/documentStore.ts +36 -5
  82. package/src/document/patchOperations.test.ts +27 -0
  83. package/src/document/patchOperations.ts +10 -1
  84. package/src/document/permissions.test.ts +3 -1
  85. package/src/document/reducers.test.ts +357 -0
  86. package/src/document/reducers.ts +125 -27
  87. package/src/document/util.test.ts +46 -0
  88. package/src/document/util.ts +37 -1
  89. package/src/favorites/favorites.test.ts +64 -24
  90. package/src/favorites/favorites.ts +73 -22
  91. package/src/installations/installations.test-d.ts +5 -0
  92. package/src/installations/installations.ts +4 -0
  93. package/src/presence/bifurTransport.test.ts +155 -3
  94. package/src/presence/bifurTransport.ts +79 -12
  95. package/src/presence/paths.test.ts +70 -0
  96. package/src/presence/paths.ts +41 -0
  97. package/src/presence/presenceStore.test.ts +822 -2
  98. package/src/presence/presenceStore.ts +518 -102
  99. package/src/presence/types.ts +175 -3
  100. package/src/preview/types.ts +0 -25
  101. package/src/projection/projectionStore.test.ts +0 -8
  102. package/src/projection/types.ts +0 -8
  103. package/src/query/queryStore.ts +7 -6
  104. package/src/store/createActionBinder.test.ts +51 -29
  105. package/src/store/createActionBinder.ts +16 -62
  106. package/src/store/createSanityInstance.test.ts +0 -68
  107. package/src/store/createSanityInstance.ts +0 -63
  108. package/src/store/createStateSourceAction.ts +1 -1
  109. package/src/store/fetcherStore.test.ts +23 -1
  110. package/src/store/fetcherStore.ts +3 -3
  111. package/src/users/reducers.test.ts +0 -3
  112. package/dist/_chunks-dts/createGroqSearchFilter.d.ts +0 -1147
  113. package/dist/_chunks-dts/createGroqSearchFilter.d.ts.map +0 -1
  114. package/dist/_chunks-es/_internal.js +0 -134
  115. package/dist/_chunks-es/_internal.js.map +0 -1
  116. package/dist/_chunks-es/createGroqSearchFilter.js +0 -1864
  117. package/dist/_chunks-es/createGroqSearchFilter.js.map +0 -1
  118. package/dist/_chunks-es/telemetryManager.js +0 -93
  119. package/dist/_chunks-es/telemetryManager.js.map +0 -1
  120. package/dist/index.d.ts.map +0 -1
  121. package/src/applications/userApplications.test.ts +0 -126
  122. package/src/applications/userApplications.ts +0 -261
  123. package/src/preview/getPreviewState.test.ts +0 -137
  124. package/src/preview/getPreviewState.ts +0 -69
  125. package/src/preview/resolvePreview.test.ts +0 -64
  126. package/src/preview/resolvePreview.ts +0 -43
  127. package/src/utils/createFetcherStore.test.ts +0 -171
  128. package/src/utils/createFetcherStore.ts +0 -284
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/sdk",
3
- "version": "3.0.0-rc.1",
3
+ "version": "3.0.0",
4
4
  "private": false,
5
5
  "description": "Sanity SDK",
6
6
  "keywords": [
@@ -33,34 +33,28 @@
33
33
  "module": "./dist/index.js",
34
34
  "types": "./dist/index.d.ts",
35
35
  "exports": {
36
- ".": {
37
- "source": "./src/_exports/index.ts",
38
- "import": "./dist/index.js",
39
- "default": "./dist/index.js"
40
- },
36
+ ".": "./dist/index.js",
41
37
  "./package.json": "./package.json",
42
- "./_internal": {
43
- "source": "./src/_exports/_internal.ts",
44
- "import": "./dist/_exports/_internal.js",
45
- "default": "./dist/_exports/_internal.js"
46
- }
38
+ "./_internal": "./dist/_exports/_internal.js",
39
+ "./agent": "./dist/_exports/agent.js",
40
+ "./comlink": "./dist/_exports/comlink.js"
47
41
  },
48
42
  "publishConfig": {
49
43
  "access": "public"
50
44
  },
51
45
  "dependencies": {
52
46
  "@sanity/bifur-client": "^1.0.0",
53
- "@sanity/client": "^7.23.2",
54
- "@sanity/comlink": "^4.0.1",
47
+ "@sanity/client": "^7.26.2",
48
+ "@sanity/comlink": "^4.0.3",
55
49
  "@sanity/diff-match-patch": "^3.2.0",
56
50
  "@sanity/diff-patch": "^6.0.0",
57
51
  "@sanity/id-utils": "^1.0.0",
58
52
  "@sanity/image-url": "^2.1.1",
59
53
  "@sanity/json-match": "^1.0.5",
60
- "@sanity/message-protocol": "^0.23.0",
54
+ "@sanity/message-protocol": "^0.24.0",
61
55
  "@sanity/mutate": "^0.18.1",
62
56
  "@sanity/telemetry": "^1.1.0",
63
- "@sanity/types": "^6.5.0",
57
+ "@sanity/types": "^6.9.1",
64
58
  "groq": "3.88.1-typegen-experimental.0",
65
59
  "groq-js": "^2.0.0",
66
60
  "reselect": "^5.1.1",
@@ -69,12 +63,12 @@
69
63
  },
70
64
  "devDependencies": {
71
65
  "@sanity/browserslist-config": "^1.0.5",
72
- "@sanity/pkg-utils": "^10.9.2",
66
+ "@sanity/pkg-utils": "^12.1.0",
73
67
  "@types/node": "^24.13.3",
74
68
  "@vitest/coverage-v8": "^4.1.10",
75
69
  "eslint": "^10.6.0",
76
70
  "oxfmt": "^0.58.0",
77
- "rollup-plugin-visualizer": "^7.0.1",
71
+ "rolldown": "^1.2.3",
78
72
  "typescript": "^6.0.3",
79
73
  "vite": "^8.1.5",
80
74
  "vitest": "^4.1.10",
@@ -6,6 +6,7 @@ export {
6
6
  getClientErrorApiType,
7
7
  isProjectUserNotFoundClientError,
8
8
  } from '../auth/utils'
9
+ export {getCommentsOptionsKey, parseCommentsOptionsKey} from '../comments/commentsStore' // only used for memoizing in React, not needed for actual functionality
9
10
  export {PREVIEW_PROJECTION} from '../preview/previewConstants'
10
11
  export {transformProjectionToPreview} from '../preview/previewProjectionUtils'
11
12
  export {getQueryKey, parseQueryKey} from '../query/queryStore' // only used for memoizing in React, not needed for actual functionality
@@ -0,0 +1,19 @@
1
+ export type {
2
+ AgentGenerateOptions,
3
+ AgentGenerateResult,
4
+ AgentPatchOptions,
5
+ AgentPatchResult,
6
+ AgentPromptOptions,
7
+ AgentPromptResult,
8
+ AgentTransformOptions,
9
+ AgentTransformResult,
10
+ AgentTranslateOptions,
11
+ AgentTranslateResult,
12
+ } from '../agent/agentActions'
13
+ export {
14
+ agentGenerate,
15
+ agentPatch,
16
+ agentPrompt,
17
+ agentTransform,
18
+ agentTranslate,
19
+ } from '../agent/agentActions'
@@ -0,0 +1,16 @@
1
+ export {
2
+ type ComlinkControllerState,
3
+ destroyController,
4
+ getOrCreateChannel,
5
+ getOrCreateController,
6
+ releaseChannel,
7
+ } from '../comlink/controller/comlinkControllerStore'
8
+ export type {ComlinkNodeState} from '../comlink/node/comlinkNodeStore'
9
+ export {getOrCreateNode, releaseNode} from '../comlink/node/comlinkNodeStore'
10
+ export {getNodeState, type NodeState} from '../comlink/node/getNodeState'
11
+ export {
12
+ type FrameMessage,
13
+ type NewTokenResponseMessage,
14
+ type RequestNewTokenMessage,
15
+ type WindowMessage,
16
+ } from '../comlink/types'
@@ -7,25 +7,6 @@ export type SanityProject = _SanityProject
7
7
 
8
8
  export {checkPermissions} from '../access/checkPermissions'
9
9
  export {type AccessResourceType} from '../access/checkPermissions'
10
- export type {
11
- AgentGenerateOptions,
12
- AgentGenerateResult,
13
- AgentPatchOptions,
14
- AgentPatchResult,
15
- AgentPromptOptions,
16
- AgentPromptResult,
17
- AgentTransformOptions,
18
- AgentTransformResult,
19
- AgentTranslateOptions,
20
- AgentTranslateResult,
21
- } from '../agent/agentActions'
22
- export {
23
- agentGenerate,
24
- agentPatch,
25
- agentPrompt,
26
- agentTransform,
27
- agentTranslate,
28
- } from '../agent/agentActions'
29
10
  export {
30
11
  application,
31
12
  applications,
@@ -48,23 +29,6 @@ export {
48
29
  type DeletedResult,
49
30
  type UpdateApplicationInput,
50
31
  } from '../applications/applications'
51
- export {
52
- createUserApplication,
53
- deleteUserApplication,
54
- updateUserApplication,
55
- userApplication,
56
- userApplications,
57
- } from '../applications/userApplications'
58
- export {
59
- type CreateUserApplicationInput,
60
- type DeleteUserApplicationInput,
61
- type UpdateUserApplicationInput,
62
- type UserApplication,
63
- type UserApplicationDeployment,
64
- type UserApplicationsOptions,
65
- type UserApplicationWriteFields,
66
- } from '../applications/userApplications'
67
- export {isStudioConfig} from '../auth/authMode'
68
32
  export {AuthStateType} from '../auth/authStateType'
69
33
  export {
70
34
  type AuthState,
@@ -84,31 +48,38 @@ export {
84
48
  export {observeOrganizationVerificationState} from '../auth/getOrganizationVerificationState'
85
49
  export {handleAuthCallback} from '../auth/handleAuthCallback'
86
50
  export {logout} from '../auth/logout'
87
- export {
88
- type ApiErrorBody,
89
- getClientErrorApiBody,
90
- getClientErrorApiDescription,
91
- getClientErrorApiType,
92
- isProjectUserNotFoundClientError,
93
- } from '../auth/utils'
94
51
  export type {ClientStoreState as ClientState} from '../client/clientStore'
95
52
  export {type ClientOptions, getClient, getClientState} from '../client/clientStore'
96
53
  export {
97
- type ComlinkControllerState,
98
- destroyController,
99
- getOrCreateChannel,
100
- getOrCreateController,
101
- releaseChannel,
102
- } from '../comlink/controller/comlinkControllerStore'
103
- export type {ComlinkNodeState} from '../comlink/node/comlinkNodeStore'
104
- export {getOrCreateNode, releaseNode} from '../comlink/node/comlinkNodeStore'
105
- export {getNodeState, type NodeState} from '../comlink/node/getNodeState'
54
+ createComment,
55
+ type CreateCommentOptions,
56
+ removeComment,
57
+ type RemoveCommentOptions,
58
+ replyToComment,
59
+ type ReplyToCommentOptions,
60
+ setCommentStatus,
61
+ type SetCommentStatusOptions,
62
+ updateComment,
63
+ type UpdateCommentOptions,
64
+ } from '../comments/commentActions'
106
65
  export {
107
- type FrameMessage,
108
- type NewTokenResponseMessage,
109
- type RequestNewTokenMessage,
110
- type WindowMessage,
111
- } from '../comlink/types'
66
+ type CommentsOptions,
67
+ getCommentsState,
68
+ getCommentThreadsState,
69
+ resolveComments,
70
+ type ResolveCommentsOptions,
71
+ resolveCommentThreads,
72
+ } from '../comments/commentsStore'
73
+ export {
74
+ type Comment,
75
+ type CommentLocalState,
76
+ type CommentMessage,
77
+ type CommentReaction,
78
+ type CommentStatus,
79
+ type CommentTextSelection,
80
+ type CommentTextSelectionItem,
81
+ type CommentThread,
82
+ } from '../comments/types'
112
83
  export {type AuthConfig, type AuthProvider} from '../config/authConfig'
113
84
  export {
114
85
  createDatasetHandle,
@@ -127,22 +98,15 @@ export {
127
98
  } from '../config/loggingConfig'
128
99
  export {
129
100
  type CanvasResource,
130
- type CanvasSource,
131
101
  type DatasetHandle,
132
102
  type DatasetResource,
133
- type DatasetSource,
134
103
  type DocumentHandle,
135
104
  type DocumentResource,
136
- type DocumentSource,
137
105
  type DocumentTypeHandle,
138
106
  isCanvasResource,
139
- isCanvasSource,
140
107
  isDatasetResource,
141
- isDatasetSource,
142
108
  isMediaLibraryResource,
143
- isMediaLibrarySource,
144
109
  type MediaLibraryResource,
145
- type MediaLibrarySource,
146
110
  type PerspectiveHandle,
147
111
  type ProjectHandle,
148
112
  type ReleaseHandle,
@@ -151,7 +115,7 @@ export {
151
115
  type StudioConfig,
152
116
  type TokenSource,
153
117
  } from '../config/sanityConfig'
154
- export {getDatasetsState, resolveDatasets} from '../datasets/datasets'
118
+ export {datasets} from '../datasets/datasets'
155
119
  export {
156
120
  type Action,
157
121
  archiveRelease,
@@ -217,8 +181,12 @@ export {
217
181
  export {type JsonMatch} from '../document/patchOperations'
218
182
  export {type DocumentPermissionsResult, type PermissionDeniedReason} from '../document/permissions'
219
183
  export {getReleaseDocumentId} from '../document/processActions/releaseUtil'
220
- export type {FavoriteStatusResponse} from '../favorites/favorites'
221
- export {getFavoritesState, resolveFavoritesState} from '../favorites/favorites'
184
+ export type {
185
+ FavoriteDocumentContext,
186
+ FavoriteStatusResponse,
187
+ SetFavoriteInput,
188
+ } from '../favorites/favorites'
189
+ export {favorites, setFavorite} from '../favorites/favorites'
222
190
  export {installation, installations} from '../installations/installations'
223
191
  export {
224
192
  type Installation,
@@ -239,26 +207,22 @@ export {
239
207
  } from '../organization/organization'
240
208
  export {organizations} from '../organizations/organizations'
241
209
  export {type Organizations, type OrganizationsOptions} from '../organizations/organizations'
242
- export {getPresence} from '../presence/presenceStore'
210
+ export {getDocumentPresence, getPresence, reportPresence} from '../presence/presenceStore'
243
211
  export type {
244
212
  DisconnectEvent,
213
+ DocumentPresence,
214
+ DocumentPresenceOptions,
245
215
  PresenceLocation,
216
+ PresencePerspectiveOptions,
217
+ PresenceSelection,
218
+ PresenceSelectionPoint,
219
+ ReportPresenceOptions,
246
220
  RollCallEvent,
247
221
  StateEvent,
248
222
  TransportEvent,
249
223
  UserPresence,
250
224
  } from '../presence/types'
251
- export {getPreviewState, type GetPreviewStateOptions} from '../preview/getPreviewState'
252
- export {PREVIEW_PROJECTION} from '../preview/previewConstants'
253
- export {transformProjectionToPreview} from '../preview/previewProjectionUtils'
254
- export {resolvePreview, type ResolvePreviewOptions} from '../preview/resolvePreview'
255
- export type {
256
- PreviewMedia,
257
- PreviewQueryResult,
258
- PreviewStoreState,
259
- PreviewValue,
260
- ValuePending,
261
- } from '../preview/types'
225
+ export type {PreviewMedia, PreviewQueryResult, PreviewValue} from '../preview/types'
262
226
  export {type OrgVerificationResult} from '../project/organizationVerification'
263
227
  export {project} from '../project/project'
264
228
  export {
@@ -271,22 +235,15 @@ export {
271
235
  } from '../project/project'
272
236
  export {getProjectionState} from '../projection/getProjectionState'
273
237
  export {resolveProjection} from '../projection/resolveProjection'
274
- export {type ProjectionValuePending, type ValidProjection} from '../projection/types'
238
+ export {type ProjectionValuePending} from '../projection/types'
275
239
  export {projects} from '../projects/projects'
276
240
  export {type ProjectsOptions} from '../projects/projects'
277
- export {
278
- getQueryKey,
279
- getQueryState,
280
- parseQueryKey,
281
- type QueryOptions,
282
- resolveQuery,
283
- } from '../query/queryStore'
241
+ export {getQueryState, type QueryOptions, resolveQuery} from '../query/queryStore'
284
242
  export {getPerspectiveState} from '../releases/getPerspectiveState'
285
243
  export type {ReleaseState} from '../releases/releasesStore'
286
244
  export {getActiveReleasesState, getAllReleasesState} from '../releases/releasesStore'
287
245
  export {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
288
246
  export {type Selector, type StateSource} from '../store/createStateSourceAction'
289
- export {getUsersKey, parseUsersKey} from '../users/reducers'
290
247
  export {
291
248
  type GetUserOptions,
292
249
  type GetUsersOptions,
@@ -306,8 +263,6 @@ export {
306
263
  resolveUser,
307
264
  resolveUsers,
308
265
  } from '../users/usersStore'
309
- export {type FetcherStore, type FetcherStoreState} from '../utils/createFetcherStore'
310
- export {createGroqSearchFilter} from '../utils/createGroqSearchFilter'
311
266
  export {getCorsErrorProjectId} from '../utils/getCorsErrorProjectId'
312
267
  export {isImportError} from '../utils/isImportError'
313
268
  export {CORE_SDK_VERSION} from '../version'
@@ -30,6 +30,11 @@ test('Application — no includes: only the base shape, no config or activeDeplo
30
30
  expectTypeOf<Extract<keyof Application, ChildKeys>>().toEqualTypeOf<never>()
31
31
  })
32
32
 
33
+ test('ApplicationBase — carries name and reference as strings', () => {
34
+ expectTypeOf<ApplicationBase['name']>().toEqualTypeOf<string>()
35
+ expectTypeOf<ApplicationBase['reference']>().toEqualTypeOf<string>()
36
+ })
37
+
33
38
  test('Application — config.studio adds a required config.studio', () => {
34
39
  expectTypeOf<
35
40
  Application<'config.studio'>['config']['studio']
@@ -120,6 +120,10 @@ export interface ApplicationBase {
120
120
  id: string
121
121
  type: 'studio' | 'coreApp'
122
122
  title: string
123
+ /** Stable, immutable identity, distinct from the mutable `slug` address. */
124
+ name: string
125
+ /** Qualified, globally-unique handle: `sanity/<name>` for singletons, `<organizationId>/<name>` otherwise. */
126
+ reference: string
123
127
  icon: string | null
124
128
  isSingleton: boolean
125
129
  visibility: 'default' | 'unlisted' | 'disabled'
@@ -280,8 +284,7 @@ export interface DeletedResult {
280
284
  }
281
285
 
282
286
  /**
283
- * Dashboard visibility for an application. The same set of values backs a user
284
- * application's `dashboardStatus`.
287
+ * Dashboard visibility for an application.
285
288
  *
286
289
  * @see https://www.sanity.io/docs/http-reference/applications-api
287
290
  * @internal
@@ -9,11 +9,6 @@ describe('resolveAuthMode', () => {
9
9
  expect(resolveAuthMode(config, 'https://example.com')).toBe('studio')
10
10
  })
11
11
 
12
- it('returns "studio" when deprecated studioMode.enabled is true', () => {
13
- const config: SanityConfig = {studioMode: {enabled: true}}
14
- expect(resolveAuthMode(config, 'https://example.com')).toBe('studio')
15
- })
16
-
17
12
  it('returns "dashboard" when _context URL param has a non-empty JSON object', () => {
18
13
  const context = encodeURIComponent(JSON.stringify({orgId: '123'}))
19
14
  const href = `https://example.com?_context=${context}`
@@ -23,14 +18,6 @@ describe('resolveAuthMode', () => {
23
18
  it('returns "standalone" by default', () => {
24
19
  expect(resolveAuthMode({}, 'https://example.com')).toBe('standalone')
25
20
  })
26
-
27
- it('prefers studio config over studioMode', () => {
28
- const config: SanityConfig = {
29
- studio: {},
30
- studioMode: {enabled: true},
31
- }
32
- expect(resolveAuthMode(config, 'https://example.com')).toBe('studio')
33
- })
34
21
  })
35
22
 
36
23
  describe('isStudioConfig', () => {
@@ -38,19 +25,7 @@ describe('isStudioConfig', () => {
38
25
  expect(isStudioConfig({studio: {}})).toBe(true)
39
26
  })
40
27
 
41
- it('returns true when deprecated studioMode.enabled is true', () => {
42
- expect(isStudioConfig({studioMode: {enabled: true}})).toBe(true)
43
- })
44
-
45
- it('returns false when studioMode.enabled is false', () => {
46
- expect(isStudioConfig({studioMode: {enabled: false}})).toBe(false)
47
- })
48
-
49
28
  it('returns false for empty config', () => {
50
29
  expect(isStudioConfig({})).toBe(false)
51
30
  })
52
-
53
- it('returns true when both studio and studioMode are present', () => {
54
- expect(isStudioConfig({studio: {}, studioMode: {enabled: true}})).toBe(true)
55
- })
56
31
  })
@@ -20,9 +20,8 @@ type AuthMode = 'studio' | 'dashboard' | 'standalone'
20
20
  *
21
21
  * Priority:
22
22
  * 1. `studio` config provided → `'studio'`
23
- * 2. `studioMode.enabled` in config (deprecated) → `'studio'`
24
- * 3. Dashboard context detected (`_context` URL param with content) → `'dashboard'`
25
- * 4. Otherwise → `'standalone'`
23
+ * 2. Dashboard context detected (`_context` URL param with content) → `'dashboard'`
24
+ * 3. Otherwise → `'standalone'`
26
25
  *
27
26
  * @internal
28
27
  */
@@ -34,13 +33,11 @@ export function resolveAuthMode(config: SanityConfig, locationHref: string): Aut
34
33
 
35
34
  /**
36
35
  * Returns `true` when the config indicates the SDK is running inside a Studio.
37
- * Checks the new `studio` field first, then falls back to the deprecated
38
- * `studioMode.enabled` for backwards compatibility.
39
36
  *
40
37
  * @internal
41
38
  */
42
39
  export function isStudioConfig(config: SanityConfig): boolean {
43
- return !!config.studio || !!config.studioMode?.enabled
40
+ return !!config.studio
44
41
  }
45
42
 
46
43
  /**
@@ -654,7 +654,7 @@ describe('authStore', () => {
654
654
  instance = createSanityInstance({
655
655
  projectId,
656
656
  dataset: 'd',
657
- studioMode: {enabled: true},
657
+ studio: {},
658
658
  auth: {storageArea: mockStorage},
659
659
  })
660
660
 
@@ -110,7 +110,6 @@ export const authStore = defineStore<AuthStoreState>({
110
110
  hasCustomProviders: !!(
111
111
  instance.config.auth?.providers && instance.config.auth.providers.length > 0
112
112
  ),
113
- studioMode: instance.config.studioMode?.enabled ?? false,
114
113
  })
115
114
 
116
115
  const {
@@ -0,0 +1,215 @@
1
+ import {type SanityClient} from '@sanity/client'
2
+ import {BehaviorSubject, firstValueFrom, NEVER, of, throwError} from 'rxjs'
3
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
4
+
5
+ import {getClient, getClientState} from '../client/clientStore'
6
+ import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
7
+ import {type StateSource} from '../store/createStateSourceAction'
8
+ import {
9
+ getAddonDatasetState,
10
+ observeAddonDatasetClient,
11
+ provisionAddonDataset,
12
+ } from './addonDatasetStore'
13
+
14
+ vi.mock('../client/clientStore', () => ({
15
+ getClient: vi.fn(),
16
+ getClientState: vi.fn(),
17
+ }))
18
+
19
+ let instance: SanityInstance
20
+ let observableRequest: ReturnType<typeof vi.fn>
21
+ let request: ReturnType<typeof vi.fn>
22
+
23
+ function mockClient() {
24
+ observableRequest = vi.fn()
25
+ request = vi.fn()
26
+ const client = {observable: {request: observableRequest}, request} as unknown as SanityClient
27
+
28
+ vi.mocked(getClient).mockReturnValue(client)
29
+ vi.mocked(getClientState).mockReturnValue({
30
+ observable: of(client),
31
+ getCurrent: () => client,
32
+ subscribe: () => () => {},
33
+ } as unknown as StateSource<SanityClient>)
34
+
35
+ return client
36
+ }
37
+
38
+ /** Waits for discovery, which runs asynchronously when the store initializes. */
39
+ function resolvedAddonDataset() {
40
+ return firstValueFrom(getAddonDatasetState(instance, {}).observable.pipe())
41
+ }
42
+
43
+ beforeEach(() => {
44
+ vi.resetAllMocks()
45
+ instance = createSanityInstance({projectId: 'p', dataset: 'd'})
46
+ mockClient()
47
+ })
48
+
49
+ afterEach(() => {
50
+ instance.dispose()
51
+ })
52
+
53
+ describe('getAddonDatasetState', () => {
54
+ it('reports the dataset name once discovery answers', async () => {
55
+ observableRequest.mockReturnValue(of([{name: 'd-comments'}]))
56
+
57
+ await vi.waitFor(() =>
58
+ expect(getAddonDatasetState(instance, {}).getCurrent()).toBe('d-comments'),
59
+ )
60
+
61
+ expect(observableRequest).toHaveBeenCalledWith({
62
+ uri: '/projects/p/datasets?datasetProfile=comments&addonFor=d',
63
+ tag: 'comments.addon-dataset.list',
64
+ })
65
+ })
66
+
67
+ it('reports null when the project has no comments dataset', async () => {
68
+ observableRequest.mockReturnValue(of([]))
69
+
70
+ await vi.waitFor(() => expect(getAddonDatasetState(instance, {}).getCurrent()).toBe(null))
71
+ })
72
+
73
+ it('reports null rather than erroring when discovery is forbidden', async () => {
74
+ // A member without access to the addon dataset gets a 403. There is nothing
75
+ // for them to read, which is the same outcome as the dataset not existing.
76
+ observableRequest.mockReturnValue(throwError(() => ({statusCode: 403})))
77
+
78
+ await vi.waitFor(() => expect(getAddonDatasetState(instance, {}).getCurrent()).toBe(null))
79
+ })
80
+
81
+ it('recovers when a new authenticated client arrives after a transient failure', async () => {
82
+ const firstClient = {
83
+ observable: {request: vi.fn(() => throwError(() => ({statusCode: 500})))},
84
+ } as unknown as SanityClient
85
+ const secondClient = {
86
+ observable: {request: vi.fn(() => of([{name: 'd-comments'}]))},
87
+ } as unknown as SanityClient
88
+ const clients = new BehaviorSubject(firstClient)
89
+
90
+ vi.mocked(getClientState).mockReturnValue({
91
+ observable: clients,
92
+ getCurrent: () => clients.getValue(),
93
+ subscribe: () => () => {},
94
+ } as unknown as StateSource<SanityClient>)
95
+
96
+ const state = getAddonDatasetState(instance, {})
97
+ expect(state.getCurrent()).toBe(undefined)
98
+
99
+ clients.next(secondClient)
100
+
101
+ await vi.waitFor(() => expect(state.getCurrent()).toBe('d-comments'))
102
+ })
103
+
104
+ it('stays undefined while discovery is in flight', () => {
105
+ observableRequest.mockReturnValue(NEVER)
106
+ expect(getAddonDatasetState(instance, {}).getCurrent()).toBe(undefined)
107
+ })
108
+
109
+ it('throws for a resource that is not a dataset', () => {
110
+ expect(() => getAddonDatasetState(instance, {resource: {mediaLibraryId: 'ml-1'}})).toThrow(
111
+ /only supported for dataset resources/,
112
+ )
113
+ })
114
+ })
115
+
116
+ describe('provisionAddonDataset', () => {
117
+ it('returns the existing dataset without writing anything', async () => {
118
+ observableRequest.mockReturnValue(of([{name: 'd-comments'}]))
119
+ await resolvedAddonDataset()
120
+
121
+ await expect(provisionAddonDataset(instance, {})).resolves.toBe('d-comments')
122
+ expect(request).not.toHaveBeenCalled()
123
+ })
124
+
125
+ it('re-checks before creating, and adopts a dataset another user just made', async () => {
126
+ // Discovery said missing when this client started, but someone else has
127
+ // provisioned it since. Creating it again would fail.
128
+ observableRequest.mockReturnValueOnce(of([])).mockReturnValueOnce(of([{name: 'd-comments'}]))
129
+ await resolvedAddonDataset()
130
+
131
+ await expect(provisionAddonDataset(instance, {})).resolves.toBe('d-comments')
132
+ expect(request).not.toHaveBeenCalled()
133
+ })
134
+
135
+ it('creates the dataset when the re-check confirms it is missing', async () => {
136
+ observableRequest.mockReturnValue(of([]))
137
+ request.mockResolvedValue({datasetName: 'd-comments'})
138
+ await resolvedAddonDataset()
139
+
140
+ await expect(provisionAddonDataset(instance, {})).resolves.toBe('d-comments')
141
+ expect(request).toHaveBeenCalledWith({
142
+ uri: '/comments/d/setup',
143
+ method: 'POST',
144
+ tag: 'comments.addon-dataset.setup',
145
+ })
146
+ expect(getAddonDatasetState(instance, {}).getCurrent()).toBe('d-comments')
147
+ })
148
+
149
+ it('issues one POST for concurrent callers', async () => {
150
+ observableRequest.mockReturnValue(of([]))
151
+ request.mockResolvedValue({datasetName: 'd-comments'})
152
+ await resolvedAddonDataset()
153
+
154
+ const results = await Promise.all([
155
+ provisionAddonDataset(instance, {}),
156
+ provisionAddonDataset(instance, {}),
157
+ provisionAddonDataset(instance, {}),
158
+ ])
159
+
160
+ expect(results).toEqual(['d-comments', 'd-comments', 'd-comments'])
161
+ expect(request).toHaveBeenCalledTimes(1)
162
+ })
163
+
164
+ it('throws when setup returns no dataset name', async () => {
165
+ observableRequest.mockReturnValue(of([]))
166
+ request.mockResolvedValue({})
167
+ await resolvedAddonDataset()
168
+
169
+ await expect(provisionAddonDataset(instance, {})).rejects.toThrow(/no dataset name/)
170
+ })
171
+
172
+ it('allows a retry after a failure', async () => {
173
+ observableRequest.mockReturnValue(of([]))
174
+ request.mockRejectedValueOnce(new Error('boom')).mockResolvedValue({datasetName: 'd-comments'})
175
+ await resolvedAddonDataset()
176
+
177
+ await expect(provisionAddonDataset(instance, {})).rejects.toThrow('boom')
178
+ await expect(provisionAddonDataset(instance, {})).resolves.toBe('d-comments')
179
+ })
180
+ })
181
+
182
+ describe('observeAddonDatasetClient', () => {
183
+ it('does not report a missing dataset while discovery is still in flight', () => {
184
+ observableRequest.mockReturnValue(NEVER)
185
+ const values: Array<SanityClient | null> = []
186
+
187
+ const subscription = observeAddonDatasetClient(instance, {}).subscribe((value) =>
188
+ values.push(value),
189
+ )
190
+
191
+ expect(values).toEqual([])
192
+ subscription.unsubscribe()
193
+ })
194
+
195
+ it('emits null while the project has no comments dataset', async () => {
196
+ observableRequest.mockReturnValue(of([]))
197
+
198
+ await expect(firstValueFrom(observeAddonDatasetClient(instance, {}))).resolves.toBe(null)
199
+ })
200
+
201
+ it('emits a client once the dataset is known', async () => {
202
+ const client = mockClient()
203
+ observableRequest.mockReturnValue(of([{name: 'd-comments'}]))
204
+
205
+ await vi.waitFor(async () =>
206
+ expect(await firstValueFrom(observeAddonDatasetClient(instance, {}))).toBe(client),
207
+ )
208
+
209
+ expect(getClientState).toHaveBeenCalledWith(instance, {
210
+ apiVersion: 'v2025-05-06',
211
+ projectId: 'p',
212
+ dataset: 'd-comments',
213
+ })
214
+ })
215
+ })