Something went wrong. Try again.
forked from tangled.org/core
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154import type { BobbinContext, XrpcRequestInit } from "#lib/api/client.ts";import type { Did, Handle, Nsid } from "@atcute/lexicons/syntax";import { INVALID_HANDLE, type MiniDoc } from "#lib/api/identity.ts";import { jsonPost } from "#lib/api/_request.ts";import { authorDidOf, rkeyFromUri } from "#lib/api/uri.ts";import type * as OrgTangledRepoDefs from "#lib/api/lexicons/types/org/tangled/repo/defs.ts";import type * as OrgTangledQueryEnrichResponse from "#lib/api/lexicons/types/org/tangled/query/enrichResponse.ts";
// payload types, also the keys payloads land under in the data sidecarexport const TYPE_COUNT = "org.tangled.query.enrichResponse#count";export const TYPE_VIEWER = "org.tangled.query.enrichResponse#viewer";export const TYPE_MINIDOC = "blue.microcosm.identity.resolveMiniDoc";export const TYPE_REPO_VIEW = "org.tangled.repo.defs#repoViewBasic";export const TYPE_LABELS = "org.tangled.query.enrichResponse#labels";export const TYPE_REACTIONS = "org.tangled.query.enrichResponse#reactions";
export type PayloadType = | typeof TYPE_COUNT | typeof TYPE_VIEWER | typeof TYPE_MINIDOC | typeof TYPE_REPO_VIEW | typeof TYPE_LABELS | typeof TYPE_REACTIONS;
export type RecordPath = string & {};export type EnvelopePath = ".repo" | ".collection" | ".rkey" | ".";export type LinkSource = `${Nsid}:${RecordPath | EnvelopePath}`;
// a descriptor is also the key its payload reads back under, so the readers// take the whole descriptor, a request and its reads can't drift apartexport interface LinkDescriptor<T extends PayloadType = PayloadType> { source: LinkSource; type: T; targets?: RecordPath[];}
export type CountDescriptor = LinkDescriptor<typeof TYPE_COUNT>;export type ViewerDescriptor = LinkDescriptor<typeof TYPE_VIEWER>;export type DocDescriptor = LinkDescriptor<typeof TYPE_MINIDOC>;export type RepoViewDescriptor = LinkDescriptor<typeof TYPE_REPO_VIEW>;export type LabelsDescriptor = LinkDescriptor<typeof TYPE_LABELS>;export type ReactionsDescriptor = LinkDescriptor<typeof TYPE_REACTIONS>;
// data[ref][source][type] = payload, payload shape depends on the typeexport type Sidecar = Record<string, Record<string, Record<string, unknown>>>;
export interface Enriched<O> { output: O; data: Sidecar;}
export interface EnrichRequest { xrpc: string; params?: Record<string, unknown>; enrich: LinkDescriptor[]; viewer?: string;}
export const target = <T extends PayloadType>( descriptor: LinkDescriptor<T>, targets: RecordPath[]): LinkDescriptor<T> => ({ ...descriptor, targets });
export const targetAll = <T extends PayloadType>( descriptors: LinkDescriptor<T>[], targets: RecordPath[]): LinkDescriptor<T>[] => descriptors.map((descriptor) => target(descriptor, targets));
// bobbin rejects viewer payloads with no viewer, so a logged-out request drops// them here instead of at every call siteexport const enrich = <O>( ctx: BobbinContext, req: EnrichRequest, init?: XrpcRequestInit): Promise<Enriched<O>> => jsonPost<Enriched<O>>( ctx, "org.tangled.query.enrichResponse", { ...req, enrich: req.viewer ? req.enrich : req.enrich.filter((descriptor) => descriptor.type !== TYPE_VIEWER) }, init );
const payloadOf = ( data: Sidecar, ref: string | undefined, { source, type }: LinkDescriptor): unknown => (ref !== undefined ? data[ref]?.[source]?.[type] : undefined);
export const countOf = (data: Sidecar, ref: string | undefined, of: CountDescriptor): number => (payloadOf(data, ref, of) as number | undefined) ?? 0;
// undefined means no viewer or inapplicable descriptor, null means no matching recordexport const viewerUriOf = ( data: Sidecar, ref: string | undefined, of: ViewerDescriptor): string | null | undefined => payloadOf(data, ref, of) as string | null | undefined;
export const viewerRkeyOf = ( data: Sidecar, ref: string | undefined, of: ViewerDescriptor): string | null | undefined => { const uri = viewerUriOf(data, ref, of); return uri ? rkeyFromUri(uri) : uri;};
export const miniDocOf = ( data: Sidecar, did: string | undefined, of: DocDescriptor): MiniDoc | undefined => payloadOf(data, did, of) as MiniDoc | undefined;
export const repoViewOf = ( data: Sidecar, ref: string | undefined, of: RepoViewDescriptor): OrgTangledRepoDefs.RepoViewBasic | undefined => payloadOf(data, ref, of) as OrgTangledRepoDefs.RepoViewBasic | undefined;
export const handleOf = (data: Sidecar, did: string | undefined, of: DocDescriptor): Handle => miniDocOf(data, did, of)?.handle ?? INVALID_HANDLE;
// bobbin only sends the writer's profile when a target asks for author or value.x-tngl-editor// a ticket's author is who opened it, x-tngl-editor only names who wrote the latest versionexport const authorOf = ( data: Sidecar, record: { uri: string; value?: unknown; author?: Did }, of: DocDescriptor): { did: Did; handle: Handle } => { const did = record.author ?? authorDidOf(record); return { did, handle: handleOf(data, did, of) };};
export type Label = OrgTangledQueryEnrichResponse.Labels["labels"][number];
export const labelsOf = (data: Sidecar, ref: string | undefined, of: LabelsDescriptor): Label[] => (payloadOf(data, ref, of) as OrgTangledQueryEnrichResponse.Labels | undefined)?.labels ?? [];
export type ReactionSummary = OrgTangledQueryEnrichResponse.ReactionSummary;
export const reactionsOf = ( data: Sidecar, ref: string | undefined, of: ReactionsDescriptor): ReactionSummary[] => (payloadOf(data, ref, of) as OrgTangledQueryEnrichResponse.Reactions | undefined)?.reactions ?? [];