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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/dist/_chunks-dts/createGroqSearchFilter.d.ts +1147 -0
  2. package/dist/_chunks-dts/createGroqSearchFilter.d.ts.map +1 -0
  3. package/dist/_chunks-es/_internal.js +134 -0
  4. package/dist/_chunks-es/_internal.js.map +1 -0
  5. package/dist/_chunks-es/createGroqSearchFilter.js +1864 -0
  6. package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -0
  7. package/dist/_chunks-es/telemetryManager.js +93 -0
  8. package/dist/_chunks-es/telemetryManager.js.map +1 -0
  9. package/dist/_exports/_internal.d.ts +95 -0
  10. package/dist/_exports/_internal.d.ts.map +1 -0
  11. package/dist/_exports/_internal.js +24 -0
  12. package/dist/_exports/_internal.js.map +1 -0
  13. package/dist/index.d.ts +1079 -1076
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +1967 -2302
  16. package/dist/index.js.map +1 -1
  17. package/package.json +48 -47
  18. package/src/_exports/_internal.ts +26 -0
  19. package/src/_exports/index.ts +120 -11
  20. package/src/access/checkPermissions.test.ts +55 -0
  21. package/src/access/checkPermissions.ts +54 -0
  22. package/src/agent/agentActions.test.ts +16 -60
  23. package/src/agent/agentActions.ts +25 -38
  24. package/src/applications/applications.test-d.ts +83 -0
  25. package/src/applications/applications.test.ts +130 -0
  26. package/src/applications/applications.ts +364 -0
  27. package/src/applications/userApplications.test.ts +126 -0
  28. package/src/applications/userApplications.ts +261 -0
  29. package/src/auth/authLogger.ts +30 -0
  30. package/src/auth/authMode.test.ts +25 -0
  31. package/src/auth/authMode.ts +6 -3
  32. package/src/auth/authStore.test.ts +194 -13
  33. package/src/auth/authStore.ts +59 -26
  34. package/src/auth/getOrganizationVerificationState.test.ts +18 -17
  35. package/src/auth/getOrganizationVerificationState.ts +4 -4
  36. package/src/auth/handleAuthCallback.test.ts +35 -1
  37. package/src/auth/handleAuthCallback.ts +25 -6
  38. package/src/auth/logout.test.ts +75 -2
  39. package/src/auth/logout.ts +23 -4
  40. package/src/auth/refreshStampedToken.test.ts +219 -469
  41. package/src/auth/refreshStampedToken.ts +79 -195
  42. package/src/auth/studioAuth.ts +1 -1
  43. package/src/auth/studioModeAuth.ts +2 -1
  44. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +146 -13
  45. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +37 -6
  46. package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +2 -0
  47. package/src/client/clientStore.test.ts +62 -37
  48. package/src/client/clientStore.ts +45 -75
  49. package/src/client/liveEvents.test.ts +151 -0
  50. package/src/client/liveEvents.ts +107 -0
  51. package/src/comlink/controller/actions/destroyController.test.ts +4 -1
  52. package/src/comlink/controller/actions/getOrCreateChannel.test.ts +4 -1
  53. package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
  54. package/src/comlink/controller/actions/getOrCreateController.test.ts +4 -1
  55. package/src/comlink/controller/actions/releaseChannel.test.ts +1 -1
  56. package/src/comlink/controller/comlinkControllerStore.test.ts +4 -1
  57. package/src/comlink/node/actions/getOrCreateNode.test.ts +9 -3
  58. package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
  59. package/src/comlink/node/actions/releaseNode.test.ts +7 -4
  60. package/src/comlink/node/comlinkNodeStore.test.ts +2 -2
  61. package/src/comlink/node/getNodeState.test.ts +1 -1
  62. package/src/comlink/node/getNodeState.ts +2 -1
  63. package/src/config/__tests__/handles.test.ts +18 -12
  64. package/src/config/handles.ts +25 -7
  65. package/src/config/sanityConfig.ts +109 -79
  66. package/src/datasets/datasets.test.ts +2 -2
  67. package/src/datasets/datasets.ts +10 -4
  68. package/src/document/actions.test.ts +115 -33
  69. package/src/document/actions.ts +212 -15
  70. package/src/document/applyDocumentActions.test.ts +38 -12
  71. package/src/document/applyDocumentActions.ts +30 -9
  72. package/src/document/documentConstants.ts +7 -0
  73. package/src/document/documentStore.concurrency.test.ts +1306 -0
  74. package/src/document/documentStore.test.ts +673 -278
  75. package/src/document/documentStore.ts +162 -71
  76. package/src/document/events.test.ts +57 -2
  77. package/src/document/events.ts +82 -26
  78. package/src/document/listen.test.ts +56 -0
  79. package/src/document/listen.ts +74 -21
  80. package/src/document/listenerEventOperators.test.ts +311 -0
  81. package/src/document/listenerEventOperators.ts +217 -0
  82. package/src/document/patchOperations.test.ts +49 -2
  83. package/src/document/patchOperations.ts +50 -0
  84. package/src/document/permissions.test.ts +105 -60
  85. package/src/document/permissions.ts +44 -19
  86. package/src/document/processActions/create.ts +138 -0
  87. package/src/document/processActions/delete.ts +100 -0
  88. package/src/document/processActions/discard.ts +63 -0
  89. package/src/document/processActions/edit.ts +154 -0
  90. package/src/document/processActions/processActions.ts +218 -0
  91. package/src/document/processActions/publish.ts +120 -0
  92. package/src/document/processActions/releaseArchive.ts +77 -0
  93. package/src/document/processActions/releaseCreate.ts +59 -0
  94. package/src/document/processActions/releaseDelete.ts +65 -0
  95. package/src/document/processActions/releaseEdit.ts +37 -0
  96. package/src/document/processActions/releasePublish.ts +45 -0
  97. package/src/document/processActions/releaseSchedule.ts +87 -0
  98. package/src/document/processActions/releaseUtil.ts +31 -0
  99. package/src/document/processActions/shared.ts +308 -0
  100. package/src/document/processActions/unpublish.ts +85 -0
  101. package/src/document/processActions.test.ts +1129 -121
  102. package/src/document/reducers.test.ts +639 -64
  103. package/src/document/reducers.ts +187 -48
  104. package/src/document/resourceRules.test.ts +178 -0
  105. package/src/document/resourceRules.ts +117 -0
  106. package/src/document/sharedListener.test.ts +16 -14
  107. package/src/document/sharedListener.ts +24 -8
  108. package/src/document/util.ts +14 -0
  109. package/src/favorites/favorites.test.ts +5 -6
  110. package/src/installations/installations.test-d.ts +68 -0
  111. package/src/installations/installations.test.ts +62 -0
  112. package/src/installations/installations.ts +202 -0
  113. package/src/organization/organization.test-d.ts +36 -0
  114. package/src/organization/organization.test.ts +138 -0
  115. package/src/organization/organization.ts +138 -0
  116. package/src/organizations/organizations.test-d.ts +56 -0
  117. package/src/organizations/organizations.test.ts +150 -0
  118. package/src/organizations/organizations.ts +107 -0
  119. package/src/presence/bifurTransport.test.ts +46 -6
  120. package/src/presence/bifurTransport.ts +19 -2
  121. package/src/presence/presenceStore.test.ts +94 -23
  122. package/src/presence/presenceStore.ts +85 -29
  123. package/src/preview/getPreviewState.test.ts +137 -0
  124. package/src/preview/getPreviewState.ts +69 -0
  125. package/src/preview/previewProjectionUtils.test.ts +9 -22
  126. package/src/preview/previewProjectionUtils.ts +7 -3
  127. package/src/preview/resolvePreview.test.ts +64 -0
  128. package/src/preview/resolvePreview.ts +43 -0
  129. package/src/preview/types.ts +10 -0
  130. package/src/project/project.test-d.ts +37 -0
  131. package/src/project/project.test.ts +157 -12
  132. package/src/project/project.ts +138 -25
  133. package/src/projection/getProjectionState.test.ts +18 -20
  134. package/src/projection/getProjectionState.ts +23 -3
  135. package/src/projection/projectionQuery.ts +5 -5
  136. package/src/projection/projectionStore.test.ts +1 -1
  137. package/src/projection/resolveProjection.test.ts +1 -2
  138. package/src/projection/resolveProjection.ts +16 -0
  139. package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
  140. package/src/projection/subscribeToStateAndFetchBatches.ts +32 -23
  141. package/src/projection/types.ts +8 -0
  142. package/src/projects/projects.test-d.ts +32 -0
  143. package/src/projects/projects.test.ts +108 -42
  144. package/src/projects/projects.ts +63 -27
  145. package/src/query/queryStore.test.ts +259 -123
  146. package/src/query/queryStore.ts +78 -63
  147. package/src/query/reducers.test.ts +0 -42
  148. package/src/query/reducers.ts +2 -18
  149. package/src/releases/getPerspectiveState.test.ts +17 -23
  150. package/src/releases/getPerspectiveState.ts +8 -20
  151. package/src/releases/observeReleases.test.ts +127 -0
  152. package/src/releases/observeReleases.ts +102 -0
  153. package/src/releases/releasesStore.test.ts +100 -53
  154. package/src/releases/releasesStore.ts +69 -44
  155. package/src/releases/utils/sortReleases.test.ts +38 -2
  156. package/src/releases/utils/sortReleases.ts +15 -10
  157. package/src/store/createActionBinder.test.ts +87 -90
  158. package/src/store/createActionBinder.ts +79 -25
  159. package/src/store/createSanityInstance.test.ts +83 -13
  160. package/src/store/createSanityInstance.ts +77 -2
  161. package/src/store/createStateSourceAction.test.ts +64 -2
  162. package/src/store/createStateSourceAction.ts +39 -44
  163. package/src/store/createStoreInstance.test.ts +4 -2
  164. package/src/store/fetcherStore.test-d.ts +121 -0
  165. package/src/store/fetcherStore.test.ts +299 -0
  166. package/src/store/fetcherStore.ts +801 -0
  167. package/src/telemetry/environment.test.ts +119 -0
  168. package/src/telemetry/environment.ts +92 -0
  169. package/src/telemetry/events.ts +42 -0
  170. package/src/telemetry/initTelemetry.test.ts +432 -0
  171. package/src/telemetry/initTelemetry.ts +218 -0
  172. package/src/telemetry/telemetryManager.test.ts +327 -0
  173. package/src/telemetry/telemetryManager.ts +199 -0
  174. package/src/users/reducers.test.ts +6 -1
  175. package/src/users/reducers.ts +5 -6
  176. package/src/users/types.ts +4 -4
  177. package/src/users/usersStore.test.ts +16 -12
  178. package/src/users/usersStore.ts +5 -1
  179. package/src/utils/buildQuery.test.ts +14 -0
  180. package/src/utils/buildQuery.ts +17 -0
  181. package/src/utils/createFetcherStore.test.ts +7 -5
  182. package/src/utils/createFetcherStore.ts +8 -5
  183. package/src/utils/getStagingApiHost.test.ts +21 -0
  184. package/src/utils/getStagingApiHost.ts +14 -0
  185. package/src/utils/ids.test.ts +1 -29
  186. package/src/utils/ids.ts +0 -10
  187. package/src/utils/includeShape.ts +36 -0
  188. package/src/utils/isImportError.test.ts +72 -0
  189. package/src/utils/isImportError.ts +34 -0
  190. package/src/utils/logger.test.ts +12 -0
  191. package/src/utils/logger.ts +8 -3
  192. package/src/utils/object.test.ts +95 -0
  193. package/src/utils/object.ts +142 -0
  194. package/src/utils/setCleanupTimeout.ts +37 -0
  195. package/src/document/processActions.ts +0 -707
  196. package/src/utils/defineIntent.test.ts +0 -477
  197. package/src/utils/defineIntent.ts +0 -244
