@sanity/sdk 2.8.0 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks-dts/utils.d.ts +2396 -0
- package/dist/_chunks-es/_internal.js +129 -0
- package/dist/_chunks-es/_internal.js.map +1 -0
- package/dist/_chunks-es/createGroqSearchFilter.js +1460 -0
- package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -0
- package/dist/_chunks-es/telemetryManager.js +87 -0
- package/dist/_chunks-es/telemetryManager.js.map +1 -0
- package/dist/_chunks-es/version.js +7 -0
- package/dist/_chunks-es/version.js.map +1 -0
- package/dist/_exports/_internal.d.ts +64 -0
- package/dist/_exports/_internal.js +20 -0
- package/dist/_exports/_internal.js.map +1 -0
- package/dist/index.d.ts +2 -2343
- package/dist/index.js +383 -1777
- package/dist/index.js.map +1 -1
- package/package.json +11 -4
- package/src/_exports/_internal.ts +14 -0
- package/src/_exports/index.ts +10 -1
- package/src/auth/authStore.test.ts +150 -1
- package/src/auth/authStore.ts +11 -11
- package/src/auth/dashboardAuth.ts +2 -2
- package/src/auth/handleAuthCallback.ts +9 -3
- package/src/auth/logout.test.ts +1 -1
- package/src/auth/logout.ts +1 -1
- package/src/auth/refreshStampedToken.test.ts +118 -1
- package/src/auth/refreshStampedToken.ts +3 -2
- package/src/auth/standaloneAuth.ts +9 -3
- package/src/auth/studioAuth.ts +34 -7
- package/src/auth/studioModeAuth.ts +2 -1
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +10 -2
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +5 -1
- package/src/auth/subscribeToStorageEventsAndSetToken.ts +2 -2
- package/src/auth/utils.ts +33 -0
- package/src/client/clientStore.test.ts +14 -0
- package/src/client/clientStore.ts +2 -1
- package/src/comlink/node/getNodeState.ts +2 -1
- package/src/config/sanityConfig.ts +6 -0
- package/src/document/actions.ts +18 -11
- package/src/document/applyDocumentActions.test.ts +7 -6
- package/src/document/applyDocumentActions.ts +10 -4
- package/src/document/documentStore.test.ts +536 -188
- package/src/document/documentStore.ts +142 -76
- package/src/document/events.ts +7 -2
- package/src/document/permissions.test.ts +18 -16
- package/src/document/permissions.ts +35 -11
- package/src/document/processActions.test.ts +359 -32
- package/src/document/processActions.ts +104 -76
- package/src/document/reducers.test.ts +117 -29
- package/src/document/reducers.ts +43 -36
- package/src/document/sharedListener.ts +16 -6
- package/src/document/util.ts +14 -0
- package/src/favorites/favorites.test.ts +9 -2
- package/src/presence/bifurTransport.ts +6 -1
- package/src/preview/getPreviewState.test.ts +115 -98
- package/src/preview/getPreviewState.ts +38 -60
- package/src/preview/previewProjectionUtils.test.ts +179 -0
- package/src/preview/previewProjectionUtils.ts +93 -0
- package/src/preview/resolvePreview.test.ts +42 -25
- package/src/preview/resolvePreview.ts +29 -10
- package/src/preview/{previewStore.ts → types.ts} +8 -17
- package/src/projection/getProjectionState.test.ts +16 -16
- package/src/projection/getProjectionState.ts +2 -1
- package/src/projection/projectionQuery.ts +2 -3
- package/src/projection/types.ts +1 -1
- package/src/query/queryStore.ts +2 -1
- package/src/releases/getPerspectiveState.ts +7 -6
- package/src/releases/releasesStore.test.ts +20 -5
- package/src/releases/releasesStore.ts +20 -8
- package/src/store/createStateSourceAction.test.ts +62 -0
- package/src/store/createStateSourceAction.ts +34 -39
- package/src/telemetry/__telemetry__/sdk.telemetry.ts +42 -0
- package/src/telemetry/devMode.test.ts +52 -0
- package/src/telemetry/devMode.ts +40 -0
- package/src/telemetry/initTelemetry.test.ts +225 -0
- package/src/telemetry/initTelemetry.ts +205 -0
- package/src/telemetry/telemetryManager.test.ts +263 -0
- package/src/telemetry/telemetryManager.ts +187 -0
- package/src/users/usersStore.test.ts +1 -0
- package/src/users/usersStore.ts +5 -1
- package/src/utils/createFetcherStore.test.ts +6 -4
- package/src/utils/createFetcherStore.ts +2 -1
- package/src/utils/getStagingApiHost.test.ts +21 -0
- package/src/utils/getStagingApiHost.ts +14 -0
- package/src/utils/ids.test.ts +1 -29
- package/src/utils/ids.ts +0 -10
- package/src/utils/setCleanupTimeout.ts +24 -0
- package/src/preview/previewQuery.test.ts +0 -236
- package/src/preview/previewQuery.ts +0 -153
- package/src/preview/previewStore.test.ts +0 -36
- package/src/preview/subscribeToStateAndFetchBatches.test.ts +0 -221
- package/src/preview/subscribeToStateAndFetchBatches.ts +0 -112
- package/src/preview/util.ts +0 -13
package/src/auth/studioAuth.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {refreshStampedToken} from './refreshStampedToken'
|
|
|
9
9
|
import {checkForCookieAuth, getStudioTokenFromLocalStorage} from './studioModeAuth'
|
|
10
10
|
import {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser'
|
|
11
11
|
import {subscribeToStorageEventsAndSetToken} from './subscribeToStorageEventsAndSetToken'
|
|
12
|
-
import {getDefaultStorage} from './utils'
|
|
12
|
+
import {createLoggedInAuthState, getDefaultStorage} from './utils'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Resolves the initial auth state for Studio mode.
|
|
@@ -55,7 +55,7 @@ export function getStudioInitialState(options: AuthStrategyOptions): AuthStrateg
|
|
|
55
55
|
|
|
56
56
|
if (providedToken) {
|
|
57
57
|
return {
|
|
58
|
-
authState:
|
|
58
|
+
authState: createLoggedInAuthState(providedToken, null),
|
|
59
59
|
storageKey: studioStorageKey,
|
|
60
60
|
storageArea,
|
|
61
61
|
authMethod,
|
|
@@ -65,7 +65,7 @@ export function getStudioInitialState(options: AuthStrategyOptions): AuthStrateg
|
|
|
65
65
|
|
|
66
66
|
if (token) {
|
|
67
67
|
return {
|
|
68
|
-
authState:
|
|
68
|
+
authState: createLoggedInAuthState(token, null),
|
|
69
69
|
storageKey: studioStorageKey,
|
|
70
70
|
storageArea,
|
|
71
71
|
authMethod: 'localstorage',
|
|
@@ -115,14 +115,29 @@ export function initializeStudioAuth(
|
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
117
|
* Subscribe to a reactive token source from the Studio workspace.
|
|
118
|
-
*
|
|
119
|
-
*
|
|
118
|
+
*
|
|
119
|
+
* When the token source emits a non-null token, the SDK uses it directly.
|
|
120
|
+
* When it emits `null`, the behavior depends on the `authenticated` flag
|
|
121
|
+
* from the Studio's workspace config:
|
|
122
|
+
*
|
|
123
|
+
* - `authenticated: true` — the Studio has already verified the user is
|
|
124
|
+
* logged in (e.g. via cookie auth). The SDK treats the null token as
|
|
125
|
+
* cookie-based auth and stays in the LOGGED_IN state.
|
|
126
|
+
*
|
|
127
|
+
* - `authenticated` absent/false — the user is genuinely not authenticated;
|
|
128
|
+
* transition to LOGGED_OUT.
|
|
129
|
+
*
|
|
130
|
+
* No async cookie probing is needed here because this code path only runs
|
|
131
|
+
* when a Studio provides SDKStudioContext, and the Studio's Workspace type
|
|
132
|
+
* always includes `authenticated`. The async `checkForCookieAuth` fallback
|
|
133
|
+
* remains in `initializeWithFallback` for the non-Studio path.
|
|
120
134
|
*/
|
|
121
135
|
function initializeWithTokenSource(
|
|
122
136
|
context: StoreContext<AuthStoreState>,
|
|
123
137
|
tokenSource: TokenSource,
|
|
124
138
|
): {dispose: () => void; tokenRefresherStarted: boolean} {
|
|
125
139
|
const subscriptions: Subscription[] = []
|
|
140
|
+
const studioAuthenticated = context.instance.config.studio?.authenticated === true
|
|
126
141
|
|
|
127
142
|
// Subscribe to the current user fetcher — runs whenever auth state changes
|
|
128
143
|
subscriptions.push(subscribeToStateAndFetchCurrentUser(context, {useProjectHostname: true}))
|
|
@@ -132,11 +147,23 @@ function initializeWithTokenSource(
|
|
|
132
147
|
next: (token) => {
|
|
133
148
|
const {state} = context
|
|
134
149
|
if (token) {
|
|
150
|
+
// Studio provided a real token — use it directly
|
|
135
151
|
state.set('studioTokenSource', (prev) => ({
|
|
136
152
|
options: {...prev.options, authMethod: undefined},
|
|
137
|
-
authState:
|
|
153
|
+
authState: createLoggedInAuthState(token, null),
|
|
154
|
+
}))
|
|
155
|
+
} else if (studioAuthenticated) {
|
|
156
|
+
// The Studio says the user is authenticated — null token means
|
|
157
|
+
// cookie-based auth is in use. Stay logged in with cookie method.
|
|
158
|
+
state.set('studioTokenSourceCookieAuth', (prev) => ({
|
|
159
|
+
options: {...prev.options, authMethod: 'cookie'},
|
|
160
|
+
authState:
|
|
161
|
+
prev.authState.type === AuthStateType.LOGGED_IN
|
|
162
|
+
? prev.authState
|
|
163
|
+
: createLoggedInAuthState('', null),
|
|
138
164
|
}))
|
|
139
165
|
} else {
|
|
166
|
+
// No token and Studio doesn't confirm authentication — logged out
|
|
140
167
|
state.set('studioTokenSourceLoggedOut', (prev) => ({
|
|
141
168
|
options: {...prev.options, authMethod: undefined},
|
|
142
169
|
authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},
|
|
@@ -193,7 +220,7 @@ function initializeWithFallback(
|
|
|
193
220
|
authState:
|
|
194
221
|
prev.authState.type === AuthStateType.LOGGED_IN
|
|
195
222
|
? prev.authState
|
|
196
|
-
:
|
|
223
|
+
: createLoggedInAuthState('', null),
|
|
197
224
|
}))
|
|
198
225
|
})
|
|
199
226
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {type ClientConfig, type SanityClient} from '@sanity/client'
|
|
2
2
|
|
|
3
|
+
import {REQUEST_TAG_PREFIX} from './authConstants'
|
|
3
4
|
import {getTokenFromStorage} from './utils'
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -25,7 +26,7 @@ export async function checkForCookieAuth(
|
|
|
25
26
|
const client = clientFactory({
|
|
26
27
|
projectId,
|
|
27
28
|
useCdn: false,
|
|
28
|
-
requestTagPrefix:
|
|
29
|
+
requestTagPrefix: REQUEST_TAG_PREFIX,
|
|
29
30
|
timeout: COOKIE_AUTH_TIMEOUT_MS,
|
|
30
31
|
})
|
|
31
32
|
const user = await client.request({
|
|
@@ -40,7 +40,11 @@ describe('subscribeToStateAndFetchCurrentUser', () => {
|
|
|
40
40
|
useProjectHostname: false,
|
|
41
41
|
useCdn: false,
|
|
42
42
|
})
|
|
43
|
-
expect(mockRequest).toHaveBeenCalledWith({
|
|
43
|
+
expect(mockRequest).toHaveBeenCalledWith({
|
|
44
|
+
method: 'GET',
|
|
45
|
+
uri: '/users/me',
|
|
46
|
+
tag: 'users.get-current',
|
|
47
|
+
})
|
|
44
48
|
|
|
45
49
|
subscription.unsubscribe()
|
|
46
50
|
})
|
|
@@ -76,7 +80,11 @@ describe('subscribeToStateAndFetchCurrentUser', () => {
|
|
|
76
80
|
useProjectHostname: false,
|
|
77
81
|
useCdn: false,
|
|
78
82
|
})
|
|
79
|
-
expect(mockRequest).toHaveBeenCalledWith({
|
|
83
|
+
expect(mockRequest).toHaveBeenCalledWith({
|
|
84
|
+
method: 'GET',
|
|
85
|
+
uri: '/users/me',
|
|
86
|
+
tag: 'users.get-current',
|
|
87
|
+
})
|
|
80
88
|
|
|
81
89
|
subscription.unsubscribe()
|
|
82
90
|
})
|
|
@@ -60,7 +60,11 @@ export const subscribeToStateAndFetchCurrentUser = (
|
|
|
60
60
|
}),
|
|
61
61
|
),
|
|
62
62
|
switchMap((client) =>
|
|
63
|
-
client.observable.request<CurrentUser>({
|
|
63
|
+
client.observable.request<CurrentUser>({
|
|
64
|
+
uri: '/users/me',
|
|
65
|
+
method: 'GET',
|
|
66
|
+
tag: 'users.get-current',
|
|
67
|
+
}),
|
|
64
68
|
),
|
|
65
69
|
)
|
|
66
70
|
|
|
@@ -3,7 +3,7 @@ import {defer, distinctUntilChanged, filter, map, type Subscription} from 'rxjs'
|
|
|
3
3
|
import {type StoreContext} from '../store/defineStore'
|
|
4
4
|
import {AuthStateType} from './authStateType'
|
|
5
5
|
import {type AuthStoreState} from './authStore'
|
|
6
|
-
import {getStorageEvents, getTokenFromStorage} from './utils'
|
|
6
|
+
import {createLoggedInAuthState, getStorageEvents, getTokenFromStorage} from './utils'
|
|
7
7
|
|
|
8
8
|
export const subscribeToStorageEventsAndSetToken = ({
|
|
9
9
|
state,
|
|
@@ -22,7 +22,7 @@ export const subscribeToStorageEventsAndSetToken = ({
|
|
|
22
22
|
return tokenFromStorage$.subscribe((token) => {
|
|
23
23
|
state.set('updateTokenFromStorageEvent', {
|
|
24
24
|
authState: token
|
|
25
|
-
?
|
|
25
|
+
? createLoggedInAuthState(token, null)
|
|
26
26
|
: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},
|
|
27
27
|
})
|
|
28
28
|
})
|
package/src/auth/utils.ts
CHANGED
|
@@ -1,7 +1,40 @@
|
|
|
1
1
|
import {type ClientError} from '@sanity/client'
|
|
2
|
+
import {type CurrentUser} from '@sanity/types'
|
|
2
3
|
import {EMPTY, fromEvent, Observable} from 'rxjs'
|
|
3
4
|
|
|
4
5
|
import {AUTH_CODE_PARAM, DEFAULT_BASE} from './authConstants'
|
|
6
|
+
import {AuthStateType} from './authStateType'
|
|
7
|
+
import {type LoggedInAuthState} from './authStore'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates a properly initialized {@link LoggedInAuthState}.
|
|
11
|
+
*
|
|
12
|
+
* For stamped tokens (containing `"-st"`), `lastTokenRefresh` is set to
|
|
13
|
+
* `Date.now()` so that the visibility-change handler in
|
|
14
|
+
* {@link refreshStampedToken} does not trigger an unnecessary refresh the
|
|
15
|
+
* first time the tab becomes visible.
|
|
16
|
+
*
|
|
17
|
+
* @param token - The auth token.
|
|
18
|
+
* @param currentUser - The current user, or `null` if not yet fetched.
|
|
19
|
+
* @param existingLastTokenRefresh - An existing timestamp to preserve
|
|
20
|
+
* (e.g. when updating a token while keeping the previous refresh time).
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export function createLoggedInAuthState(
|
|
24
|
+
token: string,
|
|
25
|
+
currentUser: CurrentUser | null,
|
|
26
|
+
existingLastTokenRefresh?: number,
|
|
27
|
+
): LoggedInAuthState {
|
|
28
|
+
const isStampedToken = token.includes('-st')
|
|
29
|
+
const lastTokenRefresh = existingLastTokenRefresh ?? (isStampedToken ? Date.now() : undefined)
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
type: AuthStateType.LOGGED_IN,
|
|
33
|
+
token,
|
|
34
|
+
currentUser,
|
|
35
|
+
...(lastTokenRefresh !== undefined && {lastTokenRefresh}),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
5
38
|
|
|
6
39
|
export function getAuthCode(callbackUrl: string | undefined, locationHref: string): string | null {
|
|
7
40
|
const loc = new URL(locationHref, DEFAULT_BASE)
|
|
@@ -68,6 +68,20 @@ describe('clientStore', () => {
|
|
|
68
68
|
})
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
+
it('should pass staging apiHost when __SANITY_STAGING__ is true and no explicit apiHost', () => {
|
|
72
|
+
vi.stubGlobal('__SANITY_STAGING__', true)
|
|
73
|
+
|
|
74
|
+
getClient(instance, {apiVersion: '2024-11-12'})
|
|
75
|
+
|
|
76
|
+
expect(vi.mocked(createClient)).toHaveBeenCalledWith(
|
|
77
|
+
expect.objectContaining({
|
|
78
|
+
apiHost: 'https://api.sanity.work',
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
vi.unstubAllGlobals()
|
|
83
|
+
})
|
|
84
|
+
|
|
71
85
|
it('should throw when using disallowed configuration keys', () => {
|
|
72
86
|
expect(() =>
|
|
73
87
|
getClient(instance, {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import {bindActionGlobally} from '../store/createActionBinder'
|
|
12
12
|
import {createStateSourceAction} from '../store/createStateSourceAction'
|
|
13
13
|
import {defineStore, type StoreContext} from '../store/defineStore'
|
|
14
|
+
import {getStagingApiHost} from '../utils/getStagingApiHost'
|
|
14
15
|
|
|
15
16
|
const DEFAULT_API_VERSION = '2024-11-12'
|
|
16
17
|
const DEFAULT_REQUEST_TAG_PREFIX = 'sanity.sdk'
|
|
@@ -195,7 +196,7 @@ export const getClient = bindActionGlobally(
|
|
|
195
196
|
|
|
196
197
|
const projectId = options.projectId ?? instance.config.projectId
|
|
197
198
|
const dataset = options.dataset ?? instance.config.dataset
|
|
198
|
-
const apiHost = options.apiHost ?? instance.config.auth?.apiHost
|
|
199
|
+
const apiHost = options.apiHost ?? instance.config.auth?.apiHost ?? getStagingApiHost()
|
|
199
200
|
|
|
200
201
|
const effectiveOptions: ClientOptions = {
|
|
201
202
|
...DEFAULT_CLIENT_CONFIG,
|
|
@@ -3,6 +3,7 @@ import {createSelector} from 'reselect'
|
|
|
3
3
|
|
|
4
4
|
import {bindActionGlobally} from '../../store/createActionBinder'
|
|
5
5
|
import {createStateSourceAction, type SelectorContext} from '../../store/createStateSourceAction'
|
|
6
|
+
import {setCleanupTimeout} from '../../utils/setCleanupTimeout'
|
|
6
7
|
import {type FrameMessage, type WindowMessage} from '../types'
|
|
7
8
|
import {
|
|
8
9
|
type ComlinkNodeState,
|
|
@@ -57,7 +58,7 @@ export const getNodeState = bindActionGlobally(
|
|
|
57
58
|
subs.add(subscriberId)
|
|
58
59
|
|
|
59
60
|
return () => {
|
|
60
|
-
|
|
61
|
+
setCleanupTimeout(() => {
|
|
61
62
|
const activeSubs = state.get().subscriptions.get(nodeName)
|
|
62
63
|
if (activeSubs) {
|
|
63
64
|
activeSubs.delete(subscriberId)
|
|
@@ -23,6 +23,12 @@ export interface TokenSource {
|
|
|
23
23
|
* @public
|
|
24
24
|
*/
|
|
25
25
|
export interface StudioConfig {
|
|
26
|
+
/**
|
|
27
|
+
* Whether the Studio has already determined the user is authenticated.
|
|
28
|
+
* When `true` and the token source emits `null`, the SDK infers
|
|
29
|
+
* cookie-based auth is in use rather than transitioning to logged-out.
|
|
30
|
+
*/
|
|
31
|
+
authenticated?: boolean
|
|
26
32
|
/** Reactive auth token source from the Studio's auth store. */
|
|
27
33
|
auth?: {
|
|
28
34
|
/**
|
package/src/document/actions.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {type PatchMutation, type PatchOperations} from '@sanity/types'
|
|
|
4
4
|
import {type SanityDocument} from 'groq'
|
|
5
5
|
|
|
6
6
|
import {type DocumentHandle, type DocumentTypeHandle} from '../config/sanityConfig'
|
|
7
|
-
import {
|
|
7
|
+
import {getEffectiveDocumentId} from './util'
|
|
8
8
|
|
|
9
9
|
const isSanityMutatePatch = (value: unknown): value is SanityMutatePatchMutation => {
|
|
10
10
|
if (typeof value !== 'object' || !value) return false
|
|
@@ -140,12 +140,15 @@ export function createDocument<
|
|
|
140
140
|
>
|
|
141
141
|
>,
|
|
142
142
|
): CreateDocumentAction<TDocumentType, TDataset, TProjectId> {
|
|
143
|
+
// users may pass in an explicit documentId -- make sure we format it correctly for the action
|
|
144
|
+
let effectiveDocumentId
|
|
145
|
+
if (typeof doc.documentId === 'string') {
|
|
146
|
+
effectiveDocumentId = getEffectiveDocumentId({...doc, documentId: doc.documentId})
|
|
147
|
+
}
|
|
143
148
|
return {
|
|
144
149
|
type: 'document.create',
|
|
145
150
|
...doc,
|
|
146
|
-
...(
|
|
147
|
-
documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
|
|
148
|
-
}),
|
|
151
|
+
...(effectiveDocumentId && {documentId: effectiveDocumentId}),
|
|
149
152
|
...(initialValue && {initialValue}),
|
|
150
153
|
}
|
|
151
154
|
}
|
|
@@ -163,10 +166,11 @@ export function deleteDocument<
|
|
|
163
166
|
>(
|
|
164
167
|
doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
|
|
165
168
|
): DeleteDocumentAction<TDocumentType, TDataset, TProjectId> {
|
|
169
|
+
const effectiveDocumentId = getEffectiveDocumentId(doc)
|
|
166
170
|
return {
|
|
167
171
|
type: 'document.delete',
|
|
168
172
|
...doc,
|
|
169
|
-
documentId:
|
|
173
|
+
documentId: effectiveDocumentId,
|
|
170
174
|
}
|
|
171
175
|
}
|
|
172
176
|
|
|
@@ -230,14 +234,14 @@ export function editDocument<
|
|
|
230
234
|
doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
|
|
231
235
|
patches?: PatchOperations | PatchOperations[] | SanityMutatePatchMutation,
|
|
232
236
|
): EditDocumentAction<TDocumentType, TDataset, TProjectId> {
|
|
233
|
-
const
|
|
237
|
+
const effectiveDocumentId = getEffectiveDocumentId(doc)
|
|
234
238
|
|
|
235
239
|
if (isSanityMutatePatch(patches)) {
|
|
236
240
|
const converted = convertSanityMutatePatch(patches) ?? []
|
|
237
241
|
return {
|
|
238
242
|
...doc,
|
|
239
243
|
type: 'document.edit',
|
|
240
|
-
documentId,
|
|
244
|
+
documentId: effectiveDocumentId,
|
|
241
245
|
patches: converted,
|
|
242
246
|
}
|
|
243
247
|
}
|
|
@@ -245,7 +249,7 @@ export function editDocument<
|
|
|
245
249
|
return {
|
|
246
250
|
...doc,
|
|
247
251
|
type: 'document.edit',
|
|
248
|
-
documentId,
|
|
252
|
+
documentId: effectiveDocumentId,
|
|
249
253
|
...(patches && {patches: Array.isArray(patches) ? patches : [patches]}),
|
|
250
254
|
}
|
|
251
255
|
}
|
|
@@ -263,10 +267,11 @@ export function publishDocument<
|
|
|
263
267
|
>(
|
|
264
268
|
doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
|
|
265
269
|
): PublishDocumentAction<TDocumentType, TDataset, TProjectId> {
|
|
270
|
+
const effectiveDocumentId = getEffectiveDocumentId(doc)
|
|
266
271
|
return {
|
|
267
272
|
type: 'document.publish',
|
|
268
273
|
...doc,
|
|
269
|
-
documentId:
|
|
274
|
+
documentId: effectiveDocumentId,
|
|
270
275
|
}
|
|
271
276
|
}
|
|
272
277
|
|
|
@@ -283,10 +288,11 @@ export function unpublishDocument<
|
|
|
283
288
|
>(
|
|
284
289
|
doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
|
|
285
290
|
): UnpublishDocumentAction<TDocumentType, TDataset, TProjectId> {
|
|
291
|
+
const effectiveDocumentId = getEffectiveDocumentId(doc)
|
|
286
292
|
return {
|
|
287
293
|
type: 'document.unpublish',
|
|
288
294
|
...doc,
|
|
289
|
-
documentId:
|
|
295
|
+
documentId: effectiveDocumentId,
|
|
290
296
|
}
|
|
291
297
|
}
|
|
292
298
|
|
|
@@ -303,9 +309,10 @@ export function discardDocument<
|
|
|
303
309
|
>(
|
|
304
310
|
doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
|
|
305
311
|
): DiscardDocumentAction<TDocumentType, TDataset, TProjectId> {
|
|
312
|
+
const effectiveDocumentId = getEffectiveDocumentId(doc)
|
|
306
313
|
return {
|
|
307
314
|
type: 'document.discard',
|
|
308
315
|
...doc,
|
|
309
|
-
documentId:
|
|
316
|
+
documentId: effectiveDocumentId,
|
|
310
317
|
}
|
|
311
318
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
// applyDocumentActions.test.ts
|
|
2
1
|
import {type SanityDocument} from '@sanity/types'
|
|
3
2
|
import {Subject} from 'rxjs'
|
|
4
3
|
import {describe, expect, it} from 'vitest'
|
|
5
4
|
|
|
6
|
-
import {
|
|
5
|
+
import {bindActionBySource} from '../store/createActionBinder'
|
|
7
6
|
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
|
|
8
|
-
import {} from '../store/createStateSourceAction'
|
|
9
7
|
import {createStoreState, type StoreState} from '../store/createStoreState'
|
|
10
8
|
import {type DocumentAction} from './actions'
|
|
11
9
|
import {type DocumentStoreState} from './documentStore'
|
|
@@ -14,7 +12,7 @@ import {type AppliedTransaction, type OutgoingTransaction} from './reducers'
|
|
|
14
12
|
|
|
15
13
|
vi.mock('../store/createActionBinder', async (importOriginal) => ({
|
|
16
14
|
...(await importOriginal<typeof import('../store/createActionBinder')>()),
|
|
17
|
-
|
|
15
|
+
bindActionBySource: vi.fn(),
|
|
18
16
|
}))
|
|
19
17
|
|
|
20
18
|
type TestState = Pick<
|
|
@@ -48,9 +46,9 @@ describe('applyDocumentActions', () => {
|
|
|
48
46
|
}
|
|
49
47
|
state = createStoreState(initialState)
|
|
50
48
|
instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
51
|
-
const key = {name: 'p.d', projectId: 'p', dataset: 'd'}
|
|
49
|
+
const key = {name: 'p.d', source: {projectId: 'p', dataset: 'd'}}
|
|
52
50
|
|
|
53
|
-
vi.mocked(
|
|
51
|
+
vi.mocked(bindActionBySource).mockImplementation(
|
|
54
52
|
(_storeDef, action) => (instanceParam: SanityInstance, options) =>
|
|
55
53
|
action({instance: instanceParam, state, key}, options),
|
|
56
54
|
)
|
|
@@ -75,6 +73,7 @@ describe('applyDocumentActions', () => {
|
|
|
75
73
|
const applyPromise = applyDocumentActions(instance, {
|
|
76
74
|
actions: [action],
|
|
77
75
|
transactionId: 'txn-success',
|
|
76
|
+
source: {projectId: 'p', dataset: 'd'},
|
|
78
77
|
})
|
|
79
78
|
|
|
80
79
|
const appliedTx: AppliedTransaction = {
|
|
@@ -132,6 +131,7 @@ describe('applyDocumentActions', () => {
|
|
|
132
131
|
const applyPromise = applyDocumentActions(instance, {
|
|
133
132
|
actions: [action],
|
|
134
133
|
transactionId: 'txn-error',
|
|
134
|
+
source: {projectId: 'p', dataset: 'd'},
|
|
135
135
|
})
|
|
136
136
|
|
|
137
137
|
const errorEvent: DocumentEvent = {
|
|
@@ -165,6 +165,7 @@ describe('applyDocumentActions', () => {
|
|
|
165
165
|
const applyPromise = applyDocumentActions(childInstance, {
|
|
166
166
|
actions: [action],
|
|
167
167
|
transactionId: 'txn-child-match',
|
|
168
|
+
source: {projectId: 'p', dataset: 'd'},
|
|
168
169
|
})
|
|
169
170
|
|
|
170
171
|
// Simulate an applied transaction on the parent's instance
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {type SanityClient} from '@sanity/client'
|
|
2
1
|
import {type SanityDocument} from 'groq'
|
|
3
2
|
import {distinctUntilChanged, filter, first, firstValueFrom, map, race} from 'rxjs'
|
|
4
3
|
|
|
5
|
-
import {
|
|
4
|
+
import {type DocumentSource} from '../config/sanityConfig'
|
|
5
|
+
import {bindActionBySource} from '../store/createActionBinder'
|
|
6
6
|
import {type SanityInstance} from '../store/createSanityInstance'
|
|
7
7
|
import {type StoreContext} from '../store/defineStore'
|
|
8
8
|
import {type DocumentAction} from './actions'
|
|
9
9
|
import {documentStore, type DocumentStoreState} from './documentStore'
|
|
10
|
+
import {type DocumentTransactionSubmissionResult} from './events'
|
|
10
11
|
import {type DocumentSet} from './processMutations'
|
|
11
12
|
import {type AppliedTransaction, type QueuedTransaction, queueTransaction} from './reducers'
|
|
12
13
|
|
|
@@ -19,7 +20,7 @@ export interface ActionsResult<TDocument extends SanityDocument = SanityDocument
|
|
|
19
20
|
appeared: string[]
|
|
20
21
|
updated: string[]
|
|
21
22
|
disappeared: string[]
|
|
22
|
-
submitted: () =>
|
|
23
|
+
submitted: () => Promise<DocumentTransactionSubmissionResult>
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/** @beta */
|
|
@@ -29,6 +30,11 @@ export interface ApplyDocumentActionsOptions {
|
|
|
29
30
|
*/
|
|
30
31
|
actions: DocumentAction[]
|
|
31
32
|
|
|
33
|
+
/**
|
|
34
|
+
* The source to which the documents being acted on belong.
|
|
35
|
+
*/
|
|
36
|
+
source?: DocumentSource
|
|
37
|
+
|
|
32
38
|
/**
|
|
33
39
|
* Optionally provide an ID to be used as this transaction ID
|
|
34
40
|
*/
|
|
@@ -61,7 +67,7 @@ export function applyDocumentActions(
|
|
|
61
67
|
return boundApplyDocumentActions(...args)
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
const boundApplyDocumentActions =
|
|
70
|
+
const boundApplyDocumentActions = bindActionBySource(documentStore, _applyDocumentActions)
|
|
65
71
|
|
|
66
72
|
/** @internal */
|
|
67
73
|
async function _applyDocumentActions(
|