@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,164 @@
1
+ import {type Observable, lastValueFrom} from 'rxjs'
2
+ import {filter, map} from 'rxjs/operators'
3
+
4
+ import {_requestObservable} from '../data/dataMethods'
5
+ import type {ObservableSanityClient, SanityClient} from '../SanityClient'
6
+ import type {
7
+ HttpRequest,
8
+ HttpRequestEvent,
9
+ ResponseEvent,
10
+ SanityAssetDocument,
11
+ SanityImageAssetDocument,
12
+ UploadClientConfig,
13
+ } from '../types'
14
+ import * as validators from '../validators'
15
+
16
+ export class ObservableAssetsClient {
17
+ #client: ObservableSanityClient
18
+ #httpRequest: HttpRequest
19
+ constructor(client: ObservableSanityClient, httpRequest: HttpRequest) {
20
+ this.#client = client
21
+ this.#httpRequest = httpRequest
22
+ }
23
+
24
+ /**
25
+ * Uploads a file asset to the configured dataset
26
+ *
27
+ * @param assetType Asset type (file/image)
28
+ * @param body Asset content - can be a browser File instance, a Blob, a Node.js Buffer instance or a Node.js ReadableStream.
29
+ * @param options Options to use for the upload
30
+ */
31
+ upload(
32
+ assetType: 'file',
33
+ body: File | Blob | Buffer | NodeJS.ReadableStream,
34
+ options?: UploadClientConfig
35
+ ): Observable<HttpRequestEvent<{document: SanityAssetDocument}>>
36
+
37
+ /**
38
+ * Uploads an image asset to the configured dataset
39
+ *
40
+ * @param assetType Asset type (file/image)
41
+ * @param body Asset content - can be a browser File instance, a Blob, a Node.js Buffer instance or a Node.js ReadableStream.
42
+ * @param options Options to use for the upload
43
+ */
44
+ upload(
45
+ assetType: 'image',
46
+ body: File | Blob | Buffer | NodeJS.ReadableStream,
47
+ options?: UploadClientConfig
48
+ ): Observable<HttpRequestEvent<{document: SanityImageAssetDocument}>>
49
+ upload(
50
+ assetType: 'file' | 'image',
51
+ body: File | Blob | Buffer | NodeJS.ReadableStream,
52
+ options?: UploadClientConfig
53
+ ): Observable<HttpRequestEvent<{document: SanityAssetDocument | SanityImageAssetDocument}>> {
54
+ return _upload(this.#client, this.#httpRequest, assetType, body, options)
55
+ }
56
+ }
57
+
58
+ export class AssetsClient {
59
+ #client: SanityClient
60
+ #httpRequest: HttpRequest
61
+ constructor(client: SanityClient, httpRequest: HttpRequest) {
62
+ this.#client = client
63
+ this.#httpRequest = httpRequest
64
+ }
65
+
66
+ /**
67
+ * Uploads a file asset to the configured dataset
68
+ *
69
+ * @param assetType Asset type (file/image)
70
+ * @param body Asset content - can be a browser File instance, a Blob, a Node.js Buffer instance or a Node.js ReadableStream.
71
+ * @param options Options to use for the upload
72
+ */
73
+ upload(
74
+ assetType: 'file',
75
+ body: File | Blob | Buffer | NodeJS.ReadableStream,
76
+ options?: UploadClientConfig
77
+ ): Promise<SanityAssetDocument>
78
+ /**
79
+ * Uploads an image asset to the configured dataset
80
+ *
81
+ * @param assetType Asset type (file/image)
82
+ * @param body Asset content - can be a browser File instance, a Blob, a Node.js Buffer instance or a Node.js ReadableStream.
83
+ * @param options Options to use for the upload
84
+ */
85
+ upload(
86
+ assetType: 'image',
87
+ body: File | Blob | Buffer | NodeJS.ReadableStream,
88
+ options?: UploadClientConfig
89
+ ): Promise<SanityImageAssetDocument>
90
+ upload(
91
+ assetType: 'file' | 'image',
92
+ body: File | Blob | Buffer | NodeJS.ReadableStream,
93
+ options?: UploadClientConfig
94
+ ): Promise<SanityAssetDocument | SanityImageAssetDocument> {
95
+ const observable = _upload(this.#client, this.#httpRequest, assetType, body, options)
96
+ return lastValueFrom(
97
+ observable.pipe(
98
+ filter((event: any) => event.type === 'response'),
99
+ map(
100
+ (event) =>
101
+ (event as ResponseEvent<{document: SanityAssetDocument | SanityImageAssetDocument}>)
102
+ .body.document
103
+ )
104
+ )
105
+ )
106
+ }
107
+ }
108
+
109
+ function _upload(
110
+ client: SanityClient | ObservableSanityClient,
111
+ httpRequest: HttpRequest,
112
+ assetType: 'image' | 'file',
113
+ body: File | Blob | Buffer | NodeJS.ReadableStream,
114
+ opts: UploadClientConfig = {}
115
+ ): Observable<HttpRequestEvent<{document: SanityAssetDocument | SanityImageAssetDocument}>> {
116
+ validators.validateAssetType(assetType)
117
+
118
+ // If an empty array is given, explicitly set `none` to override API defaults
119
+ let meta = opts.extract || undefined
120
+ if (meta && !meta.length) {
121
+ meta = ['none']
122
+ }
123
+
124
+ const dataset = validators.hasDataset(client.config())
125
+ const assetEndpoint = assetType === 'image' ? 'images' : 'files'
126
+ const options = optionsFromFile(opts, body)
127
+ const {tag, label, title, description, creditLine, filename, source} = options
128
+ const query: any = {
129
+ label,
130
+ title,
131
+ description,
132
+ filename,
133
+ meta,
134
+ creditLine,
135
+ }
136
+ if (source) {
137
+ query.sourceId = source.id
138
+ query.sourceName = source.name
139
+ query.sourceUrl = source.url
140
+ }
141
+ return _requestObservable(client, httpRequest, {
142
+ tag,
143
+ method: 'POST',
144
+ timeout: options.timeout || 0,
145
+ uri: `/assets/${assetEndpoint}/${dataset}`,
146
+ headers: options.contentType ? {'Content-Type': options.contentType} : {},
147
+ query,
148
+ body,
149
+ })
150
+ }
151
+
152
+ function optionsFromFile(opts: Record<string, any>, file: any) {
153
+ if (typeof window === 'undefined' || !(file instanceof window.File)) {
154
+ return opts
155
+ }
156
+
157
+ return Object.assign(
158
+ {
159
+ filename: opts.preserveFilename === false ? undefined : file.name,
160
+ contentType: file.type,
161
+ },
162
+ opts
163
+ )
164
+ }
@@ -0,0 +1,43 @@
1
+ import {type Observable} from 'rxjs'
2
+
3
+ import type {ObservableSanityClient, SanityClient} from '../SanityClient'
4
+ import type {AuthProviderResponse} from '../types'
5
+
6
+ export class BaseAuthClient {
7
+ client: SanityClient | ObservableSanityClient
8
+
9
+ /**
10
+ * Fetch available login providers
11
+ */
12
+ getLoginProviders(this: AuthClient): Promise<AuthProviderResponse>
13
+ getLoginProviders(this: ObservableAuthClient): Observable<AuthProviderResponse>
14
+ getLoginProviders(): Promise<AuthProviderResponse> | Observable<AuthProviderResponse> {
15
+ return this.client.request({uri: '/auth/providers'})
16
+ }
17
+
18
+ /**
19
+ * Revoke the configured session/token
20
+ */
21
+ logout(this: AuthClient): Promise<any>
22
+ logout(this: ObservableAuthClient): Observable<any>
23
+ logout(): Promise<any> | Observable<any> {
24
+ return this.client.request({uri: '/auth/logout', method: 'POST'})
25
+ }
26
+ }
27
+
28
+ export class ObservableAuthClient extends BaseAuthClient {
29
+ client: ObservableSanityClient
30
+
31
+ constructor(client: ObservableSanityClient) {
32
+ super()
33
+ this.client = client
34
+ }
35
+ }
36
+
37
+ export class AuthClient extends BaseAuthClient {
38
+ client: SanityClient
39
+ constructor(client: SanityClient) {
40
+ super()
41
+ this.client = client
42
+ }
43
+ }
package/src/config.ts ADDED
@@ -0,0 +1,95 @@
1
+ import generateHelpUrl from './generateHelpUrl'
2
+ import type {ClientConfig, InitializedClientConfig} from './types'
3
+ import * as validate from './validators'
4
+ import * as warnings from './warnings'
5
+
6
+ const defaultCdnHost = 'apicdn.sanity.io'
7
+ const defaultConfig = {
8
+ apiHost: 'https://api.sanity.io',
9
+ apiVersion: '1',
10
+ useProjectHostname: true,
11
+ } satisfies ClientConfig
12
+
13
+ const LOCALHOSTS = ['localhost', '127.0.0.1', '0.0.0.0']
14
+ const isLocal = (host: string) => LOCALHOSTS.indexOf(host) !== -1
15
+
16
+ export const validateApiVersion = function validateApiVersion(apiVersion: string) {
17
+ if (apiVersion === '1' || apiVersion === 'X') {
18
+ return
19
+ }
20
+
21
+ const apiDate = new Date(apiVersion)
22
+ const apiVersionValid =
23
+ /^\d{4}-\d{2}-\d{2}$/.test(apiVersion) && apiDate instanceof Date && apiDate.getTime() > 0
24
+
25
+ if (!apiVersionValid) {
26
+ throw new Error('Invalid API version string, expected `1` or date in format `YYYY-MM-DD`')
27
+ }
28
+ }
29
+
30
+ export const initConfig = (
31
+ config: Partial<ClientConfig>,
32
+ prevConfig: Partial<ClientConfig>
33
+ ): InitializedClientConfig => {
34
+ const specifiedConfig = Object.assign({}, prevConfig, config)
35
+ if (!specifiedConfig.apiVersion) {
36
+ warnings.printNoApiVersionSpecifiedWarning()
37
+ }
38
+
39
+ const newConfig = Object.assign({} as InitializedClientConfig, defaultConfig, specifiedConfig)
40
+ const projectBased = newConfig.useProjectHostname
41
+
42
+ if (typeof Promise === 'undefined') {
43
+ const helpUrl = generateHelpUrl('js-client-promise-polyfill')
44
+ throw new Error(`No native Promise-implementation found, polyfill needed - see ${helpUrl}`)
45
+ }
46
+
47
+ if (projectBased && !newConfig.projectId) {
48
+ throw new Error('Configuration must contain `projectId`')
49
+ }
50
+
51
+ const isBrowser = typeof window !== 'undefined' && window.location && window.location.hostname
52
+ const isLocalhost = isBrowser && isLocal(window.location.hostname)
53
+
54
+ if (isBrowser && isLocalhost && newConfig.token && newConfig.ignoreBrowserTokenWarning !== true) {
55
+ warnings.printBrowserTokenWarning()
56
+ } else if (typeof newConfig.useCdn === 'undefined') {
57
+ warnings.printCdnWarning()
58
+ }
59
+
60
+ if (projectBased) {
61
+ validate.projectId(newConfig.projectId)
62
+ }
63
+
64
+ if (newConfig.dataset) {
65
+ validate.dataset(newConfig.dataset)
66
+ }
67
+
68
+ if ('requestTagPrefix' in newConfig) {
69
+ // Allow setting and unsetting request tag prefix
70
+ newConfig.requestTagPrefix = newConfig.requestTagPrefix
71
+ ? validate.requestTag(newConfig.requestTagPrefix).replace(/\.+$/, '')
72
+ : undefined
73
+ }
74
+
75
+ newConfig.apiVersion = `${newConfig.apiVersion}`.replace(/^v/, '')
76
+ newConfig.isDefaultApi = newConfig.apiHost === defaultConfig.apiHost
77
+ newConfig.useCdn = Boolean(newConfig.useCdn) && !newConfig.withCredentials
78
+
79
+ validateApiVersion(newConfig.apiVersion)
80
+
81
+ const hostParts = newConfig.apiHost.split('://', 2)
82
+ const protocol = hostParts[0]
83
+ const host = hostParts[1]
84
+ const cdnHost = newConfig.isDefaultApi ? defaultCdnHost : host
85
+
86
+ if (newConfig.useProjectHostname) {
87
+ newConfig.url = `${protocol}://${newConfig.projectId}.${host}/v${newConfig.apiVersion}`
88
+ newConfig.cdnUrl = `${protocol}://${newConfig.projectId}.${cdnHost}/v${newConfig.apiVersion}`
89
+ } else {
90
+ newConfig.url = `${newConfig.apiHost}/v${newConfig.apiVersion}`
91
+ newConfig.cdnUrl = newConfig.url
92
+ }
93
+
94
+ return newConfig
95
+ }
@@ -0,0 +1,328 @@
1
+ import {Observable} from 'rxjs'
2
+ import {filter, map} from 'rxjs/operators'
3
+
4
+ import getRequestOptions from '../http/requestOptions'
5
+ import type {ObservableSanityClient, SanityClient} from '../SanityClient'
6
+ import type {
7
+ AllDocumentIdsMutationOptions,
8
+ AllDocumentsMutationOptions,
9
+ BaseMutationOptions,
10
+ FilteredResponseQueryOptions,
11
+ FirstDocumentIdMutationOptions,
12
+ FirstDocumentMutationOptions,
13
+ HttpRequest,
14
+ HttpRequestEvent,
15
+ IdentifiedSanityDocumentStub,
16
+ MultipleMutationResult,
17
+ Mutation,
18
+ MutationSelection,
19
+ QueryParams,
20
+ RawQueryResponse,
21
+ RequestObservableOptions,
22
+ RequestOptions,
23
+ SanityDocument,
24
+ SingleMutationResult,
25
+ UnfilteredResponseQueryOptions,
26
+ } from '../types'
27
+ import getSelection from '../util/getSelection'
28
+ import * as validate from '../validators'
29
+ import * as validators from '../validators'
30
+ import encodeQueryString from './encodeQueryString'
31
+ import {ObservablePatch, Patch} from './patch'
32
+ import {ObservableTransaction, Transaction} from './transaction'
33
+
34
+ const excludeFalsey = (param: any, defValue: any) => {
35
+ const value = typeof param === 'undefined' ? defValue : param
36
+ return param === false ? undefined : value
37
+ }
38
+
39
+ const getMutationQuery = (options: BaseMutationOptions = {}) => {
40
+ return {
41
+ dryRun: options.dryRun,
42
+ returnIds: true,
43
+ returnDocuments: excludeFalsey(options.returnDocuments, true),
44
+ visibility: options.visibility || 'sync',
45
+ autoGenerateArrayKeys: options.autoGenerateArrayKeys,
46
+ skipCrossDatasetReferenceValidation: options.skipCrossDatasetReferenceValidation,
47
+ }
48
+ }
49
+
50
+ const isResponse = (event: any) => event.type === 'response'
51
+ const getBody = (event: any) => event.body
52
+
53
+ const indexBy = (docs: any[], attr: any) =>
54
+ docs.reduce((indexed, doc) => {
55
+ indexed[attr(doc)] = doc
56
+ return indexed
57
+ }, Object.create(null))
58
+
59
+ const getQuerySizeLimit = 11264
60
+
61
+ /** @internal */
62
+ export function _fetch<R>(
63
+ client: ObservableSanityClient | SanityClient,
64
+ httpRequest: HttpRequest,
65
+ query: string,
66
+ params?: QueryParams,
67
+ options: FilteredResponseQueryOptions | UnfilteredResponseQueryOptions = {}
68
+ ): Observable<RawQueryResponse<R> | R> {
69
+ const mapResponse =
70
+ options.filterResponse === false ? (res: any) => res : (res: any) => res.result
71
+
72
+ return _dataRequest(client, httpRequest, 'query', {query, params}, options).pipe(map(mapResponse))
73
+ }
74
+
75
+ /** @internal */
76
+ export function _getDocument<R extends Record<string, any>>(
77
+ client: ObservableSanityClient | SanityClient,
78
+ httpRequest: HttpRequest,
79
+ id: string,
80
+ opts: {tag?: string} = {}
81
+ ): Observable<SanityDocument<R> | undefined> {
82
+ const options = {uri: client.getDataUrl('doc', id), json: true, tag: opts.tag}
83
+ return _requestObservable<SanityDocument<R> | undefined>(client, httpRequest, options).pipe(
84
+ filter(isResponse),
85
+ map((event) => event.body.documents && event.body.documents[0])
86
+ )
87
+ }
88
+
89
+ /** @internal */
90
+ export function _getDocuments<R extends Record<string, any>>(
91
+ client: ObservableSanityClient | SanityClient,
92
+ httpRequest: HttpRequest,
93
+ ids: string[],
94
+ opts: {tag?: string} = {}
95
+ ): Observable<(SanityDocument<R> | null)[]> {
96
+ const options = {uri: client.getDataUrl('doc', ids.join(',')), json: true, tag: opts.tag}
97
+ return _requestObservable<(SanityDocument<R> | null)[]>(client, httpRequest, options).pipe(
98
+ filter(isResponse),
99
+ map((event: any) => {
100
+ const indexed = indexBy(event.body.documents || [], (doc: any) => doc._id)
101
+ return ids.map((id) => indexed[id] || null)
102
+ })
103
+ )
104
+ }
105
+
106
+ /** @internal */
107
+ export function _createIfNotExists<R extends Record<string, any>>(
108
+ client: ObservableSanityClient | SanityClient,
109
+ httpRequest: HttpRequest,
110
+ doc: IdentifiedSanityDocumentStub<R>,
111
+ options?:
112
+ | AllDocumentIdsMutationOptions
113
+ | AllDocumentsMutationOptions
114
+ | BaseMutationOptions
115
+ | FirstDocumentIdMutationOptions
116
+ | FirstDocumentMutationOptions
117
+ ): Observable<
118
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
119
+ > {
120
+ validators.requireDocumentId('createIfNotExists', doc)
121
+ return _create<R>(client, httpRequest, doc, 'createIfNotExists', options)
122
+ }
123
+
124
+ /** @internal */
125
+ export function _createOrReplace<R extends Record<string, any>>(
126
+ client: ObservableSanityClient | SanityClient,
127
+ httpRequest: HttpRequest,
128
+ doc: IdentifiedSanityDocumentStub<R>,
129
+ options?:
130
+ | AllDocumentIdsMutationOptions
131
+ | AllDocumentsMutationOptions
132
+ | BaseMutationOptions
133
+ | FirstDocumentIdMutationOptions
134
+ | FirstDocumentMutationOptions
135
+ ): Observable<
136
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
137
+ > {
138
+ validators.requireDocumentId('createOrReplace', doc)
139
+ return _create<R>(client, httpRequest, doc, 'createOrReplace', options)
140
+ }
141
+
142
+ /** @internal */
143
+ export function _delete<R extends Record<string, any>>(
144
+ client: ObservableSanityClient | SanityClient,
145
+ httpRequest: HttpRequest,
146
+ selection: string | MutationSelection,
147
+ options?:
148
+ | AllDocumentIdsMutationOptions
149
+ | AllDocumentsMutationOptions
150
+ | BaseMutationOptions
151
+ | FirstDocumentIdMutationOptions
152
+ | FirstDocumentMutationOptions
153
+ ): Observable<
154
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
155
+ > {
156
+ return _dataRequest(
157
+ client,
158
+ httpRequest,
159
+ 'mutate',
160
+ {mutations: [{delete: getSelection(selection)}]},
161
+ options
162
+ )
163
+ }
164
+
165
+ /** @internal */
166
+ export function _mutate<R extends Record<string, any>>(
167
+ client: SanityClient | ObservableSanityClient,
168
+ httpRequest: HttpRequest,
169
+ mutations: Mutation<R>[] | Patch | ObservablePatch | Transaction | ObservableTransaction,
170
+ options?:
171
+ | AllDocumentIdsMutationOptions
172
+ | AllDocumentsMutationOptions
173
+ | BaseMutationOptions
174
+ | FirstDocumentIdMutationOptions
175
+ | FirstDocumentMutationOptions
176
+ ): Observable<
177
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
178
+ > {
179
+ const mut =
180
+ mutations instanceof Patch ||
181
+ mutations instanceof ObservablePatch ||
182
+ mutations instanceof Transaction ||
183
+ mutations instanceof ObservableTransaction
184
+ ? mutations.serialize()
185
+ : mutations
186
+
187
+ const muts = Array.isArray(mut) ? mut : [mut]
188
+ const transactionId = options && (options as any).transactionId
189
+ return _dataRequest(client, httpRequest, 'mutate', {mutations: muts, transactionId}, options)
190
+ }
191
+
192
+ /**
193
+ * @internal
194
+ */
195
+ export function _dataRequest(
196
+ client: SanityClient | ObservableSanityClient,
197
+ httpRequest: HttpRequest,
198
+ endpoint: string,
199
+ body: any,
200
+ options: any = {}
201
+ ): any {
202
+ const isMutation = endpoint === 'mutate'
203
+ const isQuery = endpoint === 'query'
204
+
205
+ // Check if the query string is within a configured threshold,
206
+ // in which case we can use GET. Otherwise, use POST.
207
+ const strQuery = isMutation ? '' : encodeQueryString(body)
208
+ const useGet = !isMutation && strQuery.length < getQuerySizeLimit
209
+ const stringQuery = useGet ? strQuery : ''
210
+ const returnFirst = options.returnFirst
211
+ const {timeout, token, tag, headers} = options
212
+
213
+ const uri = client.getDataUrl(endpoint, stringQuery)
214
+
215
+ const reqOptions = {
216
+ method: useGet ? 'GET' : 'POST',
217
+ uri: uri,
218
+ json: true,
219
+ body: useGet ? undefined : body,
220
+ query: isMutation && getMutationQuery(options),
221
+ timeout,
222
+ headers,
223
+ token,
224
+ tag,
225
+ canUseCdn: isQuery,
226
+ }
227
+
228
+ return _requestObservable(client, httpRequest, reqOptions).pipe(
229
+ filter(isResponse),
230
+ map(getBody),
231
+ map((res) => {
232
+ if (!isMutation) {
233
+ return res
234
+ }
235
+
236
+ // Should we return documents?
237
+ const results = res.results || []
238
+ if (options.returnDocuments) {
239
+ return returnFirst
240
+ ? results[0] && results[0].document
241
+ : results.map((mut: any) => mut.document)
242
+ }
243
+
244
+ // Return a reduced subset
245
+ const key = returnFirst ? 'documentId' : 'documentIds'
246
+ const ids = returnFirst ? results[0] && results[0].id : results.map((mut: any) => mut.id)
247
+ return {
248
+ transactionId: res.transactionId,
249
+ results: results,
250
+ [key]: ids,
251
+ }
252
+ })
253
+ )
254
+ }
255
+
256
+ /**
257
+ * @internal
258
+ */
259
+ export function _create<R extends Record<string, any>>(
260
+ client: SanityClient | ObservableSanityClient,
261
+ httpRequest: HttpRequest,
262
+ doc: any,
263
+ op: any,
264
+ options: any = {}
265
+ ): Observable<
266
+ SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
267
+ > {
268
+ const mutation = {[op]: doc}
269
+ const opts = Object.assign({returnFirst: true, returnDocuments: true}, options)
270
+ return _dataRequest(client, httpRequest, 'mutate', {mutations: [mutation]}, opts)
271
+ }
272
+
273
+ /**
274
+ * @internal
275
+ */
276
+ export function _requestObservable<R>(
277
+ client: SanityClient | ObservableSanityClient,
278
+ httpRequest: HttpRequest,
279
+ options: RequestObservableOptions
280
+ ): Observable<HttpRequestEvent<R>> {
281
+ const uri = options.url || (options.uri as string)
282
+ const config = client.config()
283
+
284
+ // If the `canUseCdn`-option is not set we detect it automatically based on the method + URL.
285
+ // Only the /data endpoint is currently available through API-CDN.
286
+ const canUseCdn =
287
+ typeof options.canUseCdn === 'undefined'
288
+ ? ['GET', 'HEAD'].indexOf(options.method || 'GET') >= 0 && uri.indexOf('/data/') === 0
289
+ : options.canUseCdn
290
+
291
+ const useCdn = config.useCdn && canUseCdn
292
+
293
+ const tag =
294
+ options.tag && config.requestTagPrefix
295
+ ? [config.requestTagPrefix, options.tag].join('.')
296
+ : options.tag || config.requestTagPrefix
297
+
298
+ if (tag) {
299
+ options.query = {tag: validate.requestTag(tag), ...options.query}
300
+ }
301
+
302
+ const reqOptions = getRequestOptions(
303
+ config,
304
+ Object.assign({}, options, {
305
+ url: client.getUrl(uri, useCdn),
306
+ })
307
+ ) as RequestOptions
308
+
309
+ return new Observable<HttpRequestEvent<R>>((subscriber) =>
310
+ httpRequest(reqOptions, config.requester!).subscribe(subscriber)
311
+ )
312
+ }
313
+
314
+ /**
315
+ * @internal
316
+ */
317
+ export function _request<R>(
318
+ client: SanityClient | ObservableSanityClient,
319
+ httpRequest: HttpRequest,
320
+ options: any
321
+ ): Observable<R> {
322
+ const observable = _requestObservable<R>(client, httpRequest, options).pipe(
323
+ filter((event: any) => event.type === 'response'),
324
+ map((event: any) => event.body)
325
+ )
326
+
327
+ return observable
328
+ }
@@ -0,0 +1,28 @@
1
+ import type {QueryParams} from '../types'
2
+
3
+ const enc = encodeURIComponent
4
+
5
+ export default ({
6
+ query,
7
+ params = {} as any,
8
+ options = {} as any,
9
+ }: {
10
+ query: string
11
+ params?: QueryParams
12
+ options?: any
13
+ }) => {
14
+ // We generally want tag at the start of the query string
15
+ const {tag, ...opts} = options
16
+ const q = `query=${enc(query)}`
17
+ const base = tag ? `?tag=${enc(tag)}&${q}` : `?${q}`
18
+
19
+ const qString = Object.keys(params).reduce(
20
+ (qs, param) => `${qs}&${enc(`$${param}`)}=${enc(JSON.stringify(params[param]))}`,
21
+ base
22
+ )
23
+
24
+ return Object.keys(opts).reduce((qs, option) => {
25
+ // Only include the option if it is truthy
26
+ return options[option] ? `${qs}&${enc(option)}=${enc(options[option])}` : qs
27
+ }, qString)
28
+ }