{"version":3,"file":"all.mjs","names":[],"sources":["../../src/core/all.ts"],"sourcesContent":["import type { SearchResult, SearchOptions } from './types.ts'\nimport { UnknownProviderError, NoProviderConfiguredError, NoProviderAvailableError, EmptyQueryError, validateDateFilters } from './errors.ts'\nimport { create, has } from './registry.ts'\nimport { detectAvailableProviders, detectAvailableProvidersAsync } from './resolve.ts'\n\nexport interface SearchAllOptions extends SearchOptions {\n  providers?: string[]\n}\n\nexport interface SearchAllResult extends SearchResult {\n  provider: string\n}\n\nexport interface ProviderError {\n  provider: string\n  error: Error\n}\n\nexport interface SearchAllResponse {\n  results: SearchAllResult[]\n  errors: ProviderError[]\n}\n\n/**\n * Query multiple providers in parallel and return deduplicated results.\n * Providers are auto-detected from env vars unless explicitly specified.\n * Individual provider failures don't affect other results.\n */\nexport async function searchAll(query: string, options?: SearchAllOptions): Promise<SearchAllResult[]> {\n  const response = await searchAllDetailed(query, options)\n  return response.results\n}\n\n/**\n * Like {@link searchAll}, but also returns per-provider errors so callers\n * can tell which providers failed and why.\n */\nexport async function searchAllDetailed(query: string, options?: SearchAllOptions): Promise<SearchAllResponse> {\n  if (!query.trim()) {\n    throw new EmptyQueryError()\n  }\n\n  validateDateFilters(options?.startPublishedDate, options?.endPublishedDate)\n\n  const { providers: providerList, ...searchOptions } = options ?? {}\n\n  if (providerList) {\n    const unknown = providerList.find(name => !has(name))\n    if (unknown) {\n      throw new UnknownProviderError(unknown)\n    }\n  }\n\n  const providerNames = providerList ?? await detectAvailableProvidersAsync()\n\n  if (providerNames.length === 0) {\n    if (providerList !== undefined) {\n      throw new NoProviderConfiguredError()\n    }\n\n    const configuredProviders = detectAvailableProviders()\n    if (configuredProviders.length === 0) {\n      throw new NoProviderConfiguredError()\n    }\n    throw new NoProviderAvailableError(configuredProviders)\n  }\n\n  const settled = await Promise.allSettled(\n    providerNames.map(async (name) => {\n      const provider = create(name)\n      const results = await provider.search(query, searchOptions)\n      return results.map(result => ({ ...result, provider: name }))\n    }),\n  )\n\n  const allResults: SearchAllResult[] = []\n  const errors: ProviderError[] = []\n\n  for (let i = 0; i < settled.length; i++) {\n    const result = settled[i]\n    if (result.status === 'fulfilled') {\n      allResults.push(...result.value)\n    } else {\n      errors.push({\n        provider: providerNames[i],\n        error: result.reason instanceof Error ? result.reason : new Error(String(result.reason)),\n      })\n    }\n  }\n\n  return {\n    results: deduplicateByUrl(allResults),\n    errors,\n  }\n}\n\n\n\nfunction deduplicateByUrl(results: SearchAllResult[]): SearchAllResult[] {\n  const seen = new Map<string, SearchAllResult>()\n\n  for (const result of results) {\n    const normalized = normalizeUrl(result.url)\n    const existing = seen.get(normalized)\n\n    if (!existing) {\n      seen.set(normalized, result)\n    } else {\n      if (result.score != null && (existing.score == null || result.score > existing.score)) {\n        seen.set(normalized, result)\n      }\n    }\n  }\n\n  return Array.from(seen.values())\n}\n\nfunction normalizeUrl(url: string): string {\n  try {\n    const parsed = new URL(url)\n    parsed.hash = ''\n    const path = parsed.pathname.replace(/\\/+$/, '') || '/'\n    const search = canonicalizeSearchParams(parsed.searchParams)\n    return `${parsed.protocol}//${parsed.host}${path}${search}`\n  } catch {\n    return url\n  }\n}\n\nfunction canonicalizeSearchParams(searchParams: URLSearchParams): string {\n  const filteredSortedEntries = Array.from(searchParams.entries())\n    .filter(([key]) => !isTrackingParam(key))\n    .map(([key, value], index) => ({ key, value, index }))\n    .sort((a, b) => {\n      const keyOrder = a.key.localeCompare(b.key, 'en')\n      if (keyOrder !== 0) {\n        return keyOrder\n      }\n\n      return a.index - b.index\n    })\n    .map(({ key, value }): [string, string] => [key, value])\n\n  if (filteredSortedEntries.length === 0) {\n    return ''\n  }\n\n  return `?${new URLSearchParams(filteredSortedEntries).toString()}`\n}\n\nfunction isTrackingParam(key: string): boolean {\n  return key.toLowerCase().startsWith('utm_')\n}\n"],"mappings":"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"}