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
145 lines (124 loc) • 3.79 kB
text/typescript
import {type SanityDocument, type SanityDocumentLike} from '@sanity/types'
import {isNonNullable} from './isNonNullable'
/** @internal */
// nominal/opaque type hack
export type Opaque<T, K> = T & {__opaqueId__: K}
/** @internal */
export type DraftId = Opaque<string, 'draftId'>
/** @internal */
export type PublishedId = Opaque<string, 'publishedId'>
/** @internal */
export const DRAFTS_FOLDER = 'drafts'
const DRAFTS_PREFIX = `${DRAFTS_FOLDER}.`
/**
*
* Checks if the document ID `documentId` has the same ID as `equalsDocumentId`,
* ignoring the draft prefix.
*
* @public
*
* @param documentId - The document ID to check
* @param equalsDocumentId - The document ID to check against
*
* @example
* Draft vs published document ID, but representing the same document:
* ```
* // Prints "true":
* console.log(documentIdEquals('drafts.agot', 'agot'));
* ```
* @example
* Different documents:
* ```
* // Prints "false":
* console.log(documentIdEquals('hp-tcos', 'hp-hbp'));
* ```
*
* @returns `true` if the document IDs are equal, `false` otherwise
*/
export function documentIdEquals(documentId: string, equalsDocumentId: string): boolean {
return getPublishedId(documentId) === getPublishedId(equalsDocumentId)
}
/** @internal */
export function isDraft(document: SanityDocumentLike): boolean {
return isDraftId(document._id)
}
/** @internal */
export function isDraftId(id: string): id is DraftId {
return id.startsWith(DRAFTS_PREFIX)
}
/** @internal */
export function getIdPair(id: string): {draftId: DraftId; publishedId: PublishedId} {
return {
draftId: getDraftId(id),
publishedId: getPublishedId(id),
}
}
/** @internal */
export function isPublishedId(id: string): id is PublishedId {
return !isDraftId(id)
}
/** @internal */
export function getDraftId(id: string): DraftId {
return isDraftId(id) ? id : ((DRAFTS_PREFIX + id) as DraftId)
}
/** @internal */
export function getPublishedId(id: string): PublishedId {
return (isDraftId(id) ? id.slice(DRAFTS_PREFIX.length) : id) as PublishedId
}
/** @internal */
export function createDraftFrom(document: SanityDocument): SanityDocument {
return {
...document,
_id: getDraftId(document._id),
}
}
/** @internal */
export function newDraftFrom(document: SanityDocument): SanityDocument {
return {
...document,
_id: DRAFTS_PREFIX,
}
}
/** @internal */
export function createPublishedFrom(document: SanityDocument): SanityDocument {
return {
...document,
_id: getPublishedId(document._id),
}
}
/**
* Takes a list of documents and collates draft/published pairs into single entries
* `{id: <published id>, draft?: <draft document>, published?: <published document>}`
*
* Note: because Map is ordered by insertion key the resulting array will be ordered by whichever
* version appeared first
*
* @internal
*/
export interface CollatedHit<T extends {_id: string} = {_id: string}> {
id: string
type: string
draft?: T
published?: T
}
/** @internal */
export function collate<T extends {_id: string; _type: string}>(documents: T[]): CollatedHit<T>[] {
const byId = documents.reduce((res, doc) => {
const publishedId = getPublishedId(doc._id)
let entry = res.get(publishedId)
if (!entry) {
entry = {id: publishedId, type: doc._type, published: undefined, draft: undefined}
res.set(publishedId, entry)
}
entry[publishedId === doc._id ? 'published' : 'draft'] = doc
return res
}, new Map())
return Array.from(byId.values())
}
/** @internal */
// Removes published documents that also has a draft
export function removeDupes(documents: SanityDocumentLike[]): SanityDocumentLike[] {
return collate(documents)
.map((entry) => entry.draft || entry.published)
.filter(isNonNullable)
}