🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@stll/text-search

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@stll/text-search - npm Package Compare versions

Comparing version
1.0.6
to
1.0.7
+2
-3
dist/index.mjs

@@ -515,8 +515,7 @@ import { AhoCorasick } from "@stll/aho-corasick";

const unique = [...new Set(literals.filter((literal) => literal.length > 0))];
if (unique.length === 1) {
if (unique.length === 1 && !options.caseInsensitive) {
const literal = unique[0];
if (literal === void 0) throw new Error("Expected single literal after length check");
const needle = options.caseInsensitive ? literal.toLowerCase() : literal;
return {
isMatch: (haystack) => options.caseInsensitive ? haystack.toLowerCase().includes(needle) : haystack.includes(needle),
isMatch: (haystack) => haystack.includes(literal),
findIter: () => []

@@ -523,0 +522,0 @@ };

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"index.mjs","names":[],"sources":["../src/engines.ts","../src/classify.ts","../src/merge.ts","../src/text-search.ts","../src/index.ts"],"sourcesContent":["/**\n * Late-bound engine registry.\n *\n * Native and WASM entry points call initEngines()\n * with their respective implementations before any\n * TextSearch instance is created.\n */\n\nimport type { Options as AhoOptions, PatternEntry as AhoPatternEntry } from \"@stll/aho-corasick\";\nimport type {\n Options as FuzzyOptions,\n PatternEntry as FuzzyPatternEntry,\n} from \"@stll/fuzzy-search\";\nimport type { Options as RegexOptions, PatternEntry as RegexPatternEntry } from \"@stll/regex-set\";\nimport type { Match } from \"./types\";\n\ntype Engine = {\n isMatch: (haystack: string) => boolean;\n findIter: (haystack: string) => Match[];\n};\n\ntype Engines = {\n AhoCorasick: new (patterns: AhoPatternEntry[], options?: AhoOptions) => Engine;\n FuzzySearch: new (patterns: FuzzyPatternEntry[], options?: FuzzyOptions) => Engine;\n RegexSet: new (patterns: RegexPatternEntry[], options?: RegexOptions) => Engine;\n};\n\nlet engines: Engines | undefined;\n\nexport const initEngines = (e: Engines): void => {\n engines = e;\n};\n\nexport const getEngines = (): Engines => {\n if (!engines) {\n throw new Error(\n \"Engines not initialized. Import from \" +\n \"@stll/text-search or @stll/text-search-wasm, \" +\n \"not from internal modules.\",\n );\n }\n return engines;\n};\n","import type { PatternEntry } from \"./types\";\n\n/**\n * Normalized pattern with metadata for routing.\n */\nexport type ClassifiedPattern = {\n /** Original index in the input array. */\n originalIndex: number;\n /** The regex-compatible pattern string. */\n pattern: string | RegExp;\n /** Optional name. */\n name?: string;\n /**\n * Number of top-level alternation branches.\n * Used to detect large alternations that should\n * be isolated into their own RegexSet instance.\n */\n alternationCount: number;\n /**\n * True if the pattern is a pure literal string\n * (no regex metacharacters). These can be routed\n * to Aho-Corasick for SIMD-accelerated matching.\n */\n isLiteral: boolean;\n /**\n * Fuzzy distance if this is a fuzzy pattern.\n * Routes to @stll/fuzzy-search.\n */\n fuzzyDistance?: number | \"auto\";\n /**\n * Per-pattern AC options. When set, this literal\n * is grouped with others that have the same\n * options into a separate AC engine instance.\n */\n acOptions?: {\n caseInsensitive?: boolean;\n wholeWords?: boolean;\n };\n regexOptions?: {\n lazy?: boolean;\n prefilterAny?: readonly string[];\n prefilterCaseInsensitive?: boolean;\n prefilterRegex?: RegExp;\n };\n /**\n * Heuristic cost used for automatic RegexSet chunking.\n * Higher scores are more likely to suffer from shared\n * DFA state interaction and should be isolated or kept\n * in smaller chunks.\n */\n regexComplexity: number;\n};\n\n/**\n * Check if a string is a pure literal (no regex\n * metacharacters). Pure literals are routed to\n * Aho-Corasick instead of the regex DFA.\n */\nexport function isLiteralPattern(pattern: string): boolean {\n // All standard regex metacharacters cause a\n // pattern to be classified as regex (→ RegexSet).\n // To force literal AC routing for patterns with\n // dots/parens (e.g., \"s.r.o.\", \"č.p.\"), use the\n // explicit { literal: true } PatternEntry flag.\n for (let i = 0; i < pattern.length; i++) {\n const ch = pattern.charAt(i);\n if (\n ch === \"\\\\\" ||\n ch === \".\" ||\n ch === \"^\" ||\n ch === \"$\" ||\n ch === \"*\" ||\n ch === \"+\" ||\n ch === \"?\" ||\n ch === \"{\" ||\n ch === \"}\" ||\n ch === \"(\" ||\n ch === \")\" ||\n ch === \"[\" ||\n ch === \"]\" ||\n ch === \"|\"\n ) {\n return false;\n }\n }\n return pattern.length > 0;\n}\n\n/**\n * Count the maximum alternation branches at any\n * depth in a regex string. Used to detect patterns\n * with large alternations (even nested inside\n * groups) that should be isolated into their own\n * RegexSet to prevent DFA state explosion.\n *\n * \"a|b|c\" → 3\n * \"(a|b)|c\" → 2 (max of top=2, depth1=2)\n * \"(?:Ing\\\\.|Mgr\\\\.|Dr\\\\.)\" → 3 (depth 1)\n */\nexport function countAlternations(pattern: string): number {\n let depth = 0;\n let inClass = false;\n let i = 0;\n\n // Track max alternation count seen at any depth.\n // Each time we enter a group, start a fresh count.\n // When we leave, update the global max.\n let max = 1;\n let currentCount = 1; // count for current group\n const stack: number[] = []; // saved counts\n\n while (i < pattern.length) {\n const ch = pattern[i];\n\n if (ch === \"\\\\\" && i + 1 < pattern.length) {\n i += 2;\n continue;\n }\n\n if (ch === \"[\") inClass = true;\n if (ch === \"]\") inClass = false;\n\n if (!inClass) {\n if (ch === \"(\") {\n stack.push(currentCount);\n currentCount = 1;\n depth++;\n }\n if (ch === \")\") {\n if (currentCount > max) max = currentCount;\n currentCount = stack.pop() ?? 1;\n depth--;\n }\n if (ch === \"|\") {\n currentCount++;\n }\n }\n\n i++;\n }\n // Check top-level count too\n if (currentCount > max) max = currentCount;\n return max;\n}\n\nexport function scoreRegexComplexity(\n pattern: string,\n alternationCount = countAlternations(pattern),\n): number {\n let score = 1;\n\n if (pattern.length > 80) score += 2;\n if (pattern.length > 160) score += 2;\n if (alternationCount > 1) score += alternationCount >= 4 ? 2 : 1;\n if (/\\\\p\\{/.test(pattern)) score += 3;\n if (/\\(\\?<?[!=]/.test(pattern)) score += 4;\n if (/\\\\[bBAZz]/.test(pattern)) score += 1;\n if (/\\.\\*|\\.\\+/.test(pattern)) score += 3;\n if (/\\[[^\\]]+\\][*+{]/.test(pattern)) score += 2;\n if (/\\{[\\d,]+\\}/.test(pattern)) score += 2;\n if (/(?:\\\\[dDsSwW]|\\\\p\\{[^}]+\\}|\\[[^\\]]+\\])[*+]/.test(pattern)) {\n score += 1;\n }\n\n return score;\n}\n\n/**\n * Classify and normalize pattern entries.\n */\nexport function classifyPatterns(entries: PatternEntry[], allLiteral = false): ClassifiedPattern[] {\n return entries.map((entry, i) => {\n if (typeof entry === \"string\") {\n const alternationCount = allLiteral ? 0 : countAlternations(entry);\n return {\n originalIndex: i,\n pattern: entry,\n alternationCount,\n isLiteral: allLiteral || isLiteralPattern(entry),\n regexComplexity: scoreRegexComplexity(entry, alternationCount),\n };\n }\n\n if (entry instanceof RegExp) {\n const alternationCount = countAlternations(entry.source);\n return {\n originalIndex: i,\n pattern: entry,\n alternationCount,\n isLiteral: false, // RegExp is never literal\n regexComplexity: scoreRegexComplexity(entry.source, alternationCount),\n };\n }\n\n // Fuzzy pattern: has `distance` field\n if (\"distance\" in entry) {\n const result: ClassifiedPattern = {\n originalIndex: i,\n pattern: entry.pattern,\n alternationCount: 0,\n isLiteral: false,\n fuzzyDistance: entry.distance,\n regexComplexity: 0,\n };\n if (entry.name !== undefined) result.name = entry.name;\n return result;\n }\n\n // Explicit literal: skip metachar detection\n if (entry.literal === true) {\n const hasPerPatternOpts = \"caseInsensitive\" in entry || \"wholeWords\" in entry;\n const result: ClassifiedPattern = {\n originalIndex: i,\n pattern: entry.pattern,\n alternationCount: 0,\n isLiteral: true,\n regexComplexity: 0,\n };\n if (entry.name !== undefined) result.name = entry.name;\n if (hasPerPatternOpts) {\n const opts: NonNullable<ClassifiedPattern[\"acOptions\"]> = {};\n if (entry.caseInsensitive !== undefined) opts.caseInsensitive = entry.caseInsensitive;\n if (entry.wholeWords !== undefined) opts.wholeWords = entry.wholeWords;\n result.acOptions = opts;\n }\n return result;\n }\n\n const pat = entry.pattern;\n const source = pat instanceof RegExp ? pat.source : pat;\n const alternationCount = allLiteral ? 0 : countAlternations(source);\n\n const result: ClassifiedPattern = {\n originalIndex: i,\n pattern: pat,\n alternationCount,\n isLiteral: typeof pat === \"string\" && (allLiteral || isLiteralPattern(pat)),\n regexComplexity: scoreRegexComplexity(source, alternationCount),\n };\n if (entry.name !== undefined) result.name = entry.name;\n const regexOptions: NonNullable<ClassifiedPattern[\"regexOptions\"]> = {};\n if (entry.lazy === true) regexOptions.lazy = true;\n if (entry.prefilterAny !== undefined) regexOptions.prefilterAny = entry.prefilterAny;\n if (entry.prefilterCaseInsensitive !== undefined)\n regexOptions.prefilterCaseInsensitive = entry.prefilterCaseInsensitive;\n if (entry.prefilterRegex !== undefined) regexOptions.prefilterRegex = entry.prefilterRegex;\n if (Object.keys(regexOptions).length > 0) {\n result.regexOptions = regexOptions;\n }\n return result;\n });\n}\n","import type { Match } from \"./types\";\n\n/**\n * Merge matches from multiple engines, sort by\n * position, and select non-overlapping (longest\n * first at ties). Same algorithm as regex-set's\n * internal select_non_overlapping.\n */\nexport function mergeAndSelect(matches: Match[]): Match[] {\n if (matches.length <= 1) return matches;\n\n // Sort: start ascending, longest first at ties\n matches.sort((a, b) => {\n if (a.start !== b.start) {\n return a.start - b.start;\n }\n return b.end - b.start - (a.end - a.start);\n });\n\n // Greedily select non-overlapping\n const selected: Match[] = [];\n let lastEnd = 0;\n\n for (const m of matches) {\n if (m.start >= lastEnd) {\n selected.push(m);\n lastEnd = m.end;\n }\n }\n\n return selected;\n}\n","import type { ClassifiedPattern } from \"./classify\";\nimport { classifyPatterns } from \"./classify\";\nimport { getEngines } from \"./engines\";\nimport { mergeAndSelect } from \"./merge\";\nimport type { Match, PatternEntry, TextSearchOptions } from \"./types\";\n\nconst AUTO_REGEX_CHUNK_MAX_SIZE = 16;\nconst AUTO_REGEX_CHUNK_COMPLEXITY_BUDGET = 6;\nconst AUTO_REGEX_ISOLATE_COMPLEXITY = 7;\nconst SPLIT_IDENTITY_AC_CHUNK_SIZE = 20_000;\nconst SPLIT_IDENTITY_AC_MIN_PATTERNS = SPLIT_IDENTITY_AC_CHUNK_SIZE;\n\n/** Common engine interface for dispatch. */\ntype Engine = {\n isMatch: (haystack: string) => boolean;\n findIter: (haystack: string) => Match[];\n};\n\ntype OverlapEngine = Engine & {\n findOverlappingIter: (haystack: string) => Match[];\n};\n\n/**\n * An engine instance with pattern index mapping.\n */\ntype RegexSlot = {\n type: \"regex\";\n rs?: Engine;\n build?: (() => Engine) | undefined;\n prefilter?: Engine | undefined;\n prefilterRegex?: RegExp | undefined;\n indexMap: number[];\n nameMap: (string | undefined)[];\n identityMap?: true;\n};\n\ntype AcSlot = {\n type: \"ac\";\n ac: Engine;\n indexMap: number[];\n nameMap: (string | undefined)[];\n identityMap?: true;\n patternCount?: number;\n};\n\ntype SplitAcSlot = {\n type: \"split-ac\";\n acs: {\n ac: OverlapEngine;\n patternOffset: number;\n }[];\n indexMap: number[];\n nameMap: (string | undefined)[];\n identityMap: true;\n patternCount: number;\n};\n\ntype FuzzySlot = {\n type: \"fuzzy\";\n fs: Engine;\n indexMap: number[];\n nameMap: (string | undefined)[];\n identityMap?: true;\n};\n\ntype EngineSlot = RegexSlot | AcSlot | SplitAcSlot | FuzzySlot;\n\n/**\n * Multi-engine text search orchestrator.\n *\n * Routes patterns to the optimal engine\n * configuration:\n * - Large alternation patterns get their own\n * RegexSet instance (prevents DFA state explosion)\n * - Normal regex patterns use complexity-aware chunks\n * (avoids bad cross-pattern DFA interaction)\n *\n * Merges results from all engines into a unified\n * non-overlapping Match[] sorted by position.\n */\nexport class TextSearch {\n private engines: EngineSlot[] = [];\n private patternCount: number;\n private overlapAll: boolean;\n /**\n * True when there's exactly one engine and all\n * patterns map to identity indices (0→0, 1→1, ...).\n * Enables zero-overhead findIter: return raw engine\n * output without remapping or object allocation.\n */\n private zeroOverhead: boolean = false;\n\n constructor(patterns: PatternEntry[], options?: TextSearchOptions) {\n this.patternCount = patterns.length;\n this.overlapAll = options?.overlapStrategy === \"all\";\n if (patterns.length > 0 && options?.allLiteral === true && allStringPatterns(patterns)) {\n const engine = buildIdentityAcEngine(patterns, {\n unicodeBoundaries: options.unicodeBoundaries ?? true,\n wholeWords: options.wholeWords ?? false,\n caseInsensitive: options.caseInsensitive ?? false,\n });\n this.engines.push(engine);\n this.zeroOverhead = true;\n return;\n }\n\n const maxAlt = options?.maxAlternations ?? 50;\n const classified = classifyPatterns(patterns, options?.allLiteral ?? false);\n\n // Four buckets:\n // 1. Fuzzy patterns → FuzzySearch (Levenshtein)\n // 2. Pure literals → Aho-Corasick (SIMD)\n // 3. Normal regex → shared RegexSet (DFA)\n // 4. Large alternations → isolated RegexSet\n const fuzzy: ClassifiedPattern[] = [];\n const literals: ClassifiedPattern[] = [];\n const shared: ClassifiedPattern[] = [];\n const isolated: ClassifiedPattern[] = [];\n\n for (const cp of classified) {\n if (cp.fuzzyDistance !== undefined) {\n fuzzy.push(cp);\n } else if (cp.isLiteral) {\n literals.push(cp);\n } else if (cp.regexOptions?.lazy === true) {\n isolated.push(cp);\n } else if (cp.alternationCount > maxAlt) {\n isolated.push(cp);\n } else {\n shared.push(cp);\n }\n }\n\n const rsOptions = {\n unicodeBoundaries: options?.unicodeBoundaries ?? true,\n wholeWords: options?.wholeWords ?? false,\n caseInsensitive: options?.caseInsensitive ?? false,\n };\n\n // Build fuzzy engine\n if (fuzzy.length > 0) {\n const fuzzyOpts: Parameters<typeof buildFuzzyEngine>[1] = {\n unicodeBoundaries: rsOptions.unicodeBoundaries,\n wholeWords: rsOptions.wholeWords,\n };\n if (options?.fuzzyMetric !== undefined) fuzzyOpts.metric = options.fuzzyMetric;\n if (options?.normalizeDiacritics !== undefined)\n fuzzyOpts.normalizeDiacritics = options.normalizeDiacritics;\n if (options?.caseInsensitive !== undefined)\n fuzzyOpts.caseInsensitive = options.caseInsensitive;\n this.engines.push(buildFuzzyEngine(fuzzy, fuzzyOpts));\n }\n\n // Build AC engine(s) for pure literals.\n // Group by per-pattern AC options so patterns\n // with different caseInsensitive/wholeWords\n // settings get separate AC instances.\n if (literals.length > 0) {\n const groups = new Map<string, ClassifiedPattern[]>();\n for (const cp of literals) {\n const ci = cp.acOptions?.caseInsensitive ?? rsOptions.caseInsensitive;\n const ww = cp.acOptions?.wholeWords ?? rsOptions.wholeWords;\n const key = `${ci ? 1 : 0}:${ww ? 1 : 0}`;\n const group = groups.get(key);\n if (group) {\n group.push(cp);\n } else {\n groups.set(key, [cp]);\n }\n }\n for (const [key, group] of groups) {\n const [ci, ww] = key.split(\":\");\n this.engines.push(\n buildAcEngine(group, {\n ...rsOptions,\n caseInsensitive: ci === \"1\",\n wholeWords: ww === \"1\",\n }),\n );\n }\n }\n\n // Keep normal regex patterns in bounded chunks.\n // One giant RegexSet can develop poor cross-pattern\n // DFA interactions; auto chunking groups simple\n // homogeneous regexes while isolating complex\n // patterns with lookarounds, Unicode classes,\n // large repeats, or long wildcard spans.\n for (const chunk of chunkSharedRegexPatterns(shared, options?.regexChunkSize)) {\n this.engines.push(buildRegexEngine(chunk, rsOptions));\n }\n\n for (const cp of isolated) {\n const lazyOptions: {\n lazy?: boolean;\n prefilterAny?: readonly string[];\n prefilterCaseInsensitive?: boolean;\n prefilterRegex?: RegExp;\n } = {};\n if (cp.regexOptions?.lazy === true) {\n lazyOptions.lazy = true;\n }\n if (cp.regexOptions?.prefilterAny !== undefined) {\n lazyOptions.prefilterAny = cp.regexOptions.prefilterAny;\n }\n if (cp.regexOptions?.prefilterCaseInsensitive !== undefined) {\n lazyOptions.prefilterCaseInsensitive = cp.regexOptions.prefilterCaseInsensitive;\n }\n if (cp.regexOptions?.prefilterRegex !== undefined) {\n lazyOptions.prefilterRegex = cp.regexOptions.prefilterRegex;\n }\n this.engines.push(buildRegexEngine([cp], rsOptions, lazyOptions));\n }\n\n // Zero-overhead fast path: when all patterns\n // land in a single engine, the indexMap is\n // identity (0→0, 1→1, ...) and no names need\n // attaching. findIter can return raw engine\n // output without any JS-side remapping.\n if (this.engines.length === 1) {\n const engine = this.engines[0];\n if (engine === undefined) {\n throw new Error(\"Expected single engine after length check\");\n }\n const hasNames = engine.nameMap.some((n) => n !== undefined);\n if (!hasNames) {\n this.zeroOverhead = true;\n }\n }\n }\n\n /** Number of patterns. */\n get length(): number {\n return this.patternCount;\n }\n\n /** Returns true if any pattern matches. */\n isMatch(haystack: string): boolean {\n for (const engine of this.engines) {\n if (engineIsMatch(engine, haystack)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Find matches in text.\n *\n * With `overlapStrategy: \"longest\"` (default):\n * returns non-overlapping matches, longest wins.\n *\n * With `overlapStrategy: \"all\"`: returns all\n * matches including overlaps, sorted by position.\n */\n findIter(haystack: string): Match[] {\n // Fast path: single engine, identity indexMap,\n // no names → return raw engine output directly.\n // Zero JS overhead: no remapping, no allocation.\n if (this.zeroOverhead) {\n const engine = this.engines[0];\n if (engine === undefined) {\n throw new Error(\"Zero-overhead path requires a single engine\");\n }\n return engineFindIter(engine, haystack);\n }\n\n // Single engine but needs name remapping\n if (this.engines.length === 1) {\n const engine = this.engines[0];\n if (engine === undefined) {\n throw new Error(\"Expected single engine after length check\");\n }\n return remapMatches(engineFindIter(engine, haystack), engine);\n }\n\n // Multi-engine: collect from all, remap in-place\n const all: Match[] = [];\n for (const engine of this.engines) {\n const matches = engineFindIter(engine, haystack);\n // In-place remapping avoids .map() allocation\n for (const m of remapMatches(matches, engine)) {\n all.push(m);\n }\n }\n\n if (this.overlapAll) {\n return all.sort((a, b) => a.start - b.start);\n }\n\n return mergeAndSelect(all);\n }\n\n /** Which pattern indices matched (not where). */\n whichMatch(haystack: string): number[] {\n const seen = new Set<number>();\n\n for (const engine of this.engines) {\n // AC doesn't have whichMatch — use findIter\n const matches = engineFindIter(engine, haystack);\n for (const m of matches) {\n const idx = getOriginalPatternIndex(engine, m.pattern);\n seen.add(idx);\n }\n }\n\n return [...seen];\n }\n\n /**\n * Replace all non-overlapping matches.\n * replacements[i] replaces pattern i.\n */\n replaceAll(haystack: string, replacements: string[]): string {\n if (replacements.length !== this.patternCount) {\n throw new Error(\n `Expected ${this.patternCount} ` + `replacements, got ${replacements.length}`,\n );\n }\n\n // Always use non-overlapping matches for\n // replacement, even if overlapStrategy is \"all\".\n const all: Match[] = [];\n for (const engine of this.engines) {\n const matches = engineFindIter(engine, haystack);\n for (const m of remapMatches(matches, engine)) {\n all.push(m);\n }\n }\n const matches = mergeAndSelect(all);\n\n let result = \"\";\n let last = 0;\n\n for (const m of matches) {\n result += haystack.slice(last, m.start);\n const replacement = replacements[m.pattern];\n if (replacement === undefined) {\n throw new Error(`Missing replacement for pattern ${m.pattern}`);\n }\n result += replacement;\n last = m.end;\n }\n\n result += haystack.slice(last);\n return result;\n }\n}\n\nfunction chunkSharedRegexPatterns(\n patterns: ClassifiedPattern[],\n explicitChunkSize: number | undefined,\n): ClassifiedPattern[][] {\n if (explicitChunkSize !== undefined) {\n const chunkSize = Math.max(1, explicitChunkSize);\n const chunks: ClassifiedPattern[][] = [];\n for (let i = 0; i < patterns.length; i += chunkSize) {\n chunks.push(patterns.slice(i, i + chunkSize));\n }\n return chunks;\n }\n\n const chunks: ClassifiedPattern[][] = [];\n let current: ClassifiedPattern[] = [];\n let currentComplexity = 0;\n\n const flush = () => {\n if (current.length === 0) return;\n chunks.push(current);\n current = [];\n currentComplexity = 0;\n };\n\n for (const pattern of patterns) {\n const complexity = pattern.regexComplexity;\n if (complexity >= AUTO_REGEX_ISOLATE_COMPLEXITY) {\n flush();\n chunks.push([pattern]);\n continue;\n }\n\n const wouldExceedSize = current.length >= AUTO_REGEX_CHUNK_MAX_SIZE;\n const wouldExceedComplexity =\n current.length > 0 && currentComplexity + complexity > AUTO_REGEX_CHUNK_COMPLEXITY_BUDGET;\n if (wouldExceedSize || wouldExceedComplexity) {\n flush();\n }\n\n current.push(pattern);\n currentComplexity += complexity;\n }\n\n flush();\n return chunks;\n}\n\n/**\n * Build a RegexSet engine from classified patterns.\n */\nfunction buildRegexEngine(\n patterns: ClassifiedPattern[],\n options: {\n unicodeBoundaries: boolean;\n wholeWords: boolean;\n caseInsensitive: boolean;\n },\n lazyOptions?: {\n lazy?: boolean;\n prefilterAny?: readonly string[];\n prefilterCaseInsensitive?: boolean;\n prefilterRegex?: RegExp;\n },\n): RegexSlot {\n const rsPatterns: (\n | string\n | RegExp\n | {\n pattern: string | RegExp;\n name?: string;\n }\n )[] = [];\n const indexMap: number[] = [];\n const nameMap: (string | undefined)[] = [];\n\n for (const cp of patterns) {\n if (cp.name !== undefined) {\n rsPatterns.push({\n pattern: cp.pattern,\n });\n } else {\n rsPatterns.push(cp.pattern);\n }\n indexMap.push(cp.originalIndex);\n nameMap.push(cp.name);\n }\n\n const { RegexSet } = getEngines();\n const build = () => new RegexSet(rsPatterns, options);\n const inferredPrefilter =\n lazyOptions === undefined && patterns.length === 1\n ? inferLeadingLiteralPrefilter(patterns[0]?.pattern)\n : undefined;\n\n if (lazyOptions?.lazy === true) {\n const prefilter =\n lazyOptions.prefilterAny && lazyOptions.prefilterAny.length > 0\n ? buildLiteralPrefilter(lazyOptions.prefilterAny, {\n caseInsensitive: lazyOptions.prefilterCaseInsensitive ?? options.caseInsensitive,\n })\n : undefined;\n const slot: RegexSlot = { type: \"regex\", build, prefilter, indexMap, nameMap };\n if (lazyOptions.prefilterRegex !== undefined) {\n slot.prefilterRegex = lazyOptions.prefilterRegex;\n }\n return slot;\n }\n\n if (inferredPrefilter !== undefined) {\n const slot: RegexSlot = { type: \"regex\", rs: build(), indexMap, nameMap };\n slot.prefilter = buildLiteralPrefilter([inferredPrefilter.literal], {\n caseInsensitive: inferredPrefilter.caseInsensitive || options.caseInsensitive,\n });\n return slot;\n }\n return { type: \"regex\", rs: build(), indexMap, nameMap };\n}\n\nfunction inferLeadingLiteralPrefilter(\n pattern: string | RegExp | undefined,\n): { literal: string; caseInsensitive: boolean } | undefined {\n if (pattern === undefined) {\n return undefined;\n }\n\n const source = pattern instanceof RegExp ? pattern.source : pattern;\n let caseInsensitive = pattern instanceof RegExp ? pattern.ignoreCase : false;\n let i = 0;\n\n if (source.startsWith(\"(?i)\")) {\n caseInsensitive = true;\n i = 4;\n }\n\n while (i < source.length) {\n if (source[i] === \"^\") {\n i++;\n continue;\n }\n if (source[i] === \"\\\\\" && /[bBAZz]/.test(source[i + 1] ?? \"\")) {\n i += 2;\n continue;\n }\n if (\n source.startsWith(\"(?=\", i) ||\n source.startsWith(\"(?!\", i) ||\n source.startsWith(\"(?<=\", i) ||\n source.startsWith(\"(?<!\", i)\n ) {\n const end = findRegexGroupEnd(source, i);\n if (end === undefined) {\n return undefined;\n }\n i = end;\n continue;\n }\n break;\n }\n\n let literal = \"\";\n while (i < source.length) {\n const ch = source[i];\n if (ch === undefined) {\n break;\n }\n if (ch === \"\\\\\") {\n const next = source[i + 1];\n if (next === undefined || /[A-Za-z]/.test(next)) {\n break;\n }\n literal += next;\n i += 2;\n continue;\n }\n if (\"^$*+?.()[]{}|\".includes(ch)) {\n break;\n }\n literal += ch;\n i++;\n }\n\n if (source[i] === \"|\") {\n return undefined;\n }\n if (source[i] === \"?\" || source[i] === \"*\") {\n literal = literal.slice(0, -1);\n } else if (source[i] === \"{\" && source.startsWith(\"{0\", i)) {\n literal = literal.slice(0, -1);\n }\n\n return literal.length >= 2 ? { literal, caseInsensitive } : undefined;\n}\n\nfunction findRegexGroupEnd(source: string, start: number): number | undefined {\n let depth = 0;\n let inClass = false;\n for (let i = start; i < source.length; i++) {\n const ch = source[i];\n if (ch === \"\\\\\") {\n i++;\n continue;\n }\n if (ch === \"[\" && !inClass) {\n inClass = true;\n continue;\n }\n if (ch === \"]\" && inClass) {\n inClass = false;\n continue;\n }\n if (inClass) {\n continue;\n }\n if (ch === \"(\") {\n depth++;\n continue;\n }\n if (ch === \")\") {\n depth--;\n if (depth === 0) {\n return i + 1;\n }\n }\n }\n return undefined;\n}\n\nfunction buildLiteralPrefilter(\n literals: readonly string[],\n options: { caseInsensitive: boolean },\n): Engine {\n const unique = [...new Set(literals.filter((literal) => literal.length > 0))];\n if (unique.length === 1) {\n const literal = unique[0];\n if (literal === undefined) {\n throw new Error(\"Expected single literal after length check\");\n }\n const needle = options.caseInsensitive ? literal.toLowerCase() : literal;\n return {\n isMatch: (haystack) =>\n options.caseInsensitive\n ? haystack.toLowerCase().includes(needle)\n : haystack.includes(needle),\n findIter: () => [],\n };\n }\n\n const { AhoCorasick } = getEngines();\n return new AhoCorasick(unique, {\n caseInsensitive: options.caseInsensitive,\n });\n}\n\nfunction allStringPatterns(patterns: PatternEntry[]): patterns is string[] {\n for (const pattern of patterns) {\n if (typeof pattern !== \"string\") {\n return false;\n }\n }\n return true;\n}\n\nfunction getRegexSlotEngine(engine: RegexSlot): Engine {\n if (engine.rs !== undefined) {\n return engine.rs;\n }\n if (engine.build === undefined) {\n throw new Error(\"Lazy regex slot is missing a builder\");\n }\n engine.rs = engine.build();\n engine.build = undefined;\n return engine.rs;\n}\n\nfunction regexSlotPrefilterMatches(engine: RegexSlot, haystack: string): boolean {\n if (engine.prefilter && !engine.prefilter.isMatch(haystack)) {\n return false;\n }\n if (engine.prefilterRegex) {\n engine.prefilterRegex.lastIndex = 0;\n if (!engine.prefilterRegex.test(haystack)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Build an Aho-Corasick engine from literal patterns.\n */\nfunction buildAcEngine(\n patterns: ClassifiedPattern[],\n options: {\n unicodeBoundaries: boolean;\n wholeWords: boolean;\n caseInsensitive: boolean;\n },\n): AcSlot {\n const literals: string[] = [];\n const indexMap: number[] = [];\n const nameMap: (string | undefined)[] = [];\n\n for (const cp of patterns) {\n literals.push(cp.pattern as string);\n indexMap.push(cp.originalIndex);\n nameMap.push(cp.name);\n }\n\n const { AhoCorasick } = getEngines();\n const ac = new AhoCorasick(literals, {\n wholeWords: options.wholeWords,\n unicodeBoundaries: options.unicodeBoundaries,\n caseInsensitive: options.caseInsensitive,\n });\n\n return { type: \"ac\", ac, indexMap, nameMap, patternCount: literals.length };\n}\n\nfunction buildIdentityAcEngine(\n patterns: string[],\n options: {\n unicodeBoundaries: boolean;\n wholeWords: boolean;\n caseInsensitive: boolean;\n },\n): AcSlot | SplitAcSlot {\n const { AhoCorasick } = getEngines();\n if (\n options.wholeWords &&\n options.unicodeBoundaries &&\n patterns.length >= SPLIT_IDENTITY_AC_MIN_PATTERNS\n ) {\n const acs: SplitAcSlot[\"acs\"] = [];\n for (let i = 0; i < patterns.length; i += SPLIT_IDENTITY_AC_CHUNK_SIZE) {\n acs.push({\n ac: new AhoCorasick(patterns.slice(i, i + SPLIT_IDENTITY_AC_CHUNK_SIZE), {\n wholeWords: options.wholeWords,\n unicodeBoundaries: options.unicodeBoundaries,\n caseInsensitive: options.caseInsensitive,\n }) as OverlapEngine,\n patternOffset: i,\n });\n }\n\n return {\n type: \"split-ac\",\n acs,\n indexMap: [],\n nameMap: [],\n identityMap: true,\n patternCount: patterns.length,\n };\n }\n\n const ac = new AhoCorasick(patterns, {\n wholeWords: options.wholeWords,\n unicodeBoundaries: options.unicodeBoundaries,\n caseInsensitive: options.caseInsensitive,\n });\n\n return {\n type: \"ac\",\n ac,\n indexMap: [],\n nameMap: [],\n identityMap: true,\n patternCount: patterns.length,\n };\n}\n\n/**\n * Build a FuzzySearch engine from fuzzy patterns.\n */\nfunction buildFuzzyEngine(\n patterns: ClassifiedPattern[],\n options: {\n unicodeBoundaries: boolean;\n wholeWords: boolean;\n metric?: \"levenshtein\" | \"damerau-levenshtein\";\n normalizeDiacritics?: boolean;\n caseInsensitive?: boolean;\n },\n): FuzzySlot {\n const fsPatterns: {\n pattern: string;\n distance?: number | \"auto\";\n name?: string;\n }[] = [];\n const indexMap: number[] = [];\n const nameMap: (string | undefined)[] = [];\n\n for (const cp of patterns) {\n const entry: (typeof fsPatterns)[number] = {\n pattern: cp.pattern as string,\n };\n if (cp.fuzzyDistance !== undefined) entry.distance = cp.fuzzyDistance;\n if (cp.name !== undefined) entry.name = cp.name;\n fsPatterns.push(entry);\n indexMap.push(cp.originalIndex);\n nameMap.push(cp.name);\n }\n\n const fsOptions: {\n unicodeBoundaries: boolean;\n wholeWords: boolean;\n metric?: \"levenshtein\" | \"damerau-levenshtein\";\n normalizeDiacritics?: boolean;\n caseInsensitive?: boolean;\n } = {\n unicodeBoundaries: options.unicodeBoundaries,\n wholeWords: options.wholeWords,\n };\n if (options.metric !== undefined) fsOptions.metric = options.metric;\n if (options.normalizeDiacritics !== undefined)\n fsOptions.normalizeDiacritics = options.normalizeDiacritics;\n if (options.caseInsensitive !== undefined) fsOptions.caseInsensitive = options.caseInsensitive;\n const { FuzzySearch } = getEngines();\n const fs = new FuzzySearch(fsPatterns, fsOptions);\n\n return { type: \"fuzzy\", fs, indexMap, nameMap };\n}\n\n/**\n * Dispatch isMatch to the correct engine.\n */\nfunction engineIsMatch(engine: EngineSlot, haystack: string): boolean {\n switch (engine.type) {\n case \"ac\":\n return engine.ac.isMatch(haystack);\n case \"split-ac\":\n return engine.acs.some(({ ac }) => ac.isMatch(haystack));\n case \"fuzzy\":\n return engine.fs.isMatch(haystack);\n case \"regex\":\n if (!regexSlotPrefilterMatches(engine, haystack)) {\n return false;\n }\n return getRegexSlotEngine(engine).isMatch(haystack);\n }\n throw new Error(\"Unsupported engine type\");\n}\n\n/**\n * Dispatch findIter to the correct engine.\n */\nfunction engineFindIter(engine: EngineSlot, haystack: string): Match[] {\n switch (engine.type) {\n case \"ac\":\n return engine.ac.findIter(haystack);\n case \"split-ac\":\n return splitAcFindIter(engine, haystack);\n case \"fuzzy\":\n return engine.fs.findIter(haystack);\n case \"regex\":\n if (!regexSlotPrefilterMatches(engine, haystack)) {\n return [];\n }\n return getRegexSlotEngine(engine).findIter(haystack);\n }\n throw new Error(\"Unsupported engine type\");\n}\n\nfunction splitAcFindIter(engine: SplitAcSlot, haystack: string): Match[] {\n const all: Match[] = [];\n for (const { ac, patternOffset } of engine.acs) {\n const matches = ac.findOverlappingIter(haystack);\n for (const match of matches) {\n all.push({\n pattern: match.pattern + patternOffset,\n start: match.start,\n end: match.end,\n text: match.text,\n });\n }\n }\n return selectLeftmostLongestMatches(all);\n}\n\nfunction selectLeftmostLongestMatches(matches: Match[]): Match[] {\n if (matches.length === 0) {\n return [];\n }\n\n matches.sort((a, b) => a.start - b.start || b.end - a.end || a.pattern - b.pattern);\n\n const selected: Match[] = [];\n let cursor = 0;\n let i = 0;\n while (i < matches.length) {\n const start = matches[i]?.start;\n if (start === undefined) {\n break;\n }\n if (start < cursor) {\n i++;\n continue;\n }\n\n let best = matches[i];\n if (best === undefined) {\n break;\n }\n i++;\n while (i < matches.length && matches[i]?.start === start) {\n const candidate = matches[i];\n if (\n candidate !== undefined &&\n (candidate.end > best.end ||\n (candidate.end === best.end && candidate.pattern < best.pattern))\n ) {\n best = candidate;\n }\n i++;\n }\n\n selected.push(best);\n cursor = best.end;\n }\n\n return selected;\n}\n\n/**\n * Remap engine-local match indices to original\n * input indices and add names.\n */\nfunction remapMatches(matches: Match[], engine: EngineSlot): Match[] {\n return matches.map((m) => {\n const originalIdx = getOriginalPatternIndex(engine, m.pattern);\n const name = engine.nameMap[m.pattern];\n const result: Match = {\n pattern: originalIdx,\n start: m.start,\n end: m.end,\n text: m.text,\n };\n if (name !== undefined) {\n result.name = name;\n }\n // Preserve edit distance from fuzzy matches\n if (m.distance !== undefined) {\n result.distance = m.distance;\n }\n return result;\n });\n}\n\nfunction getOriginalPatternIndex(engine: EngineSlot, pattern: number): number {\n if (engine.identityMap === true) {\n return pattern;\n }\n const originalIdx = engine.indexMap[pattern];\n if (originalIdx === undefined) {\n throw new Error(`Missing indexMap entry for pattern ${pattern}`);\n }\n return originalIdx;\n}\n","/* Native entry point — loads @stll engines\n * for Node.js/Bun and re-exports the public API. */\n\nimport { AhoCorasick } from \"@stll/aho-corasick\";\nimport { FuzzySearch } from \"@stll/fuzzy-search\";\nimport { RegexSet } from \"@stll/regex-set\";\n\nimport { initEngines } from \"./engines\";\n\ninitEngines({ AhoCorasick, FuzzySearch, RegexSet });\n\nexport { TextSearch } from \"./text-search\";\nexport type { Match, PatternEntry, TextSearchOptions } from \"./types\";\n"],"mappings":"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"}
{"version":3,"file":"index.mjs","names":[],"sources":["../src/engines.ts","../src/classify.ts","../src/merge.ts","../src/text-search.ts","../src/index.ts"],"sourcesContent":["/**\n * Late-bound engine registry.\n *\n * Native and WASM entry points call initEngines()\n * with their respective implementations before any\n * TextSearch instance is created.\n */\n\nimport type { Options as AhoOptions, PatternEntry as AhoPatternEntry } from \"@stll/aho-corasick\";\nimport type {\n Options as FuzzyOptions,\n PatternEntry as FuzzyPatternEntry,\n} from \"@stll/fuzzy-search\";\nimport type { Options as RegexOptions, PatternEntry as RegexPatternEntry } from \"@stll/regex-set\";\nimport type { Match } from \"./types\";\n\ntype Engine = {\n isMatch: (haystack: string) => boolean;\n findIter: (haystack: string) => Match[];\n};\n\ntype Engines = {\n AhoCorasick: new (patterns: AhoPatternEntry[], options?: AhoOptions) => Engine;\n FuzzySearch: new (patterns: FuzzyPatternEntry[], options?: FuzzyOptions) => Engine;\n RegexSet: new (patterns: RegexPatternEntry[], options?: RegexOptions) => Engine;\n};\n\nlet engines: Engines | undefined;\n\nexport const initEngines = (e: Engines): void => {\n engines = e;\n};\n\nexport const getEngines = (): Engines => {\n if (!engines) {\n throw new Error(\n \"Engines not initialized. Import from \" +\n \"@stll/text-search or @stll/text-search-wasm, \" +\n \"not from internal modules.\",\n );\n }\n return engines;\n};\n","import type { PatternEntry } from \"./types\";\n\n/**\n * Normalized pattern with metadata for routing.\n */\nexport type ClassifiedPattern = {\n /** Original index in the input array. */\n originalIndex: number;\n /** The regex-compatible pattern string. */\n pattern: string | RegExp;\n /** Optional name. */\n name?: string;\n /**\n * Number of top-level alternation branches.\n * Used to detect large alternations that should\n * be isolated into their own RegexSet instance.\n */\n alternationCount: number;\n /**\n * True if the pattern is a pure literal string\n * (no regex metacharacters). These can be routed\n * to Aho-Corasick for SIMD-accelerated matching.\n */\n isLiteral: boolean;\n /**\n * Fuzzy distance if this is a fuzzy pattern.\n * Routes to @stll/fuzzy-search.\n */\n fuzzyDistance?: number | \"auto\";\n /**\n * Per-pattern AC options. When set, this literal\n * is grouped with others that have the same\n * options into a separate AC engine instance.\n */\n acOptions?: {\n caseInsensitive?: boolean;\n wholeWords?: boolean;\n };\n regexOptions?: {\n lazy?: boolean;\n prefilterAny?: readonly string[];\n prefilterCaseInsensitive?: boolean;\n prefilterRegex?: RegExp;\n };\n /**\n * Heuristic cost used for automatic RegexSet chunking.\n * Higher scores are more likely to suffer from shared\n * DFA state interaction and should be isolated or kept\n * in smaller chunks.\n */\n regexComplexity: number;\n};\n\n/**\n * Check if a string is a pure literal (no regex\n * metacharacters). Pure literals are routed to\n * Aho-Corasick instead of the regex DFA.\n */\nexport function isLiteralPattern(pattern: string): boolean {\n // All standard regex metacharacters cause a\n // pattern to be classified as regex (→ RegexSet).\n // To force literal AC routing for patterns with\n // dots/parens (e.g., \"s.r.o.\", \"č.p.\"), use the\n // explicit { literal: true } PatternEntry flag.\n for (let i = 0; i < pattern.length; i++) {\n const ch = pattern.charAt(i);\n if (\n ch === \"\\\\\" ||\n ch === \".\" ||\n ch === \"^\" ||\n ch === \"$\" ||\n ch === \"*\" ||\n ch === \"+\" ||\n ch === \"?\" ||\n ch === \"{\" ||\n ch === \"}\" ||\n ch === \"(\" ||\n ch === \")\" ||\n ch === \"[\" ||\n ch === \"]\" ||\n ch === \"|\"\n ) {\n return false;\n }\n }\n return pattern.length > 0;\n}\n\n/**\n * Count the maximum alternation branches at any\n * depth in a regex string. Used to detect patterns\n * with large alternations (even nested inside\n * groups) that should be isolated into their own\n * RegexSet to prevent DFA state explosion.\n *\n * \"a|b|c\" → 3\n * \"(a|b)|c\" → 2 (max of top=2, depth1=2)\n * \"(?:Ing\\\\.|Mgr\\\\.|Dr\\\\.)\" → 3 (depth 1)\n */\nexport function countAlternations(pattern: string): number {\n let depth = 0;\n let inClass = false;\n let i = 0;\n\n // Track max alternation count seen at any depth.\n // Each time we enter a group, start a fresh count.\n // When we leave, update the global max.\n let max = 1;\n let currentCount = 1; // count for current group\n const stack: number[] = []; // saved counts\n\n while (i < pattern.length) {\n const ch = pattern[i];\n\n if (ch === \"\\\\\" && i + 1 < pattern.length) {\n i += 2;\n continue;\n }\n\n if (ch === \"[\") inClass = true;\n if (ch === \"]\") inClass = false;\n\n if (!inClass) {\n if (ch === \"(\") {\n stack.push(currentCount);\n currentCount = 1;\n depth++;\n }\n if (ch === \")\") {\n if (currentCount > max) max = currentCount;\n currentCount = stack.pop() ?? 1;\n depth--;\n }\n if (ch === \"|\") {\n currentCount++;\n }\n }\n\n i++;\n }\n // Check top-level count too\n if (currentCount > max) max = currentCount;\n return max;\n}\n\nexport function scoreRegexComplexity(\n pattern: string,\n alternationCount = countAlternations(pattern),\n): number {\n let score = 1;\n\n if (pattern.length > 80) score += 2;\n if (pattern.length > 160) score += 2;\n if (alternationCount > 1) score += alternationCount >= 4 ? 2 : 1;\n if (/\\\\p\\{/.test(pattern)) score += 3;\n if (/\\(\\?<?[!=]/.test(pattern)) score += 4;\n if (/\\\\[bBAZz]/.test(pattern)) score += 1;\n if (/\\.\\*|\\.\\+/.test(pattern)) score += 3;\n if (/\\[[^\\]]+\\][*+{]/.test(pattern)) score += 2;\n if (/\\{[\\d,]+\\}/.test(pattern)) score += 2;\n if (/(?:\\\\[dDsSwW]|\\\\p\\{[^}]+\\}|\\[[^\\]]+\\])[*+]/.test(pattern)) {\n score += 1;\n }\n\n return score;\n}\n\n/**\n * Classify and normalize pattern entries.\n */\nexport function classifyPatterns(entries: PatternEntry[], allLiteral = false): ClassifiedPattern[] {\n return entries.map((entry, i) => {\n if (typeof entry === \"string\") {\n const alternationCount = allLiteral ? 0 : countAlternations(entry);\n return {\n originalIndex: i,\n pattern: entry,\n alternationCount,\n isLiteral: allLiteral || isLiteralPattern(entry),\n regexComplexity: scoreRegexComplexity(entry, alternationCount),\n };\n }\n\n if (entry instanceof RegExp) {\n const alternationCount = countAlternations(entry.source);\n return {\n originalIndex: i,\n pattern: entry,\n alternationCount,\n isLiteral: false, // RegExp is never literal\n regexComplexity: scoreRegexComplexity(entry.source, alternationCount),\n };\n }\n\n // Fuzzy pattern: has `distance` field\n if (\"distance\" in entry) {\n const result: ClassifiedPattern = {\n originalIndex: i,\n pattern: entry.pattern,\n alternationCount: 0,\n isLiteral: false,\n fuzzyDistance: entry.distance,\n regexComplexity: 0,\n };\n if (entry.name !== undefined) result.name = entry.name;\n return result;\n }\n\n // Explicit literal: skip metachar detection\n if (entry.literal === true) {\n const hasPerPatternOpts = \"caseInsensitive\" in entry || \"wholeWords\" in entry;\n const result: ClassifiedPattern = {\n originalIndex: i,\n pattern: entry.pattern,\n alternationCount: 0,\n isLiteral: true,\n regexComplexity: 0,\n };\n if (entry.name !== undefined) result.name = entry.name;\n if (hasPerPatternOpts) {\n const opts: NonNullable<ClassifiedPattern[\"acOptions\"]> = {};\n if (entry.caseInsensitive !== undefined) opts.caseInsensitive = entry.caseInsensitive;\n if (entry.wholeWords !== undefined) opts.wholeWords = entry.wholeWords;\n result.acOptions = opts;\n }\n return result;\n }\n\n const pat = entry.pattern;\n const source = pat instanceof RegExp ? pat.source : pat;\n const alternationCount = allLiteral ? 0 : countAlternations(source);\n\n const result: ClassifiedPattern = {\n originalIndex: i,\n pattern: pat,\n alternationCount,\n isLiteral: typeof pat === \"string\" && (allLiteral || isLiteralPattern(pat)),\n regexComplexity: scoreRegexComplexity(source, alternationCount),\n };\n if (entry.name !== undefined) result.name = entry.name;\n const regexOptions: NonNullable<ClassifiedPattern[\"regexOptions\"]> = {};\n if (entry.lazy === true) regexOptions.lazy = true;\n if (entry.prefilterAny !== undefined) regexOptions.prefilterAny = entry.prefilterAny;\n if (entry.prefilterCaseInsensitive !== undefined)\n regexOptions.prefilterCaseInsensitive = entry.prefilterCaseInsensitive;\n if (entry.prefilterRegex !== undefined) regexOptions.prefilterRegex = entry.prefilterRegex;\n if (Object.keys(regexOptions).length > 0) {\n result.regexOptions = regexOptions;\n }\n return result;\n });\n}\n","import type { Match } from \"./types\";\n\n/**\n * Merge matches from multiple engines, sort by\n * position, and select non-overlapping (longest\n * first at ties). Same algorithm as regex-set's\n * internal select_non_overlapping.\n */\nexport function mergeAndSelect(matches: Match[]): Match[] {\n if (matches.length <= 1) return matches;\n\n // Sort: start ascending, longest first at ties\n matches.sort((a, b) => {\n if (a.start !== b.start) {\n return a.start - b.start;\n }\n return b.end - b.start - (a.end - a.start);\n });\n\n // Greedily select non-overlapping\n const selected: Match[] = [];\n let lastEnd = 0;\n\n for (const m of matches) {\n if (m.start >= lastEnd) {\n selected.push(m);\n lastEnd = m.end;\n }\n }\n\n return selected;\n}\n","import type { ClassifiedPattern } from \"./classify\";\nimport { classifyPatterns } from \"./classify\";\nimport { getEngines } from \"./engines\";\nimport { mergeAndSelect } from \"./merge\";\nimport type { Match, PatternEntry, TextSearchOptions } from \"./types\";\n\nconst AUTO_REGEX_CHUNK_MAX_SIZE = 16;\nconst AUTO_REGEX_CHUNK_COMPLEXITY_BUDGET = 6;\nconst AUTO_REGEX_ISOLATE_COMPLEXITY = 7;\nconst SPLIT_IDENTITY_AC_CHUNK_SIZE = 20_000;\nconst SPLIT_IDENTITY_AC_MIN_PATTERNS = SPLIT_IDENTITY_AC_CHUNK_SIZE;\n\n/** Common engine interface for dispatch. */\ntype Engine = {\n isMatch: (haystack: string) => boolean;\n findIter: (haystack: string) => Match[];\n};\n\ntype OverlapEngine = Engine & {\n findOverlappingIter: (haystack: string) => Match[];\n};\n\n/**\n * An engine instance with pattern index mapping.\n */\ntype RegexSlot = {\n type: \"regex\";\n rs?: Engine;\n build?: (() => Engine) | undefined;\n prefilter?: Engine | undefined;\n prefilterRegex?: RegExp | undefined;\n indexMap: number[];\n nameMap: (string | undefined)[];\n identityMap?: true;\n};\n\ntype AcSlot = {\n type: \"ac\";\n ac: Engine;\n indexMap: number[];\n nameMap: (string | undefined)[];\n identityMap?: true;\n patternCount?: number;\n};\n\ntype SplitAcSlot = {\n type: \"split-ac\";\n acs: {\n ac: OverlapEngine;\n patternOffset: number;\n }[];\n indexMap: number[];\n nameMap: (string | undefined)[];\n identityMap: true;\n patternCount: number;\n};\n\ntype FuzzySlot = {\n type: \"fuzzy\";\n fs: Engine;\n indexMap: number[];\n nameMap: (string | undefined)[];\n identityMap?: true;\n};\n\ntype EngineSlot = RegexSlot | AcSlot | SplitAcSlot | FuzzySlot;\n\n/**\n * Multi-engine text search orchestrator.\n *\n * Routes patterns to the optimal engine\n * configuration:\n * - Large alternation patterns get their own\n * RegexSet instance (prevents DFA state explosion)\n * - Normal regex patterns use complexity-aware chunks\n * (avoids bad cross-pattern DFA interaction)\n *\n * Merges results from all engines into a unified\n * non-overlapping Match[] sorted by position.\n */\nexport class TextSearch {\n private engines: EngineSlot[] = [];\n private patternCount: number;\n private overlapAll: boolean;\n /**\n * True when there's exactly one engine and all\n * patterns map to identity indices (0→0, 1→1, ...).\n * Enables zero-overhead findIter: return raw engine\n * output without remapping or object allocation.\n */\n private zeroOverhead: boolean = false;\n\n constructor(patterns: PatternEntry[], options?: TextSearchOptions) {\n this.patternCount = patterns.length;\n this.overlapAll = options?.overlapStrategy === \"all\";\n if (patterns.length > 0 && options?.allLiteral === true && allStringPatterns(patterns)) {\n const engine = buildIdentityAcEngine(patterns, {\n unicodeBoundaries: options.unicodeBoundaries ?? true,\n wholeWords: options.wholeWords ?? false,\n caseInsensitive: options.caseInsensitive ?? false,\n });\n this.engines.push(engine);\n this.zeroOverhead = true;\n return;\n }\n\n const maxAlt = options?.maxAlternations ?? 50;\n const classified = classifyPatterns(patterns, options?.allLiteral ?? false);\n\n // Four buckets:\n // 1. Fuzzy patterns → FuzzySearch (Levenshtein)\n // 2. Pure literals → Aho-Corasick (SIMD)\n // 3. Normal regex → shared RegexSet (DFA)\n // 4. Large alternations → isolated RegexSet\n const fuzzy: ClassifiedPattern[] = [];\n const literals: ClassifiedPattern[] = [];\n const shared: ClassifiedPattern[] = [];\n const isolated: ClassifiedPattern[] = [];\n\n for (const cp of classified) {\n if (cp.fuzzyDistance !== undefined) {\n fuzzy.push(cp);\n } else if (cp.isLiteral) {\n literals.push(cp);\n } else if (cp.regexOptions?.lazy === true) {\n isolated.push(cp);\n } else if (cp.alternationCount > maxAlt) {\n isolated.push(cp);\n } else {\n shared.push(cp);\n }\n }\n\n const rsOptions = {\n unicodeBoundaries: options?.unicodeBoundaries ?? true,\n wholeWords: options?.wholeWords ?? false,\n caseInsensitive: options?.caseInsensitive ?? false,\n };\n\n // Build fuzzy engine\n if (fuzzy.length > 0) {\n const fuzzyOpts: Parameters<typeof buildFuzzyEngine>[1] = {\n unicodeBoundaries: rsOptions.unicodeBoundaries,\n wholeWords: rsOptions.wholeWords,\n };\n if (options?.fuzzyMetric !== undefined) fuzzyOpts.metric = options.fuzzyMetric;\n if (options?.normalizeDiacritics !== undefined)\n fuzzyOpts.normalizeDiacritics = options.normalizeDiacritics;\n if (options?.caseInsensitive !== undefined)\n fuzzyOpts.caseInsensitive = options.caseInsensitive;\n this.engines.push(buildFuzzyEngine(fuzzy, fuzzyOpts));\n }\n\n // Build AC engine(s) for pure literals.\n // Group by per-pattern AC options so patterns\n // with different caseInsensitive/wholeWords\n // settings get separate AC instances.\n if (literals.length > 0) {\n const groups = new Map<string, ClassifiedPattern[]>();\n for (const cp of literals) {\n const ci = cp.acOptions?.caseInsensitive ?? rsOptions.caseInsensitive;\n const ww = cp.acOptions?.wholeWords ?? rsOptions.wholeWords;\n const key = `${ci ? 1 : 0}:${ww ? 1 : 0}`;\n const group = groups.get(key);\n if (group) {\n group.push(cp);\n } else {\n groups.set(key, [cp]);\n }\n }\n for (const [key, group] of groups) {\n const [ci, ww] = key.split(\":\");\n this.engines.push(\n buildAcEngine(group, {\n ...rsOptions,\n caseInsensitive: ci === \"1\",\n wholeWords: ww === \"1\",\n }),\n );\n }\n }\n\n // Keep normal regex patterns in bounded chunks.\n // One giant RegexSet can develop poor cross-pattern\n // DFA interactions; auto chunking groups simple\n // homogeneous regexes while isolating complex\n // patterns with lookarounds, Unicode classes,\n // large repeats, or long wildcard spans.\n for (const chunk of chunkSharedRegexPatterns(shared, options?.regexChunkSize)) {\n this.engines.push(buildRegexEngine(chunk, rsOptions));\n }\n\n for (const cp of isolated) {\n const lazyOptions: {\n lazy?: boolean;\n prefilterAny?: readonly string[];\n prefilterCaseInsensitive?: boolean;\n prefilterRegex?: RegExp;\n } = {};\n if (cp.regexOptions?.lazy === true) {\n lazyOptions.lazy = true;\n }\n if (cp.regexOptions?.prefilterAny !== undefined) {\n lazyOptions.prefilterAny = cp.regexOptions.prefilterAny;\n }\n if (cp.regexOptions?.prefilterCaseInsensitive !== undefined) {\n lazyOptions.prefilterCaseInsensitive = cp.regexOptions.prefilterCaseInsensitive;\n }\n if (cp.regexOptions?.prefilterRegex !== undefined) {\n lazyOptions.prefilterRegex = cp.regexOptions.prefilterRegex;\n }\n this.engines.push(buildRegexEngine([cp], rsOptions, lazyOptions));\n }\n\n // Zero-overhead fast path: when all patterns\n // land in a single engine, the indexMap is\n // identity (0→0, 1→1, ...) and no names need\n // attaching. findIter can return raw engine\n // output without any JS-side remapping.\n if (this.engines.length === 1) {\n const engine = this.engines[0];\n if (engine === undefined) {\n throw new Error(\"Expected single engine after length check\");\n }\n const hasNames = engine.nameMap.some((n) => n !== undefined);\n if (!hasNames) {\n this.zeroOverhead = true;\n }\n }\n }\n\n /** Number of patterns. */\n get length(): number {\n return this.patternCount;\n }\n\n /** Returns true if any pattern matches. */\n isMatch(haystack: string): boolean {\n for (const engine of this.engines) {\n if (engineIsMatch(engine, haystack)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Find matches in text.\n *\n * With `overlapStrategy: \"longest\"` (default):\n * returns non-overlapping matches, longest wins.\n *\n * With `overlapStrategy: \"all\"`: returns all\n * matches including overlaps, sorted by position.\n */\n findIter(haystack: string): Match[] {\n // Fast path: single engine, identity indexMap,\n // no names → return raw engine output directly.\n // Zero JS overhead: no remapping, no allocation.\n if (this.zeroOverhead) {\n const engine = this.engines[0];\n if (engine === undefined) {\n throw new Error(\"Zero-overhead path requires a single engine\");\n }\n return engineFindIter(engine, haystack);\n }\n\n // Single engine but needs name remapping\n if (this.engines.length === 1) {\n const engine = this.engines[0];\n if (engine === undefined) {\n throw new Error(\"Expected single engine after length check\");\n }\n return remapMatches(engineFindIter(engine, haystack), engine);\n }\n\n // Multi-engine: collect from all, remap in-place\n const all: Match[] = [];\n for (const engine of this.engines) {\n const matches = engineFindIter(engine, haystack);\n // In-place remapping avoids .map() allocation\n for (const m of remapMatches(matches, engine)) {\n all.push(m);\n }\n }\n\n if (this.overlapAll) {\n return all.sort((a, b) => a.start - b.start);\n }\n\n return mergeAndSelect(all);\n }\n\n /** Which pattern indices matched (not where). */\n whichMatch(haystack: string): number[] {\n const seen = new Set<number>();\n\n for (const engine of this.engines) {\n // AC doesn't have whichMatch — use findIter\n const matches = engineFindIter(engine, haystack);\n for (const m of matches) {\n const idx = getOriginalPatternIndex(engine, m.pattern);\n seen.add(idx);\n }\n }\n\n return [...seen];\n }\n\n /**\n * Replace all non-overlapping matches.\n * replacements[i] replaces pattern i.\n */\n replaceAll(haystack: string, replacements: string[]): string {\n if (replacements.length !== this.patternCount) {\n throw new Error(\n `Expected ${this.patternCount} ` + `replacements, got ${replacements.length}`,\n );\n }\n\n // Always use non-overlapping matches for\n // replacement, even if overlapStrategy is \"all\".\n const all: Match[] = [];\n for (const engine of this.engines) {\n const matches = engineFindIter(engine, haystack);\n for (const m of remapMatches(matches, engine)) {\n all.push(m);\n }\n }\n const matches = mergeAndSelect(all);\n\n let result = \"\";\n let last = 0;\n\n for (const m of matches) {\n result += haystack.slice(last, m.start);\n const replacement = replacements[m.pattern];\n if (replacement === undefined) {\n throw new Error(`Missing replacement for pattern ${m.pattern}`);\n }\n result += replacement;\n last = m.end;\n }\n\n result += haystack.slice(last);\n return result;\n }\n}\n\nfunction chunkSharedRegexPatterns(\n patterns: ClassifiedPattern[],\n explicitChunkSize: number | undefined,\n): ClassifiedPattern[][] {\n if (explicitChunkSize !== undefined) {\n const chunkSize = Math.max(1, explicitChunkSize);\n const chunks: ClassifiedPattern[][] = [];\n for (let i = 0; i < patterns.length; i += chunkSize) {\n chunks.push(patterns.slice(i, i + chunkSize));\n }\n return chunks;\n }\n\n const chunks: ClassifiedPattern[][] = [];\n let current: ClassifiedPattern[] = [];\n let currentComplexity = 0;\n\n const flush = () => {\n if (current.length === 0) return;\n chunks.push(current);\n current = [];\n currentComplexity = 0;\n };\n\n for (const pattern of patterns) {\n const complexity = pattern.regexComplexity;\n if (complexity >= AUTO_REGEX_ISOLATE_COMPLEXITY) {\n flush();\n chunks.push([pattern]);\n continue;\n }\n\n const wouldExceedSize = current.length >= AUTO_REGEX_CHUNK_MAX_SIZE;\n const wouldExceedComplexity =\n current.length > 0 && currentComplexity + complexity > AUTO_REGEX_CHUNK_COMPLEXITY_BUDGET;\n if (wouldExceedSize || wouldExceedComplexity) {\n flush();\n }\n\n current.push(pattern);\n currentComplexity += complexity;\n }\n\n flush();\n return chunks;\n}\n\n/**\n * Build a RegexSet engine from classified patterns.\n */\nfunction buildRegexEngine(\n patterns: ClassifiedPattern[],\n options: {\n unicodeBoundaries: boolean;\n wholeWords: boolean;\n caseInsensitive: boolean;\n },\n lazyOptions?: {\n lazy?: boolean;\n prefilterAny?: readonly string[];\n prefilterCaseInsensitive?: boolean;\n prefilterRegex?: RegExp;\n },\n): RegexSlot {\n const rsPatterns: (\n | string\n | RegExp\n | {\n pattern: string | RegExp;\n name?: string;\n }\n )[] = [];\n const indexMap: number[] = [];\n const nameMap: (string | undefined)[] = [];\n\n for (const cp of patterns) {\n if (cp.name !== undefined) {\n rsPatterns.push({\n pattern: cp.pattern,\n });\n } else {\n rsPatterns.push(cp.pattern);\n }\n indexMap.push(cp.originalIndex);\n nameMap.push(cp.name);\n }\n\n const { RegexSet } = getEngines();\n const build = () => new RegexSet(rsPatterns, options);\n const inferredPrefilter =\n lazyOptions === undefined && patterns.length === 1\n ? inferLeadingLiteralPrefilter(patterns[0]?.pattern)\n : undefined;\n\n if (lazyOptions?.lazy === true) {\n const prefilter =\n lazyOptions.prefilterAny && lazyOptions.prefilterAny.length > 0\n ? buildLiteralPrefilter(lazyOptions.prefilterAny, {\n caseInsensitive: lazyOptions.prefilterCaseInsensitive ?? options.caseInsensitive,\n })\n : undefined;\n const slot: RegexSlot = { type: \"regex\", build, prefilter, indexMap, nameMap };\n if (lazyOptions.prefilterRegex !== undefined) {\n slot.prefilterRegex = lazyOptions.prefilterRegex;\n }\n return slot;\n }\n\n if (inferredPrefilter !== undefined) {\n const slot: RegexSlot = { type: \"regex\", rs: build(), indexMap, nameMap };\n slot.prefilter = buildLiteralPrefilter([inferredPrefilter.literal], {\n caseInsensitive: inferredPrefilter.caseInsensitive || options.caseInsensitive,\n });\n return slot;\n }\n return { type: \"regex\", rs: build(), indexMap, nameMap };\n}\n\nfunction inferLeadingLiteralPrefilter(\n pattern: string | RegExp | undefined,\n): { literal: string; caseInsensitive: boolean } | undefined {\n if (pattern === undefined) {\n return undefined;\n }\n\n const source = pattern instanceof RegExp ? pattern.source : pattern;\n let caseInsensitive = pattern instanceof RegExp ? pattern.ignoreCase : false;\n let i = 0;\n\n if (source.startsWith(\"(?i)\")) {\n caseInsensitive = true;\n i = 4;\n }\n\n while (i < source.length) {\n if (source[i] === \"^\") {\n i++;\n continue;\n }\n if (source[i] === \"\\\\\" && /[bBAZz]/.test(source[i + 1] ?? \"\")) {\n i += 2;\n continue;\n }\n if (\n source.startsWith(\"(?=\", i) ||\n source.startsWith(\"(?!\", i) ||\n source.startsWith(\"(?<=\", i) ||\n source.startsWith(\"(?<!\", i)\n ) {\n const end = findRegexGroupEnd(source, i);\n if (end === undefined) {\n return undefined;\n }\n i = end;\n continue;\n }\n break;\n }\n\n let literal = \"\";\n while (i < source.length) {\n const ch = source[i];\n if (ch === undefined) {\n break;\n }\n if (ch === \"\\\\\") {\n const next = source[i + 1];\n if (next === undefined || /[A-Za-z]/.test(next)) {\n break;\n }\n literal += next;\n i += 2;\n continue;\n }\n if (\"^$*+?.()[]{}|\".includes(ch)) {\n break;\n }\n literal += ch;\n i++;\n }\n\n if (source[i] === \"|\") {\n return undefined;\n }\n if (source[i] === \"?\" || source[i] === \"*\") {\n literal = literal.slice(0, -1);\n } else if (source[i] === \"{\" && source.startsWith(\"{0\", i)) {\n literal = literal.slice(0, -1);\n }\n\n return literal.length >= 2 ? { literal, caseInsensitive } : undefined;\n}\n\nfunction findRegexGroupEnd(source: string, start: number): number | undefined {\n let depth = 0;\n let inClass = false;\n for (let i = start; i < source.length; i++) {\n const ch = source[i];\n if (ch === \"\\\\\") {\n i++;\n continue;\n }\n if (ch === \"[\" && !inClass) {\n inClass = true;\n continue;\n }\n if (ch === \"]\" && inClass) {\n inClass = false;\n continue;\n }\n if (inClass) {\n continue;\n }\n if (ch === \"(\") {\n depth++;\n continue;\n }\n if (ch === \")\") {\n depth--;\n if (depth === 0) {\n return i + 1;\n }\n }\n }\n return undefined;\n}\n\nfunction buildLiteralPrefilter(\n literals: readonly string[],\n options: { caseInsensitive: boolean },\n): Engine {\n const unique = [...new Set(literals.filter((literal) => literal.length > 0))];\n // Case-insensitive matching must use the engines' Unicode simple case\n // folding, which `toLowerCase` does not replicate (e.g. `ſ` folds to `s` yet\n // is already lowercase). Route case-insensitive prefilters through\n // Aho-Corasick, which folds identically to the search engines; reserve the\n // single-literal fast path for the case-sensitive exact-substring check.\n if (unique.length === 1 && !options.caseInsensitive) {\n const literal = unique[0];\n if (literal === undefined) {\n throw new Error(\"Expected single literal after length check\");\n }\n return {\n isMatch: (haystack) => haystack.includes(literal),\n findIter: () => [],\n };\n }\n\n const { AhoCorasick } = getEngines();\n return new AhoCorasick(unique, {\n caseInsensitive: options.caseInsensitive,\n });\n}\n\nfunction allStringPatterns(patterns: PatternEntry[]): patterns is string[] {\n for (const pattern of patterns) {\n if (typeof pattern !== \"string\") {\n return false;\n }\n }\n return true;\n}\n\nfunction getRegexSlotEngine(engine: RegexSlot): Engine {\n if (engine.rs !== undefined) {\n return engine.rs;\n }\n if (engine.build === undefined) {\n throw new Error(\"Lazy regex slot is missing a builder\");\n }\n engine.rs = engine.build();\n engine.build = undefined;\n return engine.rs;\n}\n\nfunction regexSlotPrefilterMatches(engine: RegexSlot, haystack: string): boolean {\n if (engine.prefilter && !engine.prefilter.isMatch(haystack)) {\n return false;\n }\n if (engine.prefilterRegex) {\n engine.prefilterRegex.lastIndex = 0;\n if (!engine.prefilterRegex.test(haystack)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Build an Aho-Corasick engine from literal patterns.\n */\nfunction buildAcEngine(\n patterns: ClassifiedPattern[],\n options: {\n unicodeBoundaries: boolean;\n wholeWords: boolean;\n caseInsensitive: boolean;\n },\n): AcSlot {\n const literals: string[] = [];\n const indexMap: number[] = [];\n const nameMap: (string | undefined)[] = [];\n\n for (const cp of patterns) {\n literals.push(cp.pattern as string);\n indexMap.push(cp.originalIndex);\n nameMap.push(cp.name);\n }\n\n const { AhoCorasick } = getEngines();\n const ac = new AhoCorasick(literals, {\n wholeWords: options.wholeWords,\n unicodeBoundaries: options.unicodeBoundaries,\n caseInsensitive: options.caseInsensitive,\n });\n\n return { type: \"ac\", ac, indexMap, nameMap, patternCount: literals.length };\n}\n\nfunction buildIdentityAcEngine(\n patterns: string[],\n options: {\n unicodeBoundaries: boolean;\n wholeWords: boolean;\n caseInsensitive: boolean;\n },\n): AcSlot | SplitAcSlot {\n const { AhoCorasick } = getEngines();\n if (\n options.wholeWords &&\n options.unicodeBoundaries &&\n patterns.length >= SPLIT_IDENTITY_AC_MIN_PATTERNS\n ) {\n const acs: SplitAcSlot[\"acs\"] = [];\n for (let i = 0; i < patterns.length; i += SPLIT_IDENTITY_AC_CHUNK_SIZE) {\n acs.push({\n ac: new AhoCorasick(patterns.slice(i, i + SPLIT_IDENTITY_AC_CHUNK_SIZE), {\n wholeWords: options.wholeWords,\n unicodeBoundaries: options.unicodeBoundaries,\n caseInsensitive: options.caseInsensitive,\n }) as OverlapEngine,\n patternOffset: i,\n });\n }\n\n return {\n type: \"split-ac\",\n acs,\n indexMap: [],\n nameMap: [],\n identityMap: true,\n patternCount: patterns.length,\n };\n }\n\n const ac = new AhoCorasick(patterns, {\n wholeWords: options.wholeWords,\n unicodeBoundaries: options.unicodeBoundaries,\n caseInsensitive: options.caseInsensitive,\n });\n\n return {\n type: \"ac\",\n ac,\n indexMap: [],\n nameMap: [],\n identityMap: true,\n patternCount: patterns.length,\n };\n}\n\n/**\n * Build a FuzzySearch engine from fuzzy patterns.\n */\nfunction buildFuzzyEngine(\n patterns: ClassifiedPattern[],\n options: {\n unicodeBoundaries: boolean;\n wholeWords: boolean;\n metric?: \"levenshtein\" | \"damerau-levenshtein\";\n normalizeDiacritics?: boolean;\n caseInsensitive?: boolean;\n },\n): FuzzySlot {\n const fsPatterns: {\n pattern: string;\n distance?: number | \"auto\";\n name?: string;\n }[] = [];\n const indexMap: number[] = [];\n const nameMap: (string | undefined)[] = [];\n\n for (const cp of patterns) {\n const entry: (typeof fsPatterns)[number] = {\n pattern: cp.pattern as string,\n };\n if (cp.fuzzyDistance !== undefined) entry.distance = cp.fuzzyDistance;\n if (cp.name !== undefined) entry.name = cp.name;\n fsPatterns.push(entry);\n indexMap.push(cp.originalIndex);\n nameMap.push(cp.name);\n }\n\n const fsOptions: {\n unicodeBoundaries: boolean;\n wholeWords: boolean;\n metric?: \"levenshtein\" | \"damerau-levenshtein\";\n normalizeDiacritics?: boolean;\n caseInsensitive?: boolean;\n } = {\n unicodeBoundaries: options.unicodeBoundaries,\n wholeWords: options.wholeWords,\n };\n if (options.metric !== undefined) fsOptions.metric = options.metric;\n if (options.normalizeDiacritics !== undefined)\n fsOptions.normalizeDiacritics = options.normalizeDiacritics;\n if (options.caseInsensitive !== undefined) fsOptions.caseInsensitive = options.caseInsensitive;\n const { FuzzySearch } = getEngines();\n const fs = new FuzzySearch(fsPatterns, fsOptions);\n\n return { type: \"fuzzy\", fs, indexMap, nameMap };\n}\n\n/**\n * Dispatch isMatch to the correct engine.\n */\nfunction engineIsMatch(engine: EngineSlot, haystack: string): boolean {\n switch (engine.type) {\n case \"ac\":\n return engine.ac.isMatch(haystack);\n case \"split-ac\":\n return engine.acs.some(({ ac }) => ac.isMatch(haystack));\n case \"fuzzy\":\n return engine.fs.isMatch(haystack);\n case \"regex\":\n if (!regexSlotPrefilterMatches(engine, haystack)) {\n return false;\n }\n return getRegexSlotEngine(engine).isMatch(haystack);\n }\n throw new Error(\"Unsupported engine type\");\n}\n\n/**\n * Dispatch findIter to the correct engine.\n */\nfunction engineFindIter(engine: EngineSlot, haystack: string): Match[] {\n switch (engine.type) {\n case \"ac\":\n return engine.ac.findIter(haystack);\n case \"split-ac\":\n return splitAcFindIter(engine, haystack);\n case \"fuzzy\":\n return engine.fs.findIter(haystack);\n case \"regex\":\n if (!regexSlotPrefilterMatches(engine, haystack)) {\n return [];\n }\n return getRegexSlotEngine(engine).findIter(haystack);\n }\n throw new Error(\"Unsupported engine type\");\n}\n\nfunction splitAcFindIter(engine: SplitAcSlot, haystack: string): Match[] {\n const all: Match[] = [];\n for (const { ac, patternOffset } of engine.acs) {\n const matches = ac.findOverlappingIter(haystack);\n for (const match of matches) {\n all.push({\n pattern: match.pattern + patternOffset,\n start: match.start,\n end: match.end,\n text: match.text,\n });\n }\n }\n return selectLeftmostLongestMatches(all);\n}\n\nfunction selectLeftmostLongestMatches(matches: Match[]): Match[] {\n if (matches.length === 0) {\n return [];\n }\n\n matches.sort((a, b) => a.start - b.start || b.end - a.end || a.pattern - b.pattern);\n\n const selected: Match[] = [];\n let cursor = 0;\n let i = 0;\n while (i < matches.length) {\n const start = matches[i]?.start;\n if (start === undefined) {\n break;\n }\n if (start < cursor) {\n i++;\n continue;\n }\n\n let best = matches[i];\n if (best === undefined) {\n break;\n }\n i++;\n while (i < matches.length && matches[i]?.start === start) {\n const candidate = matches[i];\n if (\n candidate !== undefined &&\n (candidate.end > best.end ||\n (candidate.end === best.end && candidate.pattern < best.pattern))\n ) {\n best = candidate;\n }\n i++;\n }\n\n selected.push(best);\n cursor = best.end;\n }\n\n return selected;\n}\n\n/**\n * Remap engine-local match indices to original\n * input indices and add names.\n */\nfunction remapMatches(matches: Match[], engine: EngineSlot): Match[] {\n return matches.map((m) => {\n const originalIdx = getOriginalPatternIndex(engine, m.pattern);\n const name = engine.nameMap[m.pattern];\n const result: Match = {\n pattern: originalIdx,\n start: m.start,\n end: m.end,\n text: m.text,\n };\n if (name !== undefined) {\n result.name = name;\n }\n // Preserve edit distance from fuzzy matches\n if (m.distance !== undefined) {\n result.distance = m.distance;\n }\n return result;\n });\n}\n\nfunction getOriginalPatternIndex(engine: EngineSlot, pattern: number): number {\n if (engine.identityMap === true) {\n return pattern;\n }\n const originalIdx = engine.indexMap[pattern];\n if (originalIdx === undefined) {\n throw new Error(`Missing indexMap entry for pattern ${pattern}`);\n }\n return originalIdx;\n}\n","/* Native entry point — loads @stll engines\n * for Node.js/Bun and re-exports the public API. */\n\nimport { AhoCorasick } from \"@stll/aho-corasick\";\nimport { FuzzySearch } from \"@stll/fuzzy-search\";\nimport { RegexSet } from \"@stll/regex-set\";\n\nimport { initEngines } from \"./engines\";\n\ninitEngines({ AhoCorasick, FuzzySearch, RegexSet });\n\nexport { TextSearch } from \"./text-search\";\nexport type { Match, PatternEntry, TextSearchOptions } from \"./types\";\n"],"mappings":"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"}
{
"name": "@stll/text-search",
"version": "1.0.6",
"version": "1.0.7",
"description": "Multi-engine text search orchestrator for Node.js and Bun. Routes literals, regex, and fuzzy patterns to the right engine automatically.",

@@ -58,3 +58,3 @@ "keywords": [

"@stll/aho-corasick": "^1.0.4",
"@stll/fuzzy-search": "^1.1.2",
"@stll/fuzzy-search": "^1.1.3",
"@stll/regex-set": "^1.0.5"

@@ -64,3 +64,3 @@ },

"@stll/aho-corasick-wasm": "^1.0.4",
"@stll/fuzzy-search-wasm": "^1.1.2",
"@stll/fuzzy-search-wasm": "^1.1.3",
"@stll/oxlint-config": "^0.3.0",

@@ -67,0 +67,0 @@ "@stll/regex-set-wasm": "^1.0.5",

@@ -88,2 +88,11 @@ <p align="center">

## Rust core
The repository also contains `stella-text-search-core`
in `crates/core`. It mirrors the orchestration layer:
classification, engine routing, regex chunking, lazy
prefilters, split literal search, match merging, and
replacement. The crate is not published yet; it exists
as the native core boundary for downstream bindings.
## Options

@@ -148,2 +157,5 @@

bun run build
cargo fmt --all --check
cargo clippy --all-targets --locked
cargo test --locked
```

@@ -150,0 +162,0 @@