@sanity/sdk 3.1.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 +52 -21
- package/dist/_exports/_internal.d.ts.map +1 -1
- package/dist/_exports/_internal.js +10 -9
- 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 +3 -2
- package/dist/_exports/comlink.js.map +1 -1
- package/dist/_exports/dashboard.d.ts +2 -0
- package/dist/_exports/dashboard.js +2 -0
- package/dist/{authStore-C-OEkGaM.d.ts → authStore-BO2vch3I.d.ts} +516 -63
- package/dist/authStore-BO2vch3I.d.ts.map +1 -0
- package/dist/{clientStore-gUALQ0qW.js → clientStore-BI4D8HhJ.js} +124 -7
- package/dist/clientStore-BI4D8HhJ.js.map +1 -0
- package/dist/{createSanityInstance-DCePpBYW.d.ts → createSanityInstance-BQIqyGLX.d.ts} +16 -1
- package/dist/createSanityInstance-BQIqyGLX.d.ts.map +1 -0
- package/dist/{object-BwynGPRZ.js → createStateSourceAction-DdAhEfnZ.js} +2 -82
- package/dist/createStateSourceAction-DdAhEfnZ.js.map +1 -0
- package/dist/defineProperty-Bho9Ngsi.js +31 -0
- package/dist/{getNodeState-E0hjqFOo.js → getNodeState-B4nG0sjm.js} +3 -2
- package/dist/{getNodeState-E0hjqFOo.js.map → getNodeState-B4nG0sjm.js.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +335 -51
- package/dist/index.js.map +1 -1
- package/dist/object-CxXlSVg7.js +83 -0
- package/dist/object-CxXlSVg7.js.map +1 -0
- 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/{version-C90rNWaX.js → version-BcRmCiYi.js} +6 -6
- package/dist/{version-C90rNWaX.js.map → version-BcRmCiYi.js.map} +1 -1
- package/package.json +19 -18
- package/src/_exports/_internal.ts +12 -0
- package/src/_exports/dashboard.ts +36 -0
- package/src/_exports/index.ts +17 -1
- package/src/applications/applications.test-d.ts +28 -4
- package/src/applications/applications.ts +39 -14
- 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/logout.test.ts +45 -0
- package/src/auth/logout.ts +1 -0
- 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/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 +3 -3
- package/src/document/documentStore.ts +12 -12
- package/src/document/processMutations.ts +58 -22
- package/src/installations/installations.test-d.ts +3 -3
- package/src/installations/installations.ts +2 -19
- package/src/projection/getProjectionState.ts +2 -2
- package/src/projection/resolveProjection.ts +2 -2
- package/src/query/queryStore.ts +3 -3
- 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/utils/createGroqSearchFilter.test.ts +8 -0
- package/src/utils/createGroqSearchFilter.ts +6 -3
- package/dist/authStore-C-OEkGaM.d.ts.map +0 -1
- package/dist/clientStore-gUALQ0qW.js.map +0 -1
- package/dist/createSanityInstance-DCePpBYW.d.ts.map +0 -1
- package/dist/object-BwynGPRZ.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/sdk",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Sanity SDK",
|
|
6
6
|
"keywords": [
|
|
@@ -37,46 +37,47 @@
|
|
|
37
37
|
"./package.json": "./package.json",
|
|
38
38
|
"./_internal": "./dist/_exports/_internal.js",
|
|
39
39
|
"./agent": "./dist/_exports/agent.js",
|
|
40
|
-
"./comlink": "./dist/_exports/comlink.js"
|
|
40
|
+
"./comlink": "./dist/_exports/comlink.js",
|
|
41
|
+
"./dashboard": "./dist/_exports/dashboard.js"
|
|
41
42
|
},
|
|
42
43
|
"publishConfig": {
|
|
43
44
|
"access": "public"
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
46
47
|
"@sanity/bifur-client": "^1.0.0",
|
|
47
|
-
"@sanity/client": "^8.
|
|
48
|
+
"@sanity/client": "^8.6.1",
|
|
48
49
|
"@sanity/comlink": "^4.0.3",
|
|
49
50
|
"@sanity/diff-match-patch": "^3.2.0",
|
|
50
51
|
"@sanity/diff-patch": "^6.0.0",
|
|
51
|
-
"@sanity/id-utils": "^1.
|
|
52
|
+
"@sanity/id-utils": "^1.1.0",
|
|
52
53
|
"@sanity/image-url": "^2.1.1",
|
|
53
54
|
"@sanity/json-match": "^1.0.5",
|
|
54
55
|
"@sanity/message-protocol": "^0.24.0",
|
|
55
56
|
"@sanity/mutate": "^0.18.1",
|
|
56
57
|
"@sanity/telemetry": "^1.1.0",
|
|
57
|
-
"@sanity/types": "^6.
|
|
58
|
-
"groq": "
|
|
58
|
+
"@sanity/types": "^6.13.0",
|
|
59
|
+
"groq": "^6.13.2",
|
|
59
60
|
"groq-js": "^2.0.0",
|
|
60
61
|
"reselect": "^5.1.1",
|
|
61
62
|
"rxjs": "^7.8.2",
|
|
62
|
-
"zustand": "^5.0.
|
|
63
|
+
"zustand": "^5.0.15"
|
|
63
64
|
},
|
|
64
65
|
"devDependencies": {
|
|
65
|
-
"@sanity/browserslist-config": "^1.0.5",
|
|
66
|
-
"@sanity/pkg-utils": "^12.1.0",
|
|
67
|
-
"@types/node": "^24.13.3",
|
|
68
|
-
"@vitest/coverage-v8": "^4.1.10",
|
|
69
|
-
"eslint": "^10.6.0",
|
|
70
|
-
"oxfmt": "^0.58.0",
|
|
71
|
-
"rolldown": "^1.2.3",
|
|
72
|
-
"typescript": "^6.0.3",
|
|
73
|
-
"vite": "^8.1.5",
|
|
74
|
-
"vitest": "^4.1.10",
|
|
75
66
|
"@repo/config-eslint": "0.0.0",
|
|
76
67
|
"@repo/config-test": "0.0.1",
|
|
77
68
|
"@repo/package.bundle": "3.82.0",
|
|
78
69
|
"@repo/package.config": "0.0.1",
|
|
79
|
-
"@repo/tsconfig": "0.0.1"
|
|
70
|
+
"@repo/tsconfig": "0.0.1",
|
|
71
|
+
"@sanity/browserslist-config": "^1.0.5",
|
|
72
|
+
"@sanity/pkg-utils": "^13.0.0",
|
|
73
|
+
"@types/node": "^24.13.4",
|
|
74
|
+
"@vitest/coverage-v8": "^5.0.0",
|
|
75
|
+
"eslint": "^10.10.0",
|
|
76
|
+
"oxfmt": "^0.67.0",
|
|
77
|
+
"rolldown": "^1.2.8",
|
|
78
|
+
"typescript": "^6.0.3",
|
|
79
|
+
"vite": "^8.3.0",
|
|
80
|
+
"vitest": "^5.0.0"
|
|
80
81
|
},
|
|
81
82
|
"browserslist": "extends @sanity/browserslist-config",
|
|
82
83
|
"scripts": {
|
|
@@ -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
|
|
@@ -25,4 +36,5 @@ export {getTelemetryManager, initTelemetry, trackHookMounted} from '../telemetry
|
|
|
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'
|
|
27
38
|
export {randomId, randomUuid} from '../utils/ids'
|
|
39
|
+
export {createLogger} from '../utils/logger'
|
|
28
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,
|
|
@@ -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', () => {
|
|
@@ -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.
|
|
@@ -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
|
/**
|
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'
|
|
@@ -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
|
@@ -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`)
|