Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109import { ClientResponseError } from "@atcute/client";import type { BobbinContext, XrpcRequestInit } from "#lib/api/client.ts";import { enrich, type Enriched, type LinkDescriptor } from "#lib/api/enrich.ts";import type { TicketRecord, TicketState } from "#lib/api/records.ts";import { resolveMiniDoc } from "#lib/api/identity.ts";import { TICKET_COLLECTION } from "#lib/api/ticket.ts";import { resolveAuthors } from "#lib/ticketQuery.ts";
export interface TicketHit { uri: string; cid?: string; nsid: string; score: number; value: TicketRecord; state: TicketState; commentCount: number;}
export interface StateCounts { open: number; closed: number; completed: number;}
export interface TicketSearchPage { hits: TicketHit[]; cursor?: string | null; total?: number; stateCounts?: StateCounts;}
export interface TicketSearchOptions { repoDid: string; kind: "issue" | "pull"; /** the search text, empty lists all tickets */ query: string; limit: number; offset?: number; enrich: LinkDescriptor[];}
export const searchTickets = ( ctx: BobbinContext, { repoDid, kind, query, limit, offset, enrich: descriptors }: TicketSearchOptions, init?: XrpcRequestInit): Promise<Enriched<TicketSearchPage>> => enrich<TicketSearchPage>( ctx, { xrpc: "org.tangled.search.query", params: { ...(query ? { q: query } : {}), repo: repoDid, nsid: TICKET_COLLECTION, kind, limit, ...(offset ? { offset } : {}) }, enrich: descriptors }, init );
export interface TicketSearchOutcome { result: Enriched<TicketSearchPage> | null; error?: string;}
export type SearchOutcome<T> = { result: T; error?: never } | { result: null; error: string };
export const searchWithAuthors = async <T>( ctx: BobbinContext, q: string, search: (query: string) => Promise<T>): Promise<SearchOutcome<T>> => { const { query, unresolved } = await resolveAuthors(q, (handle) => resolveMiniDoc(ctx, handle).then( (doc) => doc.did, () => undefined ) ); if (unresolved.length > 0) { const error = unresolved.map((handle) => `\`author:${handle}\` could not be found`); return { result: null, error: error.join("; ") }; } try { return { result: await search(query) }; } catch (cause) { const error = invalidQueryMessage(cause); if (error === undefined) throw cause; return { result: null, error }; }};
/** searches tickets and returns an InvalidQuery message as an error */export const trySearchTickets = ( ctx: BobbinContext, options: TicketSearchOptions): Promise<TicketSearchOutcome> => searchWithAuthors(ctx, options.query, (query) => searchTickets(ctx, { ...options, query }));
/** the message of an InvalidQuery error or undefined */export const invalidQueryMessage = (cause: unknown): string | undefined => cause instanceof ClientResponseError && cause.error === "InvalidQuery" ? (cause.description ?? "this search could not be understood") : undefined;
export const EMPTY_COUNTS: StateCounts = { open: 0, closed: 0, completed: 0 };