@sanity/client 7.26.2 → 8.1.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 (133) hide show
  1. package/README.md +63 -341
  2. package/dist/browserUpload-2tz6Sdqp.js +87 -0
  3. package/dist/browserUpload-2tz6Sdqp.js.map +1 -0
  4. package/dist/browserUpload-CwpNx7Vl.js +87 -0
  5. package/dist/browserUpload-CwpNx7Vl.js.map +1 -0
  6. package/dist/config-3wiPP-sZ.js +126 -0
  7. package/dist/config-3wiPP-sZ.js.map +1 -0
  8. package/dist/csm.d.ts +122 -335
  9. package/dist/csm.js +79 -91
  10. package/dist/csm.js.map +1 -1
  11. package/dist/dist-C9ExSk2R.js +73 -0
  12. package/dist/dist-C9ExSk2R.js.map +1 -0
  13. package/dist/dist-Z8cIRxoB.js +69 -0
  14. package/dist/dist-Z8cIRxoB.js.map +1 -0
  15. package/dist/index.d.ts +112 -6722
  16. package/dist/index.js +2950 -2848
  17. package/dist/index.js.map +1 -1
  18. package/dist/{index.d.cts → index.node.d.ts} +4529 -5322
  19. package/dist/index.node.js +3264 -0
  20. package/dist/index.node.js.map +1 -0
  21. package/dist/isRecord-Kfmt-nk-.js +7 -0
  22. package/dist/isRecord-Kfmt-nk-.js.map +1 -0
  23. package/dist/media-library.d.ts +9 -100
  24. package/dist/media-library.js +36 -14
  25. package/dist/media-library.js.map +1 -1
  26. package/dist/request-BhMuKj0D.js +373 -0
  27. package/dist/request-BhMuKj0D.js.map +1 -0
  28. package/dist/request-SnMg7nUX.js +355 -0
  29. package/dist/request-SnMg7nUX.js.map +1 -0
  30. package/dist/resolveEditInfo-Cz-smq3a.js +339 -0
  31. package/dist/resolveEditInfo-Cz-smq3a.js.map +1 -0
  32. package/dist/rolldown-runtime-vyAXikos.js +9 -0
  33. package/dist/stega.d.ts +78 -7137
  34. package/dist/stega.js +27 -20
  35. package/dist/stega.js.map +1 -1
  36. package/dist/stegaClean-C18wLWau.js +21 -0
  37. package/dist/stegaClean-C18wLWau.js.map +1 -0
  38. package/dist/stegaEncodeSourceMap-DbM2fTN4.js +373 -0
  39. package/dist/stegaEncodeSourceMap-DbM2fTN4.js.map +1 -0
  40. package/dist/stegaEncodeSourceMap-YR3NQ3iz.js +163 -0
  41. package/dist/stegaEncodeSourceMap-YR3NQ3iz.js.map +1 -0
  42. package/dist/{index.browser.d.cts → types-BODIEY7F.d.ts} +4196 -5258
  43. package/dist/types-CfGzbXrl.d.ts +108 -0
  44. package/package.json +80 -142
  45. package/src/SanityClient.ts +101 -137
  46. package/src/agent/actions/AgentActionsClient.ts +12 -12
  47. package/src/agent/actions/commonTypes.ts +2 -1
  48. package/src/agent/actions/generate.ts +26 -6
  49. package/src/agent/actions/patch.ts +27 -7
  50. package/src/agent/actions/prompt.ts +19 -5
  51. package/src/agent/actions/transform.ts +23 -5
  52. package/src/agent/actions/translate.ts +23 -5
  53. package/src/assets/AssetsClient.ts +121 -24
  54. package/src/csm/applySourceDocuments.ts +5 -5
  55. package/src/csm/createEditUrl.ts +1 -1
  56. package/src/csm/draftUtils.ts +23 -5
  57. package/src/csm/index.ts +2 -2
  58. package/src/csm/studioPath.ts +2 -1
  59. package/src/csm/types.ts +8 -2
  60. package/src/data/dataMethods.ts +784 -349
  61. package/src/data/eventsource.ts +82 -49
  62. package/src/data/listen.ts +18 -23
  63. package/src/data/live.ts +60 -35
  64. package/src/data/resolveEventSourceFetch.ts +104 -0
  65. package/src/datasets/DatasetsClient.ts +58 -41
  66. package/src/defineCreateClient.ts +38 -22
  67. package/src/functions/FunctionsClient.ts +66 -0
  68. package/src/functions/invoke.ts +176 -0
  69. package/src/http/browserMiddleware.ts +7 -1
  70. package/src/http/browserUpload.ts +163 -0
  71. package/src/http/errors.ts +61 -6
  72. package/src/http/nodeMiddleware.ts +94 -30
  73. package/src/http/request.ts +304 -87
  74. package/src/http/requestOptions.ts +131 -15
  75. package/src/{index.browser.ts → index.node.ts} +9 -4
  76. package/src/index.ts +4 -3
  77. package/src/mediaLibrary/MediaLibraryVideoClient.ts +27 -12
  78. package/src/projects/ProjectsClient.ts +18 -16
  79. package/src/releases/ReleasesClient.ts +38 -33
  80. package/src/stega/index.ts +1 -2
  81. package/src/stega/stegaEncodeSourceMap.ts +5 -0
  82. package/src/stega/types.ts +2 -2
  83. package/src/types.ts +328 -103
  84. package/src/users/UsersClient.ts +10 -14
  85. package/src/util/shareReplayLatest.ts +1 -1
  86. package/src/validators.ts +1 -1
  87. package/src/warnings.ts +13 -2
  88. package/dist/_chunks-cjs/config.cjs +0 -206
  89. package/dist/_chunks-cjs/config.cjs.map +0 -1
  90. package/dist/_chunks-cjs/isRecord.cjs +0 -6
  91. package/dist/_chunks-cjs/isRecord.cjs.map +0 -1
  92. package/dist/_chunks-cjs/resolveEditInfo.cjs +0 -334
  93. package/dist/_chunks-cjs/resolveEditInfo.cjs.map +0 -1
  94. package/dist/_chunks-cjs/stegaClean.cjs +0 -46
  95. package/dist/_chunks-cjs/stegaClean.cjs.map +0 -1
  96. package/dist/_chunks-cjs/stegaEncodeSourceMap.cjs +0 -388
  97. package/dist/_chunks-cjs/stegaEncodeSourceMap.cjs.map +0 -1
  98. package/dist/_chunks-es/config.js +0 -207
  99. package/dist/_chunks-es/config.js.map +0 -1
  100. package/dist/_chunks-es/isRecord.js +0 -7
  101. package/dist/_chunks-es/isRecord.js.map +0 -1
  102. package/dist/_chunks-es/resolveEditInfo.js +0 -335
  103. package/dist/_chunks-es/resolveEditInfo.js.map +0 -1
  104. package/dist/_chunks-es/stegaClean.js +0 -47
  105. package/dist/_chunks-es/stegaClean.js.map +0 -1
  106. package/dist/_chunks-es/stegaEncodeSourceMap.js +0 -389
  107. package/dist/_chunks-es/stegaEncodeSourceMap.js.map +0 -1
  108. package/dist/csm.cjs +0 -99
  109. package/dist/csm.cjs.map +0 -1
  110. package/dist/csm.d.cts +0 -391
  111. package/dist/index.browser.cjs +0 -3106
  112. package/dist/index.browser.cjs.map +0 -1
  113. package/dist/index.browser.d.ts +0 -6802
  114. package/dist/index.browser.js +0 -3082
  115. package/dist/index.browser.js.map +0 -1
  116. package/dist/index.cjs +0 -2946
  117. package/dist/index.cjs.map +0 -1
  118. package/dist/media-library.cjs +0 -20
  119. package/dist/media-library.cjs.map +0 -1
  120. package/dist/media-library.d.cts +0 -117
  121. package/dist/stega.browser.cjs +0 -29
  122. package/dist/stega.browser.cjs.map +0 -1
  123. package/dist/stega.browser.d.cts +0 -7200
  124. package/dist/stega.browser.d.ts +0 -7200
  125. package/dist/stega.browser.js +0 -24
  126. package/dist/stega.browser.js.map +0 -1
  127. package/dist/stega.cjs +0 -29
  128. package/dist/stega.cjs.map +0 -1
  129. package/dist/stega.d.cts +0 -7200
  130. package/src/data/eventsourcePolyfill.ts +0 -7
  131. package/umd/.gitkeep +0 -1
  132. package/umd/sanityClient.js +0 -6543
  133. package/umd/sanityClient.min.js +0 -6
