export type SearchMatcherOptions = { matchCase?: boolean; matchWholeWord?: boolean; useRegularExpression?: boolean; }; export type CompileStringMatcherResult = { matcher: (content: string) => boolean; isValid: boolean; }; export const escapeRegex = (value: string) => { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }; export const buildRegex = (pattern: string, flags = "") => { try { return new RegExp(pattern, flags); } catch { return null; } }; export const compileStringMatcher = ( query: string, options: SearchMatcherOptions = {}, ): CompileStringMatcherResult => { if (!query) return { matcher: () => true, isValid: true }; const matchCase = options.matchCase ?? false; const matchWholeWord = options.matchWholeWord ?? false; const useRegularExpression = options.useRegularExpression ?? false; const flags = matchCase ? "" : "i"; if (useRegularExpression) { const regex = buildRegex(query, flags); if (!regex) return { matcher: () => false, isValid: false }; return { matcher: (content: string) => regex.test(content), isValid: true }; } if (matchWholeWord) { const regex = buildRegex(`\\b${escapeRegex(query)}\\b`, flags); if (!regex) return { matcher: () => false, isValid: false }; return { matcher: (content: string) => regex.test(content), isValid: true }; } if (matchCase) { return { matcher: (content: string) => content.includes(query), isValid: true, }; } const target = query.toLowerCase(); return { matcher: (content: string) => content.toLowerCase().includes(target), isValid: true, }; };