@sanity/client 6.18.3 → 6.19.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/client",
3
- "version": "6.18.3",
3
+ "version": "6.19.1",
4
4
  "description": "Client for retrieving, creating and patching data from Sanity.io",
5
5
  "keywords": [
6
6
  "sanity",
@@ -129,8 +129,8 @@
129
129
  "@sanity/pkg-utils": "^6.8.18",
130
130
  "@types/json-diff": "^1.0.3",
131
131
  "@types/node": "^20.8.8",
132
- "@typescript-eslint/eslint-plugin": "^7.10.0",
133
- "@typescript-eslint/parser": "^7.10.0",
132
+ "@typescript-eslint/eslint-plugin": "^7.11.0",
133
+ "@typescript-eslint/parser": "^7.11.0",
134
134
  "@vercel/stega": "0.1.2",
135
135
  "@vitest/coverage-v8": "1.6.0",
136
136
  "eslint": "^8.57.0",
@@ -10,9 +10,11 @@ import {ObservableTransaction, Transaction} from './data/transaction'
10
10
  import {DatasetsClient, ObservableDatasetsClient} from './datasets/DatasetsClient'
11
11
  import {ObservableProjectsClient, ProjectsClient} from './projects/ProjectsClient'
12
12
  import type {
13
+ Action,
13
14
  AllDocumentIdsMutationOptions,
14
15
  AllDocumentsMutationOptions,
15
16
  Any,
17
+ BaseActionOptions,
16
18
  BaseMutationOptions,
17
19
  ClientConfig,
18
20
  FilteredResponseQueryOptions,
@@ -21,6 +23,7 @@ import type {
21
23
  HttpRequest,
22
24
  IdentifiedSanityDocumentStub,
23
25
  InitializedClientConfig,
26
+ MultipleActionResult,
24
27
  MultipleMutationResult,
25
28
  Mutation,
26
29
  MutationSelection,
@@ -34,6 +37,7 @@ import type {
34
37
  RawRequestOptions,
35
38
  SanityDocument,
36
39
  SanityDocumentStub,
40
+ SingleActionResult,
37
41
  SingleMutationResult,
38
42
  UnfilteredResponseQueryOptions,
39
43
  UnfilteredResponseWithoutQuery,
@@ -665,6 +669,19 @@ export class ObservableSanityClient {
665
669
  return new ObservableTransaction(operations, this)
666
670
  }
667
671
 
672
+ /**
673
+ * Perform action operations against the configured dataset
674
+ *
675
+ * @param operations - Action operation(s) to execute
676
+ * @param options - Action options
677
+ */
678
+ action(
679
+ operations: Action | Action[],
680
+ options?: BaseActionOptions,
681
+ ): Observable<SingleActionResult | MultipleActionResult> {
682
+ return dataMethods._action(this, this.#httpRequest, operations, options)
683
+ }
684
+
668
685
  /**
669
686
  * Perform an HTTP request against the Sanity API
670
687
  *
@@ -1322,6 +1339,20 @@ export class SanityClient {
1322
1339
  return new Transaction(operations, this)
1323
1340
  }
1324
1341
 
1342
+ /**
1343
+ * Perform action operations against the configured dataset
1344
+ * Returns a promise that resolves to the transaction result
1345
+ *
1346
+ * @param operations - Action operation(s) to execute
1347
+ * @param options - Action options
1348
+ */
1349
+ action(
1350
+ operations: Action | Action[],
1351
+ options?: BaseActionOptions,
1352
+ ): Promise<SingleActionResult | MultipleActionResult> {
1353
+ return lastValueFrom(dataMethods._action(this, this.#httpRequest, operations, options))
1354
+ }
1355
+
1325
1356
  /**
1326
1357
  * Perform a request against the Sanity API
1327
1358
  * NOTE: Only use this for Sanity API endpoints, not for your own APIs!
@@ -6,9 +6,11 @@ import {requestOptions} from '../http/requestOptions'
6
6
  import type {ObservableSanityClient, SanityClient} from '../SanityClient'
7
7
  import {stegaClean} from '../stega/stegaClean'
8
8
  import type {
9
+ Action,
9
10
  AllDocumentIdsMutationOptions,
10
11
  AllDocumentsMutationOptions,
11
12
  Any,
13
+ BaseActionOptions,
12
14
  BaseMutationOptions,
13
15
  FirstDocumentIdMutationOptions,
14
16
  FirstDocumentMutationOptions,
@@ -16,6 +18,7 @@ import type {
16
18
  HttpRequestEvent,
17
19
  IdentifiedSanityDocumentStub,
18
20
  InitializedStegaConfig,
21
+ MultipleActionResult,
19
22
  MultipleMutationResult,
20
23
  Mutation,
21
24
  MutationSelection,
@@ -24,6 +27,7 @@ import type {
24
27
  RequestObservableOptions,
25
28
  RequestOptions,
26
29
  SanityDocument,
30
+ SingleActionResult,
27
31
  SingleMutationResult,
28
32
  } from '../types'
29
33
  import {getSelection} from '../util/getSelection'
@@ -238,6 +242,30 @@ export function _mutate<R extends Record<string, Any>>(
238
242
  return _dataRequest(client, httpRequest, 'mutate', {mutations: muts, transactionId}, options)
239
243
  }
240
244
 
245
+ /**
246
+ * @internal
247
+ */
248
+ export function _action(
249
+ client: SanityClient | ObservableSanityClient,
250
+ httpRequest: HttpRequest,
251
+ actions: Action | Action[],
252
+ options?: BaseActionOptions,
253
+ ): Observable<SingleActionResult | MultipleActionResult> {
254
+ const acts = Array.isArray(actions) ? actions : [actions]
255
+ const transactionId = (options && options.transactionId) || undefined
256
+ const skipCrossDatasetReferenceValidation =
257
+ (options && options.skipCrossDatasetReferenceValidation) || undefined
258
+ const dryRun = (options && options.dryRun) || undefined
259
+
260
+ return _dataRequest(
261
+ client,
262
+ httpRequest,
263
+ 'actions',
264
+ {actions: acts, transactionId, skipCrossDatasetReferenceValidation, dryRun},
265
+ options,
266
+ )
267
+ }
268
+
241
269
  /**
242
270
  * @internal
243
271
  */
@@ -249,12 +277,13 @@ export function _dataRequest(
249
277
  options: Any = {},
250
278
  ): Any {
251
279
  const isMutation = endpoint === 'mutate'
280
+ const isAction = endpoint === 'actions'
252
281
  const isQuery = endpoint === 'query'
253
282
 
254
283
  // Check if the query string is within a configured threshold,
255
284
  // in which case we can use GET. Otherwise, use POST.
256
- const strQuery = isMutation ? '' : encodeQueryString(body)
257
- const useGet = !isMutation && strQuery.length < getQuerySizeLimit
285
+ const strQuery = isMutation || isAction ? '' : encodeQueryString(body)
286
+ const useGet = !isMutation && !isAction && strQuery.length < getQuerySizeLimit
258
287
  const stringQuery = useGet ? strQuery : ''
259
288
  const returnFirst = options.returnFirst
260
289
  const {timeout, token, tag, headers, returnQuery, lastLiveEventId} = options
@@ -1,4 +1,4 @@
1
- import type {Any, ErrorProps, MutationError} from '../types'
1
+ import type {ActionError, Any, ErrorProps, MutationError} from '../types'
2
2
 
3
3
  const MAX_ITEMS_IN_ERROR_MESSAGE = 5
4
4
 
@@ -47,7 +47,7 @@ function extractErrorProps(res: Any): ErrorProps {
47
47
  }
48
48
 
49
49
  // Mutation errors (specifically)
50
- if (isMutationError(body)) {
50
+ if (isMutationError(body) || isActionError(body)) {
51
51
  const allItems = body.error.items || []
52
52
  const items = allItems
53
53
  .slice(0, MAX_ITEMS_IN_ERROR_MESSAGE)
@@ -83,6 +83,15 @@ function isMutationError(body: Any): body is MutationError {
83
83
  )
84
84
  }
85
85
 
86
+ function isActionError(body: Any): body is ActionError {
87
+ return (
88
+ isPlainObject(body) &&
89
+ isPlainObject(body.error) &&
90
+ body.error.type === 'actionError' &&
91
+ typeof body.error.description === 'string'
92
+ )
93
+ }
94
+
86
95
  function isPlainObject(obj: Any): obj is Record<string, unknown> {
87
96
  return typeof obj === 'object' && obj !== null && !Array.isArray(obj)
88
97
  }
package/src/types.ts CHANGED
@@ -511,6 +511,188 @@ export type Mutation<R extends Record<string, Any> = Record<string, Any>> =
511
511
  | {delete: MutationSelection}
512
512
  | {patch: PatchMutationOperation}
513
513
 
514
+ /** @public */
515
+ export type Action =
516
+ | CreateAction
517
+ | ReplaceDraftAction
518
+ | EditAction
519
+ | DeleteAction
520
+ | DiscardAction
521
+ | PublishAction
522
+ | UnpublishAction
523
+
524
+ /**
525
+ * Creates a new draft document. The published version of the document must not already exist.
526
+ * If the draft version of the document already exists the action will fail by default, but
527
+ * this can be adjusted to instead leave the existing document in place.
528
+ *
529
+ * @public
530
+ */
531
+ export type CreateAction = {
532
+ actionType: 'sanity.action.document.create'
533
+
534
+ /**
535
+ * ID of the published document to create a draft for.
536
+ */
537
+ publishedId: string
538
+
539
+ /**
540
+ * Document to create. Requires a `_type` property.
541
+ */
542
+ attributes: IdentifiedSanityDocumentStub
543
+
544
+ /**
545
+ * ifExists controls what to do if the draft already exists
546
+ */
547
+ ifExists: 'fail' | 'ignore'
548
+ }
549
+
550
+ /**
551
+ * Replaces an existing draft document.
552
+ * At least one of the draft or published versions of the document must exist.
553
+ *
554
+ * @public
555
+ */
556
+ export type ReplaceDraftAction = {
557
+ actionType: 'sanity.action.document.replaceDraft'
558
+
559
+ /**
560
+ * Published document ID to create draft from, if draft does not exist
561
+ */
562
+ publishedId: string
563
+
564
+ /**
565
+ * Document to create if it does not already exist. Requires `_id` and `_type` properties.
566
+ */
567
+ attributes: IdentifiedSanityDocumentStub
568
+ }
569
+
570
+ /**
571
+ * Modifies an existing draft document.
572
+ * It applies the given patch to the document referenced by draftId.
573
+ * If there is no such document then one is created using the current state of the published version and then that is updated accordingly.
574
+ *
575
+ * @public
576
+ */
577
+ export type EditAction = {
578
+ actionType: 'sanity.action.document.edit'
579
+
580
+ /**
581
+ * Draft document ID to edit
582
+ */
583
+ draftId: string
584
+
585
+ /**
586
+ * Published document ID to create draft from, if draft does not exist
587
+ */
588
+ publishedId: string
589
+
590
+ /**
591
+ * Patch operations to apply
592
+ */
593
+ patch: PatchOperations
594
+ }
595
+
596
+ /**
597
+ * Deletes the published version of a document and optionally some (likely all known) draft versions.
598
+ * If any draft version exists that is not specified for deletion this is an error.
599
+ * If the purge flag is set then the document history is also deleted.
600
+ *
601
+ * @public
602
+ */
603
+ export type DeleteAction = {
604
+ actionType: 'sanity.action.document.delete'
605
+
606
+ /**
607
+ * Published document ID to delete
608
+ */
609
+ publishedId: string
610
+
611
+ /**
612
+ * Draft document ID to delete
613
+ */
614
+ includeDrafts: string[]
615
+
616
+ /**
617
+ * Delete document history
618
+ */
619
+ purge?: boolean
620
+ }
621
+
622
+ /**
623
+ * Delete the draft version of a document.
624
+ * It is an error if it does not exist. If the purge flag is set, the document history is also deleted.
625
+ *
626
+ * @public
627
+ */
628
+ export type DiscardAction = {
629
+ actionType: 'sanity.action.document.discard'
630
+
631
+ /**
632
+ * Draft document ID to delete
633
+ */
634
+ draftId: string
635
+
636
+ /**
637
+ * Delete document history
638
+ */
639
+ purge?: boolean
640
+ }
641
+
642
+ /**
643
+ * Publishes a draft document.
644
+ * If a published version of the document already exists this is replaced by the current draft document.
645
+ * In either case the draft document is deleted.
646
+ * The optional revision id parameters can be used for optimistic locking to ensure
647
+ * that the draft and/or published versions of the document have not been changed by another client.
648
+ *
649
+ * @public
650
+ */
651
+ export type PublishAction = {
652
+ actionType: 'sanity.action.document.publish'
653
+
654
+ /**
655
+ * Draft document ID to publish
656
+ */
657
+ draftId: string
658
+
659
+ /**
660
+ * Draft revision ID to match
661
+ */
662
+ ifDraftRevisionId?: string
663
+
664
+ /**
665
+ * Published document ID to replace
666
+ */
667
+ publishedId: string
668
+
669
+ /**
670
+ * Published revision ID to match
671
+ */
672
+ ifPublishedRevisionId?: string
673
+ }
674
+
675
+ /**
676
+ * Retract a published document.
677
+ * If there is no draft version then this is created from the published version.
678
+ * In either case the published version is deleted.
679
+ *
680
+ * @public
681
+ */
682
+ export type UnpublishAction = {
683
+ actionType: 'sanity.action.document.unpublish'
684
+
685
+ /**
686
+ * Draft document ID to replace the published document with
687
+ */
688
+ draftId: string
689
+
690
+ /**
691
+ * Published document ID to delete
692
+ */
693
+ publishedId: string
694
+ }
695
+
514
696
  /**
515
697
  * A mutation was performed. Note that when updating multiple documents in a transaction,
516
698
  * each document affected will get a separate mutation event.
@@ -884,6 +1066,23 @@ export type TransactionMutationOptions =
884
1066
  | TransactionAllDocumentsMutationOptions
885
1067
  | TransactionAllDocumentIdsMutationOptions
886
1068
 
1069
+ /** @internal */
1070
+ export type BaseActionOptions = RequestOptions & {
1071
+ transactionId?: string
1072
+ skipCrossDatasetReferenceValidation?: boolean
1073
+ dryRun?: boolean
1074
+ }
1075
+
1076
+ /** @internal */
1077
+ export interface SingleActionResult {
1078
+ transactionId: string
1079
+ }
1080
+
1081
+ /** @internal */
1082
+ export interface MultipleActionResult {
1083
+ transactionId: string
1084
+ }
1085
+
887
1086
  /** @internal */
888
1087
  export interface RawRequestOptions {
889
1088
  url?: string
@@ -927,6 +1126,25 @@ export interface MutationErrorItem {
927
1126
  }
928
1127
  }
929
1128
 
1129
+ /** @internal */
1130
+ export interface ActionError {
1131
+ error: {
1132
+ type: 'actionError'
1133
+ description: string
1134
+ items?: ActionErrorItem[]
1135
+ }
1136
+ }
1137
+
1138
+ /** @internal */
1139
+ export interface ActionErrorItem {
1140
+ error: {
1141
+ type: string
1142
+ description: string
1143
+ value?: unknown
1144
+ }
1145
+ index: number
1146
+ }
1147
+
930
1148
  /**
931
1149
  * DocumentValueSource is a path to a value within a document
932
1150
  * @public
@@ -1839,7 +1839,7 @@
1839
1839
  };
1840
1840
  if (body.error && body.message)
1841
1841
  return props.message = `${body.error} - ${body.message}`, props;
1842
- if (isMutationError(body)) {
1842
+ if (isMutationError(body) || isActionError(body)) {
1843
1843
  const allItems = body.error.items || [], items = allItems.slice(0, 5).map((item) => {
1844
1844
  var _a;
1845
1845
  return (_a = item.error) == null ? void 0 : _a.description;
@@ -1855,6 +1855,9 @@
1855
1855
  function isMutationError(body) {
1856
1856
  return isPlainObject(body) && isPlainObject(body.error) && body.error.type === "mutationError" && typeof body.error.description == "string";
1857
1857
  }
1858
+ function isActionError(body) {
1859
+ return isPlainObject(body) && isPlainObject(body.error) && body.error.type === "actionError" && typeof body.error.description == "string";
1860
+ }
1858
1861
  function isPlainObject(obj) {
1859
1862
  return typeof obj == "object" && obj !== null && !Array.isArray(obj);
1860
1863
  }
@@ -2486,8 +2489,18 @@ ${selectionOpts}`);
2486
2489
  const muts = Array.isArray(mut) ? mut : [mut], transactionId = options && options.transactionId || void 0;
2487
2490
  return _dataRequest(client, httpRequest, "mutate", { mutations: muts, transactionId }, options);
2488
2491
  }
2492
+ function _action(client, httpRequest, actions, options) {
2493
+ const acts = Array.isArray(actions) ? actions : [actions], transactionId = options && options.transactionId || void 0, skipCrossDatasetReferenceValidation = options && options.skipCrossDatasetReferenceValidation || void 0, dryRun = options && options.dryRun || void 0;
2494
+ return _dataRequest(
2495
+ client,
2496
+ httpRequest,
2497
+ "actions",
2498
+ { actions: acts, transactionId, skipCrossDatasetReferenceValidation, dryRun },
2499
+ options
2500
+ );
2501
+ }
2489
2502
  function _dataRequest(client, httpRequest, endpoint, body, options = {}) {
2490
- const isMutation = endpoint === "mutate", isQuery = endpoint === "query", strQuery = isMutation ? "" : encodeQueryString(body), useGet = !isMutation && strQuery.length < getQuerySizeLimit, stringQuery = useGet ? strQuery : "", returnFirst = options.returnFirst, { timeout, token, tag, headers, returnQuery, lastLiveEventId } = options, uri = _getDataUrl(client, endpoint, stringQuery), reqOptions = {
2503
+ const isMutation = endpoint === "mutate", isAction = endpoint === "actions", isQuery = endpoint === "query", strQuery = isMutation || isAction ? "" : encodeQueryString(body), useGet = !isMutation && !isAction && strQuery.length < getQuerySizeLimit, stringQuery = useGet ? strQuery : "", returnFirst = options.returnFirst, { timeout, token, tag, headers, returnQuery, lastLiveEventId } = options, uri = _getDataUrl(client, endpoint, stringQuery), reqOptions = {
2491
2504
  method: useGet ? "GET" : "POST",
2492
2505
  uri,
2493
2506
  json: !0,
@@ -3099,6 +3112,15 @@ ${selectionOpts}`);
3099
3112
  transaction(operations) {
3100
3113
  return new ObservableTransaction(operations, this);
3101
3114
  }
3115
+ /**
3116
+ * Perform action operations against the configured dataset
3117
+ *
3118
+ * @param operations - Action operation(s) to execute
3119
+ * @param options - Action options
3120
+ */
3121
+ action(operations, options) {
3122
+ return _action(this, __privateGet(this, _httpRequest), operations, options);
3123
+ }
3102
3124
  /**
3103
3125
  * Perform an HTTP request against the Sanity API
3104
3126
  *
@@ -3236,6 +3258,16 @@ ${selectionOpts}`);
3236
3258
  transaction(operations) {
3237
3259
  return new Transaction(operations, this);
3238
3260
  }
3261
+ /**
3262
+ * Perform action operations against the configured dataset
3263
+ * Returns a promise that resolves to the transaction result
3264
+ *
3265
+ * @param operations - Action operation(s) to execute
3266
+ * @param options - Action options
3267
+ */
3268
+ action(operations, options) {
3269
+ return lastValueFrom(_action(this, __privateGet(this, _httpRequest2), operations, options));
3270
+ }
3239
3271
  /**
3240
3272
  * Perform a request against the Sanity API
3241
3273
  * NOTE: Only use this for Sanity API endpoints, not for your own APIs!