@@ -1,11 +1,11 @@
1
1
  import {getDraftId, getVersionFromId, getVersionId, isDraftId} from '@sanity/client/csm'
2
- import {from, type MonoTypeOperatorFunction, Observable, of} from 'rxjs'
3
- import {combineLatestWith, concatMap, filter, map, reduce} from 'rxjs/operators'
2
+ import {type MonoTypeOperatorFunction, Observable} from 'rxjs'
3
+ import {filter, map} from 'rxjs/operators'
4
4
 
5
5
  import {validateApiPerspective} from '../config'
6
- import {requestOptions} from '../http/requestOptions'
6
+ import {type FetchRequest, requestOptions} from '../http/requestOptions'
7
7
  import type {ObservableSanityClient, SanityClient} from '../SanityClient'
8
- import {stegaClean} from '../stega/stegaClean'
8
+ import {stegaClean, type StegaCleaned} from '../stega/stegaClean'
9
9
  import type {
10
10
  Action,
11
11
  AllDocumentIdsMutationOptions,
@@ -19,7 +19,6 @@ import type {
19
19
  FirstDocumentIdMutationOptions,
20
20
  FirstDocumentMutationOptions,
21
21
  HttpRequest,
22
- HttpRequestEvent,
23
22
  IdentifiedSanityDocumentStub,
24
23
  InitializedClientConfig,
25
24
  InitializedStegaConfig,
@@ -31,11 +30,11 @@ import type {
31
30
  RawQueryResponse,
32
31
  ReplaceVersionAction,
33
32
  RequestObservableOptions,
34
- RequestOptions,
35
33
  SanityDocument,
36
34
  SingleActionResult,
37
35
  SingleMutationResult,
38
36
  UnpublishVersionAction,
37
+ UploadEvent,
39
38
  } from '../types'
40
39
  import {getSelection} from '../util/getSelection'
41
40
  import * as validate from '../validators'
@@ -43,6 +42,7 @@ import * as validators from '../validators'
43
42
  import {
44
43
  printCdnPreviewDraftsWarning,
45
44
  printCreateVersionWithBaseIdWarning,
45
+ printDeprecatedUriOptionWarning,
46
46
  printPreviewDraftsDeprecationWarning,
47
47
  } from '../warnings'
48
48
  import {encodeQueryString} from './encodeQueryString'
@@ -67,9 +67,6 @@ const getMutationQuery = (options: BaseMutationOptions = {}) => {
67
67
  }
68
68
  }
69
69
 
70
- const isResponse = (event: Any) => event.type === 'response'
71
- const getBody = (event: Any) => event.body
72
-
73
70
  const indexBy = (docs: Any[], attr: Any) =>
74
71
  docs.reduce((indexed, doc) => {
75
72
  indexed[attr(doc)] = doc
@@ -78,19 +75,28 @@ const indexBy = (docs: Any[], attr: Any) =>
78
75
 
79
76
  const getQuerySizeLimit = 11264
80
77
 
81
- /** @internal */
82
- export function _fetch<R, Q>(
83
- client: Client,
84
- httpRequest: HttpRequest,
78
+ /**
79
+ * Resolve the effective stega config, cleaned params, response mapper and
80
+ * transport request options for a `fetch()` call. Shared by the observable and
81
+ * promise paths.
82
+ *
83
+ * @internal
84
+ */
85
+ function _fetchRequest<Q>(
85
86
  _stega: InitializedStegaConfig,
86
- query: string,
87
- _params: Q = {} as Q,
88
- options: QueryOptions = {},
89
- ): Observable<RawQueryResponse<R> | R> {
87
+ _params: Q,
88
+ options: QueryOptions,
89
+ ): {
90
+ stega: InitializedStegaConfig
91
+ // Stega-enabled requests send the cleaned (brand-stripped) params.
92
+ params: Q | StegaCleaned<Q>
93
+ mapResponse: (res: Any) => Any
94
+ reqOpts: Any
95
+ } {
90
96
  const stega =
91
97
  'stega' in options
92
98
  ? {
93
- ...(_stega || {}),
99
+ ..._stega,
94
100
  ...(typeof options.stega === 'boolean' ? {enabled: options.stega} : options.stega || {}),
95
101
  }
96
102
  : _stega
@@ -114,55 +120,128 @@ export function _fetch<R, Q>(
114
120
  ? {...opts, fetch: {cache, next}}
115
121
  : opts
116
122
 
117
- const $request = _dataRequest(client, httpRequest, 'query', {query, params}, reqOpts)
118
- return stega.enabled
119
- ? $request.pipe(
120
- combineLatestWith(
121
- from(
122
- import('../stega/stegaEncodeSourceMap').then(
123
- ({stegaEncodeSourceMap}) => stegaEncodeSourceMap,
124
- ),
125
- ),
126
- ),
127
- map(
128
- ([res, stegaEncodeSourceMap]: [
129
- Any,
130
- (typeof import('../stega/stegaEncodeSourceMap'))['stegaEncodeSourceMap'],
131
- ]) => {
132
- const result = stegaEncodeSourceMap(res.result, res.resultSourceMap, stega)
133
- return mapResponse({...res, result})
134
- },
135
- ),
136
- )
137
- : $request.pipe(map(mapResponse))
123
+ return {stega, params, mapResponse, reqOpts}
138
124
  }
139
125
 
140
126
  /** @internal */
141
- export function _getDocument<R extends Record<string, Any>>(
127
+ export function _fetchObservable<R, Q>(
128
+ client: Client,
129
+ httpRequest: HttpRequest,
130
+ _stega: InitializedStegaConfig,
131
+ query: string,
132
+ _params: Q = {} as Q,
133
+ options: QueryOptions = {},
134
+ ): Observable<RawQueryResponse<R> | R> {
135
+ return _observe(options.signal, (signal) =>
136
+ _fetch<R, Q>(client, httpRequest, _stega, query, _params, {...options, signal}),
137
+ )
138
+ }
139
+
140
+ /**
141
+ * Promise-based sibling of {@link _fetchObservable}.
142
+ *
143
+ * @internal
144
+ */
145
+ export function _fetch<R, Q>(
146
+ client: Client,
147
+ httpRequest: HttpRequest,
148
+ _stega: InitializedStegaConfig,
149
+ query: string,
150
+ _params: Q = {} as Q,
151
+ options: QueryOptions = {},
152
+ ): Promise<RawQueryResponse<R> | R> {
153
+ const {stega, params, mapResponse, reqOpts} = _fetchRequest(_stega, _params, options)
154
+
155
+ // Kick off the request synchronously (not in an `async` body) so option
156
+ // validation - e.g. an invalid request tag or a missing dataset - throws
157
+ // synchronously at call time, matching the rest of the promise surface.
158
+ const request = _dataRequest(client, httpRequest, 'query', {query, params}, reqOpts)
159
+ if (!stega.enabled) {
160
+ return request.then(mapResponse)
161
+ }
162
+ return Promise.all([request, import('../stega/stegaEncodeSourceMap')]).then(
163
+ ([res, {stegaEncodeSourceMap}]) => {
164
+ const result = stegaEncodeSourceMap(res.result, res.resultSourceMap, stega)
165
+ return mapResponse({...res, result})
166
+ },
167
+ )
168
+ }
169
+
170
+ /** @internal */
171
+ export function _getDocumentObservable<R extends Record<string, Any>>(
142
172
  client: Client,
143
173
  httpRequest: HttpRequest,
144
174
  id: string,
145
175
  opts: {signal?: AbortSignal; tag?: string; releaseId?: string; includeAllVersions: true},
146
176
  ): Observable<SanityDocument<R>[]>
147
177
  /** @internal */
148
- export function _getDocument<R extends Record<string, Any>>(
178
+ export function _getDocumentObservable<R extends Record<string, Any>>(
149
179
  client: Client,
150
180
  httpRequest: HttpRequest,
151
181
  id: string,
152
182
  opts?: {signal?: AbortSignal; tag?: string; releaseId?: string; includeAllVersions?: false},
153
183
  ): Observable<SanityDocument<R> | undefined>
154
184
  /** @internal */
155
- export function _getDocument<R extends Record<string, Any>>(
185
+ export function _getDocumentObservable<R extends Record<string, Any>>(
156
186
  client: Client,
157
187
  httpRequest: HttpRequest,
158
188
  id: string,
159
189
  opts: {signal?: AbortSignal; tag?: string; releaseId?: string; includeAllVersions?: boolean} = {},
160
190
  ): Observable<SanityDocument<R> | undefined | SanityDocument<R>[]> {
161
- const getDocId = () => {
162
- if (!opts.releaseId) {
163
- return id
164
- }
191
+ return _observe(opts.signal, (signal) =>
192
+ _request<{documents?: SanityDocument<R>[]}>(
193
+ client,
194
+ httpRequest,
195
+ _getDocumentOptions(client, id, {...opts, signal}),
196
+ ).then((body) => _mapGetDocument<R>(body, opts.includeAllVersions)),
197
+ )
198
+ }
165
199
 
200
+ /**
201
+ * Promise-based sibling of {@link _getDocumentObservable}.
202
+ *
203
+ * @internal
204
+ */
205
+ export function _getDocument<R extends Record<string, Any>>(
206
+ client: Client,
207
+ httpRequest: HttpRequest,
208
+ id: string,
209
+ opts: {signal?: AbortSignal; tag?: string; releaseId?: string; includeAllVersions: true},
210
+ ): Promise<SanityDocument<R>[]>
211
+ /** @internal */
212
+ export function _getDocument<R extends Record<string, Any>>(
213
+ client: Client,
214
+ httpRequest: HttpRequest,
215
+ id: string,
216
+ opts?: {signal?: AbortSignal; tag?: string; releaseId?: string; includeAllVersions?: false},
217
+ ): Promise<SanityDocument<R> | undefined>
218
+ /** @internal */
219
+ export function _getDocument<R extends Record<string, Any>>(
220
+ client: Client,
221
+ httpRequest: HttpRequest,
222
+ id: string,
223
+ opts: {signal?: AbortSignal; tag?: string; releaseId?: string; includeAllVersions?: boolean} = {},
224
+ ): Promise<SanityDocument<R> | undefined | SanityDocument<R>[]> {
225
+ const options = _getDocumentOptions(client, id, opts)
226
+ return _request<{documents?: SanityDocument<R>[]}>(client, httpRequest, options).then((body) =>
227
+ _mapGetDocument<R>(body, opts.includeAllVersions),
228
+ )
229
+ }
230
+
231
+ /**
232
+ * Resolve the document id (honoring `releaseId`) and build the transport
233
+ * request options for {@link _getDocumentObservable}. Shared by the observable and
234
+ * promise paths.
235
+ *
236
+ * @internal
237
+ */
238
+ function _getDocumentOptions(
239
+ client: Client,
240
+ id: string,
241
+ opts: {signal?: AbortSignal; tag?: string; releaseId?: string; includeAllVersions?: boolean},
242
+ ): Any {
243
+ let docId = id
244
+ if (opts.releaseId) {
166
245
  const versionId = getVersionFromId(id)
167
246
  if (!versionId) {
168
247
  if (isDraftId(id)) {
@@ -170,23 +249,16 @@ export function _getDocument<R extends Record<string, Any>>(
170
249
  `The document ID (\`${id}\`) is a draft, but \`options.releaseId\` is set as \`${opts.releaseId}\``,
171
250
  )
172
251
  }
173
-
174
- return getVersionId(id, opts.releaseId)
175
- }
176
-
177
- if (versionId !== opts.releaseId) {
252
+ docId = getVersionId(id, opts.releaseId)
253
+ } else if (versionId !== opts.releaseId) {
178
254
  throw new Error(
179
255
  `The document ID (\`${id}\`) is already a version of \`${versionId}\` release, but this does not match the provided \`options.releaseId\` (\`${opts.releaseId}\`)`,
180
256
  )
181
257
  }
182
-
183
- return id
184
258
  }
185
- const docId = getDocId()
186
259
 
187
- const options = {
188
- uri: _getDataUrl(client, 'doc', docId),
189
- json: true,
260
+ return {
261
+ url: _getDataUrl(client, 'doc', docId),
190
262
  tag: opts.tag,
191
263
  signal: opts.signal,
192
264
  query:
@@ -194,111 +266,546 @@ export function _getDocument<R extends Record<string, Any>>(
194
266
  ? {includeAllVersions: opts.includeAllVersions}
195
267
  : undefined,
196
268
  }
197
- return _requestObservable<SanityDocument<R> | undefined | SanityDocument<R>[]>(
198
- client,
199
- httpRequest,
200
- options,
201
- ).pipe(
202
- filter(isResponse),
203
- map((event) => {
204
- const documents = event.body.documents
205
- if (!documents) {
206
- return opts.includeAllVersions ? [] : undefined
207
- }
208
- return opts.includeAllVersions ? documents : documents[0]
209
- }),
210
- )
269
+ }
270
+
271
+ function _mapGetDocument<R extends Record<string, Any>>(
272
+ body: {documents?: SanityDocument<R>[]},
273
+ includeAllVersions?: boolean,
274
+ ): SanityDocument<R> | undefined | SanityDocument<R>[] {
275
+ const documents = body.documents
276
+ if (!documents) {
277
+ return includeAllVersions ? [] : undefined
278
+ }
279
+ return includeAllVersions ? documents : documents[0]
211
280
  }
212
281
 
213
282
  /** @internal */
214
- export function _getDocuments<R extends Record<string, Any>>(
283
+ export function _getDocumentsObservable<R extends Record<string, Any>>(
215
284
  client: Client,
216
285
  httpRequest: HttpRequest,
217
286
  ids: string[],
218
287
  opts: {signal?: AbortSignal; tag?: string} = {},
219
288
  ): Observable<(SanityDocument<R> | null)[]> {
220
- const options = {
221
- uri: _getDataUrl(client, 'doc', ids.join(',')),
222
- json: true,
289
+ return _observe(opts.signal, (signal) =>
290
+ _getDocuments<R>(client, httpRequest, ids, {...opts, signal}),
291
+ )
292
+ }
293
+
294
+ /**
295
+ * Promise-based sibling of {@link _getDocumentsObservable}.
296
+ *
297
+ * @internal
298
+ */
299
+ export function _getDocuments<R extends Record<string, Any>>(
300
+ client: Client,
301
+ httpRequest: HttpRequest,
302
+ ids: string[],
303
+ opts: {signal?: AbortSignal; tag?: string} = {},
304
+ ): Promise<(SanityDocument<R> | null)[]> {
305
+ const options = _getDocumentsOptions(client, ids, opts)
306
+ return _request<{documents?: SanityDocument<R>[]}>(client, httpRequest, options).then((body) =>
307
+ _mapGetDocuments<R>(body, ids),
308
+ )
309
+ }
310
+
311
+ function _getDocumentsOptions(
312
+ client: Client,
313
+ ids: string[],
314
+ opts: {signal?: AbortSignal; tag?: string},
315
+ ): Any {
316
+ return {
317
+ url: _getDataUrl(client, 'doc', ids.join(',')),
223
318
  tag: opts.tag,
224
319
  signal: opts.signal,
225
320
  }
226
- return _requestObservable<(SanityDocument<R> | null)[]>(client, httpRequest, options).pipe(
227
- filter(isResponse),
228
- map((event: Any) => {
229
- const indexed = indexBy(event.body.documents || [], (doc: Any) => doc._id)
230
- return ids.map((id) => indexed[id] || null)
231
- }),
232
- )
321
+ }
322
+
323
+ function _mapGetDocuments<R extends Record<string, Any>>(
324
+ body: {documents?: SanityDocument<R>[]},
325
+ ids: string[],
326
+ ): (SanityDocument<R> | null)[] {
327
+ const indexed = indexBy(body.documents || [], (doc: Any) => doc._id)
328
+ return ids.map((id) => indexed[id] || null)
233
329
  }
234
330
 
235
331
  const DOCUMENTS_EXISTS_BATCH_SIZE = 100
236
332
 
237
333
  /** @internal */
238
- export function _documentsExists(
334
+ export function _documentsExistsObservable(
239
335
  client: Client,
240
336
  httpRequest: HttpRequest,
241
337
  ids: string[],
242
338
  opts: {signal?: AbortSignal; tag?: string} = {},
243
339
  ): Observable<Set<string>> {
340
+ return _observe(opts.signal, (signal) =>
341
+ _documentsExists(client, httpRequest, ids, {...opts, signal}),
342
+ )
343
+ }
344
+
345
+ /**
346
+ * Promise-based sibling of {@link _documentsExistsObservable}. Checks document
347
+ * existence in batches, resolving to the set of IDs that exist.
348
+ *
349
+ * @internal
350
+ */
351
+ export async function _documentsExists(
352
+ client: Client,
353
+ httpRequest: HttpRequest,
354
+ ids: string[],
355
+ opts: {signal?: AbortSignal; tag?: string} = {},
356
+ ): Promise<Set<string>> {
357
+ const existing = new Set<string>()
244
358
  if (ids.length === 0) {
245
- return of(new Set<string>())
359
+ return existing
246
360
  }
247
361
 
248
- const batches: string[][] = []
249
362
  for (let i = 0; i < ids.length; i += DOCUMENTS_EXISTS_BATCH_SIZE) {
250
- batches.push(ids.slice(i, i + DOCUMENTS_EXISTS_BATCH_SIZE))
363
+ const batchIds = ids.slice(i, i + DOCUMENTS_EXISTS_BATCH_SIZE)
364
+ const body = await _request<{omitted?: {id: string; reason: string}[]}>(client, httpRequest, {
365
+ url: _getDataUrl(client, 'doc', batchIds.map(encodeURIComponent).join(',')),
366
+ tag: opts.tag,
367
+ signal: opts.signal,
368
+ query: {excludeContent: true},
369
+ })
370
+
371
+ const missing = new Set<string>()
372
+ for (const omitted of body.omitted || []) {
373
+ if (omitted.reason !== 'existence') continue
374
+ missing.add(omitted.id)
375
+ }
376
+ for (const id of batchIds) {
377
+ if (!missing.has(id)) existing.add(id)
378
+ }
379
+ }
380
+
381
+ return existing
382
+ }
383
+
384
+ /** @internal */
385
+ export function _getReleaseDocumentsObservable<R extends Record<string, Any>>(
386
+ client: ObservableSanityClient | SanityClient,
387
+ httpRequest: HttpRequest,
388
+ releaseId: string,
389
+ opts: BaseMutationOptions = {},
390
+ ): Observable<RawQueryResponse<SanityDocument<R>[]>> {
391
+ return _observe(opts.signal, (signal) =>
392
+ _getReleaseDocuments<R>(client, httpRequest, releaseId, {...opts, signal}),
393
+ )
394
+ }
395
+
396
+ /**
397
+ * Promise-based sibling of {@link _getReleaseDocumentsObservable}.
398
+ *
399
+ * @internal
400
+ */
401
+ export function _getReleaseDocuments<R extends Record<string, Any>>(
402
+ client: ObservableSanityClient | SanityClient,
403
+ httpRequest: HttpRequest,
404
+ releaseId: string,
405
+ opts: BaseMutationOptions = {},
406
+ ): Promise<RawQueryResponse<SanityDocument<R>[]>> {
407
+ return _dataRequest(
408
+ client,
409
+ httpRequest,
410
+ 'query',
411
+ {
412
+ query: '*[sanity::partOfRelease($releaseId)]',
413
+ params: {
414
+ releaseId,
415
+ },
416
+ },
417
+ opts,
418
+ )
419
+ }
420
+
421
+ /** @internal */
422
+ export function _createIfNotExistsObservable<R extends Record<string, Any>>(
423
+ client: Client,
424
+ httpRequest: HttpRequest,
425
+ doc: IdentifiedSanityDocumentStub<R>,
426
+ options?:
427
+ | AllDocumentIdsMutationOptions
428
+ | AllDocumentsMutationOptions
429
+ | BaseMutationOptions
430
+ | FirstDocumentIdMutationOptions
431
+ | FirstDocumentMutationOptions,
432
+ ): Observable<
433
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
434
+ > {
435
+ return _observe(options?.signal, (signal) =>
436
+ _createIfNotExists<R>(client, httpRequest, doc, {...options, signal}),
437
+ )
438
+ }
439
+
440
+ /** @internal */
441
+ export function _createOrReplaceObservable<R extends Record<string, Any>>(
442
+ client: Client,
443
+ httpRequest: HttpRequest,
444
+ doc: IdentifiedSanityDocumentStub<R>,
445
+ options?:
446
+ | AllDocumentIdsMutationOptions
447
+ | AllDocumentsMutationOptions
448
+ | BaseMutationOptions
449
+ | FirstDocumentIdMutationOptions
450
+ | FirstDocumentMutationOptions,
451
+ ): Observable<
452
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
453
+ > {
454
+ return _observe(options?.signal, (signal) =>
455
+ _createOrReplace<R>(client, httpRequest, doc, {...options, signal}),
456
+ )
457
+ }
458
+
459
+ /** @internal */
460
+ export function _createVersionObservable<R extends Record<string, Any>>(
461
+ client: ObservableSanityClient | SanityClient,
462
+ httpRequest: HttpRequest,
463
+ doc: IdentifiedSanityDocumentStub<R>,
464
+ publishedId: string,
465
+ options?: BaseActionOptions,
466
+ ): Observable<SingleActionResult> {
467
+ return _observe(options?.signal, (signal) =>
468
+ _createVersion<R>(client, httpRequest, doc, publishedId, {...options, signal}),
469
+ )
470
+ }
471
+
472
+ /** @internal */
473
+ export function _createVersionFromBaseObservable(
474
+ client: ObservableSanityClient | SanityClient,
475
+ httpRequest: HttpRequest,
476
+ publishedId?: string,
477
+ baseId?: string,
478
+ releaseId?: string,
479
+ ifBaseRevisionId?: string,
480
+ options?: BaseActionOptions,
481
+ ): Observable<SingleActionResult> {
482
+ return _observe(options?.signal, (signal) =>
483
+ _createVersionFromBase(client, httpRequest, publishedId, baseId, releaseId, ifBaseRevisionId, {
484
+ ...options,
485
+ signal,
486
+ }),
487
+ )
488
+ }
489
+
490
+ /** @internal */
491
+ export function _deleteObservable<R extends Record<string, Any>>(
492
+ client: Client,
493
+ httpRequest: HttpRequest,
494
+ selection: string | MutationSelection,
495
+ options?:
496
+ | AllDocumentIdsMutationOptions
497
+ | AllDocumentsMutationOptions
498
+ | BaseMutationOptions
499
+ | FirstDocumentIdMutationOptions
500
+ | FirstDocumentMutationOptions,
501
+ ): Observable<
502
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
503
+ > {
504
+ return _observe(options?.signal, (signal) =>
505
+ _delete<R>(client, httpRequest, selection, {...options, signal}),
506
+ )
507
+ }
508
+
509
+ /** @internal */
510
+ export function _discardVersionObservable(
511
+ client: ObservableSanityClient | SanityClient,
512
+ httpRequest: HttpRequest,
513
+ versionId: string,
514
+ purge: boolean = false,
515
+ options?: BaseActionOptions,
516
+ ): Observable<SingleActionResult> {
517
+ return _observe(options?.signal, (signal) =>
518
+ _discardVersion(client, httpRequest, versionId, purge, {...options, signal}),
519
+ )
520
+ }
521
+
522
+ /** @internal */
523
+ export function _replaceVersionObservable<R extends Record<string, Any>>(
524
+ client: ObservableSanityClient | SanityClient,
525
+ httpRequest: HttpRequest,
526
+ doc: IdentifiedSanityDocumentStub<R>,
527
+ options?: BaseActionOptions,
528
+ ): Observable<SingleActionResult> {
529
+ return _observe(options?.signal, (signal) =>
530
+ _replaceVersion<R>(client, httpRequest, doc, {...options, signal}),
531
+ )
532
+ }
533
+
534
+ /** @internal */
535
+ export function _unpublishVersionObservable(
536
+ client: ObservableSanityClient | SanityClient,
537
+ httpRequest: HttpRequest,
538
+ versionId: string,
539
+ publishedId: string,
540
+ options?: BaseActionOptions,
541
+ ): Observable<SingleActionResult> {
542
+ return _observe(options?.signal, (signal) =>
543
+ _unpublishVersion(client, httpRequest, versionId, publishedId, {
544
+ ...options,
545
+ signal,
546
+ }),
547
+ )
548
+ }
549
+
550
+ /** @internal */
551
+ export function _mutateObservable<R extends Record<string, Any>>(
552
+ client: Client,
553
+ httpRequest: HttpRequest,
554
+ mutations: Mutation<R>[] | Patch | ObservablePatch | Transaction | ObservableTransaction,
555
+ options?:
556
+ | AllDocumentIdsMutationOptions
557
+ | AllDocumentsMutationOptions
558
+ | BaseMutationOptions
559
+ | FirstDocumentIdMutationOptions
560
+ | FirstDocumentMutationOptions,
561
+ ): Observable<
562
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
563
+ > {
564
+ return _observe(options?.signal, (signal) =>
565
+ _mutate<R>(client, httpRequest, mutations, {...options, signal}),
566
+ )
567
+ }
568
+
569
+ /**
570
+ * @internal
571
+ */
572
+ export function _actionObservable(
573
+ client: Client,
574
+ httpRequest: HttpRequest,
575
+ actions: Action | Action[],
576
+ options?: BaseActionOptions,
577
+ ): Observable<SingleActionResult | MultipleActionResult> {
578
+ return _observe(options?.signal, (signal) =>
579
+ _action(client, httpRequest, actions, {...options, signal}),
580
+ )
581
+ }
582
+
583
+ /**
584
+ * Build the transport request options for a data endpoint (`query` / `mutate`
585
+ * / `actions`). Shared by the observable and promise request paths.
586
+ *
587
+ * @internal
588
+ */
589
+ function _dataRequestOptions(
590
+ client: Client,
591
+ endpoint: string,
592
+ body: Any,
593
+ options: Any = {},
594
+ ): {reqOptions: Any; isMutation: boolean; returnFirst: boolean} {
595
+ const isMutation = endpoint === 'mutate'
596
+ const isAction = endpoint === 'actions'
597
+ const isQuery = endpoint === 'query'
598
+
599
+ // Check if the query string is within a configured threshold,
600
+ // in which case we can use GET. Otherwise, use POST.
601
+ const strQuery = isMutation || isAction ? '' : encodeQueryString(body)
602
+ const useGet = !isMutation && !isAction && strQuery.length < getQuerySizeLimit
603
+ const stringQuery = useGet ? strQuery : ''
604
+ const returnFirst = options.returnFirst
605
+ const {timeout, token, tag, headers, returnQuery, lastLiveEventId, cacheMode} = options
606
+
607
+ const url = _getDataUrl(client, endpoint, stringQuery)
608
+
609
+ const reqOptions = {
610
+ method: useGet ? 'GET' : 'POST',
611
+ url,
612
+ body: useGet ? undefined : body,
613
+ query: isMutation && getMutationQuery(options),
614
+ timeout,
615
+ headers,
616
+ token,
617
+ tag,
618
+ returnQuery,
619
+ perspective: options.perspective,
620
+ variant: options.variant,
621
+ resultSourceMap: options.resultSourceMap,
622
+ lastLiveEventId: Array.isArray(lastLiveEventId) ? lastLiveEventId[0] : lastLiveEventId,
623
+ cacheMode: cacheMode,
624
+ canUseCdn: isQuery,
625
+ signal: options.signal,
626
+ fetch: options.fetch,
627
+ useAbortSignal: options.useAbortSignal,
628
+ useCdn: options.useCdn,
629
+ }
630
+
631
+ return {reqOptions, isMutation, returnFirst}
632
+ }
633
+
634
+ /**
635
+ * Shape the raw response body of a data request. For mutations this reduces
636
+ * the API response to documents or ids; everything else passes through. Pure
637
+ * function shared by the observable and promise request paths.
638
+ *
639
+ * @internal
640
+ */
641
+ function _mapDataResponse(
642
+ res: Any,
643
+ isMutation: boolean,
644
+ returnFirst: boolean,
645
+ returnDocuments: boolean,
646
+ ): Any {
647
+ if (!isMutation) {
648
+ return res
649
+ }
650
+
651
+ // Should we return documents?
652
+ const results = res.results || []
653
+ if (returnDocuments) {
654
+ return returnFirst ? results[0] && results[0].document : results.map((mut: Any) => mut.document)
655
+ }
656
+
657
+ // Return a reduced subset
658
+ const key = returnFirst ? 'documentId' : 'documentIds'
659
+ const ids = returnFirst ? results[0] && results[0].id : results.map((mut: Any) => mut.id)
660
+ return {
661
+ transactionId: res.transactionId,
662
+ results: results,
663
+ [key]: ids,
664
+ }
665
+ }
666
+
667
+ /**
668
+ * @internal
669
+ */
670
+ export function _dataRequest(
671
+ client: Client,
672
+ httpRequest: HttpRequest,
673
+ endpoint: string,
674
+ body: Any,
675
+ options: Any = {},
676
+ ): Promise<Any> {
677
+ const {reqOptions, isMutation, returnFirst} = _dataRequestOptions(client, endpoint, body, options)
678
+ return _request(client, httpRequest, reqOptions).then((res: Any) =>
679
+ _mapDataResponse(res, isMutation, returnFirst, options.returnDocuments),
680
+ )
681
+ }
682
+
683
+ /**
684
+ * @internal
685
+ */
686
+ export function _createObservable<R extends Record<string, Any>>(
687
+ client: Client,
688
+ httpRequest: HttpRequest,
689
+ doc: Any,
690
+ op: Any,
691
+ options: Any = {},
692
+ ): Observable<
693
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
694
+ > {
695
+ return _observe(options.signal, (signal) =>
696
+ _create<R>(client, httpRequest, doc, op, {...options, signal}),
697
+ )
698
+ }
699
+
700
+ /**
701
+ * Promise-based sibling of {@link _createObservable}.
702
+ *
703
+ * @internal
704
+ */
705
+ export function _create<R extends Record<string, Any>>(
706
+ client: Client,
707
+ httpRequest: HttpRequest,
708
+ doc: Any,
709
+ op: Any,
710
+ options: Any = {},
711
+ ): Promise<
712
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
713
+ > {
714
+ const mutation = {[op]: doc}
715
+ const opts = Object.assign({returnFirst: true, returnDocuments: true}, options)
716
+ return _dataRequest(client, httpRequest, 'mutate', {mutations: [mutation]}, opts)
717
+ }
718
+
719
+ /**
720
+ * Promise-based sibling of {@link _actionObservable}.
721
+ *
722
+ * @internal
723
+ */
724
+ export function _action(
725
+ client: Client,
726
+ httpRequest: HttpRequest,
727
+ actions: Action | Action[],
728
+ options?: BaseActionOptions,
729
+ ): Promise<SingleActionResult | MultipleActionResult> {
730
+ const acts = Array.isArray(actions) ? actions : [actions]
731
+ const transactionId = (options && options.transactionId) || undefined
732
+ const skipCrossDatasetReferenceValidation =
733
+ (options && options.skipCrossDatasetReferenceValidation) || undefined
734
+ const dryRun = (options && options.dryRun) || undefined
735
+
736
+ return _dataRequest(
737
+ client,
738
+ httpRequest,
739
+ 'actions',
740
+ {actions: acts, transactionId, skipCrossDatasetReferenceValidation, dryRun},
741
+ options,
742
+ )
743
+ }
744
+
745
+ /**
746
+ * Promise-based sibling of {@link _mutateObservable}.
747
+ *
748
+ * @internal
749
+ */
750
+ export function _mutate<R extends Record<string, Any>>(
751
+ client: Client,
752
+ httpRequest: HttpRequest,
753
+ mutations: Mutation<R>[] | Patch | ObservablePatch | Transaction | ObservableTransaction,
754
+ options?:
755
+ | AllDocumentIdsMutationOptions
756
+ | AllDocumentsMutationOptions
757
+ | BaseMutationOptions
758
+ | FirstDocumentIdMutationOptions
759
+ | FirstDocumentMutationOptions,
760
+ ): Promise<
761
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
762
+ > {
763
+ let mut: Mutation | Mutation[]
764
+ if (mutations instanceof Patch || mutations instanceof ObservablePatch) {
765
+ mut = {patch: mutations.serialize()}
766
+ } else if (mutations instanceof Transaction || mutations instanceof ObservableTransaction) {
767
+ mut = mutations.serialize()
768
+ } else {
769
+ mut = mutations
251
770
  }
252
771
 
253
- const fetchBatch = (batchIds: string[]) =>
254
- _requestObservable<Any>(client, httpRequest, {
255
- uri: _getDataUrl(client, 'doc', batchIds.map(encodeURIComponent).join(',')),
256
- tag: opts.tag,
257
- signal: opts.signal,
258
- query: {excludeContent: true},
259
- }).pipe(
260
- filter(isResponse),
261
- map((event: Any) => {
262
- const missing = new Set<string>()
263
- for (const omitted of event.body.omitted || []) {
264
- if (omitted.reason !== 'existence') continue
265
- missing.add(omitted.id)
266
- }
267
- return new Set(batchIds.filter((id) => !missing.has(id)))
268
- }),
269
- )
270
-
271
- return from(batches).pipe(
272
- concatMap(fetchBatch),
273
- reduce((acc, set) => {
274
- for (const id of set) acc.add(id)
275
- return acc
276
- }, new Set<string>()),
277
- )
772
+ const muts = Array.isArray(mut) ? mut : [mut]
773
+ const transactionId = (options && options.transactionId) || undefined
774
+ return _dataRequest(client, httpRequest, 'mutate', {mutations: muts, transactionId}, options)
278
775
  }
279
776
 
280
- /** @internal */
281
- export function _getReleaseDocuments<R extends Record<string, Any>>(
282
- client: ObservableSanityClient | SanityClient,
777
+ /**
778
+ * Promise-based sibling of {@link _deleteObservable}.
779
+ *
780
+ * @internal
781
+ */
782
+ export function _delete<R extends Record<string, Any>>(
783
+ client: Client,
283
784
  httpRequest: HttpRequest,
284
- releaseId: string,
285
- opts: BaseMutationOptions = {},
286
- ): Observable<RawQueryResponse<SanityDocument<R>[]>> {
785
+ selection: string | MutationSelection,
786
+ options?:
787
+ | AllDocumentIdsMutationOptions
788
+ | AllDocumentsMutationOptions
789
+ | BaseMutationOptions
790
+ | FirstDocumentIdMutationOptions
791
+ | FirstDocumentMutationOptions,
792
+ ): Promise<
793
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
794
+ > {
287
795
  return _dataRequest(
288
796
  client,
289
797
  httpRequest,
290
- 'query',
291
- {
292
- query: '*[sanity::partOfRelease($releaseId)]',
293
- params: {
294
- releaseId,
295
- },
296
- },
297
- opts,
798
+ 'mutate',
799
+ {mutations: [{delete: getSelection(selection)}]},
800
+ options,
298
801
  )
299
802
  }
300
803
 
301
- /** @internal */
804
+ /**
805
+ * Promise-based sibling of {@link _createIfNotExistsObservable}.
806
+ *
807
+ * @internal
808
+ */
302
809
  export function _createIfNotExists<R extends Record<string, Any>>(
303
810
  client: Client,
304
811
  httpRequest: HttpRequest,
@@ -309,14 +816,18 @@ export function _createIfNotExists<R extends Record<string, Any>>(
309
816
  | BaseMutationOptions
310
817
  | FirstDocumentIdMutationOptions
311
818
  | FirstDocumentMutationOptions,
312
- ): Observable<
819
+ ): Promise<
313
820
  SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
314
821
  > {
315
822
  validators.requireDocumentId('createIfNotExists', doc)
316
823
  return _create<R>(client, httpRequest, doc, 'createIfNotExists', options)
317
824
  }
318
825
 
319
- /** @internal */
826
+ /**
827
+ * Promise-based sibling of {@link _createOrReplaceObservable}.
828
+ *
829
+ * @internal
830
+ */
320
831
  export function _createOrReplace<R extends Record<string, Any>>(
321
832
  client: Client,
322
833
  httpRequest: HttpRequest,
@@ -327,21 +838,25 @@ export function _createOrReplace<R extends Record<string, Any>>(
327
838
  | BaseMutationOptions
328
839
  | FirstDocumentIdMutationOptions
329
840
  | FirstDocumentMutationOptions,
330
- ): Observable<
841
+ ): Promise<
331
842
  SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
332
843
  > {
333
844
  validators.requireDocumentId('createOrReplace', doc)
334
845
  return _create<R>(client, httpRequest, doc, 'createOrReplace', options)
335
846
  }
336
847
 
337
- /** @internal */
848
+ /**
849
+ * Promise-based sibling of {@link _createVersionObservable}.
850
+ *
851
+ * @internal
852
+ */
338
853
  export function _createVersion<R extends Record<string, Any>>(
339
854
  client: ObservableSanityClient | SanityClient,
340
855
  httpRequest: HttpRequest,
341
856
  doc: IdentifiedSanityDocumentStub<R>,
342
857
  publishedId: string,
343
858
  options?: BaseActionOptions,
344
- ): Observable<SingleActionResult> {
859
+ ): Promise<SingleActionResult> {
345
860
  validators.requireDocumentId('createVersion', doc)
346
861
  validators.requireDocumentType('createVersion', doc)
347
862
  printCreateVersionWithBaseIdWarning()
@@ -355,7 +870,11 @@ export function _createVersion<R extends Record<string, Any>>(
355
870
  return _action(client, httpRequest, createVersionAction, options)
356
871
  }
357
872
 
358
- /** @internal */
873
+ /**
874
+ * Promise-based sibling of {@link _createVersionFromBaseObservable}.
875
+ *
876
+ * @internal
877
+ */
359
878
  export function _createVersionFromBase(
360
879
  client: ObservableSanityClient | SanityClient,
361
880
  httpRequest: HttpRequest,
@@ -364,7 +883,7 @@ export function _createVersionFromBase(
364
883
  releaseId?: string,
365
884
  ifBaseRevisionId?: string,
366
885
  options?: BaseActionOptions,
367
- ): Observable<SingleActionResult> {
886
+ ): Promise<SingleActionResult> {
368
887
  if (!baseId) {
369
888
  throw new Error('`createVersion()` requires `baseId` when no `document` is provided')
370
889
  }
@@ -387,37 +906,18 @@ export function _createVersionFromBase(
387
906
  return _action(client, httpRequest, createVersionAction, options)
388
907
  }
389
908
 
390
- /** @internal */
391
- export function _delete<R extends Record<string, Any>>(
392
- client: Client,
393
- httpRequest: HttpRequest,
394
- selection: string | MutationSelection,
395
- options?:
396
- | AllDocumentIdsMutationOptions
397
- | AllDocumentsMutationOptions
398
- | BaseMutationOptions
399
- | FirstDocumentIdMutationOptions
400
- | FirstDocumentMutationOptions,
401
- ): Observable<
402
- SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
403
- > {
404
- return _dataRequest(
405
- client,
406
- httpRequest,
407
- 'mutate',
408
- {mutations: [{delete: getSelection(selection)}]},
409
- options,
410
- )
411
- }
412
-
413
- /** @internal */
909
+ /**
910
+ * Promise-based sibling of {@link _discardVersionObservable}.
911
+ *
912
+ * @internal
913
+ */
414
914
  export function _discardVersion(
415
915
  client: ObservableSanityClient | SanityClient,
416
916
  httpRequest: HttpRequest,
417
917
  versionId: string,
418
918
  purge: boolean = false,
419
919
  options?: BaseActionOptions,
420
- ): Observable<SingleActionResult> {
920
+ ): Promise<SingleActionResult> {
421
921
  const discardVersionAction: DiscardVersionAction = {
422
922
  actionType: 'sanity.action.document.version.discard',
423
923
  versionId,
@@ -427,13 +927,17 @@ export function _discardVersion(
427
927
  return _action(client, httpRequest, discardVersionAction, options)
428
928
  }
429
929
 
430
- /** @internal */
930
+ /**
931
+ * Promise-based sibling of {@link _replaceVersionObservable}.
932
+ *
933
+ * @internal
934
+ */
431
935
  export function _replaceVersion<R extends Record<string, Any>>(
432
936
  client: ObservableSanityClient | SanityClient,
433
937
  httpRequest: HttpRequest,
434
938
  doc: IdentifiedSanityDocumentStub<R>,
435
939
  options?: BaseActionOptions,
436
- ): Observable<SingleActionResult> {
940
+ ): Promise<SingleActionResult> {
437
941
  validators.requireDocumentId('replaceVersion', doc)
438
942
  validators.requireDocumentType('replaceVersion', doc)
439
943
 
@@ -445,14 +949,18 @@ export function _replaceVersion<R extends Record<string, Any>>(
445
949
  return _action(client, httpRequest, replaceVersionAction, options)
446
950
  }
447
951
 
448
- /** @internal */
952
+ /**
953
+ * Promise-based sibling of {@link _unpublishVersionObservable}.
954
+ *
955
+ * @internal
956
+ */
449
957
  export function _unpublishVersion(
450
958
  client: ObservableSanityClient | SanityClient,
451
959
  httpRequest: HttpRequest,
452
960
  versionId: string,
453
961
  publishedId: string,
454
962
  options?: BaseActionOptions,
455
- ): Observable<SingleActionResult> {
963
+ ): Promise<SingleActionResult> {
456
964
  const unpublishVersionAction: UnpublishVersionAction = {
457
965
  actionType: 'sanity.action.document.version.unpublish',
458
966
  versionId,
@@ -462,150 +970,6 @@ export function _unpublishVersion(
462
970
  return _action(client, httpRequest, unpublishVersionAction, options)
463
971
  }
464
972
 
465
- /** @internal */
466
- export function _mutate<R extends Record<string, Any>>(
467
- client: Client,
468
- httpRequest: HttpRequest,
469
- mutations: Mutation<R>[] | Patch | ObservablePatch | Transaction | ObservableTransaction,
470
- options?:
471
- | AllDocumentIdsMutationOptions
472
- | AllDocumentsMutationOptions
473
- | BaseMutationOptions
474
- | FirstDocumentIdMutationOptions
475
- | FirstDocumentMutationOptions,
476
- ): Observable<
477
- SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
478
- > {
479
- let mut: Mutation | Mutation[]
480
- if (mutations instanceof Patch || mutations instanceof ObservablePatch) {
481
- mut = {patch: mutations.serialize()}
482
- } else if (mutations instanceof Transaction || mutations instanceof ObservableTransaction) {
483
- mut = mutations.serialize()
484
- } else {
485
- mut = mutations
486
- }
487
-
488
- const muts = Array.isArray(mut) ? mut : [mut]
489
- const transactionId = (options && options.transactionId) || undefined
490
- return _dataRequest(client, httpRequest, 'mutate', {mutations: muts, transactionId}, options)
491
- }
492
-
493
- /**
494
- * @internal
495
- */
496
- export function _action(
497
- client: Client,
498
- httpRequest: HttpRequest,
499
- actions: Action | Action[],
500
- options?: BaseActionOptions,
501
- ): Observable<SingleActionResult | MultipleActionResult> {
502
- const acts = Array.isArray(actions) ? actions : [actions]
503
- const transactionId = (options && options.transactionId) || undefined
504
- const skipCrossDatasetReferenceValidation =
505
- (options && options.skipCrossDatasetReferenceValidation) || undefined
506
- const dryRun = (options && options.dryRun) || undefined
507
-
508
- return _dataRequest(
509
- client,
510
- httpRequest,
511
- 'actions',
512
- {actions: acts, transactionId, skipCrossDatasetReferenceValidation, dryRun},
513
- options,
514
- )
515
- }
516
-
517
- /**
518
- * @internal
519
- */
520
- export function _dataRequest(
521
- client: Client,
522
- httpRequest: HttpRequest,
523
- endpoint: string,
524
- body: Any,
525
- options: Any = {},
526
- ): Any {
527
- const isMutation = endpoint === 'mutate'
528
- const isAction = endpoint === 'actions'
529
- const isQuery = endpoint === 'query'
530
-
531
- // Check if the query string is within a configured threshold,
532
- // in which case we can use GET. Otherwise, use POST.
533
- const strQuery = isMutation || isAction ? '' : encodeQueryString(body)
534
- const useGet = !isMutation && !isAction && strQuery.length < getQuerySizeLimit
535
- const stringQuery = useGet ? strQuery : ''
536
- const returnFirst = options.returnFirst
537
- const {timeout, token, tag, headers, returnQuery, lastLiveEventId, cacheMode} = options
538
-
539
- const uri = _getDataUrl(client, endpoint, stringQuery)
540
-
541
- const reqOptions = {
542
- method: useGet ? 'GET' : 'POST',
543
- uri: uri,
544
- json: true,
545
- body: useGet ? undefined : body,
546
- query: isMutation && getMutationQuery(options),
547
- timeout,
548
- headers,
549
- token,
550
- tag,
551
- returnQuery,
552
- perspective: options.perspective,
553
- variant: options.variant,
554
- resultSourceMap: options.resultSourceMap,
555
- lastLiveEventId: Array.isArray(lastLiveEventId) ? lastLiveEventId[0] : lastLiveEventId,
556
- cacheMode: cacheMode,
557
- canUseCdn: isQuery,
558
- signal: options.signal,
559
- fetch: options.fetch,
560
- useAbortSignal: options.useAbortSignal,
561
- useCdn: options.useCdn,
562
- }
563
-
564
- return _requestObservable(client, httpRequest, reqOptions).pipe(
565
- filter(isResponse),
566
- map(getBody),
567
- map((res) => {
568
- if (!isMutation) {
569
- return res
570
- }
571
-
572
- // Should we return documents?
573
- const results = res.results || []
574
- if (options.returnDocuments) {
575
- return returnFirst
576
- ? results[0] && results[0].document
577
- : results.map((mut: Any) => mut.document)
578
- }
579
-
580
- // Return a reduced subset
581
- const key = returnFirst ? 'documentId' : 'documentIds'
582
- const ids = returnFirst ? results[0] && results[0].id : results.map((mut: Any) => mut.id)
583
- return {
584
- transactionId: res.transactionId,
585
- results: results,
586
- [key]: ids,
587
- }
588
- }),
589
- )
590
- }
591
-
592
- /**
593
- * @internal
594
- */
595
- export function _create<R extends Record<string, Any>>(
596
- client: Client,
597
- httpRequest: HttpRequest,
598
- doc: Any,
599
- op: Any,
600
- options: Any = {},
601
- ): Observable<
602
- SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
603
- > {
604
- const mutation = {[op]: doc}
605
- const opts = Object.assign({returnFirst: true, returnDocuments: true}, options)
606
- return _dataRequest(client, httpRequest, 'mutate', {mutations: [mutation]}, opts)
607
- }
608
-
609
973
  const hasDataConfig = (client: Client) => {
610
974
  const config = client.config()
611
975
  return (
@@ -638,19 +1002,23 @@ const isData = (client: Client, uri: string) =>
638
1002
  isHistory(client, uri)
639
1003
 
640
1004
  /**
1005
+ * Build the final request options (URL, headers, query params, etc.) used by
1006
+ * both the regular request pipeline and the asset upload path.
1007
+ *
641
1008
  * @internal
642
1009
  */
643
- export function _requestObservable<R>(
644
- client: Client,
645
- httpRequest: HttpRequest,
646
- options: RequestObservableOptions,
647
- ): Observable<HttpRequestEvent<R>> {
648
- // Capture the call-site error here, where the caller's code is still
649
- // on the synchronous call stack, and pass it through to get-it so errors
650
- // include the originating call site rather than just internal plumbing.
651
- const callSiteStack = new Error()
1010
+ export function _prepareRequest(client: Client, options: RequestObservableOptions): FetchRequest {
1011
+ if (options.uri !== undefined) {
1012
+ printDeprecatedUriOptionWarning()
1013
+ }
652
1014
 
653
- const uri = options.url || (options.uri as string)
1015
+ // `uri` takes precedence over `url` to match the v8 behaviour this alias
1016
+ // restores. The types make passing both an error, so this only matters to
1017
+ // untyped callers.
1018
+ const uri = options.uri || options.url
1019
+ if (typeof uri !== 'string') {
1020
+ throw new TypeError('Request options must include a `url`')
1021
+ }
654
1022
  const config = client.config()
655
1023
 
656
1024
  // If the `canUseCdn`-option is not set we detect it automatically based on the method + URL.
@@ -731,31 +1099,114 @@ export function _requestObservable<R>(
731
1099
  }
732
1100
  }
733
1101
 
734
- const reqOptions = requestOptions(
1102
+ return requestOptions(
735
1103
  config,
736
1104
  Object.assign({}, options, {
737
1105
  url: _getUrl(client, uri, useCdn),
738
- callSiteStack,
739
1106
  }),
740
- ) as RequestOptions
741
-
742
- const request = new Observable<HttpRequestEvent<R>>((subscriber) =>
743
- httpRequest(reqOptions, config.requester!).subscribe(subscriber),
744
1107
  )
1108
+ }
745
1109
 
746
- return options.signal ? request.pipe(_withAbortSignal(options.signal)) : request
1110
+ /**
1111
+ * Wrap a promise-returning request in a cold, single-value Observable.
1112
+ *
1113
+ * Each subscription invokes `run` with a fresh `AbortSignal` that is aborted
1114
+ * when the subscriber unsubscribes — so unsubscribing cancels the in-flight
1115
+ * `fetch`. A caller-supplied `userSignal` is chained in too, so the request
1116
+ * still aborts when the caller's own signal fires. The single resolved value
1117
+ * (or rejection) is forwarded to the subscriber.
1118
+ *
1119
+ * This is the bridge that lets the observable client surface reuse the exact
1120
+ * same promise implementations as the promise client surface, with no
1121
+ * duplicated request logic.
1122
+ *
1123
+ * @internal
1124
+ */
1125
+ function _observe<R>(
1126
+ userSignal: AbortSignal | undefined,
1127
+ run: (signal: AbortSignal) => Promise<R>,
1128
+ ): Observable<R> {
1129
+ return new Observable<R>((subscriber) => {
1130
+ const controller = new AbortController()
1131
+ // `AbortSignal.any` rather than an `addEventListener` on the caller's
1132
+ // signal: that signal can be long-lived and reused across many requests,
1133
+ // and a listener per subscription would accumulate there (`{once: true}`
1134
+ // only cleans up if the signal actually fires).
1135
+ const signal = userSignal ? AbortSignal.any([userSignal, controller.signal]) : controller.signal
1136
+ run(signal).then(
1137
+ (value) => {
1138
+ subscriber.next(value)
1139
+ subscriber.complete()
1140
+ },
1141
+ (err) => subscriber.error(err),
1142
+ )
1143
+ return () => controller.abort()
1144
+ })
1145
+ }
1146
+
1147
+ /**
1148
+ * Promise-based sibling of {@link _requestObservable}. Resolves directly to
1149
+ * the parsed response body without ever constructing an Observable. The abort
1150
+ * signal is honored by the underlying `fetch` (it is threaded onto the request
1151
+ * options), so no RxJS-level teardown is required.
1152
+ *
1153
+ * @internal
1154
+ */
1155
+ export function _request<R>(client: Client, httpRequest: HttpRequest, options: Any): Promise<R> {
1156
+ const reqOptions = _prepareRequest(client, options)
1157
+ return httpRequest(reqOptions, client.config().requestHandler).then((body) => body as R)
747
1158
  }
748
1159
 
749
1160
  /**
1161
+ * Execute an HTTP request through the regular pipeline and resolve to the
1162
+ * parsed response body. Observable wrapper over {@link _request}.
1163
+ *
750
1164
  * @internal
751
1165
  */
752
- export function _request<R>(client: Client, httpRequest: HttpRequest, options: Any): Observable<R> {
753
- const observable = _requestObservable<R>(client, httpRequest, options).pipe(
754
- filter((event: Any) => event.type === 'response'),
755
- map((event: Any) => event.body),
1166
+ export function _requestObservable<R>(
1167
+ client: Client,
1168
+ httpRequest: HttpRequest,
1169
+ options: Any,
1170
+ ): Observable<R> {
1171
+ return _observe(options.signal, (signal) =>
1172
+ _request<R>(client, httpRequest, {...options, signal}),
756
1173
  )
1174
+ }
757
1175
 
758
- return observable
1176
+ /**
1177
+ * Execute an asset upload through the underlying requester directly,
1178
+ * exposing the full upload event stream (progress + response).
1179
+ *
1180
+ * Bypasses the body-only `HttpRequest` boundary so progress events from the
1181
+ * transport layer can surface to consumers, reading the resolved requester
1182
+ * straight off the initialized config.
1183
+ *
1184
+ * @internal
1185
+ */
1186
+ export function _uploadObservable<T>(
1187
+ client: Client,
1188
+ options: RequestObservableOptions,
1189
+ ): Observable<UploadEvent<T>> {
1190
+ const reqOptions = _prepareRequest(client, options)
1191
+ const requester = client.config().requester
1192
+ const request = new Observable<Any>((subscriber) =>
1193
+ requester(reqOptions).subscribe(subscriber),
1194
+ ).pipe(
1195
+ filter((event: Any) => event?.type === 'progress' || event?.type === 'response'),
1196
+ map((event: Any): UploadEvent<T> =>
1197
+ event.type === 'progress'
1198
+ ? {
1199
+ type: 'progress',
1200
+ stage: event.stage,
1201
+ percent: event.percent,
1202
+ total: event.total,
1203
+ loaded: event.loaded,
1204
+ lengthComputable: event.lengthComputable,
1205
+ }
1206
+ : {type: 'response', body: event.body as T},
1207
+ ),
1208
+ )
1209
+ return options.signal ? request.pipe(_withAbortSignal(options.signal)) : request
759
1210
  }
760
1211
 
761
1212
  /**
@@ -806,30 +1257,14 @@ function _withAbortSignal<T>(signal: AbortSignal): MonoTypeOperatorFunction<T> {
806
1257
  })
807
1258
  }
808
1259
  }
809
- // DOMException is supported on most modern browsers and Node.js 18+.
810
- // @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException#browser_compatibility
811
- const isDomExceptionSupported = Boolean(globalThis.DOMException)
812
-
813
1260
  /**
1261
+ * `DOMException` is globally available in every supported runtime
1262
+ * (Node 22.12+ and all modern browsers), so we can construct one directly.
1263
+ *
814
1264
  * @internal
815
- * @param signal - The abort signal to use.
816
- * Original source copied from https://github.com/sindresorhus/ky/blob/740732c78aad97e9aec199e9871bdbf0ae29b805/source/errors/DOMException.ts
817
- * TODO: When targeting Node.js 18, use `signal.throwIfAborted()` (https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/throwIfAborted)
818
1265
  */
819
- function _createAbortError(signal?: AbortSignal) {
820
- /*
821
- NOTE: Use DomException with AbortError name as specified in MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort)
822
- > When abort() is called, the fetch() promise rejects with an Error of type DOMException, with name AbortError.
823
- */
824
- if (isDomExceptionSupported) {
825
- return new DOMException(signal?.reason ?? 'The operation was aborted.', 'AbortError')
826
- }
827
-
828
- // DOMException not supported. Fall back to use of error and override name.
829
- const error = new Error(signal?.reason ?? 'The operation was aborted.')
830
- error.name = 'AbortError'
831
-
832
- return error
1266
+ function _createAbortError(signal?: AbortSignal): DOMException {
1267
+ return new DOMException(signal?.reason ?? 'The operation was aborted.', 'AbortError')
833
1268
  }
834
1269
 
835
1270
  const resourceDataBase = (config: InitializedClientConfig): string => {