@@ -0,0 +1,126 @@
1
+ import {type SanityClient} from '@sanity/client'
2
+ import {of} from 'rxjs'
3
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
4
+
5
+ import {getClientState} from '../client/clientStore'
6
+ import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
7
+ import {type StateSource} from '../store/createStateSourceAction'
8
+ import {
9
+ createUserApplication,
10
+ deleteUserApplication,
11
+ updateUserApplication,
12
+ userApplication,
13
+ userApplications,
14
+ } from './userApplications'
15
+
16
+ vi.mock('../client/clientStore')
17
+
18
+ describe('userApplications', () => {
19
+ let instance: SanityInstance
20
+ const request = vi.fn()
21
+
22
+ beforeEach(() => {
23
+ instance = createSanityInstance({projectId: 'p', dataset: 'd'})
24
+ request.mockReset()
25
+ vi.mocked(getClientState).mockReturnValue({
26
+ observable: of({observable: {request}} as unknown as SanityClient),
27
+ } as StateSource<SanityClient>)
28
+ })
29
+
30
+ afterEach(() => {
31
+ instance.dispose()
32
+ })
33
+
34
+ it('lists user applications for an organization on the global client', async () => {
35
+ const apps = [{id: 'ua1'}, {id: 'ua2'}]
36
+ request.mockReturnValue(of(apps))
37
+
38
+ const result = await userApplications.resolveState(instance, {organizationId: 'org1'})
39
+
40
+ expect(result).toEqual(apps)
41
+ expect(getClientState).toHaveBeenCalledWith(instance, {
42
+ apiVersion: 'v2024-08-01',
43
+ scope: 'global',
44
+ })
45
+ expect(request).toHaveBeenCalledWith({
46
+ uri: '/user-applications',
47
+ query: {organizationId: 'org1'},
48
+ tag: 'user-applications.list',
49
+ })
50
+ })
51
+
52
+ it('fetches a single user application by id', async () => {
53
+ const app = {id: 'ua1', appHost: 'my-studio'}
54
+ request.mockReturnValue(of(app))
55
+
56
+ const result = await userApplication.resolveState(instance, 'ua1')
57
+
58
+ expect(result).toEqual(app)
59
+ expect(request).toHaveBeenCalledWith({
60
+ uri: '/user-applications/ua1',
61
+ tag: 'user-applications.get',
62
+ })
63
+ })
64
+
65
+ it('creates a user application, sending query params and body, and refetches the list', async () => {
66
+ request.mockImplementation((opts) => {
67
+ const {method} = opts as {method?: string}
68
+ return method === 'POST' ? of({id: 'ua-new', title: 'New'}) : of([{id: 'ua-new'}])
69
+ })
70
+
71
+ const source = userApplications.getState(instance, {organizationId: 'org1'})
72
+ const unsubscribe = source.subscribe()
73
+ await vi.waitFor(() => expect(source.getCurrent().status).toBe('success'))
74
+
75
+ const {data, invalidated} = await createUserApplication(instance, {
76
+ organizationId: 'org1',
77
+ appType: 'studio',
78
+ urlType: 'internal',
79
+ appHost: 'my-studio',
80
+ title: 'New',
81
+ })
82
+ await invalidated
83
+
84
+ expect(data).toEqual({id: 'ua-new', title: 'New'})
85
+ expect(request).toHaveBeenCalledWith({
86
+ uri: '/user-applications',
87
+ method: 'POST',
88
+ query: {organizationId: 'org1', appType: 'studio'},
89
+ body: {urlType: 'internal', appHost: 'my-studio', title: 'New'},
90
+ tag: 'user-applications.create',
91
+ })
92
+ // initial list GET + POST + list refetch after invalidation
93
+ expect(request).toHaveBeenCalledTimes(3)
94
+ unsubscribe()
95
+ })
96
+
97
+ it('updates a user application via PATCH', async () => {
98
+ request.mockReturnValue(of({id: 'ua1', title: 'Renamed'}))
99
+
100
+ const {data} = await updateUserApplication(instance, {
101
+ userApplicationId: 'ua1',
102
+ title: 'Renamed',
103
+ })
104
+
105
+ expect(data).toEqual({id: 'ua1', title: 'Renamed'})
106
+ expect(request).toHaveBeenCalledWith({
107
+ uri: '/user-applications/ua1',
108
+ method: 'PATCH',
109
+ body: {title: 'Renamed'},
110
+ tag: 'user-applications.update',
111
+ })
112
+ })
113
+
114
+ it('deletes a user application via DELETE with no body', async () => {
115
+ request.mockReturnValue(of({deleted: true}))
116
+
117
+ const {data} = await deleteUserApplication(instance, {userApplicationId: 'ua1'})
118
+
119
+ expect(data).toEqual({deleted: true})
120
+ expect(request).toHaveBeenCalledWith({
121
+ uri: '/user-applications/ua1',
122
+ method: 'DELETE',
123
+ tag: 'user-applications.delete',
124
+ })
125
+ })
126
+ })
@@ -0,0 +1,261 @@
1
+ import {switchMap} from 'rxjs'
2
+
3
+ import {getClientState} from '../client/clientStore'
4
+ import {defineFetcher, defineMutation} from '../store/fetcherStore'
5
+ import {buildQuery} from '../utils/buildQuery'
6
+ import {type ApplicationVisibility, type DeletedResult} from './applications'
7
+
8
+ // The version the CLI pins for this API family; verify against the deployed
9
+ // API before promoting these stores past @internal.
10
+ const API_VERSION = 'v2024-08-01'
11
+
12
+ /**
13
+ * A deployment of a user application, embedded when `activeDeployment` is
14
+ * included on the User Applications API. Distinct from the org Applications
15
+ * API's {@link ApplicationDeployment} summary.
16
+ *
17
+ * @see https://www.sanity.io/docs/http-reference/applications-api
18
+ * @public
19
+ */
20
+ export interface UserApplicationDeployment {
21
+ id: string
22
+ version: string | null
23
+ isActiveDeployment: boolean
24
+ userApplicationId: string
25
+ isAutoUpdating: boolean | null
26
+ size: number | null
27
+ deployedAt: string
28
+ deployedBy: string | null
29
+ createdAt: string
30
+ updatedAt: string
31
+ manifest: unknown
32
+ }
33
+
34
+ /**
35
+ * A user application (deployed studio or custom app) as returned by
36
+ * `/user-applications`.
37
+ *
38
+ * @see https://www.sanity.io/docs/http-reference/applications-api
39
+ * @public
40
+ */
41
+ export interface UserApplication {
42
+ id: string
43
+ projectId: string | null
44
+ organizationId: string | null
45
+ title: string | null
46
+ type: string
47
+ urlType: string
48
+ appHost: string
49
+ dashboardStatus: string
50
+ createdAt: string
51
+ updatedAt: string
52
+ autoUpdatingVersion: string | null
53
+ activeDeployment: UserApplicationDeployment | null
54
+ manifestData: unknown
55
+ config: Record<string, unknown> | null
56
+ manifest: unknown
57
+ }
58
+
59
+ /**
60
+ * Options for listing an organization's user applications.
61
+ *
62
+ * @see https://www.sanity.io/docs/http-reference/applications-api
63
+ * @public
64
+ */
65
+ export interface UserApplicationsOptions {
66
+ organizationId: string
67
+ }
68
+
69
+ /**
70
+ * Fetcher for an organization's user applications (`GET /user-applications`),
71
+ * on the shared fetcher cache.
72
+ *
73
+ * @see https://www.sanity.io/docs/http-reference/applications-api
74
+ * @internal
75
+ */
76
+ export const userApplications = defineFetcher<
77
+ [options: UserApplicationsOptions],
78
+ UserApplication[]
79
+ >({
80
+ name: 'userApplications',
81
+ getKey: (_instance, options) => options.organizationId,
82
+ fetch: (instance) => (options) =>
83
+ getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(
84
+ switchMap((client) =>
85
+ client.observable.request<UserApplication[]>({
86
+ uri: '/user-applications',
87
+ query: buildQuery({organizationId: options.organizationId}),
88
+ tag: 'user-applications.list',
89
+ }),
90
+ ),
91
+ ),
92
+ tags: (data) => [
93
+ {type: 'user-application', id: 'LIST'},
94
+ ...data.map((app) => ({type: 'user-application', id: app.id})),
95
+ ],
96
+ })
97
+
98
+ /**
99
+ * Fetcher for a single user application (`GET /user-applications/:id`), on the
100
+ * shared fetcher cache. No pre-seeding from {@link userApplications}: its list
101
+ * keys include the organization, which this fetcher's params can't
102
+ * reconstruct.
103
+ *
104
+ * @see https://www.sanity.io/docs/http-reference/applications-api
105
+ * @internal
106
+ */
107
+ export const userApplication = defineFetcher<[userApplicationId: string], UserApplication>({
108
+ name: 'userApplication',
109
+ getKey: (_instance, userApplicationId) => userApplicationId,
110
+ fetch: (instance) => (userApplicationId) =>
111
+ getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(
112
+ switchMap((client) =>
113
+ client.observable.request<UserApplication>({
114
+ uri: `/user-applications/${userApplicationId}`,
115
+ tag: 'user-applications.get',
116
+ }),
117
+ ),
118
+ ),
119
+ tags: (data) => [{type: 'user-application', id: data.id}],
120
+ })
121
+
122
+ /**
123
+ * Fields you can set when creating or updating a user application.
124
+ *
125
+ * @see https://www.sanity.io/docs/http-reference/applications-api
126
+ * @internal
127
+ */
128
+ export interface UserApplicationWriteFields {
129
+ /** Human-friendly display name; `null` clears it */
130
+ title?: string | null
131
+ /** `internal` for Sanity-hosted studios, `external` for externally hosted */
132
+ urlType?: 'internal' | 'external'
133
+ /** Studio subdomain (internal) or full `https` URL (external) */
134
+ appHost?: string
135
+ /** Internal apps only: make this the default app for the deployment */
136
+ isDefaultForDeployment?: boolean
137
+ /** Dashboard visibility */
138
+ dashboardStatus?: ApplicationVisibility
139
+ /** Auto-update channel or a pinned semver */
140
+ autoUpdatingVersion?: string | null
141
+ }
142
+
143
+ /**
144
+ * Input for {@link createUserApplication}: the writable fields, with `urlType`
145
+ * and `appHost` required. `organizationId` and `appType` are sent as query
146
+ * parameters; the rest form the request body.
147
+ *
148
+ * @see https://www.sanity.io/docs/http-reference/applications-api
149
+ * @internal
150
+ */
151
+ export interface CreateUserApplicationInput extends UserApplicationWriteFields {
152
+ /** Query param: owning organization */
153
+ organizationId?: string
154
+ /** Query param: `studio` or `coreApp` */
155
+ appType?: 'studio' | 'coreApp'
156
+ urlType: 'internal' | 'external'
157
+ appHost: string
158
+ /** Create does not accept a `null` title */
159
+ title?: string
160
+ }
161
+
162
+ /**
163
+ * Mutation to create a user application (`POST /user-applications`). Invalidates
164
+ * the list so it reconverges to include the new entry.
165
+ *
166
+ * @see https://www.sanity.io/docs/http-reference/applications-api
167
+ * @internal
168
+ */
169
+ export const createUserApplication = defineMutation<CreateUserApplicationInput, UserApplication>({
170
+ name: 'createUserApplication',
171
+ mutationFn:
172
+ (instance) =>
173
+ ({organizationId, appType, ...body}) =>
174
+ getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(
175
+ switchMap((client) =>
176
+ client.observable.request<UserApplication>({
177
+ uri: '/user-applications',
178
+ method: 'POST',
179
+ query: buildQuery({organizationId, appType}),
180
+ body,
181
+ tag: 'user-applications.create',
182
+ }),
183
+ ),
184
+ ),
185
+ invalidates: [{type: 'user-application', id: 'LIST'}],
186
+ })
187
+
188
+ /**
189
+ * Input for {@link updateUserApplication}: the writable fields (all optional),
190
+ * keyed by id.
191
+ *
192
+ * @see https://www.sanity.io/docs/http-reference/applications-api
193
+ * @internal
194
+ */
195
+ export interface UpdateUserApplicationInput extends UserApplicationWriteFields {
196
+ userApplicationId: string
197
+ }
198
+
199
+ /**
200
+ * Mutation to update a user application (`PATCH /user-applications/:id`).
201
+ * Invalidates the updated entry and the list.
202
+ *
203
+ * @see https://www.sanity.io/docs/http-reference/applications-api
204
+ * @internal
205
+ */
206
+ export const updateUserApplication = defineMutation<UpdateUserApplicationInput, UserApplication>({
207
+ name: 'updateUserApplication',
208
+ mutationFn:
209
+ (instance) =>
210
+ ({userApplicationId, ...body}) =>
211
+ getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(
212
+ switchMap((client) =>
213
+ client.observable.request<UserApplication>({
214
+ uri: `/user-applications/${userApplicationId}`,
215
+ method: 'PATCH',
216
+ body,
217
+ tag: 'user-applications.update',
218
+ }),
219
+ ),
220
+ ),
221
+ invalidates: (_result, {userApplicationId}) => [
222
+ {type: 'user-application', id: 'LIST'},
223
+ {type: 'user-application', id: userApplicationId},
224
+ ],
225
+ })
226
+
227
+ /**
228
+ * Input for {@link deleteUserApplication}.
229
+ *
230
+ * @internal
231
+ */
232
+ export interface DeleteUserApplicationInput {
233
+ userApplicationId: string
234
+ }
235
+
236
+ /**
237
+ * Mutation to delete a user application (`DELETE /user-applications/:id`).
238
+ * Invalidates the deleted entry and the list.
239
+ *
240
+ * @see https://www.sanity.io/docs/http-reference/applications-api
241
+ * @internal
242
+ */
243
+ export const deleteUserApplication = defineMutation<DeleteUserApplicationInput, DeletedResult>({
244
+ name: 'deleteUserApplication',
245
+ mutationFn:
246
+ (instance) =>
247
+ ({userApplicationId}) =>
248
+ getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(
249
+ switchMap((client) =>
250
+ client.observable.request<DeletedResult>({
251
+ uri: `/user-applications/${userApplicationId}`,
252
+ method: 'DELETE',
253
+ tag: 'user-applications.delete',
254
+ }),
255
+ ),
256
+ ),
257
+ invalidates: (_result, {userApplicationId}) => [
258
+ {type: 'user-application', id: 'LIST'},
259
+ {type: 'user-application', id: userApplicationId},
260
+ ],
261
+ })
@@ -0,0 +1,30 @@
1
+ import {type SanityInstance} from '../store/createSanityInstance'
2
+ import {createLogger, type Logger} from '../utils/logger'
3
+
4
+ const loggers = new WeakMap<SanityInstance, Logger>()
5
+
6
+ /**
7
+ * Returns the auth logger for a Sanity instance, creating it on first use.
8
+ *
9
+ * The logger is cached per instance so repeated action calls (logout,
10
+ * setAuthToken, handleAuthCallback) reuse the same logger instead of
11
+ * rebuilding it. The instance details are nested under `instanceContext`,
12
+ * which is what the logger's formatter reads to render the
13
+ * `[project:x] [dataset:y] [instance:z]` prefix.
14
+ *
15
+ * @internal
16
+ */
17
+ export function getAuthLogger(instance: SanityInstance): Logger {
18
+ let logger = loggers.get(instance)
19
+ if (!logger) {
20
+ logger = createLogger('auth', {
21
+ instanceContext: {
22
+ instanceId: instance.instanceId,
23
+ projectId: instance.config.projectId,
24
+ dataset: instance.config.dataset,
25
+ },
26
+ })
27
+ loggers.set(instance, logger)
28
+ }
29
+ return logger
30
+ }
@@ -9,6 +9,11 @@ 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
+
12
17
  it('returns "dashboard" when _context URL param has a non-empty JSON object', () => {
13
18
  const context = encodeURIComponent(JSON.stringify({orgId: '123'}))
14
19
  const href = `https://example.com?_context=${context}`
@@ -18,6 +23,14 @@ describe('resolveAuthMode', () => {
18
23
  it('returns "standalone" by default', () => {
19
24
  expect(resolveAuthMode({}, 'https://example.com')).toBe('standalone')
20
25
  })
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
+ })
21
34
  })
