@sanity/sdk 2.8.0 → 2.10.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 +2450 -0
- package/dist/_chunks-es/_internal.js +129 -0
- package/dist/_chunks-es/_internal.js.map +1 -0
- package/dist/_chunks-es/createGroqSearchFilter.js +1537 -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 +465 -1813
- package/dist/index.js.map +1 -1
- package/package.json +17 -12
- package/src/_exports/_internal.ts +14 -0
- package/src/_exports/index.ts +18 -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 +44 -30
- package/src/client/clientStore.ts +49 -48
- package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
- package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
- package/src/comlink/node/getNodeState.ts +2 -1
- package/src/config/sanityConfig.ts +78 -12
- 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 +542 -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 +106 -78
- package/src/document/reducers.test.ts +117 -29
- package/src/document/reducers.ts +47 -40
- 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.test.ts +46 -6
- package/src/presence/bifurTransport.ts +19 -2
- package/src/presence/presenceStore.test.ts +96 -0
- package/src/presence/presenceStore.ts +96 -24
- 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 +33 -10
- package/src/preview/{previewStore.ts → types.ts} +8 -17
- package/src/projection/getProjectionState.test.ts +16 -16
- package/src/projection/getProjectionState.ts +6 -5
- package/src/projection/projectionQuery.ts +2 -3
- package/src/projection/projectionStore.test.ts +2 -2
- package/src/projection/resolveProjection.ts +2 -2
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
- package/src/projection/subscribeToStateAndFetchBatches.ts +12 -11
- package/src/projection/types.ts +1 -1
- package/src/query/queryStore.test.ts +12 -12
- package/src/query/queryStore.ts +12 -11
- package/src/query/reducers.ts +3 -3
- 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/createActionBinder.test.ts +31 -31
- package/src/store/createActionBinder.ts +43 -38
- package/src/store/createSanityInstance.ts +2 -3
- 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/reducers.ts +3 -4
- 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 +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/isImportError.test.ts +72 -0
- package/src/utils/isImportError.ts +34 -0
- package/src/utils/object.test.ts +95 -0
- package/src/utils/object.ts +142 -0
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {diffValue} from '@sanity/diff-patch'
|
|
2
|
+
import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'
|
|
2
3
|
import {
|
|
3
4
|
type Mutation,
|
|
4
5
|
type PatchOperations,
|
|
@@ -6,9 +7,9 @@ import {
|
|
|
6
7
|
type SanityDocument,
|
|
7
8
|
} from '@sanity/types'
|
|
8
9
|
import {evaluateSync, type ExprNode} from 'groq-js'
|
|
9
|
-
import {isEqual} from 'lodash-es'
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import {isReleasePerspective} from '../releases/utils/isReleasePerspective'
|
|
12
|
+
import {isDeepEqual} from '../utils/object'
|
|
12
13
|
import {type DocumentAction} from './actions'
|
|
13
14
|
import {type Grant} from './permissions'
|
|
14
15
|
import {type DocumentSet, getId, processMutations} from './processMutations'
|
|
@@ -151,8 +152,12 @@ export function processActions({
|
|
|
151
152
|
})
|
|
152
153
|
}
|
|
153
154
|
|
|
154
|
-
const newDocBase = {_type: action.documentType, _id: documentId}
|
|
155
|
-
const newDocWorking = {
|
|
155
|
+
const newDocBase = {_type: action.documentType, _id: documentId, ...action.initialValue}
|
|
156
|
+
const newDocWorking = {
|
|
157
|
+
_type: action.documentType,
|
|
158
|
+
_id: documentId,
|
|
159
|
+
...action.initialValue,
|
|
160
|
+
}
|
|
156
161
|
const mutations: Mutation[] = [{create: newDocWorking}]
|
|
157
162
|
|
|
158
163
|
base = processMutations({
|
|
@@ -176,38 +181,41 @@ export function processActions({
|
|
|
176
181
|
})
|
|
177
182
|
}
|
|
178
183
|
|
|
184
|
+
// liveEdit documents use the mutation endpoint directly -- we don't send actions
|
|
179
185
|
outgoingMutations.push(...mutations)
|
|
180
|
-
outgoingActions.push({
|
|
181
|
-
actionType: 'sanity.action.document.create',
|
|
182
|
-
publishedId: documentId,
|
|
183
|
-
attributes: newDocWorking,
|
|
184
|
-
})
|
|
185
186
|
continue
|
|
186
187
|
}
|
|
187
188
|
|
|
188
|
-
// Standard draft/published logic
|
|
189
|
-
const
|
|
190
|
-
|
|
189
|
+
// Standard draft/published/version logic
|
|
190
|
+
const versionId = isReleasePerspective(action.perspective)
|
|
191
|
+
? getVersionId(DocumentId(documentId), action.perspective.releaseName)
|
|
192
|
+
: undefined
|
|
193
|
+
const draftId = getDraftId(DocumentId(documentId))
|
|
194
|
+
const publishedId = getPublishedId(DocumentId(documentId))
|
|
195
|
+
|
|
196
|
+
const alreadyHasVersion = versionId ? working[versionId] : working[draftId]
|
|
191
197
|
|
|
192
|
-
if (
|
|
198
|
+
if (alreadyHasVersion) {
|
|
199
|
+
const errorDocType = versionId ? 'release version' : 'draft'
|
|
193
200
|
throw new ActionError({
|
|
194
201
|
documentId,
|
|
195
202
|
transactionId,
|
|
196
|
-
message: `A
|
|
203
|
+
message: `A ${errorDocType} of this document already exists. Please use or discard the existing ${errorDocType} before creating a new one.`,
|
|
197
204
|
})
|
|
198
205
|
}
|
|
199
206
|
|
|
200
|
-
// Spread the (possibly undefined) published version directly.
|
|
207
|
+
// Spread the (possibly undefined) draft or published version directly.
|
|
208
|
+
// (studio uses the draft version as a base if you are in a release perspective)
|
|
201
209
|
const newDocBase = {
|
|
202
|
-
...base[publishedId],
|
|
210
|
+
...(base[draftId] ?? base[publishedId]),
|
|
203
211
|
_type: action.documentType,
|
|
204
|
-
_id: draftId,
|
|
212
|
+
_id: versionId ?? draftId,
|
|
205
213
|
...action.initialValue,
|
|
206
214
|
}
|
|
207
215
|
const newDocWorking = {
|
|
208
|
-
...working[publishedId],
|
|
216
|
+
...(working[draftId] ?? working[publishedId]),
|
|
209
217
|
_type: action.documentType,
|
|
210
|
-
_id: draftId,
|
|
218
|
+
_id: versionId ?? draftId,
|
|
211
219
|
...action.initialValue,
|
|
212
220
|
}
|
|
213
221
|
const mutations: Mutation[] = [{create: newDocWorking}]
|
|
@@ -225,7 +233,13 @@ export function processActions({
|
|
|
225
233
|
timestamp,
|
|
226
234
|
})
|
|
227
235
|
|
|
228
|
-
if (!checkGrant(grants.create, working[
|
|
236
|
+
if (versionId && !checkGrant(grants.create, working[versionId] as SanityDocument)) {
|
|
237
|
+
throw new PermissionActionError({
|
|
238
|
+
documentId,
|
|
239
|
+
transactionId,
|
|
240
|
+
message: `You do not have permission to create a release version for document "${documentId}".`,
|
|
241
|
+
})
|
|
242
|
+
} else if (!versionId && !checkGrant(grants.create, working[draftId] as SanityDocument)) {
|
|
229
243
|
throw new PermissionActionError({
|
|
230
244
|
documentId,
|
|
231
245
|
transactionId,
|
|
@@ -245,8 +259,15 @@ export function processActions({
|
|
|
245
259
|
case 'document.delete': {
|
|
246
260
|
const documentId = action.documentId
|
|
247
261
|
|
|
262
|
+
if (isReleasePerspective(action.perspective)) {
|
|
263
|
+
throw new ActionError({
|
|
264
|
+
documentId,
|
|
265
|
+
transactionId,
|
|
266
|
+
message: `Cannot delete a version document. You may want to use the "unpublish" or "discard" actions instead.`,
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
|
|
248
270
|
if (action.liveEdit) {
|
|
249
|
-
// For liveEdit documents, delete directly
|
|
250
271
|
if (!working[documentId]) {
|
|
251
272
|
throw new ActionError({
|
|
252
273
|
documentId,
|
|
@@ -268,17 +289,16 @@ export function processActions({
|
|
|
268
289
|
base = processMutations({documents: base, transactionId, mutations, timestamp})
|
|
269
290
|
working = processMutations({documents: working, transactionId, mutations, timestamp})
|
|
270
291
|
|
|
292
|
+
// although liveEdit documents can use the actions API for deletion,
|
|
293
|
+
// having this be an action while other operations are mutations creates an inconsistency
|
|
294
|
+
// (and a possible race condition in document store where mutations might get skipped)
|
|
271
295
|
outgoingMutations.push(...mutations)
|
|
272
|
-
outgoingActions.push({
|
|
273
|
-
actionType: 'sanity.action.document.delete',
|
|
274
|
-
publishedId: documentId,
|
|
275
|
-
})
|
|
276
296
|
continue
|
|
277
297
|
}
|
|
278
298
|
|
|
279
299
|
// Standard draft/published logic
|
|
280
|
-
const draftId = getDraftId(documentId)
|
|
281
|
-
const publishedId = getPublishedId(documentId)
|
|
300
|
+
const draftId = getDraftId(DocumentId(documentId))
|
|
301
|
+
const publishedId = getPublishedId(DocumentId(documentId))
|
|
282
302
|
|
|
283
303
|
if (!working[publishedId]) {
|
|
284
304
|
throw new ActionError({
|
|
@@ -328,19 +348,21 @@ export function processActions({
|
|
|
328
348
|
})
|
|
329
349
|
}
|
|
330
350
|
|
|
331
|
-
//
|
|
332
|
-
const
|
|
333
|
-
|
|
351
|
+
// draft/published or version logic
|
|
352
|
+
const versionId = isReleasePerspective(action.perspective)
|
|
353
|
+
? getVersionId(DocumentId(documentId), action.perspective.releaseName)
|
|
354
|
+
: getDraftId(DocumentId(documentId))
|
|
355
|
+
const mutations: Mutation[] = [{delete: {id: versionId}}]
|
|
334
356
|
|
|
335
|
-
if (!working[
|
|
357
|
+
if (!working[versionId]) {
|
|
336
358
|
throw new ActionError({
|
|
337
359
|
documentId,
|
|
338
360
|
transactionId,
|
|
339
|
-
message: `There is no draft available to discard for document "${documentId}".`,
|
|
361
|
+
message: `There is no draft or version available to discard for document "${documentId}".`,
|
|
340
362
|
})
|
|
341
363
|
}
|
|
342
364
|
|
|
343
|
-
if (!checkGrant(grants.update, working[
|
|
365
|
+
if (!checkGrant(grants.update, working[versionId])) {
|
|
344
366
|
throw new PermissionActionError({
|
|
345
367
|
documentId,
|
|
346
368
|
transactionId,
|
|
@@ -354,7 +376,7 @@ export function processActions({
|
|
|
354
376
|
outgoingMutations.push(...mutations)
|
|
355
377
|
outgoingActions.push({
|
|
356
378
|
actionType: 'sanity.action.document.version.discard',
|
|
357
|
-
versionId
|
|
379
|
+
versionId,
|
|
358
380
|
})
|
|
359
381
|
continue
|
|
360
382
|
}
|
|
@@ -363,7 +385,7 @@ export function processActions({
|
|
|
363
385
|
const documentId = getId(action.documentId)
|
|
364
386
|
|
|
365
387
|
if (action.liveEdit) {
|
|
366
|
-
//
|
|
388
|
+
// Single-document mode (liveEdit or release perspective): edit directly without draft logic
|
|
367
389
|
const userPatches = action.patches?.map((patch) => ({patch: {id: documentId, ...patch}}))
|
|
368
390
|
|
|
369
391
|
// skip this action if there are no associated patches
|
|
@@ -408,31 +430,33 @@ export function processActions({
|
|
|
408
430
|
timestamp,
|
|
409
431
|
})
|
|
410
432
|
|
|
433
|
+
// liveEdit documents use the mutation endpoint directly -- we don't send actions
|
|
411
434
|
outgoingMutations.push(...workingMutations)
|
|
412
|
-
outgoingActions.push(
|
|
413
|
-
...patches.map(
|
|
414
|
-
(patch): HttpAction => ({
|
|
415
|
-
actionType: 'sanity.action.document.edit',
|
|
416
|
-
// Server requires draftId to have drafts. prefix for validation, even for liveEdit
|
|
417
|
-
draftId: getDraftId(documentId),
|
|
418
|
-
publishedId: documentId,
|
|
419
|
-
patch: patch as PatchOperations,
|
|
420
|
-
}),
|
|
421
|
-
),
|
|
422
|
-
)
|
|
423
|
-
|
|
424
435
|
continue
|
|
425
436
|
}
|
|
426
437
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
const
|
|
438
|
+
const versionId = isReleasePerspective(action.perspective)
|
|
439
|
+
? getVersionId(DocumentId(documentId), action.perspective.releaseName)
|
|
440
|
+
: undefined
|
|
441
|
+
const draftId = getDraftId(DocumentId(documentId))
|
|
442
|
+
const publishedId = getPublishedId(DocumentId(documentId))
|
|
443
|
+
const patchDocumentId = isReleasePerspective(action.perspective) ? versionId! : draftId
|
|
444
|
+
const userPatches = action.patches?.map((patch) => ({
|
|
445
|
+
patch: {id: patchDocumentId, ...patch},
|
|
446
|
+
}))
|
|
431
447
|
|
|
432
448
|
// skip this action if there are no associated patches
|
|
433
449
|
if (!userPatches?.length) continue
|
|
434
450
|
|
|
435
|
-
if (
|
|
451
|
+
if (isReleasePerspective(action.perspective)) {
|
|
452
|
+
if (!working[versionId!] && !base[versionId!]) {
|
|
453
|
+
throw new ActionError({
|
|
454
|
+
documentId,
|
|
455
|
+
transactionId,
|
|
456
|
+
message: `This document does not exist in the release. Please create it or add it to the release first.`,
|
|
457
|
+
})
|
|
458
|
+
}
|
|
459
|
+
} else if (
|
|
436
460
|
(!working[draftId] && !working[publishedId]) ||
|
|
437
461
|
(!base[draftId] && !base[publishedId])
|
|
438
462
|
) {
|
|
@@ -444,12 +468,14 @@ export function processActions({
|
|
|
444
468
|
}
|
|
445
469
|
|
|
446
470
|
const baseMutations: Mutation[] = []
|
|
447
|
-
|
|
471
|
+
// don't create a draft from the published version in a release perspective
|
|
472
|
+
if (!isReleasePerspective(action.perspective) && !base[draftId] && base[publishedId]) {
|
|
473
|
+
// otherwise make a draft from the published version
|
|
448
474
|
baseMutations.push({create: {...base[publishedId], _id: draftId}})
|
|
449
475
|
}
|
|
450
476
|
|
|
451
|
-
// the
|
|
452
|
-
const baseBefore =
|
|
477
|
+
// the above statement and guards should make this never be null or undefined
|
|
478
|
+
const baseBefore = base[patchDocumentId] ?? base[publishedId]
|
|
453
479
|
if (userPatches) {
|
|
454
480
|
baseMutations.push(...userPatches)
|
|
455
481
|
}
|
|
@@ -462,11 +488,15 @@ export function processActions({
|
|
|
462
488
|
})
|
|
463
489
|
// this one will always be defined because a patch mutation will never
|
|
464
490
|
// delete an input document
|
|
465
|
-
const baseAfter = base[
|
|
491
|
+
const baseAfter = base[patchDocumentId] as SanityDocument
|
|
466
492
|
const patches = diffValue(baseBefore, baseAfter)
|
|
467
493
|
|
|
468
494
|
const workingMutations: Mutation[] = []
|
|
469
|
-
if (
|
|
495
|
+
if (
|
|
496
|
+
!isReleasePerspective(action.perspective) &&
|
|
497
|
+
!working[draftId] &&
|
|
498
|
+
working[publishedId]
|
|
499
|
+
) {
|
|
470
500
|
const newDraftFromPublished = {...working[publishedId], _id: draftId}
|
|
471
501
|
|
|
472
502
|
if (!checkGrant(grants.create, newDraftFromPublished)) {
|
|
@@ -481,15 +511,15 @@ export function processActions({
|
|
|
481
511
|
}
|
|
482
512
|
|
|
483
513
|
// the first if statement should make this never be null or undefined
|
|
484
|
-
const workingBefore =
|
|
485
|
-
if (!checkGrant(grants.update, workingBefore)) {
|
|
514
|
+
const workingBefore = working[patchDocumentId] ?? working[publishedId]
|
|
515
|
+
if (!checkGrant(grants.update, workingBefore!)) {
|
|
486
516
|
throw new PermissionActionError({
|
|
487
517
|
documentId,
|
|
488
518
|
transactionId,
|
|
489
519
|
message: `You do not have permission to edit document "${documentId}".`,
|
|
490
520
|
})
|
|
491
521
|
}
|
|
492
|
-
workingMutations.push(...patches.map((patch) => ({patch: {id:
|
|
522
|
+
workingMutations.push(...patches.map((patch) => ({patch: {id: patchDocumentId, ...patch}})))
|
|
493
523
|
|
|
494
524
|
working = processMutations({
|
|
495
525
|
documents: working,
|
|
@@ -500,14 +530,12 @@ export function processActions({
|
|
|
500
530
|
|
|
501
531
|
outgoingMutations.push(...workingMutations)
|
|
502
532
|
outgoingActions.push(
|
|
503
|
-
...patches.map(
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}),
|
|
510
|
-
),
|
|
533
|
+
...patches.map((patch) => ({
|
|
534
|
+
actionType: 'sanity.action.document.edit' as const,
|
|
535
|
+
draftId: patchDocumentId,
|
|
536
|
+
publishedId,
|
|
537
|
+
patch: patch as PatchOperations,
|
|
538
|
+
})),
|
|
511
539
|
)
|
|
512
540
|
|
|
513
541
|
continue
|
|
@@ -516,17 +544,17 @@ export function processActions({
|
|
|
516
544
|
case 'document.publish': {
|
|
517
545
|
const documentId = getId(action.documentId)
|
|
518
546
|
|
|
519
|
-
if (action.liveEdit) {
|
|
547
|
+
if (action.liveEdit || isReleasePerspective(action.perspective)) {
|
|
520
548
|
throw new ActionError({
|
|
521
549
|
documentId,
|
|
522
550
|
transactionId,
|
|
523
|
-
message: `Cannot publish
|
|
551
|
+
message: `Cannot publish this document. Publishing is not supported for liveEdit or version (release) documents.`,
|
|
524
552
|
})
|
|
525
553
|
}
|
|
526
554
|
|
|
527
555
|
// Standard draft/published logic
|
|
528
|
-
const draftId = getDraftId(documentId)
|
|
529
|
-
const publishedId = getPublishedId(documentId)
|
|
556
|
+
const draftId = getDraftId(DocumentId(documentId))
|
|
557
|
+
const publishedId = getPublishedId(DocumentId(documentId))
|
|
530
558
|
|
|
531
559
|
const workingDraft = working[draftId]
|
|
532
560
|
const baseDraft = base[draftId]
|
|
@@ -540,7 +568,7 @@ export function processActions({
|
|
|
540
568
|
|
|
541
569
|
// Before proceeding, verify that the working draft is identical to the base draft.
|
|
542
570
|
// TODO: is it enough just to check for the _rev or nah?
|
|
543
|
-
if (!
|
|
571
|
+
if (!isDeepEqual(workingDraft, baseDraft)) {
|
|
544
572
|
throw new ActionError({
|
|
545
573
|
documentId,
|
|
546
574
|
transactionId,
|
|
@@ -592,17 +620,17 @@ export function processActions({
|
|
|
592
620
|
case 'document.unpublish': {
|
|
593
621
|
const documentId = getId(action.documentId)
|
|
594
622
|
|
|
595
|
-
if (action.liveEdit) {
|
|
623
|
+
if (action.liveEdit || isReleasePerspective(action.perspective)) {
|
|
596
624
|
throw new ActionError({
|
|
597
625
|
documentId,
|
|
598
626
|
transactionId,
|
|
599
|
-
message: `Cannot unpublish
|
|
627
|
+
message: `Cannot unpublish this document. Unpublishing is not supported for liveEdit or version (release) documents.`,
|
|
600
628
|
})
|
|
601
629
|
}
|
|
602
630
|
|
|
603
|
-
// Standard draft/published logic
|
|
604
|
-
const draftId = getDraftId(documentId)
|
|
605
|
-
const publishedId = getPublishedId(documentId)
|
|
631
|
+
// Standard draft/published or version logic
|
|
632
|
+
const draftId = getDraftId(DocumentId(documentId))
|
|
633
|
+
const publishedId = getPublishedId(DocumentId(documentId))
|
|
606
634
|
|
|
607
635
|
if (!working[publishedId] && !base[publishedId]) {
|
|
608
636
|
throw new ActionError({
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import {DocumentId, getDraftId, getPublishedId} from '@sanity/id-utils'
|
|
1
2
|
import {type SanityDocument} from '@sanity/types'
|
|
2
3
|
import {parse} from 'groq-js'
|
|
3
4
|
import {Subject} from 'rxjs'
|
|
4
5
|
import {describe, expect, it} from 'vitest'
|
|
5
6
|
|
|
6
|
-
import {getDraftId, getPublishedId} from '../utils/ids'
|
|
7
7
|
import {type DocumentEvent} from './events'
|
|
8
8
|
import {type RemoteDocument} from './listen'
|
|
9
9
|
import {type DocumentSet} from './processMutations'
|
|
@@ -66,8 +66,8 @@ describe('queueTransaction', () => {
|
|
|
66
66
|
expect(newState.queued[0]).toEqual(transaction)
|
|
67
67
|
|
|
68
68
|
// Check that both the published and draft documentStates got a subscription id added.
|
|
69
|
-
const draftId = getDraftId('doc1')
|
|
70
|
-
const pubId = getPublishedId('doc1')
|
|
69
|
+
const draftId = getDraftId(DocumentId('doc1'))
|
|
70
|
+
const pubId = getPublishedId(DocumentId('doc1'))
|
|
71
71
|
expect(newState.documentStates[draftId]).toBeDefined()
|
|
72
72
|
expect(newState.documentStates[pubId]).toBeDefined()
|
|
73
73
|
expect(newState.documentStates[draftId]?.subscriptions).toContain('txn1')
|
|
@@ -77,8 +77,8 @@ describe('queueTransaction', () => {
|
|
|
77
77
|
|
|
78
78
|
describe('removeQueuedTransaction', () => {
|
|
79
79
|
it('removes the transaction from queued and removes subscription ids from documents', () => {
|
|
80
|
-
const draftId = getDraftId('doc1')
|
|
81
|
-
const pubId = getPublishedId('doc1')
|
|
80
|
+
const draftId = getDraftId(DocumentId('doc1'))
|
|
81
|
+
const pubId = getPublishedId(DocumentId('doc1'))
|
|
82
82
|
|
|
83
83
|
const initialState: SyncTransactionState = {
|
|
84
84
|
queued: [
|
|
@@ -98,8 +98,8 @@ describe('removeQueuedTransaction', () => {
|
|
|
98
98
|
outgoing: undefined,
|
|
99
99
|
grants,
|
|
100
100
|
documentStates: {
|
|
101
|
-
[draftId]: {id: draftId, subscriptions: ['txn1']
|
|
102
|
-
[pubId]: {id: pubId, subscriptions: ['txn1']
|
|
101
|
+
[draftId]: {id: draftId, subscriptions: ['txn1']},
|
|
102
|
+
[pubId]: {id: pubId, subscriptions: ['txn1']},
|
|
103
103
|
} as SyncTransactionState['documentStates'],
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -138,7 +138,7 @@ describe('applyFirstQueuedTransaction', () => {
|
|
|
138
138
|
|
|
139
139
|
it('returns unchanged state if any required document is not yet loaded', () => {
|
|
140
140
|
// If a document's local value is undefined, the reducer should do nothing.
|
|
141
|
-
const draftId = getDraftId('doc1')
|
|
141
|
+
const draftId = getDraftId(DocumentId('doc1'))
|
|
142
142
|
const state: SyncTransactionState = {
|
|
143
143
|
queued: [
|
|
144
144
|
{
|
|
@@ -176,8 +176,8 @@ describe('applyFirstQueuedTransaction', () => {
|
|
|
176
176
|
|
|
177
177
|
it('applies the first queued transaction (using a discard action)', () => {
|
|
178
178
|
// For a discard action, processActions deletes the draft document.
|
|
179
|
-
const draftId = getDraftId('doc1')
|
|
180
|
-
const pubId = getPublishedId('doc1')
|
|
179
|
+
const draftId = getDraftId(DocumentId('doc1'))
|
|
180
|
+
const pubId = getPublishedId(DocumentId('doc1'))
|
|
181
181
|
const initialDraft = {...exampleDoc, _id: draftId, foo: 'bar', _rev: 'rev1'}
|
|
182
182
|
const initialPub = {...exampleDoc, _id: pubId, foo: 'bar', _rev: 'rev1'}
|
|
183
183
|
|
|
@@ -235,18 +235,26 @@ describe('batchAppliedTransactions', () => {
|
|
|
235
235
|
disableBatching: false,
|
|
236
236
|
outgoingActions: [],
|
|
237
237
|
outgoingMutations: [],
|
|
238
|
-
base: {
|
|
238
|
+
base: {
|
|
239
|
+
[getDraftId(DocumentId('doc1'))]: {_id: getDraftId(DocumentId('doc1')), _rev: 'rev1'},
|
|
240
|
+
} as unknown as DocumentSet,
|
|
239
241
|
working: {
|
|
240
|
-
[getDraftId('doc1')]: {
|
|
242
|
+
[getDraftId(DocumentId('doc1'))]: {
|
|
241
243
|
...exampleDoc,
|
|
242
|
-
_id: getDraftId('doc1'),
|
|
244
|
+
_id: getDraftId(DocumentId('doc1')),
|
|
243
245
|
_rev: 'rev2',
|
|
244
246
|
foo: 'a',
|
|
245
247
|
bar: 'b',
|
|
246
248
|
},
|
|
247
249
|
},
|
|
248
|
-
previous: {
|
|
249
|
-
|
|
250
|
+
previous: {
|
|
251
|
+
[getDraftId(DocumentId('doc1'))]: {
|
|
252
|
+
...exampleDoc,
|
|
253
|
+
_id: getDraftId(DocumentId('doc1')),
|
|
254
|
+
_rev: 'rev1',
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
previousRevs: {[getDraftId(DocumentId('doc1'))]: 'rev1'},
|
|
250
258
|
timestamp: '2025-02-06T00:00:00.000Z',
|
|
251
259
|
}
|
|
252
260
|
const result = batchAppliedTransactions([appliedTx])
|
|
@@ -256,13 +264,14 @@ describe('batchAppliedTransactions', () => {
|
|
|
256
264
|
})
|
|
257
265
|
|
|
258
266
|
it('batches two edit transactions when possible', () => {
|
|
259
|
-
const
|
|
267
|
+
const documentId = DocumentId('doc1')
|
|
268
|
+
const draftId = getDraftId(documentId)
|
|
260
269
|
const appliedTx1: AppliedTransaction = {
|
|
261
270
|
transactionId: 'txn5',
|
|
262
271
|
actions: [
|
|
263
272
|
{
|
|
264
273
|
type: 'document.edit',
|
|
265
|
-
documentId
|
|
274
|
+
documentId,
|
|
266
275
|
documentType: 'book',
|
|
267
276
|
patches: [{set: {foo: 'a'}}],
|
|
268
277
|
},
|
|
@@ -272,7 +281,7 @@ describe('batchAppliedTransactions', () => {
|
|
|
272
281
|
{
|
|
273
282
|
actionType: 'sanity.action.document.edit',
|
|
274
283
|
draftId,
|
|
275
|
-
publishedId: getPublishedId(
|
|
284
|
+
publishedId: getPublishedId(documentId),
|
|
276
285
|
patch: {set: {foo: 'a'}},
|
|
277
286
|
},
|
|
278
287
|
],
|
|
@@ -288,7 +297,7 @@ describe('batchAppliedTransactions', () => {
|
|
|
288
297
|
actions: [
|
|
289
298
|
{
|
|
290
299
|
type: 'document.edit',
|
|
291
|
-
documentId
|
|
300
|
+
documentId,
|
|
292
301
|
documentType: 'book',
|
|
293
302
|
patches: [{set: {bar: 'b'}}],
|
|
294
303
|
},
|
|
@@ -298,7 +307,7 @@ describe('batchAppliedTransactions', () => {
|
|
|
298
307
|
{
|
|
299
308
|
actionType: 'sanity.action.document.edit',
|
|
300
309
|
draftId,
|
|
301
|
-
publishedId: getPublishedId(
|
|
310
|
+
publishedId: getPublishedId(documentId),
|
|
302
311
|
patch: {set: {bar: 'b'}},
|
|
303
312
|
},
|
|
304
313
|
],
|
|
@@ -322,7 +331,7 @@ describe('batchAppliedTransactions', () => {
|
|
|
322
331
|
})
|
|
323
332
|
|
|
324
333
|
it('returns a transaction with disableBatching true if a single edit action already has disableBatching set', () => {
|
|
325
|
-
const draftId = getDraftId('docA')
|
|
334
|
+
const draftId = getDraftId(DocumentId('docA'))
|
|
326
335
|
const appliedTx: AppliedTransaction = {
|
|
327
336
|
transactionId: 'txn-disable',
|
|
328
337
|
actions: [
|
|
@@ -350,6 +359,85 @@ describe('batchAppliedTransactions', () => {
|
|
|
350
359
|
// The actions array should be the same as the input's.
|
|
351
360
|
expect(result?.actions).toEqual(appliedTx.actions)
|
|
352
361
|
})
|
|
362
|
+
|
|
363
|
+
it('does not batch a liveEdit edit with a non-liveEdit edit', () => {
|
|
364
|
+
const nonLiveEditTx: AppliedTransaction = {
|
|
365
|
+
transactionId: 'txn-nonlive',
|
|
366
|
+
actions: [
|
|
367
|
+
{
|
|
368
|
+
type: 'document.edit',
|
|
369
|
+
documentId: 'doc1',
|
|
370
|
+
documentType: 'article',
|
|
371
|
+
patches: [{set: {foo: 'a'}}],
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
disableBatching: false,
|
|
375
|
+
outgoingActions: [
|
|
376
|
+
{
|
|
377
|
+
actionType: 'sanity.action.document.edit',
|
|
378
|
+
draftId: getDraftId(DocumentId('doc1')),
|
|
379
|
+
publishedId: getPublishedId(DocumentId('doc1')),
|
|
380
|
+
patch: {set: {foo: 'a'}},
|
|
381
|
+
},
|
|
382
|
+
],
|
|
383
|
+
outgoingMutations: [],
|
|
384
|
+
base: {
|
|
385
|
+
[getDraftId(DocumentId('doc1'))]: {
|
|
386
|
+
...exampleDoc,
|
|
387
|
+
_id: getDraftId(DocumentId('doc1')),
|
|
388
|
+
_rev: 'rev1',
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
working: {
|
|
392
|
+
[getDraftId(DocumentId('doc1'))]: {
|
|
393
|
+
...exampleDoc,
|
|
394
|
+
_id: getDraftId(DocumentId('doc1')),
|
|
395
|
+
_rev: 'rev2',
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
previous: {
|
|
399
|
+
[getDraftId(DocumentId('doc1'))]: {
|
|
400
|
+
...exampleDoc,
|
|
401
|
+
_id: getDraftId(DocumentId('doc1')),
|
|
402
|
+
_rev: 'rev1',
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
previousRevs: {[getDraftId(DocumentId('doc1'))]: 'rev1'},
|
|
406
|
+
timestamp: '2025-02-06T00:00:00.000Z',
|
|
407
|
+
}
|
|
408
|
+
const liveEditTx: AppliedTransaction = {
|
|
409
|
+
transactionId: 'txn-live',
|
|
410
|
+
actions: [
|
|
411
|
+
{
|
|
412
|
+
type: 'document.edit',
|
|
413
|
+
documentId: 'doc2',
|
|
414
|
+
documentType: 'liveArticle',
|
|
415
|
+
liveEdit: true,
|
|
416
|
+
patches: [{set: {bar: 'b'}}],
|
|
417
|
+
},
|
|
418
|
+
],
|
|
419
|
+
disableBatching: false,
|
|
420
|
+
outgoingActions: [],
|
|
421
|
+
outgoingMutations: [{patch: {id: 'doc2', set: {bar: 'b'}}}],
|
|
422
|
+
base: {doc2: {...exampleDoc, _id: 'doc2', _rev: 'rev1'}},
|
|
423
|
+
working: {doc2: {...exampleDoc, _id: 'doc2', _rev: 'rev2'}},
|
|
424
|
+
previous: {doc2: {...exampleDoc, _id: 'doc2', _rev: 'rev1'}},
|
|
425
|
+
previousRevs: {doc2: 'rev1'},
|
|
426
|
+
timestamp: '2025-02-06T00:01:00.000Z',
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// liveEdit first: should return only the liveEdit transaction
|
|
430
|
+
const resultLiveFirst = batchAppliedTransactions([liveEditTx, nonLiveEditTx])
|
|
431
|
+
expect(resultLiveFirst?.batchedTransactionIds).toEqual(['txn-live'])
|
|
432
|
+
expect(resultLiveFirst?.outgoingMutations).toEqual(liveEditTx.outgoingMutations)
|
|
433
|
+
expect(resultLiveFirst?.outgoingActions).toHaveLength(0)
|
|
434
|
+
|
|
435
|
+
// non-liveEdit first: should return only the non-liveEdit transaction
|
|
436
|
+
const resultNonLiveFirst = batchAppliedTransactions([nonLiveEditTx, liveEditTx])
|
|
437
|
+
expect(resultNonLiveFirst?.batchedTransactionIds).toEqual(['txn-nonlive'])
|
|
438
|
+
expect(resultNonLiveFirst?.outgoingActions).toEqual(nonLiveEditTx.outgoingActions)
|
|
439
|
+
expect(resultNonLiveFirst?.outgoingMutations).toHaveLength(0)
|
|
440
|
+
})
|
|
353
441
|
})
|
|
354
442
|
|
|
355
443
|
describe('transitionAppliedTransactionsToOutgoing', () => {
|
|
@@ -366,7 +454,7 @@ describe('transitionAppliedTransactionsToOutgoing', () => {
|
|
|
366
454
|
})
|
|
367
455
|
|
|
368
456
|
it('transitions applied transactions to an outgoing transaction', () => {
|
|
369
|
-
const draftId = getDraftId('doc1')
|
|
457
|
+
const draftId = getDraftId(DocumentId('doc1'))
|
|
370
458
|
const initialDoc = {...exampleDoc, _id: draftId, foo: 'old', _rev: 'rev1'}
|
|
371
459
|
const state: SyncTransactionState = {
|
|
372
460
|
queued: [],
|
|
@@ -426,8 +514,8 @@ describe('cleanupOutgoingTransaction', () => {
|
|
|
426
514
|
})
|
|
427
515
|
|
|
428
516
|
it('removes subscription ids for all documents associated with the outgoing transaction and then clears outgoing', () => {
|
|
429
|
-
const draftId = getDraftId('doc1')
|
|
430
|
-
const pubId = getPublishedId('doc1')
|
|
517
|
+
const draftId = getDraftId(DocumentId('doc1'))
|
|
518
|
+
const pubId = getPublishedId(DocumentId('doc1'))
|
|
431
519
|
const state: SyncTransactionState = {
|
|
432
520
|
queued: [],
|
|
433
521
|
applied: [],
|
|
@@ -467,8 +555,8 @@ describe('cleanupOutgoingTransaction', () => {
|
|
|
467
555
|
|
|
468
556
|
describe('revertOutgoingTransaction', () => {
|
|
469
557
|
it('reverts the outgoing transaction and updates documentStates by removing unverified revisions', () => {
|
|
470
|
-
const draftId = getDraftId('doc1')
|
|
471
|
-
const pubId = getPublishedId('doc1')
|
|
558
|
+
const draftId = getDraftId(DocumentId('doc1'))
|
|
559
|
+
const pubId = getPublishedId(DocumentId('doc1'))
|
|
472
560
|
// In this test we simulate a state with one applied transaction and an outgoing transaction.
|
|
473
561
|
const state: SyncTransactionState = {
|
|
474
562
|
queued: [],
|
|
@@ -565,7 +653,7 @@ describe('applyRemoteDocument', () => {
|
|
|
565
653
|
})
|
|
566
654
|
|
|
567
655
|
it('verifies an unverified revision when the revision matches and previousRev is as expected', () => {
|
|
568
|
-
const docId = getDraftId('doc1')
|
|
656
|
+
const docId = getDraftId(DocumentId('doc1'))
|
|
569
657
|
const initialState: SyncTransactionState = {
|
|
570
658
|
queued: [],
|
|
571
659
|
applied: [],
|
|
@@ -607,7 +695,7 @@ describe('applyRemoteDocument', () => {
|
|
|
607
695
|
|
|
608
696
|
it('rebases local changes when no matching unverified revision is found', () => {
|
|
609
697
|
// In this branch we simply let processActions rebase so that the local becomes the remote.
|
|
610
|
-
const docId = getDraftId('doc1')
|
|
698
|
+
const docId = getDraftId(DocumentId('doc1'))
|
|
611
699
|
const initialState: SyncTransactionState = {
|
|
612
700
|
queued: [],
|
|
613
701
|
applied: [],
|
|
@@ -642,7 +730,7 @@ describe('applyRemoteDocument', () => {
|
|
|
642
730
|
|
|
643
731
|
// Test that a sync event removes outdated unverified revisions.
|
|
644
732
|
it('removes outdated unverified revisions when a sync event is received', () => {
|
|
645
|
-
const docId = getDraftId('doc1')
|
|
733
|
+
const docId = getDraftId(DocumentId('doc1'))
|
|
646
734
|
// An unverified revision created at an earlier time.
|
|
647
735
|
const outdatedTimestamp = new Date('2025-02-06T00:09:00.000Z').toISOString()
|
|
648
736
|
// The incoming sync event timestamp is later.
|