@sanity/client 4.0.1 → 5.0.0-esm.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.
Files changed (68) hide show
  1. package/dist/index.browser.cjs +1335 -0
  2. package/dist/index.browser.cjs.map +1 -0
  3. package/dist/index.browser.js +1312 -0
  4. package/dist/index.browser.js.map +1 -0
  5. package/dist/index.cjs +1344 -0
  6. package/dist/index.cjs.js +15 -0
  7. package/dist/index.cjs.map +1 -0
  8. package/{sanityClient.d.ts → dist/index.d.ts} +991 -1117
  9. package/dist/index.js +1321 -0
  10. package/dist/index.js.map +1 -0
  11. package/package.json +81 -73
  12. package/src/SanityClient.ts +1261 -0
  13. package/src/assets/AssetsClient.ts +164 -0
  14. package/src/auth/AuthClient.ts +43 -0
  15. package/src/config.ts +95 -0
  16. package/src/data/dataMethods.ts +328 -0
  17. package/src/data/encodeQueryString.ts +28 -0
  18. package/src/data/listen.ts +195 -0
  19. package/src/data/patch.ts +353 -0
  20. package/src/data/transaction.ts +352 -0
  21. package/src/datasets/DatasetsClient.ts +97 -0
  22. package/src/generateHelpUrl.ts +5 -0
  23. package/src/http/browserMiddleware.ts +1 -0
  24. package/src/http/errors.ts +68 -0
  25. package/src/http/nodeMiddleware.ts +11 -0
  26. package/src/http/request.ts +50 -0
  27. package/src/http/requestOptions.ts +31 -0
  28. package/src/index.browser.ts +18 -0
  29. package/src/index.ts +57 -0
  30. package/src/projects/ProjectsClient.ts +45 -0
  31. package/src/types.ts +502 -0
  32. package/src/users/UsersClient.ts +46 -0
  33. package/src/util/defaults.ts +8 -0
  34. package/src/util/getSelection.ts +21 -0
  35. package/src/util/once.ts +12 -0
  36. package/src/util/pick.ts +9 -0
  37. package/src/validators.ts +76 -0
  38. package/src/warnings.ts +25 -0
  39. package/umd/sanityClient.js +5199 -5302
  40. package/umd/sanityClient.min.js +13 -13
  41. package/dist/sanityClient.browser.mjs +0 -2806
  42. package/dist/sanityClient.browser.mjs.map +0 -7
  43. package/index.js +0 -7
  44. package/lib/assets/assetsClient.js +0 -145
  45. package/lib/auth/authClient.js +0 -26
  46. package/lib/config.js +0 -88
  47. package/lib/data/dataMethods.js +0 -205
  48. package/lib/data/encodeQueryString.js +0 -31
  49. package/lib/data/listen.js +0 -164
  50. package/lib/data/patch.js +0 -121
  51. package/lib/data/transaction.js +0 -117
  52. package/lib/datasets/datasetsClient.js +0 -41
  53. package/lib/generateHelpUrl.js +0 -11
  54. package/lib/http/browserMiddleware.js +0 -9
  55. package/lib/http/errors.js +0 -56
  56. package/lib/http/nodeMiddleware.js +0 -22
  57. package/lib/http/queryString.js +0 -17
  58. package/lib/http/request.js +0 -52
  59. package/lib/http/requestOptions.js +0 -30
  60. package/lib/projects/projectsClient.js +0 -25
  61. package/lib/sanityClient.js +0 -118
  62. package/lib/users/usersClient.js +0 -20
  63. package/lib/util/defaults.js +0 -14
  64. package/lib/util/getSelection.js +0 -24
  65. package/lib/util/once.js +0 -20
  66. package/lib/util/pick.js +0 -17
  67. package/lib/validators.js +0 -76
  68. package/lib/warnings.js +0 -27
