import type { Did, ResourceUri } from '@atcute/lexicons/syntax';
import {
ShTangledActorProfile,
ShTangledLabelDefinition,
ShTangledRepo,
ShTangledRepoIssue,
ShTangledRepoIssueComment,
ShTangledRepoPull,
ShTangledRepoPullComment,
ShTangledString,
} from '@atcute/tangled';
import {
appviewJson,
type AppviewSearchResponse,
} from './appview';
import { appviewRecordToHydrated } from './hydration';
import type { HydratedRecord } from './records';
export type SearchCollection =
| 'sh.tangled.actor.profile'
| 'sh.tangled.repo'
| 'sh.tangled.repo.issue'
| 'sh.tangled.repo.issue.comment'
| 'sh.tangled.repo.pull'
| 'sh.tangled.repo.pull.comment'
| 'sh.tangled.feed.comment'
| 'sh.tangled.string'
| 'sh.tangled.label.definition';
export interface ShTangledFeedComment {
$type: 'sh.tangled.feed.comment';
subject: {
uri: ResourceUri;
cid: string;
};
body: {
$type: 'sh.tangled.markup.markdown';
text: string;
};
createdAt: string;
replyTo?: {
uri: ResourceUri;
cid: string;
};
}
export type SearchRecordValue =
| ShTangledActorProfile.Main
| ShTangledRepo.Main
| ShTangledRepoIssue.Main
| ShTangledRepoIssueComment.Main
| ShTangledRepoPull.Main
| ShTangledRepoPullComment.Main
| ShTangledString.Main
| ShTangledLabelDefinition.Main
| ShTangledFeedComment;
export interface SearchHit extends HydratedRecord<SearchRecordValue> {
nsid: SearchCollection;
score: number;
}
export interface SearchResponse {
hits: SearchHit[];
cursor?: string;
}
export interface SearchRecordsOptions {
q: string;
nsid?: SearchCollection;
author?: Did;
repo?: Did;
since?: string;
until?: string;
cursor?: string;
limit?: number;
}
export const searchRecords = async (options: SearchRecordsOptions): Promise<SearchResponse> => {
const q = options.q.trim();
if (!q) {
return { hits: [] };
}
const response = await appviewJson<AppviewSearchResponse<SearchRecordValue>>(
'sh.tangled.search.query',
{
q,
nsid: options.nsid,
author: options.author,
repo: options.repo,
since: options.since,
until: options.until,
cursor: options.cursor,
limit: options.limit,
},
);
const hits = await Promise.all(
response.hits.map(async (hit) => ({
...(await appviewRecordToHydrated(hit)),
nsid: hit.nsid as SearchCollection,
score: hit.score,
})),
);
return {
hits,
cursor: response.cursor ?? undefined,
};
};