@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.
- package/dist/index.browser.cjs +1335 -0
- package/dist/index.browser.cjs.map +1 -0
- package/dist/index.browser.js +1312 -0
- package/dist/index.browser.js.map +1 -0
- package/dist/index.cjs +1344 -0
- package/dist/index.cjs.js +15 -0
- package/dist/index.cjs.map +1 -0
- package/{sanityClient.d.ts → dist/index.d.ts} +991 -1117
- package/dist/index.js +1321 -0
- package/dist/index.js.map +1 -0
- package/package.json +81 -73
- package/src/SanityClient.ts +1261 -0
- package/src/assets/AssetsClient.ts +164 -0
- package/src/auth/AuthClient.ts +43 -0
- package/src/config.ts +95 -0
- package/src/data/dataMethods.ts +328 -0
- package/src/data/encodeQueryString.ts +28 -0
- package/src/data/listen.ts +195 -0
- package/src/data/patch.ts +353 -0
- package/src/data/transaction.ts +352 -0
- package/src/datasets/DatasetsClient.ts +97 -0
- package/src/generateHelpUrl.ts +5 -0
- package/src/http/browserMiddleware.ts +1 -0
- package/src/http/errors.ts +68 -0
- package/src/http/nodeMiddleware.ts +11 -0
- package/src/http/request.ts +50 -0
- package/src/http/requestOptions.ts +31 -0
- package/src/index.browser.ts +18 -0
- package/src/index.ts +57 -0
- package/src/projects/ProjectsClient.ts +45 -0
- package/src/types.ts +502 -0
- package/src/users/UsersClient.ts +46 -0
- package/src/util/defaults.ts +8 -0
- package/src/util/getSelection.ts +21 -0
- package/src/util/once.ts +12 -0
- package/src/util/pick.ts +9 -0
- package/src/validators.ts +76 -0
- package/src/warnings.ts +25 -0
- package/umd/sanityClient.js +5199 -5302
- package/umd/sanityClient.min.js +13 -13
- package/dist/sanityClient.browser.mjs +0 -2806
- package/dist/sanityClient.browser.mjs.map +0 -7
- package/index.js +0 -7
- package/lib/assets/assetsClient.js +0 -145
- package/lib/auth/authClient.js +0 -26
- package/lib/config.js +0 -88
- package/lib/data/dataMethods.js +0 -205
- package/lib/data/encodeQueryString.js +0 -31
- package/lib/data/listen.js +0 -164
- package/lib/data/patch.js +0 -121
- package/lib/data/transaction.js +0 -117
- package/lib/datasets/datasetsClient.js +0 -41
- package/lib/generateHelpUrl.js +0 -11
- package/lib/http/browserMiddleware.js +0 -9
- package/lib/http/errors.js +0 -56
- package/lib/http/nodeMiddleware.js +0 -22
- package/lib/http/queryString.js +0 -17
- package/lib/http/request.js +0 -52
- package/lib/http/requestOptions.js +0 -30
- package/lib/projects/projectsClient.js +0 -25
- package/lib/sanityClient.js +0 -118
- package/lib/users/usersClient.js +0 -20
- package/lib/util/defaults.js +0 -14
- package/lib/util/getSelection.js +0 -24
- package/lib/util/once.js +0 -20
- package/lib/util/pick.js +0 -17
- package/lib/validators.js +0 -76
- 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
|
+
}
|