import type { SearchContext, SearchResult } from '@/lib/search'; export interface SearchProvider { /** * `context` describes the active version/language scope. Providers may * ignore it; the built-in local provider uses it to keep results inside * the scope being browsed. */ search(query: string, limit?: number, context?: SearchContext): Promise; } export type SearchProviderFactory = ( options: Record, ) => SearchProvider | Promise; export interface ResolvedSearchProvider { provider: SearchProvider; providerId: string; fellBack: boolean; } const factories = new Map(); const BUILTIN_PROVIDERS = new Set(['local', 'pagefind']); function normalizeProviderId(id: string): string { return id.trim().toLowerCase(); } /** * Registers a runtime search provider. Call this before rendering the app. * The returned cleanup function only removes this exact registration. */ export function registerSearchProvider(id: string, factory: SearchProviderFactory): () => void { const providerId = normalizeProviderId(id); if (!providerId || BUILTIN_PROVIDERS.has(providerId)) { throw new Error( 'Search provider ids must be non-empty and cannot replace the built-in providers ("local", "pagefind").', ); } factories.set(providerId, factory); return () => { if (factories.get(providerId) === factory) { factories.delete(providerId); } }; } async function createLocalProvider(options: Record): Promise { const { createLocalSearchProvider } = await import('@/lib/search/providers/local'); return createLocalSearchProvider(options); } /** Resolves a configured provider, falling back to local for unknown ids. */ export async function resolveSearchProvider( id: string, options: Record = {}, ): Promise { const requestedId = normalizeProviderId(id) || 'local'; if (requestedId === 'local') { return { provider: await createLocalProvider(options), providerId: 'local', fellBack: false, }; } if (requestedId === 'pagefind') { const { createPagefindSearchProvider } = await import('@/lib/search/providers/pagefind'); return { provider: createPagefindSearchProvider(options), providerId: 'pagefind', fellBack: false, }; } const factory = factories.get(requestedId); if (factory) { return { provider: await factory(options), providerId: requestedId, fellBack: false, }; } return { provider: await createLocalProvider({}), providerId: 'local', fellBack: true, }; }