import { Cache } from './cache.js'; import { getSimpleTextHash } from './crypto.js'; const regexCache = Cache.getInstance( 'regexCache', 1_000, 'memory' ); // parses regex and flags, also checks for existence of a custom flag - n - for negate export function parseRegex(pattern: string): { regex: string; flags: string; } { const regexFormatMatch = /^\/(.+)\/([gimuyn]*)$/.exec(pattern); return regexFormatMatch ? { regex: regexFormatMatch[1], flags: regexFormatMatch[2] } : { regex: pattern, flags: '' }; } export async function compileRegex( pattern: string, bypassCache: boolean = false ): Promise { let { regex, flags } = parseRegex(pattern); // the n flag is not to be used when compiling the regex if (flags.includes('n')) { flags = flags.replace('n', ''); } if (bypassCache) { return new RegExp(regex, flags); } return await regexCache.wrap( (p: string, f: string) => new RegExp(p, f || undefined), getSimpleTextHash(`${regex}|${flags}`), 30 * 24 * 60 * 60, regex, flags ); } // Build the raw pattern string used to match a list of keywords against stream // attributes. Exposed separately so synchronous callers (e.g. the SEL parser, // which cannot await) can produce the exact same regex shape as the async // `formRegexFromKeywords` helper used by the keyword UI filters. export function buildKeywordRegexPattern(keywords: string[]): string { return `/(?:^|(? filter.replace(/[-[\]{}()*+?.,\\^$]/g, '\\$&')) .map((filter) => filter.replace(/\s/g, '[\\s.\\-_]?')) .join('|')})(?=[ \\)\\]_.-]|$)/i`; } export async function formRegexFromKeywords( keywords: string[] ): Promise { return await compileRegex(buildKeywordRegexPattern(keywords)); } // Synchronous variant of `formRegexFromKeywords`. Produces an identical regex // to the async version (same pattern + flags) but bypasses the async regex // cache so it can be called from synchronous contexts such as SEL function // implementations. export function formRegexFromKeywordsSync(keywords: string[]): RegExp { const { regex, flags } = parseRegex(buildKeywordRegexPattern(keywords)); const cleanedFlags = flags.includes('n') ? flags.replace('n', '') : flags; return new RegExp(regex, cleanedFlags || undefined); }