sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
93 lines (85 loc) • 2.72 kB
text/typescript
import {type SanityClient} from '@sanity/client'
import {ConcurrencyLimiter} from '@sanity/util/concurrency-limiter'
import {
bufferTime,
filter,
finalize,
firstValueFrom,
from,
map,
mergeMap,
share,
Subject,
switchMap,
} from 'rxjs'
import {type AvailabilityResponse} from '../../preview'
/**
* The amount of time reserved for waiting for new IDs.
*/
const BUFFER_TIME = 250
/**
* The upper limit for IDs sent to the `doc` endpoint at once. From some manual
* testing, 100 seems to be a safe amount.
*/
export const MAX_BUFFER_SIZE = 100
/**
* The max amount of inflight requests to the `doc` endpoint to check for
* availability. Currently set to 1 because the endpoint is expensive
*
* From: https://www.sanity.io/docs/http-doc
*
* "it is less scalable/performant than the other query-mechanisms, so should
* be used sparingly*
*/
export const MAX_REQUEST_CONCURRENCY = 1
export function createBatchedGetDocumentExists(
client: SanityClient,
): (options: {id: string}) => Promise<boolean> {
const id$ = new Subject<string>()
const limiter = new ConcurrencyLimiter(MAX_REQUEST_CONCURRENCY)
const existence$ = id$.pipe(
bufferTime(BUFFER_TIME, null, MAX_BUFFER_SIZE),
map((ids) => Array.from(new Set(ids))),
mergeMap((ids) =>
from(limiter.ready()).pipe(
switchMap(() =>
client.observable
.request<AvailabilityResponse>({
uri: client.getDataUrl('doc', ids.join(',')),
json: true,
query: {excludeContent: 'true'},
tag: 'documents-availability',
})
.pipe(map((availability) => ({availability, ids}))),
),
finalize(limiter.release),
),
),
mergeMap(({availability, ids}) =>
ids.map((id) => {
const omittedIds = availability.omitted.reduce<Record<string, 'existence' | 'permission'>>(
(acc, next) => {
acc[next.id] = next.reason
return acc
},
{},
)
// if not in the `omitted`, then it exists
if (!omittedIds[id]) return {id, exists: true}
// if in the `omitted` due to existence, then it does not exist
if (omittedIds[id] === 'existence') return {id, exists: false}
// otherwise, it must exist
return {id, exists: true}
}),
),
share(),
)
return async function getDocumentExists(options) {
// set up a promise/listener that waits for the result
const result = firstValueFrom(existence$.pipe(filter(({id}) => id === options.id)))
// send off the request to the stream for batching
id$.next(options.id)
const {exists} = await result
return exists
}
}