108 lines · 2.7 KB · TypeScript
RawDownload
import type { ServerProviderSkill } from "@t3tools/contracts";import {  dedupeProviderSkillsByName,  formatProviderSkillDisplayName,  isProviderSkillUserInvocable,} from "@t3tools/client-runtime/providerSkills";import {  insertRankedSearchResult,  normalizeSearchQuery,  scoreQueryMatch,} from "@t3tools/shared/searchRanking"; export function scoreProviderSkill(skill: ServerProviderSkill, query: string): number | null {  const normalizedName = skill.name.toLowerCase();  const normalizedLabel = formatProviderSkillDisplayName(skill).toLowerCase();  const normalizedShortDescription = skill.shortDescription?.toLowerCase() ?? "";  const normalizedDescription = skill.description?.toLowerCase() ?? "";  const normalizedScope = skill.scope?.toLowerCase() ?? "";   const scores = [    scoreQueryMatch({      value: normalizedName,      query,      exactBase: 0,      prefixBase: 2,      boundaryBase: 4,      includesBase: 6,      fuzzyBase: 100,      boundaryMarkers: ["-", "_", "/"],    }),    scoreQueryMatch({      value: normalizedLabel,      query,      exactBase: 1,      prefixBase: 3,      boundaryBase: 5,      includesBase: 7,      fuzzyBase: 110,    }),    scoreQueryMatch({      value: normalizedShortDescription,      query,      exactBase: 20,      prefixBase: 22,      boundaryBase: 24,      includesBase: 26,    }),    scoreQueryMatch({      value: normalizedDescription,      query,      exactBase: 30,      prefixBase: 32,      boundaryBase: 34,      includesBase: 36,    }),    scoreQueryMatch({      value: normalizedScope,      query,      exactBase: 40,      prefixBase: 42,      includesBase: 44,    }),  ].filter((score): score is number => score !== null);   if (scores.length === 0) {    return null;  }   return Math.min(...scores);} export function searchProviderSkills(  skills: ReadonlyArray<ServerProviderSkill>,  query: string,  limit = Number.POSITIVE_INFINITY,): ServerProviderSkill[] {  const enabledSkills = dedupeProviderSkillsByName(skills.filter(isProviderSkillUserInvocable));  const normalizedQuery = normalizeSearchQuery(query, { trimLeadingPattern: /^\$+/ });   if (!normalizedQuery) {    return enabledSkills;  }   const ranked: Array<{    item: ServerProviderSkill;    score: number;    tieBreaker: string;  }> = [];   for (const skill of enabledSkills) {    const score = scoreProviderSkill(skill, normalizedQuery);    if (score === null) {      continue;    }     insertRankedSearchResult(      ranked,      {        item: skill,        score,        tieBreaker: `${formatProviderSkillDisplayName(skill).toLowerCase()}\u0000${skill.name}`,      },      limit,    );  }   return ranked.map((entry) => entry.item);}