22
35
 
23
36
  describe('isStudioConfig', () => {
@@ -25,7 +38,19 @@ describe('isStudioConfig', () => {
25
38
  expect(isStudioConfig({studio: {}})).toBe(true)
26
39
  })
27
40
 
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
+
28
49
  it('returns false for empty config', () => {
29
50
  expect(isStudioConfig({})).toBe(false)
30
51
  })
52
+
53
+ it('returns true when both studio and studioMode are present', () => {
54
+ expect(isStudioConfig({studio: {}, studioMode: {enabled: true}})).toBe(true)
55
+ })
31
56
  })
@@ -20,8 +20,9 @@ type AuthMode = 'studio' | 'dashboard' | 'standalone'
20
20
  *
21
21
  * Priority:
22
22
  * 1. `studio` config provided → `'studio'`
23
- * 2. Dashboard context detected (`_context` URL param with content) → `'dashboard'`
24
- * 3. Otherwise → `'standalone'`
23
+ * 2. `studioMode.enabled` in config (deprecated) → `'studio'`
24
+ * 3. Dashboard context detected (`_context` URL param with content) → `'dashboard'`
25
+ * 4. Otherwise → `'standalone'`
25
26
  *
26
27
  * @internal
27
28
  */
@@ -33,11 +34,13 @@ export function resolveAuthMode(config: SanityConfig, locationHref: string): Aut
33
34
 
34
35
  /**
35
36
  * 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.
36
39
  *
37
40
  * @internal
38
41
  */
39
42
  export function isStudioConfig(config: SanityConfig): boolean {
40
- return !!config.studio
43
+ return !!config.studio || !!config.studioMode?.enabled
41
44
  }
42
45
 
43
46
  /**