@sanity/sdk 3.0.0-rc.0 → 3.0.0-rc.2
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/README.md +12 -0
- package/dist/_exports/_internal.d.ts +163 -0
- package/dist/_exports/_internal.d.ts.map +1 -0
- package/dist/_exports/_internal.js +332 -0
- package/dist/_exports/_internal.js.map +1 -0
- package/dist/_exports/agent.d.ts +65 -0
- package/dist/_exports/agent.d.ts.map +1 -0
- package/dist/_exports/agent.js +71 -0
- package/dist/_exports/agent.js.map +1 -0
- package/dist/_exports/comlink.d.ts +118 -0
- package/dist/_exports/comlink.d.ts.map +1 -0
- package/dist/_exports/comlink.js +69 -0
- package/dist/_exports/comlink.js.map +1 -0
- package/dist/authStore-CtE9bLHW.js +754 -0
- package/dist/authStore-CtE9bLHW.js.map +1 -0
- package/dist/authStore-DKFX8fn7.d.ts +3179 -0
- package/dist/authStore-DKFX8fn7.d.ts.map +1 -0
- package/dist/clientStore-BtE2-q2i.js +90 -0
- package/dist/clientStore-BtE2-q2i.js.map +1 -0
- package/dist/createSanityInstance-DUI1bGIg.d.ts +278 -0
- package/dist/createSanityInstance-DUI1bGIg.d.ts.map +1 -0
- package/dist/createStateSourceAction-BOKtqFNm.js +300 -0
- package/dist/createStateSourceAction-BOKtqFNm.js.map +1 -0
- package/dist/getEnv-D5RGkQLu.js +8 -0
- package/dist/getEnv-D5RGkQLu.js.map +1 -0
- package/dist/getNodeState-CR3__B2D.js +67 -0
- package/dist/getNodeState-CR3__B2D.js.map +1 -0
- package/dist/index.d.ts +3 -2332
- package/dist/index.js +4171 -4332
- package/dist/index.js.map +1 -1
- package/dist/logger-DAtJb5RQ.js +173 -0
- package/dist/logger-DAtJb5RQ.js.map +1 -0
- package/dist/object-CxXlSVg7.js +83 -0
- package/dist/object-CxXlSVg7.js.map +1 -0
- package/dist/queryStore-DPf9sN3V.js +1346 -0
- package/dist/queryStore-DPf9sN3V.js.map +1 -0
- package/dist/rolldown-runtime-vyAXikos.js +9 -0
- package/dist/setCleanupTimeout-BiM6q1KF.js +31 -0
- package/dist/setCleanupTimeout-BiM6q1KF.js.map +1 -0
- package/dist/telemetryManager-Dq_qCkeI.js +127 -0
- package/dist/telemetryManager-Dq_qCkeI.js.map +1 -0
- package/dist/version-BChjsi4u.js +9 -0
- package/dist/version-BChjsi4u.js.map +1 -0
- package/package.json +47 -52
- package/src/_exports/_internal.ts +27 -0
- package/src/_exports/agent.ts +19 -0
- package/src/_exports/comlink.ts +16 -0
- package/src/_exports/index.ts +125 -64
- package/src/access/checkPermissions.test.ts +55 -0
- package/src/access/checkPermissions.ts +54 -0
- package/src/agent/agentActions.test.ts +16 -60
- package/src/agent/agentActions.ts +25 -38
- package/src/applications/applications.test-d.ts +88 -0
- package/src/applications/applications.test.ts +130 -0
- package/src/applications/applications.ts +367 -0
- package/src/auth/authLogger.ts +30 -0
- package/src/auth/authStore.test.ts +194 -13
- package/src/auth/authStore.ts +58 -26
- package/src/auth/getOrganizationVerificationState.test.ts +18 -17
- package/src/auth/getOrganizationVerificationState.ts +4 -4
- package/src/auth/handleAuthCallback.test.ts +35 -1
- package/src/auth/handleAuthCallback.ts +25 -6
- package/src/auth/logout.test.ts +75 -2
- package/src/auth/logout.ts +23 -4
- package/src/auth/refreshStampedToken.test.ts +219 -469
- package/src/auth/refreshStampedToken.ts +79 -195
- package/src/auth/studioAuth.ts +1 -1
- package/src/auth/studioModeAuth.ts +2 -1
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +146 -13
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +37 -6
- package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +2 -0
- package/src/client/clientStore.test.ts +62 -37
- package/src/client/clientStore.ts +45 -75
- package/src/client/liveEvents.test.ts +151 -0
- package/src/client/liveEvents.ts +107 -0
- package/src/comlink/controller/actions/destroyController.test.ts +4 -1
- package/src/comlink/controller/actions/getOrCreateChannel.test.ts +4 -1
- package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
- package/src/comlink/controller/actions/getOrCreateController.test.ts +4 -1
- package/src/comlink/controller/actions/releaseChannel.test.ts +1 -1
- package/src/comlink/controller/comlinkControllerStore.test.ts +4 -1
- package/src/comlink/node/actions/getOrCreateNode.test.ts +9 -3
- package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
- package/src/comlink/node/actions/releaseNode.test.ts +7 -4
- package/src/comlink/node/comlinkNodeStore.test.ts +2 -2
- package/src/comlink/node/getNodeState.test.ts +1 -1
- package/src/comlink/node/getNodeState.ts +2 -1
- package/src/comments/addonDatasetStore.test.ts +215 -0
- package/src/comments/addonDatasetStore.ts +249 -0
- package/src/comments/buildCommentThreads.test.ts +83 -0
- package/src/comments/buildCommentThreads.ts +54 -0
- package/src/comments/commentActions.test.ts +540 -0
- package/src/comments/commentActions.ts +555 -0
- package/src/comments/commentFieldPath.test.ts +40 -0
- package/src/comments/commentFieldPath.ts +44 -0
- package/src/comments/commentsConstants.ts +80 -0
- package/src/comments/commentsStore.test.ts +419 -0
- package/src/comments/commentsStore.ts +483 -0
- package/src/comments/normalizeComment.test.ts +116 -0
- package/src/comments/normalizeComment.ts +45 -0
- package/src/comments/observeComments.test.ts +193 -0
- package/src/comments/observeComments.ts +113 -0
- package/src/comments/reducers.test.ts +277 -0
- package/src/comments/reducers.ts +316 -0
- package/src/comments/types.ts +249 -0
- package/src/comments/weakenReferences.test.ts +47 -0
- package/src/comments/weakenReferences.ts +29 -0
- package/src/config/__tests__/handles.test.ts +18 -12
- package/src/config/handles.ts +25 -7
- package/src/config/sanityConfig.ts +40 -82
- package/src/datasets/datasets.test.ts +2 -2
- package/src/datasets/datasets.ts +10 -4
- package/src/document/actions.test.ts +115 -33
- package/src/document/actions.ts +212 -15
- package/src/document/applyDocumentActions.test.ts +35 -13
- package/src/document/applyDocumentActions.ts +30 -9
- package/src/document/documentConstants.ts +15 -0
- package/src/document/documentStore.concurrency.test.ts +1306 -0
- package/src/document/documentStore.test.ts +753 -273
- package/src/document/documentStore.ts +197 -75
- package/src/document/events.test.ts +57 -2
- package/src/document/events.ts +82 -26
- package/src/document/listen.test.ts +56 -0
- package/src/document/listen.ts +74 -21
- package/src/document/listenerEventOperators.test.ts +311 -0
- package/src/document/listenerEventOperators.ts +217 -0
- package/src/document/patchOperations.test.ts +76 -2
- package/src/document/patchOperations.ts +60 -1
- package/src/document/permissions.test.ts +107 -60
- package/src/document/permissions.ts +44 -19
- package/src/document/processActions/create.ts +138 -0
- package/src/document/processActions/delete.ts +100 -0
- package/src/document/processActions/discard.ts +63 -0
- package/src/document/processActions/edit.ts +154 -0
- package/src/document/processActions/processActions.ts +218 -0
- package/src/document/processActions/publish.ts +120 -0
- package/src/document/processActions/releaseArchive.ts +77 -0
- package/src/document/processActions/releaseCreate.ts +59 -0
- package/src/document/processActions/releaseDelete.ts +65 -0
- package/src/document/processActions/releaseEdit.ts +37 -0
- package/src/document/processActions/releasePublish.ts +45 -0
- package/src/document/processActions/releaseSchedule.ts +87 -0
- package/src/document/processActions/releaseUtil.ts +31 -0
- package/src/document/processActions/shared.ts +308 -0
- package/src/document/processActions/unpublish.ts +85 -0
- package/src/document/processActions.test.ts +1129 -121
- package/src/document/reducers.test.ts +772 -64
- package/src/document/reducers.ts +205 -51
- package/src/document/resourceRules.test.ts +178 -0
- package/src/document/resourceRules.ts +117 -0
- package/src/document/sharedListener.test.ts +16 -14
- package/src/document/sharedListener.ts +24 -8
- package/src/document/util.test.ts +46 -0
- package/src/document/util.ts +50 -0
- package/src/favorites/favorites.test.ts +5 -6
- package/src/installations/installations.test-d.ts +73 -0
- package/src/installations/installations.test.ts +62 -0
- package/src/installations/installations.ts +206 -0
- package/src/organization/organization.test-d.ts +36 -0
- package/src/organization/organization.test.ts +138 -0
- package/src/organization/organization.ts +138 -0
- package/src/organizations/organizations.test-d.ts +56 -0
- package/src/organizations/organizations.test.ts +150 -0
- package/src/organizations/organizations.ts +107 -0
- package/src/presence/bifurTransport.test.ts +201 -9
- package/src/presence/bifurTransport.ts +98 -14
- package/src/presence/paths.test.ts +70 -0
- package/src/presence/paths.ts +41 -0
- package/src/presence/presenceStore.test.ts +915 -24
- package/src/presence/presenceStore.ts +576 -104
- package/src/presence/types.ts +175 -3
- package/src/preview/previewProjectionUtils.test.ts +9 -22
- package/src/preview/previewProjectionUtils.ts +7 -3
- package/src/preview/types.ts +0 -15
- package/src/project/project.test-d.ts +37 -0
- package/src/project/project.test.ts +157 -12
- package/src/project/project.ts +138 -25
- package/src/projection/getProjectionState.test.ts +18 -20
- package/src/projection/getProjectionState.ts +23 -3
- package/src/projection/projectionQuery.ts +5 -5
- package/src/projection/projectionStore.test.ts +1 -9
- package/src/projection/resolveProjection.test.ts +1 -2
- package/src/projection/resolveProjection.ts +16 -0
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
- package/src/projection/subscribeToStateAndFetchBatches.ts +32 -23
- package/src/projects/projects.test-d.ts +32 -0
- package/src/projects/projects.test.ts +108 -42
- package/src/projects/projects.ts +63 -27
- package/src/query/queryStore.test.ts +259 -123
- package/src/query/queryStore.ts +85 -69
- package/src/query/reducers.test.ts +0 -42
- package/src/query/reducers.ts +2 -18
- package/src/releases/getPerspectiveState.test.ts +17 -23
- package/src/releases/getPerspectiveState.ts +8 -20
- package/src/releases/observeReleases.test.ts +127 -0
- package/src/releases/observeReleases.ts +102 -0
- package/src/releases/releasesStore.test.ts +100 -53
- package/src/releases/releasesStore.ts +69 -44
- package/src/releases/utils/sortReleases.test.ts +38 -2
- package/src/releases/utils/sortReleases.ts +15 -10
- package/src/store/createActionBinder.test.ts +109 -90
- package/src/store/createActionBinder.ts +36 -28
- package/src/store/createSanityInstance.test.ts +15 -13
- package/src/store/createSanityInstance.ts +14 -2
- package/src/store/createStateSourceAction.test.ts +64 -2
- package/src/store/createStateSourceAction.ts +38 -43
- package/src/store/createStoreInstance.test.ts +4 -2
- package/src/store/fetcherStore.test-d.ts +121 -0
- package/src/store/fetcherStore.test.ts +299 -0
- package/src/store/fetcherStore.ts +801 -0
- package/src/telemetry/environment.test.ts +119 -0
- package/src/telemetry/environment.ts +92 -0
- package/src/telemetry/events.ts +42 -0
- package/src/telemetry/initTelemetry.test.ts +432 -0
- package/src/telemetry/initTelemetry.ts +218 -0
- package/src/telemetry/telemetryManager.test.ts +327 -0
- package/src/telemetry/telemetryManager.ts +199 -0
- package/src/users/reducers.test.ts +3 -1
- package/src/users/reducers.ts +5 -6
- package/src/users/types.ts +4 -4
- package/src/users/usersStore.test.ts +16 -12
- package/src/users/usersStore.ts +5 -1
- package/src/utils/buildQuery.test.ts +14 -0
- package/src/utils/buildQuery.ts +17 -0
- package/src/utils/createFetcherStore.test.ts +7 -5
- package/src/utils/createFetcherStore.ts +8 -5
- 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/includeShape.ts +36 -0
- package/src/utils/isImportError.test.ts +72 -0
- package/src/utils/isImportError.ts +34 -0
- package/src/utils/logger.test.ts +12 -0
- package/src/utils/logger.ts +8 -3
- package/src/utils/object.test.ts +95 -0
- package/src/utils/object.ts +142 -0
- package/src/utils/setCleanupTimeout.ts +37 -0
- package/src/document/processActions.ts +0 -707
- package/src/utils/defineIntent.test.ts +0 -477
- package/src/utils/defineIntent.ts +0 -244
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
import {DocumentId, getDraftId, getPublishedId} from '@sanity/id-utils'
|
|
1
2
|
import {type SanityDocument} from '@sanity/types'
|
|
2
3
|
import {evaluateSync, type ExprNode, parse} from 'groq-js'
|
|
3
4
|
import {describe, expect, it} from 'vitest'
|
|
4
5
|
|
|
5
6
|
import {createSanityInstance} from '../store/createSanityInstance'
|
|
6
|
-
import {getDraftId, getPublishedId} from '../utils/ids'
|
|
7
7
|
import {type DocumentAction} from './actions'
|
|
8
8
|
import {calculatePermissions, createGrantsLookup, type DatasetAcl, type Grant} from './permissions'
|
|
9
9
|
import {type SyncTransactionState} from './reducers'
|
|
10
10
|
|
|
11
|
-
const instance = createSanityInstance()
|
|
11
|
+
const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
12
12
|
|
|
13
13
|
afterAll(() => {
|
|
14
14
|
instance.dispose()
|
|
@@ -32,9 +32,11 @@ const alwaysDeny = parse('false')
|
|
|
32
32
|
const createState = (
|
|
33
33
|
docStates: Record<string, {local: unknown}>,
|
|
34
34
|
grants?: Record<Grant, ExprNode>,
|
|
35
|
+
identity?: string,
|
|
35
36
|
): SyncTransactionState => ({
|
|
36
37
|
documentStates: docStates as SyncTransactionState['documentStates'],
|
|
37
38
|
grants,
|
|
39
|
+
identity,
|
|
38
40
|
queued: [],
|
|
39
41
|
applied: [],
|
|
40
42
|
})
|
|
@@ -52,6 +54,18 @@ describe('createGrantsLookup', () => {
|
|
|
52
54
|
expect(evaluateSync(grants[key], {params: {document: dummyDoc}}).data).toBe(true)
|
|
53
55
|
})
|
|
54
56
|
})
|
|
57
|
+
|
|
58
|
+
it('should ignore unknown permissions like "manage"', () => {
|
|
59
|
+
const datasetAcl = [
|
|
60
|
+
{filter: '_id != null', permissions: ['history', 'read', 'update', 'create', 'manage']},
|
|
61
|
+
] as DatasetAcl
|
|
62
|
+
const grants = createGrantsLookup(datasetAcl)
|
|
63
|
+
const dummyDoc = {_id: 'doc1'}
|
|
64
|
+
;(['read', 'update', 'create', 'history'] as Grant[]).forEach((key) => {
|
|
65
|
+
expect(grants[key]).toBeDefined()
|
|
66
|
+
expect(evaluateSync(grants[key], {params: {document: dummyDoc}}).data).toBe(true)
|
|
67
|
+
})
|
|
68
|
+
})
|
|
55
69
|
})
|
|
56
70
|
|
|
57
71
|
describe('calculatePermissions', () => {
|
|
@@ -66,19 +80,16 @@ describe('calculatePermissions', () => {
|
|
|
66
80
|
// For a document.create action, the selector expects both published and draft keys.
|
|
67
81
|
const state = createState(
|
|
68
82
|
{
|
|
69
|
-
[getPublishedId('doc1')]: {
|
|
70
|
-
|
|
83
|
+
[getPublishedId(DocumentId('doc1'))]: {
|
|
84
|
+
local: createDoc(DocumentId('doc1'), 'Original Title'),
|
|
85
|
+
},
|
|
86
|
+
[getDraftId(DocumentId('doc1'))]: {local: null},
|
|
71
87
|
},
|
|
72
88
|
defaultGrants,
|
|
73
89
|
)
|
|
74
90
|
|
|
75
91
|
const actions: DocumentAction[] = [
|
|
76
|
-
{
|
|
77
|
-
documentId: 'doc1',
|
|
78
|
-
type: 'document.create',
|
|
79
|
-
documentType: 'article',
|
|
80
|
-
resource: {projectId: 'p', dataset: 'd'},
|
|
81
|
-
},
|
|
92
|
+
{documentId: 'doc1', type: 'document.create', documentType: 'article'},
|
|
82
93
|
]
|
|
83
94
|
const result = calculatePermissions({instance, state}, {actions})
|
|
84
95
|
expect(result).toEqual({allowed: true})
|
|
@@ -88,18 +99,13 @@ describe('calculatePermissions', () => {
|
|
|
88
99
|
// Missing the draft key will cause documentsSelector to return undefined.
|
|
89
100
|
const state = createState(
|
|
90
101
|
{
|
|
91
|
-
[getPublishedId('doc1')]: {local: createDoc('doc1', 'Title')},
|
|
102
|
+
[getPublishedId(DocumentId('doc1'))]: {local: createDoc(DocumentId('doc1'), 'Title')},
|
|
92
103
|
// Missing getDraftId('doc1')
|
|
93
104
|
},
|
|
94
105
|
defaultGrants,
|
|
95
106
|
)
|
|
96
107
|
const actions: DocumentAction[] = [
|
|
97
|
-
{
|
|
98
|
-
documentId: 'doc1',
|
|
99
|
-
type: 'document.create',
|
|
100
|
-
documentType: 'article',
|
|
101
|
-
resource: {projectId: 'p', dataset: 'd'},
|
|
102
|
-
},
|
|
108
|
+
{documentId: 'doc1', type: 'document.create', documentType: 'article'},
|
|
103
109
|
]
|
|
104
110
|
expect(calculatePermissions({instance, state}, {actions})).toBeUndefined()
|
|
105
111
|
})
|
|
@@ -109,18 +115,13 @@ describe('calculatePermissions', () => {
|
|
|
109
115
|
const deniedGrants = {...defaultGrants, create: alwaysDeny}
|
|
110
116
|
const state = createState(
|
|
111
117
|
{
|
|
112
|
-
[getPublishedId('doc1')]: {local: createDoc('doc1', 'Title')},
|
|
113
|
-
[getDraftId('doc1')]: {local: null},
|
|
118
|
+
[getPublishedId(DocumentId('doc1'))]: {local: createDoc(DocumentId('doc1'), 'Title')},
|
|
119
|
+
[getDraftId(DocumentId('doc1'))]: {local: null},
|
|
114
120
|
},
|
|
115
121
|
deniedGrants,
|
|
116
122
|
)
|
|
117
123
|
const actions: DocumentAction[] = [
|
|
118
|
-
{
|
|
119
|
-
documentId: 'doc1',
|
|
120
|
-
type: 'document.create',
|
|
121
|
-
documentType: 'article',
|
|
122
|
-
resource: {projectId: 'p', dataset: 'd'},
|
|
123
|
-
},
|
|
124
|
+
{documentId: 'doc1', type: 'document.create', documentType: 'article'},
|
|
124
125
|
]
|
|
125
126
|
const result = calculatePermissions({instance, state}, {actions})
|
|
126
127
|
expect(result).toBeDefined()
|
|
@@ -142,18 +143,13 @@ describe('calculatePermissions', () => {
|
|
|
142
143
|
// Both published and draft documents are present as null.
|
|
143
144
|
const state = createState(
|
|
144
145
|
{
|
|
145
|
-
[getPublishedId('doc1')]: {local: null},
|
|
146
|
-
[getDraftId('doc1')]: {local: null},
|
|
146
|
+
[getPublishedId(DocumentId('doc1'))]: {local: null},
|
|
147
|
+
[getDraftId(DocumentId('doc1'))]: {local: null},
|
|
147
148
|
},
|
|
148
149
|
defaultGrants,
|
|
149
150
|
)
|
|
150
151
|
const actions: DocumentAction[] = [
|
|
151
|
-
{
|
|
152
|
-
documentId: 'doc1',
|
|
153
|
-
documentType: 'book',
|
|
154
|
-
type: 'document.edit',
|
|
155
|
-
resource: {projectId: 'p', dataset: 'd'},
|
|
156
|
-
},
|
|
152
|
+
{documentId: 'doc1', documentType: 'book', type: 'document.edit'},
|
|
157
153
|
]
|
|
158
154
|
const result = calculatePermissions({instance, state}, {actions})
|
|
159
155
|
expect(result).toBeDefined()
|
|
@@ -173,18 +169,13 @@ describe('calculatePermissions', () => {
|
|
|
173
169
|
const deniedGrants = {...defaultGrants, update: alwaysDeny}
|
|
174
170
|
const state = createState(
|
|
175
171
|
{
|
|
176
|
-
[getPublishedId('doc1')]: {local: createDoc('doc1', 'Title')},
|
|
177
|
-
[getDraftId('doc1')]: {local: createDoc(
|
|
172
|
+
[getPublishedId(DocumentId('doc1'))]: {local: createDoc(DocumentId('doc1'), 'Title')},
|
|
173
|
+
[getDraftId(DocumentId('doc1'))]: {local: createDoc(DocumentId('doc1'), 'Draft Title')},
|
|
178
174
|
},
|
|
179
175
|
deniedGrants,
|
|
180
176
|
)
|
|
181
177
|
const actions: DocumentAction[] = [
|
|
182
|
-
{
|
|
183
|
-
documentId: 'doc1',
|
|
184
|
-
documentType: 'book',
|
|
185
|
-
type: 'document.edit',
|
|
186
|
-
resource: {projectId: 'p', dataset: 'd'},
|
|
187
|
-
},
|
|
178
|
+
{documentId: 'doc1', documentType: 'book', type: 'document.edit'},
|
|
188
179
|
]
|
|
189
180
|
const result = calculatePermissions({instance, state}, {actions})
|
|
190
181
|
expect(result).toBeDefined()
|
|
@@ -204,16 +195,11 @@ describe('calculatePermissions', () => {
|
|
|
204
195
|
|
|
205
196
|
it('should return undefined if grants are not provided', () => {
|
|
206
197
|
const state = createState({
|
|
207
|
-
[getPublishedId('doc1')]: {local: createDoc('doc1', 'Title')},
|
|
208
|
-
[getDraftId('doc1')]: {local: null},
|
|
198
|
+
[getPublishedId(DocumentId('doc1'))]: {local: createDoc(DocumentId('doc1'), 'Title')},
|
|
199
|
+
[getDraftId(DocumentId('doc1'))]: {local: null},
|
|
209
200
|
})
|
|
210
201
|
const actions: DocumentAction[] = [
|
|
211
|
-
{
|
|
212
|
-
documentId: 'doc1',
|
|
213
|
-
type: 'document.create',
|
|
214
|
-
documentType: 'article',
|
|
215
|
-
resource: {projectId: 'p', dataset: 'd'},
|
|
216
|
-
},
|
|
202
|
+
{documentId: 'doc1', type: 'document.create', documentType: 'article'},
|
|
217
203
|
]
|
|
218
204
|
expect(calculatePermissions({instance, state}, {actions})).toBeUndefined()
|
|
219
205
|
})
|
|
@@ -222,18 +208,13 @@ describe('calculatePermissions', () => {
|
|
|
222
208
|
// For document.delete, if the published document is missing, processActions throws an ActionError.
|
|
223
209
|
const state = createState(
|
|
224
210
|
{
|
|
225
|
-
[getPublishedId('doc1')]: {local: null},
|
|
226
|
-
[getDraftId('doc1')]: {local: null},
|
|
211
|
+
[getPublishedId(DocumentId('doc1'))]: {local: null},
|
|
212
|
+
[getDraftId(DocumentId('doc1'))]: {local: null},
|
|
227
213
|
},
|
|
228
214
|
defaultGrants,
|
|
229
215
|
)
|
|
230
216
|
const actions: DocumentAction[] = [
|
|
231
|
-
{
|
|
232
|
-
documentId: 'doc1',
|
|
233
|
-
documentType: 'book',
|
|
234
|
-
type: 'document.delete',
|
|
235
|
-
resource: {projectId: 'p', dataset: 'd'},
|
|
236
|
-
},
|
|
217
|
+
{documentId: 'doc1', documentType: 'book', type: 'document.delete'},
|
|
237
218
|
]
|
|
238
219
|
const result = calculatePermissions({instance, state}, {actions})
|
|
239
220
|
expect(result).toBeDefined()
|
|
@@ -252,8 +233,8 @@ describe('calculatePermissions', () => {
|
|
|
252
233
|
it('should memoize the result for identical state and actions inputs', () => {
|
|
253
234
|
const state = createState(
|
|
254
235
|
{
|
|
255
|
-
[getPublishedId('doc1')]: {local: createDoc('doc1', 'Title')},
|
|
256
|
-
[getDraftId('doc1')]: {local: null},
|
|
236
|
+
[getPublishedId(DocumentId('doc1'))]: {local: createDoc(DocumentId('doc1'), 'Title')},
|
|
237
|
+
[getDraftId(DocumentId('doc1'))]: {local: null},
|
|
257
238
|
},
|
|
258
239
|
defaultGrants,
|
|
259
240
|
)
|
|
@@ -261,11 +242,77 @@ describe('calculatePermissions', () => {
|
|
|
261
242
|
documentId: 'doc1',
|
|
262
243
|
type: 'document.create',
|
|
263
244
|
documentType: 'article',
|
|
264
|
-
resource: {projectId: 'p', dataset: 'd'},
|
|
265
245
|
}
|
|
266
246
|
// notice how the action is a copy
|
|
267
247
|
const result1 = calculatePermissions({instance, state}, {actions: [{...action}]})
|
|
268
248
|
const result2 = calculatePermissions({instance, state}, {actions: [{...action}]})
|
|
269
249
|
expect(result1).toBe(result2)
|
|
270
250
|
})
|
|
251
|
+
|
|
252
|
+
describe('identity-aware grants', () => {
|
|
253
|
+
// Mirrors the canvas ACL filter shape: only the document's creator may
|
|
254
|
+
// update it. Without an identity passed to groq, `identity()` is null and
|
|
255
|
+
// the expression collapses to null — which used to read as "denied".
|
|
256
|
+
const canvasUpdateAcl: DatasetAcl = [
|
|
257
|
+
{
|
|
258
|
+
filter: '_type == "sanity.canvas.document" && _system.createdBy == identity()',
|
|
259
|
+
permissions: ['read', 'update'],
|
|
260
|
+
},
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
const canvasDoc = (createdBy: string): SanityDocument => ({
|
|
264
|
+
_id: 'canvas-1',
|
|
265
|
+
_type: 'sanity.canvas.document',
|
|
266
|
+
_createdAt: '2025-01-01T00:00:00.000Z',
|
|
267
|
+
_updatedAt: '2025-01-01T00:00:00.000Z',
|
|
268
|
+
_rev: 'rev-1',
|
|
269
|
+
// Content Lake sets `_system.createdBy` on canvas documents; the public
|
|
270
|
+
// `DocumentSystem` type doesn't declare it, so the literal needs a cast.
|
|
271
|
+
_system: {createdBy} as SanityDocument['_system'],
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
const editAction: DocumentAction = {
|
|
275
|
+
documentId: 'canvas-1',
|
|
276
|
+
documentType: 'sanity.canvas.document',
|
|
277
|
+
type: 'document.edit',
|
|
278
|
+
liveEdit: true,
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
it('allows edits when identity matches the document creator', () => {
|
|
282
|
+
const state = createState(
|
|
283
|
+
{'canvas-1': {local: canvasDoc('user-A')}},
|
|
284
|
+
createGrantsLookup(canvasUpdateAcl),
|
|
285
|
+
'user-A',
|
|
286
|
+
)
|
|
287
|
+
expect(calculatePermissions({instance, state}, {actions: [editAction]})).toEqual({
|
|
288
|
+
allowed: true,
|
|
289
|
+
})
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('denies edits when identity does not match the document creator', () => {
|
|
293
|
+
const state = createState(
|
|
294
|
+
{'canvas-1': {local: canvasDoc('user-A')}},
|
|
295
|
+
createGrantsLookup(canvasUpdateAcl),
|
|
296
|
+
'user-B',
|
|
297
|
+
)
|
|
298
|
+
const result = calculatePermissions({instance, state}, {actions: [editAction]})
|
|
299
|
+
expect(result?.allowed).toBe(false)
|
|
300
|
+
expect(result?.reasons).toEqual(
|
|
301
|
+
expect.arrayContaining([expect.objectContaining({type: 'access', documentId: 'canvas-1'})]),
|
|
302
|
+
)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('denies edits when identity is not yet loaded', () => {
|
|
306
|
+
const state = createState(
|
|
307
|
+
{'canvas-1': {local: canvasDoc('user-A')}},
|
|
308
|
+
createGrantsLookup(canvasUpdateAcl),
|
|
309
|
+
undefined,
|
|
310
|
+
)
|
|
311
|
+
const result = calculatePermissions({instance, state}, {actions: [editAction]})
|
|
312
|
+
expect(result?.allowed).toBe(false)
|
|
313
|
+
expect(result?.reasons).toEqual(
|
|
314
|
+
expect.arrayContaining([expect.objectContaining({type: 'access', documentId: 'canvas-1'})]),
|
|
315
|
+
)
|
|
316
|
+
})
|
|
317
|
+
})
|
|
271
318
|
})
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'
|
|
1
2
|
import {type SanityDocument} from '@sanity/types'
|
|
2
|
-
import {
|
|
3
|
+
import {type ExprNode, parse} from 'groq-js'
|
|
3
4
|
import {createSelector} from 'reselect'
|
|
4
5
|
|
|
6
|
+
import {isReleasePerspective} from '../releases/utils/isReleasePerspective'
|
|
5
7
|
import {type SelectorContext} from '../store/createStateSourceAction'
|
|
6
|
-
import {getDraftId, getPublishedId} from '../utils/ids'
|
|
7
8
|
import {MultiKeyWeakMap} from '../utils/MultiKeyWeakMap'
|
|
8
9
|
import {type DocumentAction} from './actions'
|
|
9
|
-
import {ActionError, PermissionActionError, processActions} from './processActions'
|
|
10
|
+
import {ActionError, PermissionActionError, processActions} from './processActions/processActions'
|
|
11
|
+
import {checkGrant} from './processActions/shared'
|
|
10
12
|
import {type DocumentSet} from './processMutations'
|
|
11
13
|
import {type SyncTransactionState} from './reducers'
|
|
12
14
|
|
|
@@ -28,6 +30,8 @@ export function createGrantsLookup(datasetAcl: DatasetAcl): Record<Grant, ExprNo
|
|
|
28
30
|
for (const entry of datasetAcl) {
|
|
29
31
|
for (const grant of entry.permissions) {
|
|
30
32
|
const set = filtersByGrant[grant]
|
|
33
|
+
// Ignore permissions we don't model (e.g. "manage")
|
|
34
|
+
if (!set) continue
|
|
31
35
|
set.add(entry.filter)
|
|
32
36
|
filtersByGrant[grant] = set
|
|
33
37
|
}
|
|
@@ -71,7 +75,14 @@ const documentsSelector = createSelector(
|
|
|
71
75
|
// For liveEdit documents, only fetch the single document
|
|
72
76
|
if (action.liveEdit) return [action.documentId]
|
|
73
77
|
// For standard documents, fetch both draft and published
|
|
74
|
-
|
|
78
|
+
const ids: string[] = [
|
|
79
|
+
getPublishedId(DocumentId(action.documentId)),
|
|
80
|
+
getDraftId(DocumentId(action.documentId)),
|
|
81
|
+
]
|
|
82
|
+
if (isReleasePerspective(action.perspective)) {
|
|
83
|
+
ids.push(getVersionId(DocumentId(action.documentId), action.perspective.releaseName))
|
|
84
|
+
}
|
|
85
|
+
return ids
|
|
75
86
|
})
|
|
76
87
|
.flat(),
|
|
77
88
|
)
|
|
@@ -132,11 +143,6 @@ const memoizedActionsSelector = createSelector(
|
|
|
132
143
|
},
|
|
133
144
|
)
|
|
134
145
|
|
|
135
|
-
function checkGrant(grantExpr: ExprNode, document: SanityDocument): boolean {
|
|
136
|
-
const value = evaluateSync(grantExpr, {params: {document}})
|
|
137
|
-
return value.type === 'boolean' && value.data
|
|
138
|
-
}
|
|
139
|
-
|
|
140
146
|
/** @beta */
|
|
141
147
|
export interface PermissionDeniedReason {
|
|
142
148
|
type: 'precondition' | 'access'
|
|
@@ -164,11 +170,13 @@ export function calculatePermissions(
|
|
|
164
170
|
const _calculatePermissions = createSelector(
|
|
165
171
|
[
|
|
166
172
|
({state: {grants}}: SelectorContext<SyncTransactionState>) => grants,
|
|
173
|
+
({state: {identity}}: SelectorContext<SyncTransactionState>) => identity,
|
|
167
174
|
documentsSelector,
|
|
168
175
|
memoizedActionsSelector,
|
|
169
176
|
],
|
|
170
177
|
(
|
|
171
178
|
grants: Record<Grant, ExprNode> | undefined,
|
|
179
|
+
identity: string | undefined,
|
|
172
180
|
documents: DocumentSet | undefined,
|
|
173
181
|
actions: DocumentAction[] | undefined,
|
|
174
182
|
): DocumentPermissionsResult | undefined => {
|
|
@@ -187,6 +195,7 @@ const _calculatePermissions = createSelector(
|
|
|
187
195
|
base: documents,
|
|
188
196
|
timestamp,
|
|
189
197
|
grants,
|
|
198
|
+
identity,
|
|
190
199
|
})
|
|
191
200
|
} catch (error) {
|
|
192
201
|
if (error instanceof PermissionActionError) {
|
|
@@ -210,17 +219,33 @@ const _calculatePermissions = createSelector(
|
|
|
210
219
|
// Check edit actions with no patches
|
|
211
220
|
if (action.type === 'document.edit' && !action.patches?.length) {
|
|
212
221
|
const docId = action.documentId
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
222
|
+
let doc: SanityDocument | null | undefined
|
|
223
|
+
if (action.liveEdit) {
|
|
224
|
+
doc = documents[docId]
|
|
225
|
+
}
|
|
226
|
+
// don't allow users to edit version documents that don't exist
|
|
227
|
+
// they should be explicitly created first, as in studio
|
|
228
|
+
else if (isReleasePerspective(action.perspective)) {
|
|
229
|
+
doc = documents[getVersionId(DocumentId(docId), action.perspective.releaseName)]
|
|
230
|
+
} else {
|
|
231
|
+
doc =
|
|
232
|
+
documents[getDraftId(DocumentId(docId))] ?? documents[getPublishedId(DocumentId(docId))]
|
|
233
|
+
}
|
|
217
234
|
if (!doc) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
235
|
+
if (isReleasePerspective(action.perspective)) {
|
|
236
|
+
reasons.push({
|
|
237
|
+
type: 'precondition',
|
|
238
|
+
message: `The version document with ID "${docId}" could not be found. Please create it or add it to the release first.`,
|
|
239
|
+
documentId: docId,
|
|
240
|
+
})
|
|
241
|
+
} else {
|
|
242
|
+
reasons.push({
|
|
243
|
+
type: 'precondition',
|
|
244
|
+
message: `The document with ID "${docId}" could not be found. Please check that it exists before editing.`,
|
|
245
|
+
documentId: docId,
|
|
246
|
+
})
|
|
247
|
+
}
|
|
248
|
+
} else if (!checkGrant(grants.update, doc, identity)) {
|
|
224
249
|
reasons.push({
|
|
225
250
|
type: 'access',
|
|
226
251
|
message: `You are not allowed to edit the document with ID "${docId}".`,
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'
|
|
2
|
+
import {type Mutation, type SanityDocument} from '@sanity/types'
|
|
3
|
+
|
|
4
|
+
import {isReleasePerspective} from '../../releases/utils/isReleasePerspective'
|
|
5
|
+
import {type CreateDocumentAction} from '../actions'
|
|
6
|
+
import {getId, processMutations} from '../processMutations'
|
|
7
|
+
import {
|
|
8
|
+
ActionError,
|
|
9
|
+
type ActionHandlerContext,
|
|
10
|
+
type ActionHandlerResult,
|
|
11
|
+
checkGrant,
|
|
12
|
+
PermissionActionError,
|
|
13
|
+
} from './shared'
|
|
14
|
+
|
|
15
|
+
export function handleCreate(
|
|
16
|
+
action: CreateDocumentAction,
|
|
17
|
+
ctx: ActionHandlerContext,
|
|
18
|
+
): ActionHandlerResult {
|
|
19
|
+
const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx
|
|
20
|
+
let {base, working} = ctx
|
|
21
|
+
|
|
22
|
+
const documentId = getId(action.documentId)
|
|
23
|
+
|
|
24
|
+
if (action.liveEdit) {
|
|
25
|
+
if (working[documentId]) {
|
|
26
|
+
throw new ActionError({
|
|
27
|
+
documentId,
|
|
28
|
+
transactionId,
|
|
29
|
+
message: `This document already exists.`,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const newDocBase = {_type: action.documentType, _id: documentId, ...action.initialValue}
|
|
34
|
+
const newDocWorking = {
|
|
35
|
+
_type: action.documentType,
|
|
36
|
+
_id: documentId,
|
|
37
|
+
...action.initialValue,
|
|
38
|
+
}
|
|
39
|
+
const mutations: Mutation[] = [{create: newDocWorking}]
|
|
40
|
+
|
|
41
|
+
base = processMutations({
|
|
42
|
+
documents: base,
|
|
43
|
+
transactionId,
|
|
44
|
+
mutations: [{create: newDocBase}],
|
|
45
|
+
timestamp,
|
|
46
|
+
})
|
|
47
|
+
working = processMutations({
|
|
48
|
+
documents: working,
|
|
49
|
+
transactionId,
|
|
50
|
+
mutations,
|
|
51
|
+
timestamp,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
if (!checkGrant(grants.create, working[documentId] as SanityDocument, identity)) {
|
|
55
|
+
throw new PermissionActionError({
|
|
56
|
+
documentId,
|
|
57
|
+
transactionId,
|
|
58
|
+
message: `You do not have permission to create document "${documentId}".`,
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// liveEdit documents use the mutation endpoint directly -- we don't send actions
|
|
63
|
+
outgoingMutations.push(...mutations)
|
|
64
|
+
return {base, working}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Standard draft/published/version logic
|
|
68
|
+
const versionId = isReleasePerspective(action.perspective)
|
|
69
|
+
? getVersionId(DocumentId(documentId), action.perspective.releaseName)
|
|
70
|
+
: undefined
|
|
71
|
+
const draftId = getDraftId(DocumentId(documentId))
|
|
72
|
+
const publishedId = getPublishedId(DocumentId(documentId))
|
|
73
|
+
|
|
74
|
+
const alreadyHasVersion = versionId ? working[versionId] : working[draftId]
|
|
75
|
+
|
|
76
|
+
if (alreadyHasVersion) {
|
|
77
|
+
const errorDocType = versionId ? 'release version' : 'draft'
|
|
78
|
+
throw new ActionError({
|
|
79
|
+
documentId,
|
|
80
|
+
transactionId,
|
|
81
|
+
message: `A ${errorDocType} of this document already exists. Please use or discard the existing ${errorDocType} before creating a new one.`,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Spread the (possibly undefined) draft or published version directly.
|
|
86
|
+
// (studio uses the draft version as a base if you are in a release perspective)
|
|
87
|
+
const newDocBase = {
|
|
88
|
+
...(base[draftId] ?? base[publishedId]),
|
|
89
|
+
_type: action.documentType,
|
|
90
|
+
_id: versionId ?? draftId,
|
|
91
|
+
...action.initialValue,
|
|
92
|
+
}
|
|
93
|
+
const newDocWorking = {
|
|
94
|
+
...(working[draftId] ?? working[publishedId]),
|
|
95
|
+
_type: action.documentType,
|
|
96
|
+
_id: versionId ?? draftId,
|
|
97
|
+
...action.initialValue,
|
|
98
|
+
}
|
|
99
|
+
const mutations: Mutation[] = [{create: newDocWorking}]
|
|
100
|
+
|
|
101
|
+
base = processMutations({
|
|
102
|
+
documents: base,
|
|
103
|
+
transactionId,
|
|
104
|
+
mutations: [{create: newDocBase}],
|
|
105
|
+
timestamp,
|
|
106
|
+
})
|
|
107
|
+
working = processMutations({
|
|
108
|
+
documents: working,
|
|
109
|
+
transactionId,
|
|
110
|
+
mutations,
|
|
111
|
+
timestamp,
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
if (versionId && !checkGrant(grants.create, working[versionId] as SanityDocument, identity)) {
|
|
115
|
+
throw new PermissionActionError({
|
|
116
|
+
documentId,
|
|
117
|
+
transactionId,
|
|
118
|
+
message: `You do not have permission to create a release version for document "${documentId}".`,
|
|
119
|
+
})
|
|
120
|
+
} else if (
|
|
121
|
+
!versionId &&
|
|
122
|
+
!checkGrant(grants.create, working[draftId] as SanityDocument, identity)
|
|
123
|
+
) {
|
|
124
|
+
throw new PermissionActionError({
|
|
125
|
+
documentId,
|
|
126
|
+
transactionId,
|
|
127
|
+
message: `You do not have permission to create a draft for document "${documentId}".`,
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
outgoingMutations.push(...mutations)
|
|
132
|
+
outgoingActions.push({
|
|
133
|
+
actionType: 'sanity.action.document.version.create',
|
|
134
|
+
publishedId,
|
|
135
|
+
attributes: newDocWorking,
|
|
136
|
+
})
|
|
137
|
+
return {base, working}
|
|
138
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {DocumentId, getDraftId, getPublishedId} from '@sanity/id-utils'
|
|
2
|
+
import {type Mutation} from '@sanity/types'
|
|
3
|
+
|
|
4
|
+
import {isReleasePerspective} from '../../releases/utils/isReleasePerspective'
|
|
5
|
+
import {type DeleteDocumentAction} from '../actions'
|
|
6
|
+
import {processMutations} from '../processMutations'
|
|
7
|
+
import {
|
|
8
|
+
ActionError,
|
|
9
|
+
type ActionHandlerContext,
|
|
10
|
+
type ActionHandlerResult,
|
|
11
|
+
checkGrant,
|
|
12
|
+
PermissionActionError,
|
|
13
|
+
} from './shared'
|
|
14
|
+
|
|
15
|
+
export function handleDelete(
|
|
16
|
+
action: DeleteDocumentAction,
|
|
17
|
+
ctx: ActionHandlerContext,
|
|
18
|
+
): ActionHandlerResult {
|
|
19
|
+
const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx
|
|
20
|
+
let {base, working} = ctx
|
|
21
|
+
|
|
22
|
+
const documentId = action.documentId
|
|
23
|
+
|
|
24
|
+
if (isReleasePerspective(action.perspective)) {
|
|
25
|
+
throw new ActionError({
|
|
26
|
+
documentId,
|
|
27
|
+
transactionId,
|
|
28
|
+
message: `Cannot delete a version document. You may want to use the "unpublish" or "discard" actions instead.`,
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (action.liveEdit) {
|
|
33
|
+
if (!working[documentId]) {
|
|
34
|
+
throw new ActionError({
|
|
35
|
+
documentId,
|
|
36
|
+
transactionId,
|
|
37
|
+
message: 'The document you are trying to delete does not exist.',
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!checkGrant(grants.update, working[documentId], identity)) {
|
|
42
|
+
throw new PermissionActionError({
|
|
43
|
+
documentId,
|
|
44
|
+
transactionId,
|
|
45
|
+
message: `You do not have permission to delete this document.`,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const mutations: Mutation[] = [{delete: {id: documentId}}]
|
|
50
|
+
|
|
51
|
+
base = processMutations({documents: base, transactionId, mutations, timestamp})
|
|
52
|
+
working = processMutations({documents: working, transactionId, mutations, timestamp})
|
|
53
|
+
|
|
54
|
+
// although liveEdit documents can use the actions API for deletion,
|
|
55
|
+
// having this be an action while other operations are mutations creates an inconsistency
|
|
56
|
+
// (and a possible race condition in document store where mutations might get skipped)
|
|
57
|
+
outgoingMutations.push(...mutations)
|
|
58
|
+
return {base, working}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Standard draft/published logic
|
|
62
|
+
const draftId = getDraftId(DocumentId(documentId))
|
|
63
|
+
const publishedId = getPublishedId(DocumentId(documentId))
|
|
64
|
+
|
|
65
|
+
if (!working[publishedId]) {
|
|
66
|
+
throw new ActionError({
|
|
67
|
+
documentId,
|
|
68
|
+
transactionId,
|
|
69
|
+
message: working[draftId]
|
|
70
|
+
? 'Cannot delete a document without a published version.'
|
|
71
|
+
: 'The document you are trying to delete does not exist.',
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const cantDeleteDraft = working[draftId] && !checkGrant(grants.update, working[draftId], identity)
|
|
76
|
+
const cantDeletePublished =
|
|
77
|
+
working[publishedId] && !checkGrant(grants.update, working[publishedId], identity)
|
|
78
|
+
|
|
79
|
+
if (cantDeleteDraft || cantDeletePublished) {
|
|
80
|
+
throw new PermissionActionError({
|
|
81
|
+
documentId,
|
|
82
|
+
transactionId,
|
|
83
|
+
message: `You do not have permission to delete this document.`,
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const mutations: Mutation[] = [{delete: {id: publishedId}}, {delete: {id: draftId}}]
|
|
88
|
+
const includeDrafts = working[draftId] ? [draftId] : undefined
|
|
89
|
+
|
|
90
|
+
base = processMutations({documents: base, transactionId, mutations, timestamp})
|
|
91
|
+
working = processMutations({documents: working, transactionId, mutations, timestamp})
|
|
92
|
+
|
|
93
|
+
outgoingMutations.push(...mutations)
|
|
94
|
+
outgoingActions.push({
|
|
95
|
+
actionType: 'sanity.action.document.delete',
|
|
96
|
+
publishedId,
|
|
97
|
+
...(includeDrafts ? {includeDrafts} : {}),
|
|
98
|
+
})
|
|
99
|
+
return {base, working}
|
|
100
|
+
}
|