SearchEngine¶
SearchEngine is the singleton that owns the provider registry, the session cache, and the query → session dispatch logic. There is exactly one instance per app session, created in App.vue and passed down via provide/inject.
Source: src/search/engine.ts
Creation¶
export function createSearchEngine(opts: SearchEngineOptions = {}): SearchEngine {
const cache = new SessionCache<SearchSession>(opts.cacheMax ?? 30);
const registry: Record<string, SearchProvider> = {
rutracker: rutrackerProvider,
soulseek: soulseekProvider,
};
// ...
}
SessionCache is an LRU cache keyed by "${normalizedQuery}|${enabledProviders}". This means searching for the same query with the same set of enabled providers returns the existing live session rather than starting a new one — useful when the user navigates back.
query()¶
async function query(
rawQuery: string,
enabled: Record<string, boolean>, // { rutracker: true, soulseek: true }
queryOpts: { skipResolver?: boolean } = {},
): Promise<SearchSession>
The full flow:
rawQuery (e.g. "нон стоп молли")
↓
normalizeQuery → lowercase trim → cacheKey
↓ cache miss
resolveQuery(rawQuery) [Tauri IPC → Rust resolver]
↓ ResolveResult | null
build providerQ:
canonical == null → providerQ = rawQuery
canonical.title == "" → providerQ = stripBrackets(artist)
else → providerQ = "Artist Title" (brackets stripped)
↓
new SearchSession(providerQ, { providers, pipeline, resolved, rawQuery })
↓
cache.set(cacheKey, session)
↓
return session
Bracket stripping¶
Before building providerQ, brackets are stripped from the canonical:
const stripBrackets = (s: unknown): string =>
String(s ?? "")
.replace(/\([^()]*\)/g, "")
.replace(/\[[^\]]*\]/g, "")
.replace(/\s{2,}/g, " ")
.trim();
This prevents sending "1.Kla$ (Первый класс)" to providers — the parenthetical is a Genius annotation, not part of the actual search string.
skipResolver¶
When skipResolver: true, the cache lookup is also skipped and the raw query is used directly as providerQ. This is used for manual "literal search" mode in the UI.
Provider registry¶
Two providers are registered at startup:
const registry: Record<string, SearchProvider> = {
rutracker: rutrackerProvider,
soulseek: soulseekProvider,
};
The enabled parameter (from the search store, driven by UI toggles) filters which providers actually run for a given query:
Sorted to make cache keys deterministic regardless of object property order.
Cache¶
Sessions are cached by "${normalizedQuery}|${sortedEnabledProviders}". A cache hit returns the existing session — its generators may still be running, and the caller gets a live reactive view.
Cache size is 30 by default. LRU eviction removes the least-recently-used session when the cache is full.
clearCache() is called when the user explicitly clears search results or when settings change.