@sanity/client 7.1.0 → 7.2.1

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.
@@ -0,0 +1,53 @@
1
+ import type {BaseActionOptions, CreateReleaseAction, ReleaseDocument} from '@sanity/client'
2
+
3
+ import {generateReleaseId} from '../util/createVersionId'
4
+
5
+ interface ReleaseOrOptions extends BaseActionOptions {
6
+ releaseId?: string
7
+ metadata?: Partial<ReleaseDocument['metadata']>
8
+ }
9
+
10
+ interface CompleteCreateReleaseAction extends CreateReleaseAction {
11
+ metadata: ReleaseDocument['metadata']
12
+ }
13
+
14
+ const getArgs = (
15
+ releaseOrOptions?: ReleaseOrOptions,
16
+ maybeOptions?: BaseActionOptions,
17
+ ): [string, Partial<ReleaseDocument['metadata']>, BaseActionOptions | undefined] => {
18
+ const isReleaseInput =
19
+ typeof releaseOrOptions === 'object' &&
20
+ releaseOrOptions !== null &&
21
+ ('releaseId' in releaseOrOptions || 'metadata' in releaseOrOptions)
22
+
23
+ if (isReleaseInput) {
24
+ const {releaseId = generateReleaseId(), metadata = {}} = releaseOrOptions
25
+ return [releaseId, metadata, maybeOptions]
26
+ }
27
+
28
+ return [generateReleaseId(), {}, releaseOrOptions as BaseActionOptions]
29
+ }
30
+
31
+ /** @internal */
32
+ export const createRelease = (
33
+ releaseOrOptions?: ReleaseOrOptions,
34
+ maybeOptions?: BaseActionOptions,
35
+ ): {
36
+ action: CompleteCreateReleaseAction
37
+ options?: BaseActionOptions
38
+ } => {
39
+ const [releaseId, metadata, options] = getArgs(releaseOrOptions, maybeOptions)
40
+
41
+ const finalMetadata: ReleaseDocument['metadata'] = {
42
+ ...metadata,
43
+ releaseType: metadata.releaseType || 'undecided',
44
+ }
45
+
46
+ const createAction: CompleteCreateReleaseAction = {
47
+ actionType: 'sanity.action.release.create',
48
+ releaseId,
49
+ metadata: finalMetadata,
50
+ }
51
+
52
+ return {action: createAction, options}
53
+ }
package/src/types.ts CHANGED
@@ -573,6 +573,24 @@ export type Mutation<R extends Record<string, Any> = Record<string, Any>> =
573
573
  | {delete: MutationSelection}
574
574
  | {patch: PatchMutationOperation}
575
575
 
576
+ /** @public */
577
+ export type ReleaseAction =
578
+ | CreateReleaseAction
579
+ | EditReleaseAction
580
+ | PublishReleaseAction
581
+ | ArchiveReleaseAction
582
+ | UnarchiveReleaseAction
583
+ | ScheduleReleaseAction
584
+ | UnscheduleReleaseAction
585
+ | DeleteReleaseAction
586
+
587
+ /** @public */
588
+ export type VersionAction =
589
+ | CreateVersionAction
590
+ | DiscardVersionAction
591
+ | ReplaceVersionAction
592
+ | UnpublishVersionAction
593
+
576
594
  /** @public */
577
595
  export type Action =
578
596
  | CreateAction
@@ -582,6 +600,136 @@ export type Action =
582
600
  | DiscardAction
583
601
  | PublishAction
584
602
  | UnpublishAction
