sourcebot/src/lib/server/searchService.ts

93 lines
3.1 KiB
TypeScript
Raw Normal View History

2024-09-11 04:55:00 +00:00
import escapeStringRegexp from "escape-string-regexp";
import { SHARD_MAX_MATCH_COUNT, TOTAL_MAX_MATCH_COUNT } from "../environment";
2024-09-11 04:55:00 +00:00
import { FileSourceRequest, FileSourceResponse, ListRepositoriesResponse, listRepositoriesResponseSchema, SearchRequest, SearchResponse, searchResponseSchema } from "../schemas";
import { fileNotFound, invalidZoektResponse, ServiceError, unexpectedError } from "../serviceError";
import { isServiceError } from "../utils";
import { zoektFetch } from "./zoektClient";
export const search = async ({ query, numResults, whole }: SearchRequest): Promise<SearchResponse | ServiceError> => {
const body = JSON.stringify({
q: query,
// @see: https://github.com/TaqlaAI/zoekt/blob/main/api.go#L892
opts: {
NumContextLines: 2,
ChunkMatches: true,
MaxMatchDisplayCount: numResults,
Whole: !!whole,
ShardMaxMatchCount: SHARD_MAX_MATCH_COUNT,
TotalMaxMatchCount: TOTAL_MAX_MATCH_COUNT,
}
});
const searchResponse = await zoektFetch({
path: "/api/search",
body,
method: "POST",
});
if (!searchResponse.ok) {
return invalidZoektResponse(searchResponse);
}
const searchBody = await searchResponse.json();
const parsedSearchResponse = searchResponseSchema.safeParse(searchBody);
if (!parsedSearchResponse.success) {
console.error(`Failed to parse zoekt response. Error: ${parsedSearchResponse.error}`);
return unexpectedError(`Something went wrong while parsing the response from zoekt`);
}
return parsedSearchResponse.data;
}
export const getFileSource = async ({ fileName, repository }: FileSourceRequest): Promise<FileSourceResponse | ServiceError> => {
const escapedFileName = escapeStringRegexp(fileName);
const escapedRepository = escapeStringRegexp(repository);
const searchResponse = await search({
query: `${escapedFileName} repo:^${escapedRepository}$`,
numResults: 1,
whole: true,
});
if (isServiceError(searchResponse)) {
return searchResponse;
}
const files = searchResponse.Result.Files;
if (!files || files.length === 0) {
return fileNotFound(fileName, repository);
}
const source = files[0].Content ?? '';
return {
source
}
2024-09-11 04:55:00 +00:00
}
export const listRepositories = async (): Promise<ListRepositoriesResponse | ServiceError> => {
const body = JSON.stringify({
opts: {
Field: 0,
}
});
const listResponse = await zoektFetch({
path: "/api/list",
body,
2024-09-11 19:15:12 +00:00
method: "POST",
cache: "no-store",
2024-09-11 04:55:00 +00:00
});
if (!listResponse.ok) {
return invalidZoektResponse(listResponse);
}
const listBody = await listResponse.json();
const parsedListResponse = listRepositoriesResponseSchema.safeParse(listBody);
if (!parsedListResponse.success) {
console.error(`Failed to parse zoekt response. Error: ${parsedListResponse.error}`);
return unexpectedError(`Something went wrong while parsing the response from zoekt`);
}
return parsedListResponse.data;
}