Skip to content

feat(views): implement dev mode #1086

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 65 additions & 13 deletions src/SanityClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,13 @@ export class ObservableSanityClient {
): Observable<
SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
> {
return dataMethods._create<R>(this.#clientConfig, this.#httpRequest, document, 'create', options)
return dataMethods._create<R>(
this.#clientConfig,
this.#httpRequest,
document,
'create',
options,
)
}

/**
Expand Down Expand Up @@ -387,7 +393,12 @@ export class ObservableSanityClient {
): Observable<
SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
> {
return dataMethods._createIfNotExists<R>(this.#clientConfig, this.#httpRequest, document, options)
return dataMethods._createIfNotExists<R>(
this.#clientConfig,
this.#httpRequest,
document,
options,
)
}

/**
Expand Down Expand Up @@ -726,7 +737,13 @@ export class ObservableSanityClient {
): Observable<SingleActionResult | MultipleActionResult> {
const documentVersionId = getDocumentVersionId(publishedId, releaseId)

return dataMethods._discardVersion(this.config(), this.#httpRequest, documentVersionId, purge, options)
return dataMethods._discardVersion(
this.config(),
this.#httpRequest,
documentVersionId,
purge,
options,
)
}

/**
Expand Down Expand Up @@ -830,7 +847,12 @@ export class ObservableSanityClient {

const documentVersion = {...document, _id: documentVersionId}

return dataMethods._replaceVersion<R>(this.config(), this.#httpRequest, documentVersion, options)
return dataMethods._replaceVersion<R>(
this.config(),
this.#httpRequest,
documentVersion,
options,
)
}

/**
Expand Down Expand Up @@ -860,7 +882,13 @@ export class ObservableSanityClient {
): Observable<SingleActionResult | MultipleActionResult> {
const versionId = getVersionId(publishedId, releaseId)

return dataMethods._unpublishVersion(this.config(), this.#httpRequest, versionId, publishedId, options)
return dataMethods._unpublishVersion(
this.config(),
this.#httpRequest,
versionId,
publishedId,
options,
)
}

/**
Expand Down Expand Up @@ -1208,7 +1236,9 @@ export class SanityClient {
id: string,
options?: {signal?: AbortSignal; tag?: string; releaseId?: string},
): Promise<SanityDocument<R> | undefined> {
return lastValueFrom(dataMethods._getDocument<R>(this.#clientConfig, this.#httpRequest, id, options))
return lastValueFrom(
dataMethods._getDocument<R>(this.#clientConfig, this.#httpRequest, id, options),
)
}

/**
Expand All @@ -1224,7 +1254,9 @@ export class SanityClient {
ids: string[],
options?: {signal?: AbortSignal; tag?: string},
): Promise<(SanityDocument<R> | null)[]> {
return lastValueFrom(dataMethods._getDocuments<R>(this.#clientConfig, this.#httpRequest, ids, options))
return lastValueFrom(
dataMethods._getDocuments<R>(this.#clientConfig, this.#httpRequest, ids, options),
)
}

/**
Expand Down Expand Up @@ -1671,7 +1703,9 @@ export class SanityClient {
): Promise<
SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
> {
return lastValueFrom(dataMethods._delete<R>(this.#clientConfig, this.#httpRequest, selection, options))
return lastValueFrom(
dataMethods._delete<R>(this.#clientConfig, this.#httpRequest, selection, options),
)
}

/**
Expand Down Expand Up @@ -1710,7 +1744,13 @@ export class SanityClient {
const documentVersionId = getDocumentVersionId(publishedId, releaseId)

return lastValueFrom(
dataMethods._discardVersion(this.config(), this.#httpRequest, documentVersionId, purge, options),
dataMethods._discardVersion(
this.config(),
this.#httpRequest,
documentVersionId,
purge,
options,
),
)
}

Expand Down Expand Up @@ -1849,7 +1889,13 @@ export class SanityClient {
const versionId = getVersionId(publishedId, releaseId)

return lastValueFrom(
dataMethods._unpublishVersion(this.config(), this.#httpRequest, versionId, publishedId, options),
dataMethods._unpublishVersion(
this.config(),
this.#httpRequest,
versionId,
publishedId,
options,
),
)
}

Expand Down Expand Up @@ -1919,7 +1965,9 @@ export class SanityClient {
): Promise<
SanityDocument<R> | SanityDocument<R>[] | SingleMutationResult | MultipleMutationResult
> {
return lastValueFrom(dataMethods._mutate<R>(this.#clientConfig, this.#httpRequest, operations, options))
return lastValueFrom(
dataMethods._mutate<R>(this.#clientConfig, this.#httpRequest, operations, options),
)
}

/**
Expand Down Expand Up @@ -1982,7 +2030,9 @@ export class SanityClient {
operations: Action | Action[],
options?: BaseActionOptions,
): Promise<SingleActionResult | MultipleActionResult> {
return lastValueFrom(dataMethods._action(this.#clientConfig, this.#httpRequest, operations, options))
return lastValueFrom(
dataMethods._action(this.#clientConfig, this.#httpRequest, operations, options),
)
}

/**
Expand All @@ -2007,7 +2057,9 @@ export class SanityClient {
* @internal
*/
dataRequest(endpoint: string, body: unknown, options?: BaseMutationOptions): Promise<Any> {
return lastValueFrom(dataMethods._dataRequest(this.#clientConfig, this.#httpRequest, endpoint, body, options))
return lastValueFrom(
dataMethods._dataRequest(this.#clientConfig, this.#httpRequest, endpoint, body, options),
)
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,9 @@ export const initConfig = (
const protocol = hostParts[0]
const host = hostParts[1]

const cdnURL = newConfig.isDefaultApi ? `https://${defaultCdnHost}` : newConfig.apiCdnHost || newConfig.apiHost
const cdnURL = newConfig.isDefaultApi
? `https://${defaultCdnHost}`
: newConfig.apiCdnHost || newConfig.apiHost
const cdnURLParts = cdnURL.split('://', 2)
const cdnProtocol = cdnURLParts[0]
const cdnHost = cdnURLParts[1]
Expand Down
54 changes: 44 additions & 10 deletions src/data/dataMethods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,19 @@ export function _fetch<R, Q>(
const mapResponse =
options.filterResponse === false ? (res: Any) => res : (res: Any) => res.result

const {cache, next, ...opts} = {
const {cache, next, useEmulate, connections, ...opts} = {
// Opt out of setting a `signal` on an internal `fetch` if one isn't provided.
// This is necessary in React Server Components to avoid opting out of Request Memoization.
useAbortSignal: typeof options.signal !== 'undefined',
// Set `resultSourceMap' when stega is enabled, as it's required for encoding.
resultSourceMap: stega.enabled ? 'withKeyArraySelector' : options.resultSourceMap,

// Only use emulate if explicitly asked for
useEmulate: false,

// Having connections is a special case for views
connections: undefined,

...options,
// Default to not returning the query, unless `filterResponse` is `false`,
// or `returnQuery` is explicitly set. `true` is the default in Content Lake, so skip if truthy
Expand All @@ -106,7 +113,17 @@ export function _fetch<R, Q>(
? {...opts, fetch: {cache, next}}
: opts

const $request = _dataRequest(config, httpRequest, 'query', {query, params}, reqOpts)
// Use 'emulate' endpoint for view emulation, otherwise use 'query'
const endpoint = useEmulate ? 'emulate' : 'query'
const requestBody = useEmulate
? {
query,
params,
connections: connections,
}
: {query, params}

const $request = _dataRequest(config, httpRequest, endpoint, requestBody, reqOpts)
return stega.enabled
? $request.pipe(
combineLatestWith(
Expand Down Expand Up @@ -413,11 +430,13 @@ export function _dataRequest(
const isMutation = endpoint === 'mutate'
const isAction = endpoint === 'actions'
const isQuery = endpoint === 'query'
const isEmulate = endpoint === 'emulate'

// Check if the query string is within a configured threshold,
// in which case we can use GET. Otherwise, use POST.
const strQuery = isMutation || isAction ? '' : encodeQueryString(body)
const useGet = !isMutation && !isAction && strQuery.length < getQuerySizeLimit
// Emulate endpoint always uses POST
const strQuery = isMutation || isAction || isEmulate ? '' : encodeQueryString(body)
const useGet = !isMutation && !isAction && !isEmulate && strQuery.length < getQuerySizeLimit
const stringQuery = useGet ? strQuery : ''
const returnFirst = options.returnFirst
const {timeout, token, tag, headers, returnQuery, lastLiveEventId, cacheMode} = options
Expand Down Expand Up @@ -501,6 +520,9 @@ const isQuery = (config: InitializedClientConfig, uri: string) =>
const isViewQuery = (config: InitializedClientConfig, uri: string) =>
hasDataConfig(config) && uri.startsWith(_getDataUrl(config, 'views'))

const isEmulate = (config: InitializedClientConfig, uri: string) =>
hasDataConfig(config) && uri.startsWith(_getDataUrl(config, 'emulate'))

const isMutate = (config: InitializedClientConfig, uri: string) =>
hasDataConfig(config) && uri.startsWith(_getDataUrl(config, 'mutate'))

Expand All @@ -520,7 +542,8 @@ const isData = (config: InitializedClientConfig, uri: string) =>
isDoc(config, uri) ||
isListener(config, uri) ||
isHistory(config, uri) ||
isViewQuery(config, uri)
isViewQuery(config, uri) ||
isEmulate(config, uri)

/**
* @internal
Expand Down Expand Up @@ -549,8 +572,11 @@ export function _requestObservable<R>(
options.query = {tag: validate.requestTag(tag), ...options.query}
}

// GROQ query-only parameters
if (['GET', 'HEAD', 'POST'].indexOf(options.method || 'GET') >= 0 && isQuery(config, uri)) {
// GROQ query-only parameters (applies to both query and emulate endpoints)
if (
['GET', 'HEAD', 'POST'].indexOf(options.method || 'GET') >= 0 &&
(isQuery(config, uri) || isEmulate(config, uri))
) {
const resultSourceMap = options.resultSourceMap ?? config.resultSourceMap
if (resultSourceMap !== undefined && resultSourceMap !== false) {
options.query = {resultSourceMap, ...options.query}
Expand Down Expand Up @@ -610,7 +636,11 @@ export function _requestObservable<R>(
/**
* @internal
*/
export function _request<R>(config: InitializedClientConfig, httpRequest: HttpRequest, options: Any): Observable<R> {
export function _request<R>(
config: InitializedClientConfig,
httpRequest: HttpRequest,
options: Any,
): Observable<R> {
const observable = _requestObservable<R>(config, httpRequest, options).pipe(
filter((event: Any) => event.type === 'response'),
map((event: Any) => event.body),
Expand All @@ -622,7 +652,11 @@ export function _request<R>(config: InitializedClientConfig, httpRequest: HttpRe
/**
* @internal
*/
export function _getDataUrl(config: InitializedClientConfig, operation: string, path?: string): string {
export function _getDataUrl(
config: InitializedClientConfig,
operation: string,
path?: string,
): string {
if (config['~experimental_resource']) {
validators.resourceConfig(config)
const resourceBase = resourceDataBase(config)
Expand All @@ -638,7 +672,7 @@ export function _getDataUrl(config: InitializedClientConfig, operation: string,
/**
* @internal
*/
export function _getUrl(config: InitializedClientConfig, uri: string, canUseCdn = false, options: {forceApiUrl?: boolean} = {}): string {
export function _getUrl(config: InitializedClientConfig, uri: string, canUseCdn = false): string {
const {url, cdnUrl} = config
const base = canUseCdn ? cdnUrl : url
return `${base}/${uri.replace(/^\//, '')}`
Expand Down
5 changes: 4 additions & 1 deletion src/datasets/DatasetsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ export class DatasetsClient {
list(): Promise<DatasetsResponse> {
validate.resourceGuard('dataset', this.#client.config())
return lastValueFrom(
_request<DatasetsResponse>(this.#client.config(), this.#httpRequest, {uri: '/datasets', tag: null}),
_request<DatasetsResponse>(this.#client.config(), this.#httpRequest, {
uri: '/datasets',
tag: null,
}),
)
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/projects/ProjectsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export class ObservableProjectsClient {
*/
getById(projectId: string): Observable<SanityProject> {
validate.resourceGuard('projects', this.#client.config())
return _request<SanityProject>(this.#client.config(), this.#httpRequest, {uri: `/projects/${projectId}`})
return _request<SanityProject>(this.#client.config(), this.#httpRequest, {
uri: `/projects/${projectId}`,
})
}
}

Expand Down Expand Up @@ -72,7 +74,9 @@ export class ProjectsClient {
getById(projectId: string): Promise<SanityProject> {
validate.resourceGuard('projects', this.#client.config())
return lastValueFrom(
_request<SanityProject>(this.#client.config(), this.#httpRequest, {uri: `/projects/${projectId}`}),
_request<SanityProject>(this.#client.config(), this.#httpRequest, {
uri: `/projects/${projectId}`,
}),
)
}
}
12 changes: 9 additions & 3 deletions src/releases/ReleasesClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,9 @@ export class ReleasesClient {
releaseId,
}

return lastValueFrom(_action(this.#client.config(), this.#httpRequest, unarchiveAction, options))
return lastValueFrom(
_action(this.#client.config(), this.#httpRequest, unarchiveAction, options),
)
}

/**
Expand Down Expand Up @@ -638,7 +640,9 @@ export class ReleasesClient {
releaseId,
}

return lastValueFrom(_action(this.#client.config(), this.#httpRequest, unscheduleAction, options))
return lastValueFrom(
_action(this.#client.config(), this.#httpRequest, unscheduleAction, options),
)
}

/**
Expand Down Expand Up @@ -682,6 +686,8 @@ export class ReleasesClient {
{releaseId}: {releaseId: string},
options?: BaseMutationOptions,
): Promise<RawQueryResponse<SanityDocument[]>> {
return lastValueFrom(_getReleaseDocuments(this.#client.config(), this.#httpRequest, releaseId, options))
return lastValueFrom(
_getReleaseDocuments(this.#client.config(), this.#httpRequest, releaseId, options),
)
}
}
Loading