603
+ | VersionAction
604
+ | ReleaseAction
605
+
606
+ /**
607
+ * Creates a new release under the given id, with metadata.
608
+ *
609
+ * @public
610
+ */
611
+ export interface CreateReleaseAction {
612
+ actionType: 'sanity.action.release.create'
613
+ releaseId: string
614
+ metadata?: Partial<ReleaseDocument['metadata']>
615
+ }
616
+
617
+ /**
618
+ * Edits an existing release, updating the metadata.
619
+ *
620
+ * @public
621
+ */
622
+ export interface EditReleaseAction {
623
+ actionType: 'sanity.action.release.edit'
624
+ releaseId: string
625
+ patch: PatchOperations
626
+ }
627
+
628
+ /**
629
+ * Publishes all documents in a release at once.
630
+ *
631
+ * @public
632
+ */
633
+ export interface PublishReleaseAction {
634
+ actionType: 'sanity.action.release.publish'
635
+ releaseId: string
636
+ }
637
+
638
+ /**
639
+ * Archives an `active` release, and deletes all the release documents.
640
+ *
641
+ * @public
642
+ */
643
+ export interface ArchiveReleaseAction {
644
+ actionType: 'sanity.action.release.archive'
645
+ releaseId: string
646
+ }
647
+
648
+ /**
649
+ * Unarchived an `archived` release, and restores all the release documents.
650
+ *
651
+ * @public
652
+ */
653
+ export interface UnarchiveReleaseAction {
654
+ actionType: 'sanity.action.release.unarchive'
655
+ releaseId: string
656
+ }
657
+
658
+ /**
659
+ * Queues release for publishing at the given future time.
660
+ *
661
+ * @public
662
+ */
663
+ export interface ScheduleReleaseAction {
664
+ actionType: 'sanity.action.release.schedule'
665
+ releaseId: string
666
+ publishAt: string
667
+ }
668
+
669
+ /**
670
+ * Unschedules a `scheduled` release, stopping it from being published.
671
+ *
672
+ * @public
673
+ */
674
+ export interface UnscheduleReleaseAction {
675
+ actionType: 'sanity.action.release.unschedule'
676
+ releaseId: string
677
+ }
678
+
679
+ /**
680
+ * Deletes a `archived` or `published` release, and all the release documents versions.
681
+ *
682
+ * @public
683
+ */
684
+ export interface DeleteReleaseAction {
685
+ actionType: 'sanity.action.release.delete'
686
+ releaseId: string
687
+ }
688
+
689
+ /**
690
+ * Creates a new version of an existing document, attached to the release as given
691
+ * by `document._id`
692
+ *
693
+ * @public
694
+ */
695
+ export interface CreateVersionAction {
696
+ actionType: 'sanity.action.document.version.create'
697
+ publishedId: string
698
+ document: IdentifiedSanityDocumentStub
699
+ }
700
+
701
+ /**
702
+ * Delete a version of a document.
703
+ *
704
+ * @public
705
+ */
706
+ export interface DiscardVersionAction {
707
+ actionType: 'sanity.action.document.version.discard'
708
+ versionId: string
709
+ purge?: boolean
710
+ }
711
+
712
+ /**
713
+ * Replace an existing version of a document.
714
+ *
715
+ * @public
716
+ */
717
+ export interface ReplaceVersionAction {
718
+ actionType: 'sanity.action.document.version.replace'
719
+ document: IdentifiedSanityDocumentStub
720
+ }
721
+
722
+ /**
723
+ * Identify that a version of a document should be unpublished when
724
+ * the release that version is contained within is published.
725
+ *
726
+ * @public
727
+ */
728
+ export interface UnpublishVersionAction {
729
+ actionType: 'sanity.action.document.version.unpublish'
730
+ versionId: string
731
+ publishedId: string
732
+ }
585
733
 
586
734
  /**
587
735
  * Creates a new draft document. The published version of the document must not already exist.
@@ -614,6 +762,7 @@ export type CreateAction = {
614
762
  * At least one of the draft or published versions of the document must exist.
615
763
  *
616
764
  * @public
765
+ * @deprecated Use {@link ReplaceVersionAction} instead
617
766
  */
