import { all, first } from "../db"; import { visibleReposClause } from "../repos"; import type { CodeSearchHit } from "~/lib/code-intel"; import type { Viewer } from "~/lib/types"; const MIN_TERM_LENGTH = 3; const MAX_TERMS = 8; const MAX_TERM_LENGTH = 100; const RESULT_LIMIT = 30; const SNIPPET_TOKENS = 24; const PATH_PREFIX = "path:"; const SNIPPET = `snippet(code_fts, 1, char(1), char(2), '…', ${SNIPPET_TOKENS})`; function quote(term: string): string { return `"${term.replaceAll('"', '""')}"`; } /** Turns free text into a safe FTS5 expression: every term is quoted (trigram needs 3+ chars); `path:x` filters by path. */ export function ftsQuery(input: string): string | null { const terms: string[] = []; for (const raw of input.trim().split(/\s+/)) { const isPath = raw.startsWith(PATH_PREFIX); const term = (isPath ? raw.slice(PATH_PREFIX.length) : raw).slice(0, MAX_TERM_LENGTH); if (term.length < MIN_TERM_LENGTH) continue; terms.push(isPath ? `path : ${quote(term)}` : quote(term)); if (terms.length >= MAX_TERMS) break; } return terms.length > 0 ? terms.join(" ") : null; } const FROM = `FROM code_fts JOIN code_files f ON f.id = code_fts.rowid JOIN repos r ON r.id = f.repo_id JOIN accounts a ON a.id = r.owner_id`; export async function searchRepoCode(repoId: string, query: string): Promise { return all( `SELECT a.name AS owner, r.name AS repo, r.default_branch AS ref, f.path, ${SNIPPET} AS snippet ${FROM} WHERE code_fts MATCH ?1 AND f.repo_id = ?2 ORDER BY rank LIMIT ${RESULT_LIMIT}`, query, repoId, ); } export async function searchAllCode(viewer: Viewer | null, query: string): Promise { const { clause, viewerId } = visibleReposClause(viewer); return all( `SELECT a.name AS owner, r.name AS repo, r.default_branch AS ref, f.path, ${SNIPPET} AS snippet ${FROM} WHERE code_fts MATCH ?2 AND ${clause} ORDER BY rank LIMIT ${RESULT_LIMIT}`, viewerId, query, ); } export type IndexStatus = { commitSha: string; fileCount: number; truncated: boolean } | null; export async function codeIndexStatus(repoId: string): Promise { const row = await first<{ commit_sha: string; file_count: number; truncated: number }>( "SELECT commit_sha, file_count, truncated FROM code_index_state WHERE repo_id = ?", repoId, ); return row ? { commitSha: row.commit_sha, fileCount: row.file_count, truncated: row.truncated === 1 } : null; }