@@ -0,0 +1,195 @@
1
+ import polyfilledEventSource from '@sanity/eventsource'
2
+ import {Observable} from 'rxjs'
3
+
4
+ import type {ObservableSanityClient, SanityClient} from '../SanityClient'
5
+ import type {ListenEvent, ListenOptions, MutationEvent, QueryParams} from '../types'
6
+ import defaults from '../util/defaults'
7
+ import pick from '../util/pick'
8
+ import encodeQueryString from './encodeQueryString'
9
+
10
+ // Limit is 16K for a _request_, eg including headers. Have to account for an
11
+ // unknown range of headers, but an average EventSource request from Chrome seems
12
+ // to have around 700 bytes of cruft, so let us account for 1.2K to be "safe"
13
+ const MAX_URL_LENGTH = 16000 - 1200
14
+ const EventSource = polyfilledEventSource
15
+
16
+ const possibleOptions = [
17
+ 'includePreviousRevision',
18
+ 'includeResult',
19
+ 'visibility',
20
+ 'effectFormat',
21
+ 'tag',
22
+ ]
23
+
24
+ const defaultOptions = {
25
+ includeResult: true,
26
+ }
27
+
28
+ /**
29
+ * Set up a listener that will be notified when mutations occur on documents matching the provided query/filter.
30
+ *
31
+ * @param query GROQ-filter to listen to changes for
32
+ * @param params Optional query parameters
33
+ * @param options Listener options
34
+ * @internal
35
+ */
36
+ export function _listen<R extends Record<string, any> = Record<string, any>>(
37
+ this: SanityClient,
38
+ query: string,
39
+ params?: QueryParams
40
+ ): Observable<MutationEvent<R>>
41
+ /**
42
+ * Set up a listener that will be notified when mutations occur on documents matching the provided query/filter.
43
+ *
44
+ * @param query GROQ-filter to listen to changes for
45
+ * @param params Optional query parameters
46
+ * @param options Listener options
47
+ * @internal
48
+ */
49
+ export function _listen<R extends Record<string, any> = Record<string, any>>(
50
+ this: SanityClient,
51
+ query: string,
52
+ params?: QueryParams,
53
+ options?: ListenOptions
54
+ ): Observable<ListenEvent<R>>
55
+ /** @internal */
56
+ export function _listen<R extends Record<string, any> = Record<string, any>>(
57
+ this: SanityClient | ObservableSanityClient,
58
+ query: string,
59
+ params?: QueryParams,
60
+ opts: ListenOptions = {}
61
+ ): Observable<MutationEvent<R> | ListenEvent<R>> {
62
+ const {url, token, withCredentials, requestTagPrefix} = this.config()
63
+ const tag = opts.tag && requestTagPrefix ? [requestTagPrefix, opts.tag].join('.') : opts.tag
64
+ const options = {...defaults(opts, defaultOptions), tag}
65
+ const listenOpts = pick(options, possibleOptions)
66
+ const qs = encodeQueryString({query, params, options: {tag, ...listenOpts}})
67
+
68
+ const uri = `${url}${this.getDataUrl('listen', qs)}`
69
+ if (uri.length > MAX_URL_LENGTH) {
70
+ return new Observable((observer) => observer.error(new Error('Query too large for listener')))
71
+ }
72
+
73
+ const listenFor = options.events ? options.events : ['mutation']
74
+ const shouldEmitReconnect = listenFor.indexOf('reconnect') !== -1
75
+
76
+ const esOptions: EventSourceInit & {headers?: Record<string, string>} = {}
77
+ if (token || withCredentials) {
78
+ esOptions.withCredentials = true
79
+ }
80
+
81
+ if (token) {
82
+ esOptions.headers = {
83
+ Authorization: `Bearer ${token}`,
84
+ }
85
+ }
86
+
87
+ return new Observable((observer) => {
88
+ let es = getEventSource()
89
+ let reconnectTimer: NodeJS.Timeout
90
+ let stopped = false
91
+
92
+ function onError() {
93
+ if (stopped) {
94
+ return
95
+ }
96
+
97
+ emitReconnect()
98
+
99
+ // Allow event handlers of `emitReconnect` to cancel/close the reconnect attempt
100
+ if (stopped) {
101
+ return
102
+ }
103
+
104
+ // Unless we've explicitly stopped the ES (in which case `stopped` should be true),
105
+ // we should never be in a disconnected state. By default, EventSource will reconnect
106
+ // automatically, in which case it sets readyState to `CONNECTING`, but in some cases
107
+ // (like when a laptop lid is closed), it closes the connection. In these cases we need
108
+ // to explicitly reconnect.
109
+ if (es.readyState === EventSource.CLOSED) {
110
+ unsubscribe()
111
+ clearTimeout(reconnectTimer)
112
+ reconnectTimer = setTimeout(open, 100)
113
+ }
114
+ }
115
+
116
+ function onChannelError(err: any) {
117
+ observer.error(cooerceError(err))
118
+ }
119
+
120
+ function onMessage(evt: any) {
121
+ const event = parseEvent(evt)
122
+ return event instanceof Error ? observer.error(event) : observer.next(event)
123
+ }
124
+
125
+ function onDisconnect() {
126
+ stopped = true
127
+ unsubscribe()
128
+ observer.complete()
129
+ }
130
+
131
+ function unsubscribe() {
132
+ es.removeEventListener('error', onError, false)
133
+ es.removeEventListener('channelError', onChannelError, false)
134
+ es.removeEventListener('disconnect', onDisconnect, false)
135
+ listenFor.forEach((type: string) => es.removeEventListener(type, onMessage, false))
136
+ es.close()
137
+ }
138
+
139
+ function emitReconnect() {
140
+ if (shouldEmitReconnect) {
141
+ observer.next({type: 'reconnect'})
142
+ }
143
+ }
144
+
145
+ function getEventSource() {
146
+ const evs = new EventSource(uri, esOptions)
147
+ evs.addEventListener('error', onError, false)
148
+ evs.addEventListener('channelError', onChannelError, false)
149
+ evs.addEventListener('disconnect', onDisconnect, false)
150
+ listenFor.forEach((type: string) => evs.addEventListener(type, onMessage, false))
151
+ return evs
152
+ }
153
+
154
+ function open() {
155
+ es = getEventSource()
156
+ }
157
+
158
+ function stop() {
159
+ stopped = true
160
+ unsubscribe()
161
+ }
162
+
163
+ return stop
164
+ })
165
+ }
166
+
167
+ function parseEvent(event: any) {
168
+ try {
169
+ const data = (event.data && JSON.parse(event.data)) || {}
170
+ return Object.assign({type: event.type}, data)
171
+ } catch (err) {
172
+ return err
173
+ }
174
+ }
175
+
176
+ function cooerceError(err: any) {
177
+ if (err instanceof Error) {
178
+ return err
179
+ }
180
+
181
+ const evt = parseEvent(err)
182
+ return evt instanceof Error ? evt : new Error(extractErrorMessage(evt))
183
+ }
184
+
185
+ function extractErrorMessage(err: any) {
186
+ if (!err.error) {
187
+ return err.message || 'Unknown listener error'
188
+ }
189
+
190
+ if (err.error.description) {
191
+ return err.error.description
192
+ }
193
+
194
+ return typeof err.error === 'string' ? err.error : JSON.stringify(err.error, null, 2)
195
+ }
@@ -0,0 +1,353 @@
1
+ import {type Observable} from 'rxjs'
2
+
3
+ import type {ObservableSanityClient, SanityClient} from '../SanityClient'
4
+ import type {
5
+ AllDocumentIdsMutationOptions,
6
+ AllDocumentsMutationOptions,
7
+ AttributeSet,
8
+ BaseMutationOptions,
9
+ FirstDocumentIdMutationOptions,
10
+ FirstDocumentMutationOptions,
11
+ MultipleMutationResult,
12
+ PatchMutationOperation,
13
+ PatchOperations,
14
+ PatchSelection,
15
+ SanityDocument,
16
+ SingleMutationResult,
17
+ } from '../types'
18
+ import getSelection from '../util/getSelection'
19
+ import {validateInsert, validateObject} from '../validators'
20
+
21
+ export class BasePatch {
22
+ protected selection: PatchSelection
23
+ protected operations: PatchOperations
24
+ constructor(selection: PatchSelection, operations: PatchOperations = {}) {
25
+ this.selection = selection
26
+ this.operations = operations
27
+ }
28
+
29
+ /**
30
+ * DEPRECATED: Don't use.
31
+ * The operation is added to the current patch, ready to be commited by `commit()`
32
+ *
33
+ * @deprecated
34
+ * @param attrs Attributes to replace
35
+ */
36
+ replace(attrs: AttributeSet): this {
37
+ validateObject('replace', attrs)
38
+ return this._set('set', {$: attrs})
39
+ }
40
+
41
+ /**
42
+ * Sets the given attributes to the document. Does NOT merge objects.
43
+ * The operation is added to the current patch, ready to be commited by `commit()`
44
+ *
45
+ * @param attrs Attributes to set. To set a deep attribute, use JSONMatch, eg: {"nested.prop": "value"}
46
+ */
47
+ set(attrs: AttributeSet): this {
48
+ return this._assign('set', attrs)
49
+ }
50
+
51
+ /**
52
+ * Sets the given attributes to the document if they are not currently set. Does NOT merge objects.
53
+ * The operation is added to the current patch, ready to be commited by `commit()`
54
+ *
55
+ * @param attrs Attributes to set. To set a deep attribute, use JSONMatch, eg: {"nested.prop": "value"}
56
+ */
57
+ setIfMissing(attrs: AttributeSet): this {
58
+ return this._assign('setIfMissing', attrs)
59
+ }
60
+
61
+ /**
62
+ * Performs a "diff-match-patch" operation on the string attributes provided.
63
+ * The operation is added to the current patch, ready to be commited by `commit()`
64
+ *
65
+ * @param attrs Attributes to perform operation on. To set a deep attribute, use JSONMatch, eg: {"nested.prop": "dmp"}
66
+ */
67
+ diffMatchPatch(attrs: AttributeSet): this {
68
+ validateObject('diffMatchPatch', attrs)
69
+ return this._assign('diffMatchPatch', attrs)
70
+ }
71
+
72
+ /**
73
+ * Unsets the attribute paths provided.
74
+ * The operation is added to the current patch, ready to be commited by `commit()`
75
+ *
76
+ * @param attrs Attribute paths to unset.
77
+ */
78
+ unset(attrs: string[]): this {
79
+ if (!Array.isArray(attrs)) {
80
+ throw new Error('unset(attrs) takes an array of attributes to unset, non-array given')
81
+ }
82
+
83
+ this.operations = Object.assign({}, this.operations, {unset: attrs})
84
+ return this
85
+ }
86
+
87
+ /**
88
+ * Increment a numeric value. Each entry in the argument is either an attribute or a JSON path. The value may be a positive or negative integer or floating-point value. The operation will fail if target value is not a numeric value, or doesn't exist.
89
+ *
90
+ * @param attrs Object of attribute paths to increment, values representing the number to increment by.
91
+ */
92
+ inc(attrs: {[key: string]: number}): this {
93
+ return this._assign('inc', attrs)
94
+ }
95
+
96
+ /**
97
+ * Decrement a numeric value. Each entry in the argument is either an attribute or a JSON path. The value may be a positive or negative integer or floating-point value. The operation will fail if target value is not a numeric value, or doesn't exist.
98
+ *
99
+ * @param attrs Object of attribute paths to decrement, values representing the number to decrement by.
100
+ */
101
+ dec(attrs: {[key: string]: number}): this {
102
+ return this._assign('dec', attrs)
103
+ }
104
+
105
+ /**
106
+ * Provides methods for modifying arrays, by inserting, appending and replacing elements via a JSONPath expression.
107
+ *
108
+ * @param at Location to insert at, relative to the given selector, or 'replace' the matched path
109
+ * @param selector JSONPath expression, eg `comments[-1]` or `blocks[_key=="abc123"]`
110
+ * @param items Array of items to insert/replace
111
+ */
112
+ insert(at: 'before' | 'after' | 'replace', selector: string, items: any[]): this {
113
+ validateInsert(at, selector, items)
114
+ return this._assign('insert', {[at]: selector, items})
115
+ }
116
+
117
+ /**
118
+ * Append the given items to the array at the given JSONPath
119
+ *
120
+ * @param selector Attribute/path to append to, eg `comments` or `person.hobbies`
121
+ * @param items Array of items to append to the array
122
+ */
123
+ append(selector: string, items: any[]): this {
124
+ return this.insert('after', `${selector}[-1]`, items)
125
+ }
126
+
127
+ /**
128
+ * Prepend the given items to the array at the given JSONPath
129
+ *
130
+ * @param selector Attribute/path to prepend to, eg `comments` or `person.hobbies`
131
+ * @param items Array of items to prepend to the array
132
+ */
133
+ prepend(selector: string, items: any[]): this {
134
+ return this.insert('before', `${selector}[0]`, items)
135
+ }
136
+
137
+ /**
138
+ * Change the contents of an array by removing existing elements and/or adding new elements.
139
+ *
140
+ * @param selector Attribute or JSONPath expression for array
141
+ * @param start Index at which to start changing the array (with origin 0). If greater than the length of the array, actual starting index will be set to the length of the array. If negative, will begin that many elements from the end of the array (with origin -1) and will be set to 0 if absolute value is greater than the length of the array.x
142
+ * @param deleteCount An integer indicating the number of old array elements to remove.
143
+ * @param items The elements to add to the array, beginning at the start index. If you don't specify any elements, splice() will only remove elements from the array.
144
+ */
145
+ splice(selector: string, start: number, deleteCount: number, items: any[]): this {
146
+ // Negative indexes doesn't mean the same in Sanity as they do in JS;
147
+ // -1 means "actually at the end of the array", which allows inserting
148
+ // at the end of the array without knowing its length. We therefore have
149
+ // to substract negative indexes by one to match JS. If you want Sanity-
150
+ // behaviour, just use `insert('replace', selector, items)` directly
151
+ const delAll = typeof deleteCount === 'undefined' || deleteCount === -1
152
+ const startIndex = start < 0 ? start - 1 : start
153
+ const delCount = delAll ? -1 : Math.max(0, start + deleteCount)
154
+ const delRange = startIndex < 0 && delCount >= 0 ? '' : delCount
155
+ const rangeSelector = `${selector}[${startIndex}:${delRange}]`
156
+ return this.insert('replace', rangeSelector, items || [])
157
+ }
158
+
159
+ /**
160
+ * Adds a revision clause, preventing the document from being patched if the `_rev` property does not match the given value
161
+ *
162
+ * @param rev Revision to lock the patch to
163
+ */
164
+ ifRevisionId(rev: string): this {
165
+ this.operations.ifRevisionID = rev
166
+ return this
167
+ }
168
+
169
+ /**
170
+ * Return a plain JSON representation of the patch
171
+ */
172
+ serialize(): PatchMutationOperation {
173
+ return {...getSelection(this.selection), ...this.operations}
174
+ }
175
+
176
+ /**
177
+ * Return a plain JSON representation of the patch
178
+ */
179
+ toJSON(): PatchMutationOperation {
180
+ return this.serialize()
181
+ }
182
+
183
+ /**
184
+ * Clears the patch of all operations
185
+ */
186
+ reset(): this {
187
+ this.operations = {}
188
+ return this
189
+ }
190
+
191
+ protected _assign(op: keyof PatchOperations, props: any, merge = true): this {
192
+ validateObject(op, props)
193
+ this.operations = Object.assign({}, this.operations, {
194
+ [op]: Object.assign({}, (merge && this.operations[op]) || {}, props),
195
+ })
196
+ return this
197
+ }
198
+
199
+ protected _set(op: keyof PatchOperations, props: any): this {
200
+ return this._assign(op, props, false)
201
+ }
202
+ }
203
+
204
+ export class ObservablePatch extends BasePatch {
205
+ #client?: ObservableSanityClient
206
+
207
+ constructor(
208
+ selection: PatchSelection,
209
+ operations?: PatchOperations,
210
+ client?: ObservableSanityClient
211
+ ) {
212
+ super(selection, operations)
213
+ this.#client = client
214
+ }
215
+
216
+ /**
217
+ * Clones the patch
218
+ */
219
+ clone(): ObservablePatch {
220
+ return new ObservablePatch(this.selection, {...this.operations}, this.#client)
221
+ }
222
+
223
+ /**
224
+ * Commit the patch, returning an observable that produces the first patched document
225
+ *
226
+ * @param options Options for the mutation operation
227
+ */
228
+ commit<R extends Record<string, any> = Record<string, any>>(
229
+ options: FirstDocumentMutationOptions
230
+ ): Observable<SanityDocument<R>>
231
+ /**
232
+ * Commit the patch, returning an observable that produces an array of the mutated documents
233
+ *
234
+ * @param options Options for the mutation operation
235
+ */
236
+ commit<R extends Record<string, any> = Record<string, any>>(
237
+ options: AllDocumentsMutationOptions
238
+ ): Observable<SanityDocument<R>[]>
239
+ /**
240
+ * Commit the patch, returning an observable that produces a mutation result object
241
+ *
242
+ * @param options Options for the mutation operation
243
+ */
244
+ commit(options: FirstDocumentIdMutationOptions): Observable<SingleMutationResult>
245
+ /**
246
+ * Commit the patch, returning an observable that produces a mutation result object
247
+ *
248
+ * @param options Options for the mutation operation
249
+ */
250
+ commit(options: AllDocumentIdsMutationOptions): Observable<MultipleMutationResult>
251
+ /**
252
+ * Commit the patch, returning an observable that produces the first patched document
253
+ *
254
+ * @param options Options for the mutation operation
255
+ */
256
+ commit<R extends Record<string, any> = Record<string, any>>(
257
+ options?: BaseMutationOptions
258
+ ): Observable<SanityDocument<R>>
259
+ commit<R extends Record<string, any> = Record<string, any>>(
260
+ options?:
261
+ | FirstDocumentMutationOptions
262
+ | AllDocumentsMutationOptions
263
+ | FirstDocumentIdMutationOptions
264
+ | AllDocumentIdsMutationOptions
265
+ | BaseMutationOptions
266
+ ): Observable<
267
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
268
+ > {
269
+ if (!this.#client) {
270
+ throw new Error(
271
+ 'No `client` passed to patch, either provide one or pass the ' +
272
+ 'patch to a clients `mutate()` method'
273
+ )
274
+ }
275
+
276
+ const returnFirst = typeof this.selection === 'string'
277
+ const opts = Object.assign({returnFirst, returnDocuments: true}, options)
278
+ return this.#client.mutate<R>({patch: this.serialize()} as any, opts)
279
+ }
280
+ }
281
+
282
+ export class Patch extends BasePatch {
283
+ #client?: SanityClient
284
+ constructor(selection: PatchSelection, operations?: PatchOperations, client?: SanityClient) {
285
+ super(selection, operations)
286
+ this.#client = client
287
+ }
288
+
289
+ /**
290
+ * Clones the patch
291
+ */
292
+ clone(): Patch {
293
+ return new Patch(this.selection, {...this.operations}, this.#client)
294
+ }
295
+
296
+ /**
297
+ * Commit the patch, returning a promise that resolves to the first patched document
298
+ *
299
+ * @param options Options for the mutation operation
300
+ */
301
+ commit<R extends Record<string, any> = Record<string, any>>(
302
+ options: FirstDocumentMutationOptions
303
+ ): Promise<SanityDocument<R>>
304
+ /**
305
+ * Commit the patch, returning a promise that resolves to an array of the mutated documents
306
+ *
307
+ * @param options Options for the mutation operation
308
+ */
309
+ commit<R extends Record<string, any> = Record<string, any>>(
310
+ options: AllDocumentsMutationOptions
311
+ ): Promise<SanityDocument<R>[]>
312
+ /**
313
+ * Commit the patch, returning a promise that resolves to a mutation result object
314
+ *
315
+ * @param options Options for the mutation operation
316
+ */
317
+ commit(options: FirstDocumentIdMutationOptions): Promise<SingleMutationResult>
318
+ /**
319
+ * Commit the patch, returning a promise that resolves to a mutation result object
320
+ *
321
+ * @param options Options for the mutation operation
322
+ */
323
+ commit(options: AllDocumentIdsMutationOptions): Promise<MultipleMutationResult>
324
+ /**
325
+ * Commit the patch, returning a promise that resolves to the first patched document
326
+ *
327
+ * @param options Options for the mutation operation
328
+ */
329
+ commit<R extends Record<string, any> = Record<string, any>>(
330
+ options?: BaseMutationOptions
331
+ ): Promise<SanityDocument<R>>
332
+ commit<R extends Record<string, any> = Record<string, any>>(
333
+ options?:
334
+ | FirstDocumentMutationOptions
335
+ | AllDocumentsMutationOptions
336
+ | FirstDocumentIdMutationOptions
337
+ | AllDocumentIdsMutationOptions
338
+ | BaseMutationOptions
339
+ ): Promise<
340
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
341
+ > {
342
+ if (!this.#client) {
343
+ throw new Error(
344
+ 'No `client` passed to patch, either provide one or pass the ' +
345
+ 'patch to a clients `mutate()` method'
346
+ )
347
+ }
348
+
349
+ const returnFirst = typeof this.selection === 'string'
350
+ const opts = Object.assign({returnFirst, returnDocuments: true}, options)
351
+ return this.#client.mutate<R>({patch: this.serialize()} as any, opts)
352
+ }
353
+ }