618
767
  export type ReplaceDraftAction = {
619
768
  actionType: 'sanity.action.document.replaceDraft'
@@ -686,6 +835,7 @@ export type DeleteAction = {
686
835
  * It is an error if it does not exist. If the purge flag is set, the document history is also deleted.
687
836
  *
688
837
  * @public
838
+ * @deprecated Use {@link DiscardVersionAction} instead
689
839
  */
690
840
  export type DiscardAction = {
691
841
  actionType: 'sanity.action.document.discard'
@@ -1244,6 +1394,71 @@ export interface ActionErrorItem {
1244
1394
  index: number
1245
1395
  }
1246
1396
 
1397
+ /** @internal */
1398
+ export type PartialExcept<T, K extends keyof T> = Pick<T, K> & Partial<Omit<T, K>>
1399
+
1400
+ /** @beta */
1401
+ export type ReleaseState =
1402
+ | 'active'
1403
+ | 'archiving'
1404
+ | 'unarchiving'
1405
+ | 'archived'
1406
+ | 'published'
1407
+ | 'publishing'
1408
+ | 'scheduled'
1409
+ | 'scheduling'
1410
+
1411
+ /** @internal */
1412
+ export type ReleaseType = 'asap' | 'scheduled' | 'undecided'
1413
+
1414
+ /** @internal */
1415
+ export interface ReleaseDocument extends SanityDocument {
1416
+ /**
1417
+ * typically
1418
+ * `_.releases.<name>`
1419
+ */
1420
+ _id: string
1421
+ /**
1422
+ * where a release has _id `_.releases.foo`, the name is `foo`
1423
+ */
1424
+ name: string
1425
+ _type: 'system.release'
1426
+ _createdAt: string
1427
+ _updatedAt: string
1428
+ _rev: string
1429
+ state: ReleaseState
1430
+ error?: {
1431
+ message: string
1432
+ }
1433
+ finalDocumentStates?: {
1434
+ /** Document ID */
1435
+ id: string
1436
+ }[]
1437
+ /**
1438
+ * If defined, it takes precedence over the intendedPublishAt, the state should be 'scheduled'
1439
+ */
1440
+ publishAt?: string
1441
+ /**
1442
+ * If defined, it provides the time the release was actually published
1443
+ */
1444
+ publishedAt?: string
1445
+ metadata: {
1446
+ title?: string
1447
+ description?: string
1448
+ intendedPublishAt?: string
1449
+ releaseType: ReleaseType
1450
+ }
1451
+ }
1452
+
1453
+ /** @internal */
1454
+ export type EditableReleaseDocument = Omit<
1455
+ PartialExcept<ReleaseDocument, '_id'>,
1456
+ 'metadata' | '_type'
1457
+ > & {
1458
+ _id: string
1459
+ metadata: Partial<ReleaseDocument['metadata']>
1460
+ }
1461
+
1247
1462
  /**
1248
1463
  * DocumentValueSource is a path to a value within a document
1249
1464
  * @public
@@ -0,0 +1,79 @@
1
+ import {
2
+ getDraftId,
3
+ getVersionFromId,
4
+ getVersionId,
5
+ isDraftId,
6
+ isVersionId,
7
+ } from '@sanity/client/csm'
8
+ import {customAlphabet} from 'nanoid'
9
+
10
+ import type {IdentifiedSanityDocumentStub, SanityDocumentStub} from '../types'
11
+ import {validateVersionIdMatch} from '../validators'
12
+
13
+ /**
14
+ * @internal
15
+ *
16
+ * ~24 years (or 7.54e+8 seconds) needed, in order to have a 1% probability of at least one collision if 10 ID's are generated every hour.
17
+ */
18
+ export const generateReleaseId = customAlphabet(
19
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
20
+ 8,
21
+ )
22
+
23
+ /** @internal */
24
+ export const getDocumentVersionId = (publishedId: string, releaseId?: string) =>
25
+ releaseId ? getVersionId(publishedId, releaseId) : getDraftId(publishedId)
26
+
27
+ /** @internal */
28
+ export function deriveDocumentVersionId(
29
+ op: string,
30
+ {
31
+ releaseId,
32
+ publishedId,
33
+ document,
34
+ }: {
35
+ releaseId?: string
36
+ publishedId?: string
37
+ document: SanityDocumentStub | IdentifiedSanityDocumentStub
38
+ },
39
+ ): string {
40
+ if (publishedId && document._id) {
41
+ const versionId = getDocumentVersionId(publishedId, releaseId)
42
+ validateVersionIdMatch(versionId, document)
43
+ return versionId
44
+ }
45
+
46
+ if (document._id) {
47
+ const isDraft = isDraftId(document._id)
48
+ const isVersion = isVersionId(document._id)
49
+
50
+ if (!isDraft && !isVersion) {
51
+ throw new Error(
52
+ `\`${op}()\` requires a document with an \`_id\` that is a version or draft ID`,
53
+ )
54
+ }
55
+
56
+ if (releaseId) {
57
+ if (isDraft) {
58
+ throw new Error(
59
+ `\`${op}()\` was called with a document ID (\`${document._id}\`) that is a draft ID, but a release ID (\`${releaseId}\`) was also provided.`,
60
+ )
61
+ }
62
+
63
+ const builtVersionId = getVersionFromId(document._id)
64
+ if (builtVersionId !== releaseId) {
65
+ throw new Error(
66
+ `\`${op}()\` was called with a document ID (\`${document._id}\`) that is a version ID, but the release ID (\`${releaseId}\`) does not match the document's version ID (\`${builtVersionId}\`).`,
67
+ )
68
+ }
69
+ }
70
+
71
+ return document._id
72
+ }
73
+
74
+ if (publishedId) {
75
+ return getDocumentVersionId(publishedId, releaseId)
76
+ }
77
+
78
+ throw new Error(`\`${op}()\` requires either a publishedId or a document with an \`_id\``)
79
+ }
package/src/validators.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type {Any, InitializedClientConfig} from './types'
1
+ import type {Any, InitializedClientConfig, SanityDocumentStub} from './types'
2
2
 
3
3
  const VALID_ASSET_TYPES = ['image', 'file']
4
4
  const VALID_INSERT_LOCATIONS = ['before', 'after', 'replace']
@@ -43,6 +43,28 @@ export const requireDocumentId = (op: string, doc: Record<string, Any>) => {
43
43
  validateDocumentId(op, doc._id)
44
44
  }
45
45
 
46
+ export const validateDocumentType = (op: string, type: string) => {
47
+ if (typeof type !== 'string') {
48
+ throw new Error(`\`${op}()\`: \`${type}\` is not a valid document type`)
49
+ }
50
+ }
51
+
52
+ export const requireDocumentType = (op: string, doc: Record<string, Any>) => {
53
+ if (!doc._type) {
54
+ throw new Error(`\`${op}()\` requires that the document contains a type (\`_type\` property)`)
55
+ }
56
+
57
+ validateDocumentType(op, doc._type)
58
+ }
59
+
60
+ export const validateVersionIdMatch = (builtVersionId: string, document: SanityDocumentStub) => {
61
+ if (document._id && document._id !== builtVersionId) {
62
+ throw new Error(
63
+ `The provided document ID (\`${document._id}\`) does not match the generated version ID (\`${builtVersionId}\`)`,
64
+ )
65
+ }
66
+ }
67
+
46
68
  export const validateInsert = (at: string, selector: string, items: Any[]) => {
47
69
  const signature = 'insert(at, selector, items)'
48
70
  if (VALID_INSERT_LOCATIONS.indexOf(at) === -1) {