@sanity/client
Version:
Client for retrieving, creating and patching data from Sanity.io
85 lines (79 loc) • 3.25 kB
text/typescript
import {defineRequester, type EnvironmentOptions} from './http/request'
import type {ClientConfig, HttpRequest} from './types'
export {isTimeoutError, type TimeoutErrorLike} from 'get-it'
export {validateApiPerspective} from './config'
export {
ChannelError,
connectEventSource,
ConnectionFailedError,
DisconnectError,
type EventSourceEvent,
type EventSourceInstance,
MessageError,
MessageParseError,
type ServerSentEvent,
} from './data/eventsource'
export * from './data/patch'
export * from './data/transaction'
export {
ClientError,
CorsOriginError,
formatQueryParseError,
type HttpError,
isHttpError,
isQueryParseError,
ServerError,
} from './http/errors'
export * as Context from './context/types'
export * from './SanityClient'
export * from './types'
/**
* Create the `requester` and `createClient` exports, that have environment specific middleware for node and browsers
* @internal
*/
export default function defineCreateClientExports<
SanityClientType,
ClientConfigType extends ClientConfig,
>(
envOptions: EnvironmentOptions,
ClassConstructor: new (httpRequest: HttpRequest, config: ClientConfigType) => SanityClientType,
) {
// Set the http client to use for requests, and its environment specific options
const defaultRequester = defineRequester(envOptions).observable
const createClient = (config: ClientConfigType) => {
const {observable: clientRequester, promise: clientRequesterPromise} = defineRequester(
envOptions,
{
ignoreWarnings: config.ignoreWarnings,
maxRetries: config.maxRetries,
retryDelay: config.retryDelay,
},
)
// The single transport for the whole client. Resolves a request to its
// parsed response body as a Promise; the observable client surface wraps
// this in `new Observable(...)` (see `_observe` in dataMethods).
// Redirects are surfaced rather than followed unless a request opts in
// (via the public `maxRedirects` option, translated in `requestOptions`).
const performRequest = async (options: Parameters<HttpRequest>[0]): Promise<unknown> => {
const event = await clientRequesterPromise({redirect: 'manual', ...options})
return event.body
}
const httpRequest: HttpRequest = (options, requestHandler) =>
requestHandler ? requestHandler(options, performRequest) : performRequest(options)
// Populate `requester` on the initialized config so internal paths
// (e.g. the asset upload event stream) can reach the underlying transport.
// `resolveFetch` is threaded onto the config so request building and
// EventSource fetches can reach the environment's fetch without importing
// `get-it/node` from a browser-shared module (would leak `undici` into
// the browser bundle). A caller-supplied `resolveFetch` wins over the
// environment's — that is the supported way to swap the transport (the
// test suite injects its mock fetch through it).
const resolvedConfig = {
...config,
requester: clientRequester,
resolveFetch: config.resolveFetch ?? envOptions.resolveFetch,
}
return new ClassConstructor(httpRequest, resolvedConfig)
}
return {requester: defaultRequester, createClient}
}