@sanity/sdk 3.0.0 → 3.2.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/_exports/_internal.d.ts +72 -19
- package/dist/_exports/_internal.d.ts.map +1 -1
- package/dist/_exports/_internal.js +130 -11
- package/dist/_exports/_internal.js.map +1 -1
- package/dist/_exports/agent.d.ts +7 -7
- package/dist/_exports/agent.d.ts.map +1 -1
- package/dist/_exports/agent.js +1 -1
- package/dist/_exports/comlink.d.ts +9 -9
- package/dist/_exports/comlink.d.ts.map +1 -1
- package/dist/_exports/comlink.js +2 -2
- package/dist/_exports/dashboard.d.ts +2 -0
- package/dist/_exports/dashboard.js +2 -0
- package/dist/{authStore-C0o1NOxW.d.ts → authStore-BO2vch3I.d.ts} +516 -63
- package/dist/authStore-BO2vch3I.d.ts.map +1 -0
- package/dist/{authStore-CtE9bLHW.js → clientStore-BI4D8HhJ.js} +390 -15
- package/dist/clientStore-BI4D8HhJ.js.map +1 -0
- package/dist/{createSanityInstance-DUI1bGIg.d.ts → createSanityInstance-BQIqyGLX.d.ts} +17 -2
- package/dist/createSanityInstance-BQIqyGLX.d.ts.map +1 -0
- package/dist/{createStateSourceAction-BOKtqFNm.js → createStateSourceAction-DdAhEfnZ.js} +7 -3
- package/dist/createStateSourceAction-DdAhEfnZ.js.map +1 -0
- package/dist/defineProperty-Bho9Ngsi.js +31 -0
- package/dist/{getNodeState-CR3__B2D.js → getNodeState-B4nG0sjm.js} +2 -2
- package/dist/{getNodeState-CR3__B2D.js.map → getNodeState-B4nG0sjm.js.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +370 -90
- package/dist/index.js.map +1 -1
- package/dist/topicStore-Ct7IFNtY.js +565 -0
- package/dist/topicStore-Ct7IFNtY.js.map +1 -0
- package/dist/topicStore-D0WqhQGF.d.ts +435 -0
- package/dist/topicStore-D0WqhQGF.d.ts.map +1 -0
- package/dist/{queryStore-C7yMHPUI.js → version-BcRmCiYi.js} +48 -16
- package/dist/version-BcRmCiYi.js.map +1 -0
- package/package.json +19 -18
- package/src/_exports/_internal.ts +13 -0
- package/src/_exports/dashboard.ts +36 -0
- package/src/_exports/index.ts +17 -1
- package/src/access/checkPermissions.test.ts +1 -1
- package/src/access/checkPermissions.ts +1 -1
- package/src/applications/applications.test-d.ts +28 -4
- package/src/applications/applications.test.ts +4 -4
- package/src/applications/applications.ts +43 -18
- package/src/auth/authMode.test.ts +24 -0
- package/src/auth/authMode.ts +8 -3
- package/src/auth/authStore.ts +12 -0
- package/src/auth/authStrategy.ts +2 -0
- package/src/auth/handleAuthCallback.test.ts +2 -2
- package/src/auth/handleAuthCallback.ts +1 -1
- package/src/auth/logout.test.ts +46 -1
- package/src/auth/logout.ts +2 -1
- package/src/auth/oauth/oauthActions.test.ts +457 -0
- package/src/auth/oauth/oauthActions.ts +389 -0
- package/src/auth/oauth/oauthAuth.test.ts +206 -0
- package/src/auth/oauth/oauthAuth.ts +168 -0
- package/src/auth/oauth/pkce.test.ts +44 -0
- package/src/auth/oauth/pkce.ts +60 -0
- package/src/auth/oauth/types.ts +12 -0
- package/src/auth/refreshStampedToken.ts +1 -1
- package/src/auth/studioModeAuth.test.ts +1 -1
- package/src/auth/studioModeAuth.ts +1 -1
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +2 -2
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +1 -1
- package/src/comments/addonDatasetStore.test.ts +2 -2
- package/src/comments/addonDatasetStore.ts +2 -2
- package/src/comments/commentActions.ts +6 -5
- package/src/comments/commentsStore.ts +4 -4
- package/src/config/authConfig.ts +18 -0
- package/src/dashboard/messageBus/__fixtures__/test-topics.ts +18 -0
- package/src/dashboard/messageBus/bus.test.ts +1033 -0
- package/src/dashboard/messageBus/bus.ts +1246 -0
- package/src/dashboard/messageBus/store.test.ts +142 -0
- package/src/dashboard/messageBus/store.ts +74 -0
- package/src/dashboard/messageBus/topicStore.test.ts +172 -0
- package/src/dashboard/messageBus/topicStore.ts +184 -0
- package/src/dashboard/messageBus/topics.test.ts +71 -0
- package/src/dashboard/messageBus/topics.ts +260 -0
- package/src/document/actions.ts +3 -3
- package/src/document/applyDocumentActions.ts +5 -9
- package/src/document/documentStore.concurrency.test.ts +2 -1
- package/src/document/documentStore.test.ts +19 -18
- package/src/document/documentStore.ts +17 -17
- package/src/document/permissions.ts +2 -1
- package/src/document/processActions.test.ts +2 -1
- package/src/document/processMutations.ts +61 -24
- package/src/document/reducers.ts +2 -2
- package/src/installations/installations.test-d.ts +3 -3
- package/src/installations/installations.test.ts +2 -2
- package/src/installations/installations.ts +4 -21
- package/src/organization/organization.test.ts +2 -2
- package/src/organization/organization.ts +1 -1
- package/src/organizations/organizations.test.ts +2 -2
- package/src/organizations/organizations.ts +1 -1
- package/src/presence/presenceStore.test.ts +15 -22
- package/src/presence/presenceStore.ts +3 -2
- package/src/project/project.test.ts +3 -3
- package/src/project/project.ts +1 -1
- package/src/projection/getProjectionState.test.ts +6 -4
- package/src/projection/getProjectionState.ts +4 -4
- package/src/projection/resolveProjection.ts +2 -2
- package/src/projects/projects.test.ts +2 -2
- package/src/projects/projects.ts +1 -1
- package/src/query/queryStore.ts +6 -6
- package/src/store/createSanityInstance.test.ts +1 -1
- package/src/store/createSanityInstance.ts +4 -4
- package/src/store/createStoreInstance.test.ts +0 -5
- package/src/telemetry/telemetryManager.test.ts +1 -1
- package/src/telemetry/telemetryManager.ts +2 -2
- package/src/typegen/defineProjection.test.ts +17 -0
- package/src/typegen/defineProjection.ts +19 -0
- package/src/typegen/groqCompat.ts +122 -0
- package/src/typegen/resolve.ts +37 -0
- package/src/users/usersStore.test.ts +1 -1
- package/src/users/usersStore.ts +5 -5
- package/src/utils/createGroqSearchFilter.test.ts +8 -0
- package/src/utils/createGroqSearchFilter.ts +6 -3
- package/src/utils/ids.test.ts +40 -15
- package/src/utils/ids.ts +47 -2
- package/dist/authStore-C0o1NOxW.d.ts.map +0 -1
- package/dist/authStore-CtE9bLHW.js.map +0 -1
- package/dist/clientStore-BtE2-q2i.js +0 -90
- package/dist/clientStore-BtE2-q2i.js.map +0 -1
- package/dist/createSanityInstance-DUI1bGIg.d.ts.map +0 -1
- package/dist/createStateSourceAction-BOKtqFNm.js.map +0 -1
- package/dist/getEnv-D5RGkQLu.js +0 -8
- package/dist/getEnv-D5RGkQLu.js.map +0 -1
- package/dist/logger-DAtJb5RQ.js +0 -173
- package/dist/logger-DAtJb5RQ.js.map +0 -1
- package/dist/queryStore-C7yMHPUI.js.map +0 -1
- package/dist/rolldown-runtime-vyAXikos.js +0 -9
- package/dist/telemetryManager-UIEiMtC0.js +0 -127
- package/dist/telemetryManager-UIEiMtC0.js.map +0 -1
- package/dist/version-D8RP_Q59.js +0 -9
- package/dist/version-D8RP_Q59.js.map +0 -1
|
@@ -7,6 +7,17 @@ export {
|
|
|
7
7
|
isProjectUserNotFoundClientError,
|
|
8
8
|
} from '../auth/utils'
|
|
9
9
|
export {getCommentsOptionsKey, parseCommentsOptionsKey} from '../comments/commentsStore' // only used for memoizing in React, not needed for actual functionality
|
|
10
|
+
export {
|
|
11
|
+
installMessageBus,
|
|
12
|
+
type InstallMessageBusOptions,
|
|
13
|
+
resetMessageBus,
|
|
14
|
+
} from '../dashboard/messageBus/bus'
|
|
15
|
+
export {
|
|
16
|
+
getDashboardMessageBus,
|
|
17
|
+
isDashboardEnvironment,
|
|
18
|
+
requireDashboardMessageBus,
|
|
19
|
+
} from '../dashboard/messageBus/store'
|
|
20
|
+
export {getTopicState, resolveTopic} from '../dashboard/messageBus/topicStore'
|
|
10
21
|
export {PREVIEW_PROJECTION} from '../preview/previewConstants'
|
|
11
22
|
export {transformProjectionToPreview} from '../preview/previewProjectionUtils'
|
|
12
23
|
export {getQueryKey, parseQueryKey} from '../query/queryStore' // only used for memoizing in React, not needed for actual functionality
|
|
@@ -24,4 +35,6 @@ export {
|
|
|
24
35
|
export {getTelemetryManager, initTelemetry, trackHookMounted} from '../telemetry/initTelemetry'
|
|
25
36
|
export {getUsersKey, parseUsersKey} from '../users/reducers' // only used for memoizing in React, not needed for actual functionality
|
|
26
37
|
export {createGroqSearchFilter} from '../utils/createGroqSearchFilter'
|
|
38
|
+
export {randomId, randomUuid} from '../utils/ids'
|
|
39
|
+
export {createLogger} from '../utils/logger'
|
|
27
40
|
export {isDeepEqual, pickProperties} from '../utils/object'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
ConnectMessageBusOptions,
|
|
3
|
+
MessageBus,
|
|
4
|
+
MessageBusAbortOptions,
|
|
5
|
+
MessageBusClient,
|
|
6
|
+
MessageBusConnection,
|
|
7
|
+
MessageBusEmitOptions,
|
|
8
|
+
MessageBusEmitResult,
|
|
9
|
+
MessageBusErrorCode,
|
|
10
|
+
MessageBusHost,
|
|
11
|
+
MessageBusMessage,
|
|
12
|
+
MessageBusMeta,
|
|
13
|
+
MessageBusQueryOptions,
|
|
14
|
+
MessageBusStateSource,
|
|
15
|
+
} from '../dashboard/messageBus/bus'
|
|
16
|
+
export {connectMessageBus} from '../dashboard/messageBus/bus'
|
|
17
|
+
export {MessageBusError} from '../dashboard/messageBus/bus'
|
|
18
|
+
export type {
|
|
19
|
+
ApplicationConfig,
|
|
20
|
+
ApplicationConfigAppType,
|
|
21
|
+
DashboardTopics,
|
|
22
|
+
EventTopic,
|
|
23
|
+
EventTopicDef,
|
|
24
|
+
NavigationLocation,
|
|
25
|
+
NavigationTarget,
|
|
26
|
+
PayloadOf,
|
|
27
|
+
RemoteModuleRef,
|
|
28
|
+
ReplyOf,
|
|
29
|
+
StateTopic,
|
|
30
|
+
StateTopicDef,
|
|
31
|
+
TopicName,
|
|
32
|
+
TopicResult,
|
|
33
|
+
Topics,
|
|
34
|
+
ValueOf,
|
|
35
|
+
} from '../dashboard/messageBus/topics'
|
|
36
|
+
export {type TopicData, TopicError} from '../dashboard/messageBus/topicStore'
|
package/src/_exports/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ export {
|
|
|
20
20
|
type ApplicationDeployment,
|
|
21
21
|
type ApplicationInclude,
|
|
22
22
|
type ApplicationInterface,
|
|
23
|
+
type ApplicationInterfacePlacementMetadata,
|
|
23
24
|
type ApplicationsOptions,
|
|
24
25
|
type ApplicationsResponse,
|
|
25
26
|
type ApplicationStudioConfig,
|
|
@@ -48,6 +49,14 @@ export {
|
|
|
48
49
|
export {observeOrganizationVerificationState} from '../auth/getOrganizationVerificationState'
|
|
49
50
|
export {handleAuthCallback} from '../auth/handleAuthCallback'
|
|
50
51
|
export {logout} from '../auth/logout'
|
|
52
|
+
export {
|
|
53
|
+
getOAuthTokensState,
|
|
54
|
+
handleOAuthCallback,
|
|
55
|
+
refreshOAuthTokens,
|
|
56
|
+
revokeOAuthTokens,
|
|
57
|
+
startOAuthAuthorization,
|
|
58
|
+
} from '../auth/oauth/oauthActions'
|
|
59
|
+
export {type OAuthTokens} from '../auth/oauth/types'
|
|
51
60
|
export type {ClientStoreState as ClientState} from '../client/clientStore'
|
|
52
61
|
export {type ClientOptions, getClient, getClientState} from '../client/clientStore'
|
|
53
62
|
export {
|
|
@@ -181,6 +190,12 @@ export {
|
|
|
181
190
|
export {type JsonMatch} from '../document/patchOperations'
|
|
182
191
|
export {type DocumentPermissionsResult, type PermissionDeniedReason} from '../document/permissions'
|
|
183
192
|
export {getReleaseDocumentId} from '../document/processActions/releaseUtil'
|
|
193
|
+
export {
|
|
194
|
+
type DocumentSet,
|
|
195
|
+
patchOperations,
|
|
196
|
+
processMutations,
|
|
197
|
+
type ProcessMutationsOptions,
|
|
198
|
+
} from '../document/processMutations'
|
|
184
199
|
export type {
|
|
185
200
|
FavoriteDocumentContext,
|
|
186
201
|
FavoriteStatusResponse,
|
|
@@ -194,7 +209,6 @@ export {
|
|
|
194
209
|
type InstallationActiveConfig,
|
|
195
210
|
type InstallationBase,
|
|
196
211
|
type InstallationInclude,
|
|
197
|
-
type InstallationInterface,
|
|
198
212
|
type InstallationsOptions,
|
|
199
213
|
type InstallationsResponse,
|
|
200
214
|
} from '../installations/installations'
|
|
@@ -244,6 +258,8 @@ export type {ReleaseState} from '../releases/releasesStore'
|
|
|
244
258
|
export {getActiveReleasesState, getAllReleasesState} from '../releases/releasesStore'
|
|
245
259
|
export {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
246
260
|
export {type Selector, type StateSource} from '../store/createStateSourceAction'
|
|
261
|
+
export {defineProjection} from '../typegen/defineProjection'
|
|
262
|
+
export type {ResolveDocument, ResolveProjectionResult, ResolveQueryResult} from '../typegen/resolve'
|
|
247
263
|
export {
|
|
248
264
|
type GetUserOptions,
|
|
249
265
|
type GetUsersOptions,
|
|
@@ -38,7 +38,7 @@ describe('checkPermissions', () => {
|
|
|
38
38
|
scope: 'global',
|
|
39
39
|
})
|
|
40
40
|
expect(request).toHaveBeenCalledWith({
|
|
41
|
-
|
|
41
|
+
url: '/access/project/p1/user-permissions/me/check',
|
|
42
42
|
query: {permissions: ['sanity.project.read']},
|
|
43
43
|
tag: 'access.check',
|
|
44
44
|
})
|
|
@@ -41,7 +41,7 @@ export const checkPermissions = defineFetcher<
|
|
|
41
41
|
switchMap((client) =>
|
|
42
42
|
client.observable
|
|
43
43
|
.request<{data: Record<string, boolean>}>({
|
|
44
|
-
|
|
44
|
+
url: `/access/${resourceType}/${resourceId}/user-permissions/me/check`,
|
|
45
45
|
query: {permissions},
|
|
46
46
|
tag: 'access.check',
|
|
47
47
|
})
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type ApplicationDeployment,
|
|
11
11
|
type ApplicationInclude,
|
|
12
12
|
type ApplicationInterface,
|
|
13
|
+
type ApplicationInterfacePlacementMetadata,
|
|
13
14
|
applications,
|
|
14
15
|
type ApplicationsResponse,
|
|
15
16
|
type ApplicationStudioConfig,
|
|
@@ -35,10 +36,30 @@ test('ApplicationBase — carries name and reference as strings', () => {
|
|
|
35
36
|
expectTypeOf<ApplicationBase['reference']>().toEqualTypeOf<string>()
|
|
36
37
|
})
|
|
37
38
|
|
|
38
|
-
test('
|
|
39
|
+
test('ApplicationInterface — matches the interface response', () => {
|
|
40
|
+
expectTypeOf<ApplicationInterface['type']>().toEqualTypeOf<
|
|
41
|
+
'app' | 'worker' | 'asset_source' | 'panel' | 'tile'
|
|
42
|
+
>()
|
|
43
|
+
expectTypeOf<ApplicationInterfacePlacementMetadata>().toEqualTypeOf<{
|
|
44
|
+
dock?: {group?: string; order?: number}
|
|
45
|
+
}>()
|
|
46
|
+
expectTypeOf<
|
|
47
|
+
Extract<ApplicationInterface, {type: 'app'}>['metadata']
|
|
48
|
+
>().toEqualTypeOf<ApplicationInterfacePlacementMetadata | null>()
|
|
39
49
|
expectTypeOf<
|
|
40
|
-
|
|
41
|
-
>().toEqualTypeOf<
|
|
50
|
+
Extract<ApplicationInterface, {type: 'panel'}>['metadata']
|
|
51
|
+
>().toEqualTypeOf<ApplicationInterfacePlacementMetadata | null>()
|
|
52
|
+
expectTypeOf<Extract<ApplicationInterface, {type: 'worker'}>['metadata']>().toEqualTypeOf<null>()
|
|
53
|
+
expectTypeOf<Extract<ApplicationInterface, {type: 'tile'}>['metadata']>().toEqualTypeOf<{
|
|
54
|
+
order?: number
|
|
55
|
+
size: 'small' | 'large' | 'banner'
|
|
56
|
+
}>()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('Application — config.studio adds config with an optional stored value', () => {
|
|
60
|
+
expectTypeOf<Application<'config.studio'>['config']['studio']>().toEqualTypeOf<
|
|
61
|
+
ApplicationStudioConfig | undefined
|
|
62
|
+
>()
|
|
42
63
|
// mfManifest not requested → not present under config
|
|
43
64
|
expectTypeOf<
|
|
44
65
|
Extract<keyof Application<'config.studio'>['config'], 'mfManifest'>
|
|
@@ -51,7 +72,7 @@ test('Application — config.studio adds a required config.studio', () => {
|
|
|
51
72
|
|
|
52
73
|
test('Application — both config tokens merge under a single config', () => {
|
|
53
74
|
type Config = Application<'config.studio' | 'config.mfManifest'>['config']
|
|
54
|
-
expectTypeOf<Config['studio']>().toEqualTypeOf<ApplicationStudioConfig>()
|
|
75
|
+
expectTypeOf<Config['studio']>().toEqualTypeOf<ApplicationStudioConfig | undefined>()
|
|
55
76
|
expectTypeOf<Config['mfManifest']>().toEqualTypeOf<unknown>()
|
|
56
77
|
})
|
|
57
78
|
|
|
@@ -73,6 +94,9 @@ test('Application — a wide include array makes config and activeDeployment opt
|
|
|
73
94
|
type Wide = Application<ApplicationInclude>
|
|
74
95
|
expectTypeOf<KeyModifier<Wide, 'activeDeployment'>>().toEqualTypeOf<'optional'>()
|
|
75
96
|
expectTypeOf<KeyModifier<Wide, 'config'>>().toEqualTypeOf<'optional'>()
|
|
97
|
+
type Config = NonNullable<Wide['config']>
|
|
98
|
+
expectTypeOf<Config['studio']>().toEqualTypeOf<ApplicationStudioConfig | undefined>()
|
|
99
|
+
expectTypeOf<Config['mfManifest']>().toEqualTypeOf<unknown>()
|
|
76
100
|
})
|
|
77
101
|
|
|
78
102
|
test('applications.resolveState — resolves the wide list envelope', () => {
|
|
@@ -42,7 +42,7 @@ describe('applications', () => {
|
|
|
42
42
|
scope: 'global',
|
|
43
43
|
})
|
|
44
44
|
expect(request).toHaveBeenCalledWith({
|
|
45
|
-
|
|
45
|
+
url: '/applications',
|
|
46
46
|
query: {organizationId: 'org1', include: 'access,workspaces', limit: 'none'},
|
|
47
47
|
tag: 'applications.list',
|
|
48
48
|
})
|
|
@@ -56,7 +56,7 @@ describe('applications', () => {
|
|
|
56
56
|
|
|
57
57
|
expect(result).toEqual(app)
|
|
58
58
|
expect(request).toHaveBeenCalledWith({
|
|
59
|
-
|
|
59
|
+
url: '/applications/app1',
|
|
60
60
|
query: {},
|
|
61
61
|
tag: 'applications.get',
|
|
62
62
|
})
|
|
@@ -105,7 +105,7 @@ describe('applications', () => {
|
|
|
105
105
|
|
|
106
106
|
expect(data).toEqual({id: 'app1', title: 'New'})
|
|
107
107
|
expect(request).toHaveBeenCalledWith({
|
|
108
|
-
|
|
108
|
+
url: '/applications/app1',
|
|
109
109
|
method: 'PATCH',
|
|
110
110
|
body: {title: 'New'},
|
|
111
111
|
tag: 'applications.update',
|
|
@@ -122,7 +122,7 @@ describe('applications', () => {
|
|
|
122
122
|
|
|
123
123
|
expect(data).toEqual({deleted: true})
|
|
124
124
|
expect(request).toHaveBeenCalledWith({
|
|
125
|
-
|
|
125
|
+
url: '/applications/app1',
|
|
126
126
|
method: 'DELETE',
|
|
127
127
|
tag: 'applications.delete',
|
|
128
128
|
})
|
|
@@ -25,23 +25,40 @@ export type ApplicationInclude =
|
|
|
25
25
|
| 'config.mfManifest'
|
|
26
26
|
| 'activeDeployment'
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
* An interface exposed by a deployment, embedded when `interfaces` is included.
|
|
30
|
-
*
|
|
31
|
-
* @see https://www.sanity.io/docs/http-reference/applications-api
|
|
32
|
-
* @public
|
|
33
|
-
*/
|
|
34
|
-
export interface ApplicationInterface {
|
|
28
|
+
type ApplicationInterfaceBase = {
|
|
35
29
|
id: string
|
|
36
|
-
type: 'app' | 'worker' | 'asset_source' | 'panel'
|
|
37
30
|
name: string
|
|
38
31
|
title: string
|
|
39
32
|
version: string
|
|
40
33
|
/** Module federation module ID; resolved from the host mf-manifest at runtime */
|
|
41
34
|
moduleId: string
|
|
42
|
-
metadata: {group?: string; priority?: number} | null
|
|
43
35
|
}
|
|
44
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Placement metadata for application and panel interfaces.
|
|
39
|
+
*
|
|
40
|
+
* @see https://www.sanity.io/docs/http-reference/applications-api
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export type ApplicationInterfacePlacementMetadata = {
|
|
44
|
+
dock?: {group?: string; order?: number}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* An interface exposed by a deployment, embedded when `interfaces` is included.
|
|
49
|
+
*
|
|
50
|
+
* @see https://www.sanity.io/docs/http-reference/applications-api
|
|
51
|
+
* @public
|
|
52
|
+
*/
|
|
53
|
+
export type ApplicationInterface = ApplicationInterfaceBase &
|
|
54
|
+
(
|
|
55
|
+
| {type: 'app'; metadata: ApplicationInterfacePlacementMetadata | null}
|
|
56
|
+
| {type: 'panel'; metadata: ApplicationInterfacePlacementMetadata | null}
|
|
57
|
+
| {type: 'asset_source'; metadata: null}
|
|
58
|
+
| {type: 'worker'; metadata: null}
|
|
59
|
+
| {type: 'tile'; metadata: {order?: number; size: 'small' | 'large' | 'banner'}}
|
|
60
|
+
)
|
|
61
|
+
|
|
45
62
|
/**
|
|
46
63
|
* A studio workspace of a deployment, embedded when `workspaces` is included.
|
|
47
64
|
* Studio applications only.
|
|
@@ -136,16 +153,24 @@ export interface ApplicationBase {
|
|
|
136
153
|
|
|
137
154
|
// Requesting any of these tokens forces the deployment summary into the response.
|
|
138
155
|
type DeploymentToken = 'activeDeployment' | 'interfaces' | 'workspaces' | 'access'
|
|
156
|
+
type ConfigToken = 'config.studio' | 'config.mfManifest'
|
|
139
157
|
|
|
140
|
-
|
|
141
|
-
// gated on its own token.
|
|
142
|
-
type ApplicationConfigShape<Include extends ApplicationInclude> = Included<
|
|
158
|
+
type ApplicationConfig<Include extends ApplicationInclude> = Included<
|
|
143
159
|
ApplicationInclude,
|
|
144
160
|
'config.studio',
|
|
145
161
|
Include,
|
|
146
|
-
{
|
|
162
|
+
{studio?: ApplicationStudioConfig}
|
|
147
163
|
> &
|
|
148
|
-
Included<ApplicationInclude, 'config.mfManifest', Include, {
|
|
164
|
+
Included<ApplicationInclude, 'config.mfManifest', Include, {mfManifest?: unknown}>
|
|
165
|
+
|
|
166
|
+
// Brett returns `config: {}` when a requested config value is not stored.
|
|
167
|
+
type ApplicationConfigShape<Include extends ApplicationInclude> = [ApplicationInclude] extends [
|
|
168
|
+
Include,
|
|
169
|
+
]
|
|
170
|
+
? {config?: ApplicationConfig<Include>}
|
|
171
|
+
: IncludesAny<Include, ConfigToken> extends true
|
|
172
|
+
? {config: ApplicationConfig<Include>}
|
|
173
|
+
: unknown
|
|
149
174
|
|
|
150
175
|
// `activeDeployment` is present when any deployment-related token was requested;
|
|
151
176
|
// a wide include array makes it optional.
|
|
@@ -227,7 +252,7 @@ export const applications = defineFetcher<
|
|
|
227
252
|
getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(
|
|
228
253
|
switchMap((client) =>
|
|
229
254
|
client.observable.request<ApplicationsResponse<ApplicationInclude>>({
|
|
230
|
-
|
|
255
|
+
url: '/applications',
|
|
231
256
|
query: buildQuery({
|
|
232
257
|
organizationId: options.organizationId,
|
|
233
258
|
type: options.type,
|
|
@@ -264,7 +289,7 @@ export const application = defineFetcher<
|
|
|
264
289
|
getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(
|
|
265
290
|
switchMap((client) =>
|
|
266
291
|
client.observable.request<Application<ApplicationInclude>>({
|
|
267
|
-
|
|
292
|
+
url: `/applications/${applicationId}`,
|
|
268
293
|
query: buildQuery({include: serializeInclude(options?.include)}),
|
|
269
294
|
tag: 'applications.get',
|
|
270
295
|
}),
|
|
@@ -317,7 +342,7 @@ export const updateApplication = defineMutation<UpdateApplicationInput, Applicat
|
|
|
317
342
|
getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(
|
|
318
343
|
switchMap((client) =>
|
|
319
344
|
client.observable.request<Application>({
|
|
320
|
-
|
|
345
|
+
url: `/applications/${applicationId}`,
|
|
321
346
|
method: 'PATCH',
|
|
322
347
|
body,
|
|
323
348
|
tag: 'applications.update',
|
|
@@ -354,7 +379,7 @@ export const deleteApplication = defineMutation<DeleteApplicationInput, DeletedR
|
|
|
354
379
|
getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(
|
|
355
380
|
switchMap((client) =>
|
|
356
381
|
client.observable.request<DeletedResult>({
|
|
357
|
-
|
|
382
|
+
url: `/applications/${applicationId}`,
|
|
358
383
|
method: 'DELETE',
|
|
359
384
|
tag: 'applications.delete',
|
|
360
385
|
}),
|
|
@@ -18,6 +18,30 @@ describe('resolveAuthMode', () => {
|
|
|
18
18
|
it('returns "standalone" by default', () => {
|
|
19
19
|
expect(resolveAuthMode({}, 'https://example.com')).toBe('standalone')
|
|
20
20
|
})
|
|
21
|
+
|
|
22
|
+
it('returns "oauth" when auth.oauth config is provided', () => {
|
|
23
|
+
const config: SanityConfig = {
|
|
24
|
+
auth: {oauth: {clientId: 'c', redirectUri: 'https://app/cb', organizationId: 'o'}},
|
|
25
|
+
}
|
|
26
|
+
expect(resolveAuthMode(config, 'https://example.com')).toBe('oauth')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('prefers "oauth" over dashboard context detection', () => {
|
|
30
|
+
const context = encodeURIComponent(JSON.stringify({orgId: '123'}))
|
|
31
|
+
const href = `https://example.com?_context=${context}`
|
|
32
|
+
const config: SanityConfig = {
|
|
33
|
+
auth: {oauth: {clientId: 'c', redirectUri: 'https://app/cb', organizationId: 'o'}},
|
|
34
|
+
}
|
|
35
|
+
expect(resolveAuthMode(config, href)).toBe('oauth')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('prefers "studio" over oauth config', () => {
|
|
39
|
+
const config: SanityConfig = {
|
|
40
|
+
studio: {},
|
|
41
|
+
auth: {oauth: {clientId: 'c', redirectUri: 'https://app/cb', organizationId: 'o'}},
|
|
42
|
+
}
|
|
43
|
+
expect(resolveAuthMode(config, 'https://example.com')).toBe('studio')
|
|
44
|
+
})
|
|
21
45
|
})
|
|
22
46
|
|
|
23
47
|
describe('isStudioConfig', () => {
|
package/src/auth/authMode.ts
CHANGED
|
@@ -6,6 +6,8 @@ import {DEFAULT_BASE} from './authConstants'
|
|
|
6
6
|
*
|
|
7
7
|
* - `studio` — Running inside Sanity Studio. Token is discovered from
|
|
8
8
|
* Studio's localStorage entry or via cookie auth.
|
|
9
|
+
* - `oauth` — Configured with `auth.oauth`. Authenticates via Sanity's
|
|
10
|
+
* OAuth 2.0 authorization-code + PKCE flow.
|
|
9
11
|
* - `dashboard` — Running inside the Sanity Dashboard iframe. Token is
|
|
10
12
|
* provided by the parent frame via Comlink.
|
|
11
13
|
* - `standalone` — Running as an independent app. Token comes from
|
|
@@ -13,20 +15,23 @@ import {DEFAULT_BASE} from './authConstants'
|
|
|
13
15
|
*
|
|
14
16
|
* @internal
|
|
15
17
|
*/
|
|
16
|
-
type AuthMode = 'studio' | 'dashboard' | 'standalone'
|
|
18
|
+
type AuthMode = 'studio' | 'oauth' | 'dashboard' | 'standalone'
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* Determines the auth mode from instance config and environment.
|
|
20
22
|
*
|
|
21
23
|
* Priority:
|
|
22
24
|
* 1. `studio` config provided → `'studio'`
|
|
23
|
-
* 2.
|
|
24
|
-
*
|
|
25
|
+
* 2. `auth.oauth` config provided → `'oauth'` (explicit config wins over
|
|
26
|
+
* environment detection)
|
|
27
|
+
* 3. Dashboard context detected (`_context` URL param with content) → `'dashboard'`
|
|
28
|
+
* 4. Otherwise → `'standalone'`
|
|
25
29
|
*
|
|
26
30
|
* @internal
|
|
27
31
|
*/
|
|
28
32
|
export function resolveAuthMode(config: SanityConfig, locationHref: string): AuthMode {
|
|
29
33
|
if (isStudioConfig(config)) return 'studio'
|
|
34
|
+
if (config.auth?.oauth) return 'oauth'
|
|
30
35
|
if (detectDashboardContext(locationHref)) return 'dashboard'
|
|
31
36
|
return 'standalone'
|
|
32
37
|
}
|
package/src/auth/authStore.ts
CHANGED
|
@@ -11,6 +11,8 @@ import {resolveAuthMode} from './authMode'
|
|
|
11
11
|
import {AuthStateType} from './authStateType'
|
|
12
12
|
import {type AuthStrategyOptions} from './authStrategy'
|
|
13
13
|
import {getDashboardInitialState, initializeDashboardAuth} from './dashboardAuth'
|
|
14
|
+
import {getOauthInitialState, initializeOauthAuth} from './oauth/oauthAuth'
|
|
15
|
+
import {type OAuthTokens} from './oauth/types'
|
|
14
16
|
import {getStandaloneInitialState, initializeStandaloneAuth} from './standaloneAuth'
|
|
15
17
|
import {getStudioInitialState, initializeStudioAuth} from './studioAuth'
|
|
16
18
|
import {createLoggedInAuthState, getCleanedUrl, getDefaultLocation} from './utils'
|
|
@@ -91,8 +93,10 @@ export interface AuthStoreState {
|
|
|
91
93
|
callbackUrl: string | undefined
|
|
92
94
|
providedToken: string | undefined
|
|
93
95
|
authMethod: AuthMethodOptions
|
|
96
|
+
oauth: AuthConfig['oauth']
|
|
94
97
|
}
|
|
95
98
|
dashboardContext?: DashboardContext
|
|
99
|
+
oauthTokens?: OAuthTokens
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
// ---------------------------------------------------------------------------
|
|
@@ -155,6 +159,9 @@ export const authStore = defineStore<AuthStoreState>({
|
|
|
155
159
|
case 'studio':
|
|
156
160
|
result = getStudioInitialState(strategyOptions)
|
|
157
161
|
break
|
|
162
|
+
case 'oauth':
|
|
163
|
+
result = getOauthInitialState(strategyOptions)
|
|
164
|
+
break
|
|
158
165
|
case 'dashboard':
|
|
159
166
|
result = getDashboardInitialState(strategyOptions)
|
|
160
167
|
break
|
|
@@ -172,6 +179,7 @@ export const authStore = defineStore<AuthStoreState>({
|
|
|
172
179
|
return {
|
|
173
180
|
authState: result.authState,
|
|
174
181
|
dashboardContext: result.dashboardContext,
|
|
182
|
+
oauthTokens: result.oauthTokens,
|
|
175
183
|
options: {
|
|
176
184
|
apiHost,
|
|
177
185
|
loginUrl: loginUrl.toString(),
|
|
@@ -183,6 +191,7 @@ export const authStore = defineStore<AuthStoreState>({
|
|
|
183
191
|
storageKey: result.storageKey,
|
|
184
192
|
storageArea: result.storageArea,
|
|
185
193
|
authMethod: result.authMethod,
|
|
194
|
+
oauth: authConfig.oauth,
|
|
186
195
|
},
|
|
187
196
|
}
|
|
188
197
|
},
|
|
@@ -201,6 +210,9 @@ export const authStore = defineStore<AuthStoreState>({
|
|
|
201
210
|
case 'studio':
|
|
202
211
|
initResult = initializeStudioAuth(context, tokenRefresherRunning)
|
|
203
212
|
break
|
|
213
|
+
case 'oauth':
|
|
214
|
+
initResult = initializeOauthAuth(context)
|
|
215
|
+
break
|
|
204
216
|
case 'dashboard':
|
|
205
217
|
initResult = initializeDashboardAuth(context, tokenRefresherRunning)
|
|
206
218
|
break
|
package/src/auth/authStrategy.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {type ClientConfig, type SanityClient} from '@sanity/client'
|
|
|
3
3
|
import {type AuthConfig} from '../config/authConfig'
|
|
4
4
|
import {type TokenSource} from '../config/sanityConfig'
|
|
5
5
|
import {type AuthMethodOptions, type AuthState, type DashboardContext} from './authStore'
|
|
6
|
+
import {type OAuthTokens} from './oauth/types'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* The result returned by each auth strategy's `getInitialState` function.
|
|
@@ -17,6 +18,7 @@ export interface AuthStrategyResult {
|
|
|
17
18
|
storageArea: Storage | undefined
|
|
18
19
|
authMethod: AuthMethodOptions
|
|
19
20
|
dashboardContext: DashboardContext
|
|
21
|
+
oauthTokens?: OAuthTokens
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -94,7 +94,7 @@ describe('handleCallback', () => {
|
|
|
94
94
|
method: 'GET',
|
|
95
95
|
query: {sid: authCode},
|
|
96
96
|
tag: 'fetch-token',
|
|
97
|
-
|
|
97
|
+
url: '/auth/fetch',
|
|
98
98
|
})
|
|
99
99
|
expect(setItem).toHaveBeenCalledWith('__sanity_auth_token', '{"token":"new-token"}')
|
|
100
100
|
})
|
|
@@ -236,7 +236,7 @@ describe('handleCallback', () => {
|
|
|
236
236
|
method: 'GET',
|
|
237
237
|
query: {sid: authCode},
|
|
238
238
|
tag: 'fetch-token',
|
|
239
|
-
|
|
239
|
+
url: '/auth/fetch',
|
|
240
240
|
})
|
|
241
241
|
})
|
|
242
242
|
|
|
@@ -94,7 +94,7 @@ export const handleAuthCallback = bindActionGlobally(
|
|
|
94
94
|
logger.debug('Fetching token from auth endpoint')
|
|
95
95
|
const {token} = await client.request<{token: string; label: string}>({
|
|
96
96
|
method: 'GET',
|
|
97
|
-
|
|
97
|
+
url: '/auth/fetch',
|
|
98
98
|
query: {sid: authCode},
|
|
99
99
|
tag: 'fetch-token',
|
|
100
100
|
})
|
package/src/auth/logout.test.ts
CHANGED
|
@@ -5,6 +5,9 @@ import {createSanityInstance, type SanityInstance} from '../store/createSanityIn
|
|
|
5
5
|
import {AuthStateType} from './authStateType'
|
|
6
6
|
import {getAuthState} from './authStore'
|
|
7
7
|
import {logout} from './logout'
|
|
8
|
+
import {getOAuthTokensState, serializeTokens} from './oauth/oauthActions'
|
|
9
|
+
import {OAUTH_TOKENS_KEY} from './oauth/oauthAuth'
|
|
10
|
+
import {type OAuthTokens} from './oauth/types'
|
|
8
11
|
import {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser'
|
|
9
12
|
import {subscribeToStorageEventsAndSetToken} from './subscribeToStorageEventsAndSetToken'
|
|
10
13
|
import {getTokenFromStorage} from './utils'
|
|
@@ -75,7 +78,7 @@ describe('logout', () => {
|
|
|
75
78
|
useProjectHostname: false,
|
|
76
79
|
useCdn: false,
|
|
77
80
|
})
|
|
78
|
-
expect(mockRequest).toHaveBeenCalledWith({method: 'POST',
|
|
81
|
+
expect(mockRequest).toHaveBeenCalledWith({method: 'POST', url: '/auth/logout', tag: 'logout'})
|
|
79
82
|
expect(removeItem).toHaveBeenCalledWith('__sanity_auth_token')
|
|
80
83
|
})
|
|
81
84
|
|
|
@@ -166,6 +169,48 @@ describe('logout', () => {
|
|
|
166
169
|
expect(removeItem).toHaveBeenCalledWith('__sanity_auth_token')
|
|
167
170
|
})
|
|
168
171
|
|
|
172
|
+
it('clears the OAuth tokens from the store on logout', async () => {
|
|
173
|
+
const oauthTokens: OAuthTokens = {
|
|
174
|
+
accessToken: 'oauth-access',
|
|
175
|
+
tokenType: 'bearer',
|
|
176
|
+
expiresIn: 3600,
|
|
177
|
+
expiresAt: new Date('2030-01-01T00:00:00.000Z'),
|
|
178
|
+
refreshToken: 'oauth-refresh',
|
|
179
|
+
}
|
|
180
|
+
const map = new Map<string, string>([[OAUTH_TOKENS_KEY, serializeTokens(oauthTokens)]])
|
|
181
|
+
const storageArea = {
|
|
182
|
+
getItem: (k: string) => map.get(k) ?? null,
|
|
183
|
+
setItem: (k: string, v: string) => void map.set(k, v),
|
|
184
|
+
removeItem: (k: string) => void map.delete(k),
|
|
185
|
+
} as Storage
|
|
186
|
+
|
|
187
|
+
const mockRequest = vi.fn().mockResolvedValue(undefined)
|
|
188
|
+
const clientFactory = vi.fn().mockReturnValue({request: mockRequest})
|
|
189
|
+
|
|
190
|
+
instance = createSanityInstance({
|
|
191
|
+
projectId: 'p',
|
|
192
|
+
dataset: 'd',
|
|
193
|
+
auth: {
|
|
194
|
+
clientFactory,
|
|
195
|
+
storageArea,
|
|
196
|
+
oauth: {
|
|
197
|
+
clientId: 'client-abc',
|
|
198
|
+
redirectUri: 'https://app.example.com/callback',
|
|
199
|
+
organizationId: 'org123',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// Boots logged in with the persisted OAuth tokens exposed on the store.
|
|
205
|
+
expect(getOAuthTokensState(instance).getCurrent()).toMatchObject({accessToken: 'oauth-access'})
|
|
206
|
+
|
|
207
|
+
await logout(instance)
|
|
208
|
+
|
|
209
|
+
expect(getOAuthTokensState(instance).getCurrent()).toBeNull()
|
|
210
|
+
expect(getAuthState(instance).getCurrent()).toMatchObject({type: AuthStateType.LOGGED_OUT})
|
|
211
|
+
expect(map.get(OAUTH_TOKENS_KEY)).toBeUndefined()
|
|
212
|
+
})
|
|
213
|
+
|
|
169
214
|
it('cleans up storage even if logout request fails', async () => {
|
|
170
215
|
vi.mocked(getTokenFromStorage).mockReturnValue('token')
|
|
171
216
|
const error = new Error('Logout request failed')
|
package/src/auth/logout.ts
CHANGED
|
@@ -44,7 +44,7 @@ export const logout = bindActionGlobally(authStore, async ({state, instance}) =>
|
|
|
44
44
|
})
|
|
45
45
|
|
|
46
46
|
logger.debug('Calling logout endpoint')
|
|
47
|
-
await client.request<void>({
|
|
47
|
+
await client.request<void>({url: '/auth/logout', method: 'POST', tag: 'logout'})
|
|
48
48
|
} else {
|
|
49
49
|
logger.debug('No token to logout - already logged out')
|
|
50
50
|
}
|
|
@@ -57,6 +57,7 @@ export const logout = bindActionGlobally(authStore, async ({state, instance}) =>
|
|
|
57
57
|
logger.info('User logged out, clearing stored tokens')
|
|
58
58
|
state.set('logoutSuccess', {
|
|
59
59
|
authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},
|
|
60
|
+
oauthTokens: undefined,
|
|
60
61
|
})
|
|
61
62
|
storageArea?.removeItem(storageKey)
|
|
62
63
|
storageArea?.removeItem(`${storageKey}_last_refresh`)
|