import * as path from 'path'; import type { FrequencyDictionaryLookup } from '../types'; import { createFrequencyDictionaryLookup } from '../core/services'; export interface FrequencyDictionarySearchPathDeps { getDictionaryRoots: () => string[]; getSourcePath?: () => string | undefined; } export interface FrequencyDictionaryRuntimeDeps { isFrequencyDictionaryEnabled: () => boolean; getSearchPaths: () => string[]; setFrequencyRankLookup: (lookup: FrequencyDictionaryLookup) => void; log: (message: string) => void; } let frequencyDictionaryLookupInitialized = false; let frequencyDictionaryLookupInitialization: Promise | null = null; // Frequency dictionary services are initialized lazily as a process-wide singleton. // Initialization is idempotent and intentionally shared across callers. export function getFrequencyDictionarySearchPaths( deps: FrequencyDictionarySearchPathDeps, ): string[] { const dictionaryRoots = deps.getDictionaryRoots(); const sourcePath = deps.getSourcePath?.(); const rawSearchPaths: string[] = []; // User-provided path takes precedence over bundled/default roots. // Root list should include default installed frequency-dictionary locations in callers. if (sourcePath && sourcePath.trim()) { rawSearchPaths.push(sourcePath.trim()); rawSearchPaths.push(path.join(sourcePath.trim(), 'frequency-dictionary')); rawSearchPaths.push(path.join(sourcePath.trim(), 'vendor', 'frequency-dictionary')); } for (const dictionaryRoot of dictionaryRoots) { rawSearchPaths.push(dictionaryRoot); rawSearchPaths.push(path.join(dictionaryRoot, 'frequency-dictionary')); rawSearchPaths.push(path.join(dictionaryRoot, 'vendor', 'frequency-dictionary')); } return [...new Set(rawSearchPaths)]; } export async function initializeFrequencyDictionaryLookup( deps: FrequencyDictionaryRuntimeDeps, ): Promise { const lookup = await createFrequencyDictionaryLookup({ searchPaths: deps.getSearchPaths(), log: deps.log, }); deps.setFrequencyRankLookup(lookup); } export async function ensureFrequencyDictionaryLookup( deps: FrequencyDictionaryRuntimeDeps, ): Promise { if (!deps.isFrequencyDictionaryEnabled()) { return; } if (frequencyDictionaryLookupInitialized) { return; } if (!frequencyDictionaryLookupInitialization) { frequencyDictionaryLookupInitialization = initializeFrequencyDictionaryLookup(deps) .then(() => { frequencyDictionaryLookupInitialized = true; }) .catch((error) => { frequencyDictionaryLookupInitialized = true; deps.log(`Failed to initialize frequency dictionary: ${String(error)}`); deps.setFrequencyRankLookup(() => null); }); } await frequencyDictionaryLookupInitialization; } export function createFrequencyDictionaryRuntimeService(deps: FrequencyDictionaryRuntimeDeps): { ensureFrequencyDictionaryLookup: () => Promise; } { return { ensureFrequencyDictionaryLookup: () => ensureFrequencyDictionaryLookup(deps), }; }