{"version":3,"file":"providers.mjs","names":["mapResult","factory","mapResult","factory","clampMaxResults","factory","mapResult","factory","mapResult","factory","mapResult","factory"],"sources":["../../src/core/client.ts","../../src/providers/exa.ts","../../src/providers/brave.ts","../../src/providers/jina.ts","../../src/providers/tavily.ts","../../src/providers/serpapi.ts","../../src/providers/serpbase.ts","../../src/providers/searxng.ts"],"sourcesContent":["import { ofetch, FetchError } from 'ofetch'\nimport type { $Fetch } from 'ofetch'\nimport type { ClientOptions } from './types.ts'\nimport { HTTPError, RateLimitError, parseRetryAfter } from './errors.ts'\nimport { version } from '../version.ts'\n\nconst DEFAULT_MAX_RETRIES = 5\nconst DEFAULT_BASE_DELAY = 50\nconst DEFAULT_TIMEOUT = 30_000\nconst DEFAULT_USER_AGENT = `askweb/${version}`\n\n/** HTTP client with exponential backoff retry and error mapping to askweb error types. */\nexport class Client {\n  readonly maxRetries: number\n  readonly baseDelay: number\n  readonly timeout: number\n  readonly userAgent: string\n  private readonly fetch: $Fetch\n\n  constructor(options: ClientOptions = {}) {\n    this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES\n    this.baseDelay = options.baseDelay ?? DEFAULT_BASE_DELAY\n    this.timeout = options.timeout ?? DEFAULT_TIMEOUT\n    this.userAgent = options.userAgent ?? DEFAULT_USER_AGENT\n\n    const maxRetries = this.maxRetries\n    const baseDelay = this.baseDelay\n\n    this.fetch = ofetch.create({\n      retry: this.maxRetries,\n      retryDelay(context) {\n        const remaining = typeof context.options.retry === 'number' ? context.options.retry : 0\n        const attempt = maxRetries - remaining\n        const delay = baseDelay * Math.pow(2, attempt - 1)\n        const jitter = delay * Math.random() * 0.1\n        return delay + jitter\n      },\n      retryStatusCodes: [408, 429, 500, 502, 503, 504],\n      timeout: this.timeout,\n      headers: {\n        'Accept': 'application/json',\n        'User-Agent': this.userAgent,\n      },\n    })\n  }\n\n  /** Send a GET request and parse the JSON response. */\n  async getJSON<T>(url: string, headers?: Record<string, string>, signal?: AbortSignal): Promise<T> {\n    try {\n      return await this.fetch<T>(url, { headers, signal })\n    }\n    catch (error) {\n      throw this.mapError(error, url)\n    }\n  }\n\n  /** Send a POST request with a JSON body and parse the JSON response. */\n  async postJSON<T>(\n    url: string,\n    body: Record<string, unknown>,\n    headers?: Record<string, string>,\n    signal?: AbortSignal,\n  ): Promise<T> {\n    try {\n      return await this.fetch<T>(url, {\n        method: 'POST',\n        body,\n        headers,\n        signal,\n      })\n    }\n    catch (error) {\n      throw this.mapError(error, url)\n    }\n  }\n\n  private mapError(error: unknown, url: string): Error {\n    if (error instanceof FetchError) {\n      if (error.statusCode === 429) {\n        const retryAfter = parseRetryAfter(\n          error.response?.headers.get('Retry-After'),\n        )\n        return new RateLimitError(retryAfter)\n      }\n\n      const body = typeof error.data === 'string'\n        ? error.data\n        : JSON.stringify(error.data ?? '')\n\n      return new HTTPError(error.statusCode ?? 0, sanitizeUrl(url), body)\n    }\n    return error instanceof Error ? error : new Error(String(error))\n  }\n}\n\nconst SENSITIVE_PARAMS = ['api_key', 'key', 'token', 'secret', 'password', 'apikey']\nconst SENSITIVE_PARAM_SET = new Set(SENSITIVE_PARAMS.map(param => param.toLowerCase()))\n\nfunction sanitizeUrl(url: string): string {\n  try {\n    const parsed = new URL(url)\n\n    const userInfoRedacted = redactUserInfo(\n      url,\n      parsed.username.length > 0 || parsed.password.length > 0,\n      parsed.password.length > 0,\n    )\n    const queryRedacted = redactSensitiveQueryParams(userInfoRedacted.url)\n\n    if (!userInfoRedacted.changed && !queryRedacted.changed) {\n      return url\n    }\n\n    return queryRedacted.url\n  }\n  catch {\n    return url\n  }\n}\n\nfunction redactUserInfo(url: string, hasUserInfo: boolean, hasPassword: boolean): { url: string; changed: boolean } {\n  if (!hasUserInfo) {\n    return { url, changed: false }\n  }\n\n  const schemeEnd = url.indexOf('://')\n  if (schemeEnd === -1) {\n    return { url, changed: false }\n  }\n\n  const authorityStart = schemeEnd + 3\n  const pathIndex = url.indexOf('/', authorityStart)\n  const queryIndex = url.indexOf('?', authorityStart)\n  const fragmentIndex = url.indexOf('#', authorityStart)\n\n  const authorityEndCandidates = [pathIndex, queryIndex, fragmentIndex].filter(index => index !== -1)\n  const authorityEnd = authorityEndCandidates.length > 0\n    ? Math.min(...authorityEndCandidates)\n    : url.length\n\n  const authority = url.slice(authorityStart, authorityEnd)\n  const atIndex = authority.lastIndexOf('@')\n  if (atIndex === -1) {\n    return { url, changed: false }\n  }\n\n  const redactedUserInfo = hasPassword ? '[REDACTED]:[REDACTED]' : '[REDACTED]'\n  const redactedAuthority = `${redactedUserInfo}@${authority.slice(atIndex + 1)}`\n\n  return {\n    url: `${url.slice(0, authorityStart)}${redactedAuthority}${url.slice(authorityEnd)}`,\n    changed: true,\n  }\n}\n\nfunction redactSensitiveQueryParams(url: string): { url: string; changed: boolean } {\n  const queryStart = url.indexOf('?')\n  if (queryStart === -1) {\n    return { url, changed: false }\n  }\n\n  const fragmentStart = url.indexOf('#', queryStart)\n  const queryEnd = fragmentStart === -1 ? url.length : fragmentStart\n  const prefix = url.slice(0, queryStart + 1)\n  const query = url.slice(queryStart + 1, queryEnd)\n  const suffix = fragmentStart === -1 ? '' : url.slice(fragmentStart)\n\n  let changed = false\n  let redactedQuery = ''\n  let segmentStart = 0\n\n  for (let index = 0; index <= query.length; index += 1) {\n    const isEnd = index === query.length\n    const char = query[index]\n    if (!isEnd && char !== '&') {\n      continue\n    }\n\n    const segment = query.slice(segmentStart, index)\n    redactedQuery += redactSegment(segment)\n    if (!isEnd) {\n      redactedQuery += char\n    }\n    segmentStart = index + 1\n  }\n\n  if (!changed) {\n    return { url, changed: false }\n  }\n\n  return { url: `${prefix}${redactedQuery}${suffix}`, changed: true }\n\n  function redactSegment(segment: string): string {\n    if (!segment) {\n      return segment\n    }\n\n    const separatorIndex = segment.indexOf('=')\n    const rawKey = separatorIndex === -1 ? segment : segment.slice(0, separatorIndex)\n\n    let decodedKey = rawKey\n    try {\n      decodedKey = decodeURIComponent(rawKey)\n    }\n    catch {\n      decodedKey = rawKey\n    }\n\n    if (!SENSITIVE_PARAM_SET.has(decodedKey.toLowerCase())) {\n      return segment\n    }\n\n    if (separatorIndex === -1) {\n      return segment\n    }\n\n    changed = true\n    return `${rawKey}=${encodeURIComponent('[REDACTED]')}`\n  }\n}\n\nlet _defaultClient: Client | undefined\n\n/** Lazily-initialized singleton {@link Client} used by all providers. */\nexport function defaultClient(): Client {\n  if (!_defaultClient) {\n    _defaultClient = new Client()\n  }\n  return _defaultClient\n}\n\nexport function resetDefaultClientForTests(): void {\n  _defaultClient = undefined\n}\n\n","import type { SearchResult, SearchOptions, SearchProvider, ProviderConfig, ProviderFactory } from '../core/types.ts'\nimport { defaultClient } from '../core/client.ts'\nimport type { Client } from '../core/client.ts'\nimport { AuthError, normalizeError } from '../core/errors.ts'\nimport { register } from '../core/registry.ts'\n\ninterface ExaSearchRequest {\n  query: string\n  type?: string\n  numResults?: number\n  category?: string\n  includeDomains?: string[]\n  excludeDomains?: string[]\n  startPublishedDate?: string\n  endPublishedDate?: string\n  contents?: { text: boolean; highlights: boolean }\n}\n\ninterface ExaResult {\n  id: string\n  url: string\n  title: string | null\n  score?: number\n  publishedDate?: string\n  author?: string\n  image?: string\n  favicon?: string\n  text?: string\n  highlights?: string[]\n  highlightScores?: number[]\n  summary?: string\n}\n\ninterface ExaSearchResponse {\n  requestId: string\n  results: ExaResult[]\n}\n\nclass ExaProvider implements SearchProvider {\n  private readonly client: Client\n  private readonly baseURL: string\n  private readonly apiKey: string\n\n  constructor(config: ProviderConfig) {\n    if (!config.apiKey) {\n      throw new AuthError('Missing API key for Exa. Set EXA_API_KEY', 'exa')\n    }\n\n    this.client = defaultClient()\n    this.baseURL = config.baseURL ?? 'https://api.exa.ai'\n    this.apiKey = config.apiKey\n  }\n\n  name(): string {\n    return 'exa'\n  }\n\n  async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {\n    const body = {\n      query,\n      type: 'auto',\n      numResults: options?.maxResults,\n      category: options?.category,\n      includeDomains: options?.includeDomains,\n      excludeDomains: options?.excludeDomains,\n      startPublishedDate: options?.startPublishedDate,\n      endPublishedDate: options?.endPublishedDate,\n      contents: { text: true, highlights: true },\n    } satisfies ExaSearchRequest\n\n    try {\n      const url = `${this.baseURL}/search`\n      const headers = { 'x-api-key': this.apiKey }\n      const response = await this.client.postJSON<ExaSearchResponse>(url, body, headers)\n      return response.results.map(mapResult)\n    }\n    catch (error) {\n      throw normalizeError(error, 'exa')\n    }\n  }\n}\n\nfunction mapResult(result: ExaResult): SearchResult {\n  return {\n    url: result.url,\n    title: result.title ?? '',\n    snippet: result.highlights?.[0]\n      ?? (result.text ? result.text.slice(0, 200) : ''),\n    score: result.score,\n    publishedDate: result.publishedDate,\n    author: result.author,\n    image: result.image,\n    favicon: result.favicon,\n    text: result.text,\n    highlights: result.highlights,\n    summary: result.summary,\n  }\n}\n\nconst factory: ProviderFactory = (config) => new ExaProvider(config)\n\nregister('exa', 'https://api.exa.ai', factory)\n","import type { SearchResult, SearchOptions, SearchProvider, ProviderConfig, ProviderFactory } from '../core/types.ts'\nimport { defaultClient } from '../core/client.ts'\nimport type { Client } from '../core/client.ts'\nimport { AuthError, normalizeError } from '../core/errors.ts'\nimport { register } from '../core/registry.ts'\n\ninterface BraveResult {\n  title: string\n  url: string\n  description: string\n  extra_snippets?: string[]\n  age?: string\n  language?: string\n  family_friendly?: boolean\n  meta_url?: {\n    favicon?: string\n  }\n}\n\ninterface BraveSearchResponse {\n  web?: {\n    results: BraveResult[]\n  }\n}\n\nclass BraveProvider implements SearchProvider {\n  private readonly client: Client\n  private readonly baseURL: string\n  private readonly apiKey: string\n\n  constructor(config: ProviderConfig) {\n    if (!config.apiKey) {\n      throw new AuthError('Missing API key for Brave Search. Set BRAVE_API_KEY', 'brave')\n    }\n\n    this.client = defaultClient()\n    this.baseURL = config.baseURL ?? 'https://api.search.brave.com'\n    this.apiKey = config.apiKey\n  }\n\n  name(): string {\n    return 'brave'\n  }\n\n  async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {\n    try {\n      const url = `${this.baseURL}/res/v1/web/search?q=${encodeURIComponent(query)}&count=${options?.maxResults ?? 10}`\n      const headers = { 'X-Subscription-Token': this.apiKey }\n      const response = await this.client.getJSON<BraveSearchResponse>(url, headers)\n      return (response.web?.results ?? []).map(mapResult)\n    }\n    catch (error) {\n      throw normalizeError(error, 'brave')\n    }\n  }\n}\n\nfunction mapResult(result: BraveResult): SearchResult {\n  return {\n    url: result.url,\n    title: result.title,\n    snippet: result.description,\n    favicon: result.meta_url?.favicon,\n    text: result.extra_snippets ? result.extra_snippets.join('\\n') : undefined,\n  }\n}\n\nconst factory: ProviderFactory = (config) => new BraveProvider(config)\n\nregister('brave', 'https://api.search.brave.com', factory)\n","import type { SearchResult, SearchOptions, ReadResult, ReadOptions, SearchProvider, ProviderConfig, ProviderFactory } from '../core/types.ts'\nimport { defaultClient } from '../core/client.ts'\nimport type { Client } from '../core/client.ts'\nimport { AuthError, HTTPError, normalizeError } from '../core/errors.ts'\nimport { register } from '../core/registry.ts'\n\ninterface JinaResult {\n  title?: string\n  description?: string\n  url: string\n  content?: string\n  text?: string\n  html?: string\n  publishedTime?: string\n  links?: string[] | Record<string, string> | null\n  images?: string[] | Record<string, string> | null\n  metadata?: Record<string, unknown> | null\n  warning?: string\n  screenshotUrl?: string\n  pageshotUrl?: string\n}\n\ninterface JinaEnvelope {\n  code?: number\n  status?: number\n  message?: string\n  error?: string\n  detail?: unknown\n  meta?: Record<string, unknown>\n}\n\ninterface JinaSearchResponse extends JinaEnvelope {\n  data?: JinaResult[] | null\n}\n\ninterface JinaReadResponse extends JinaEnvelope {\n  data?: JinaResult | null\n}\n\nconst JINA_MAX_RESULTS = 20\n\nclass JinaProvider implements SearchProvider {\n  private readonly client: Client\n  private readonly searchBaseURL: string\n  private readonly readBaseURL: string\n  private readonly apiKey?: string\n\n  constructor(config: ProviderConfig) {\n    this.client = defaultClient()\n    this.searchBaseURL = (config.baseURL ?? 'https://s.jina.ai').replace(/\\/+$/, '')\n    this.readBaseURL = (config.readBaseURL ?? deriveReadBaseURL(this.searchBaseURL)).replace(/\\/+$/, '')\n    this.apiKey = config.apiKey\n  }\n\n  name(): string {\n    return 'jina'\n  }\n\n  async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {\n    if (!this.apiKey) {\n      throw new AuthError('Missing API key for Jina. Set JINA_API_KEY', 'jina')\n    }\n\n    try {\n      const params = new URLSearchParams({\n        q: query,\n        count: String(clampMaxResults(options?.maxResults ?? 10)),\n      })\n\n      if (isJinaSearchType(options?.category)) {\n        params.set('type', options.category)\n      }\n\n      for (const domain of options?.includeDomains ?? []) {\n        params.append('site', domain)\n      }\n\n      const url = `${this.searchBaseURL}/search?${params.toString()}`\n      const headers = {\n        'Authorization': `Bearer ${this.apiKey}`,\n        'Accept': 'application/json',\n      }\n      const response = await this.client.getJSON<JinaSearchResponse>(url, headers)\n      assertJinaSuccess(response, url)\n      return (response.data ?? []).map(mapSearchResult)\n    }\n    catch (error) {\n      throw normalizeError(error, 'jina')\n    }\n  }\n\n  async read(url: string, options?: ReadOptions): Promise<ReadResult> {\n    try {\n      const requestUrl = `${this.readBaseURL}/${encodeURIComponent(url)}`\n      const response = await this.client.getJSON<JinaReadResponse>(requestUrl, readHeaders(this.apiKey, options))\n      assertJinaSuccess(response, requestUrl)\n      return mapReadResult(response.data ?? { url, content: '' })\n    }\n    catch (error) {\n      throw normalizeError(error, 'jina')\n    }\n  }\n}\n\nfunction deriveReadBaseURL(searchBaseURL: string): string {\n  const match = searchBaseURL.match(/^(https?:\\/\\/)(.+\\.)?s\\.jina\\.ai$/)\n  if (!match) return searchBaseURL\n  return `${match[1]}${match[2] ?? ''}r.jina.ai`\n}\n\nfunction readHeaders(apiKey: string | undefined, options: ReadOptions | undefined): Record<string, string> {\n  const headers: Record<string, string> = { Accept: 'application/json' }\n\n  if (apiKey) headers.Authorization = `Bearer ${apiKey}`\n  if (options?.format) headers['X-Return-Format'] = options.format\n  if (options?.maxTokens !== undefined) headers['X-Token-Budget'] = String(options.maxTokens)\n  if (options?.targetSelector) headers['X-Target-Selector'] = options.targetSelector\n  if (options?.removeSelector) headers['X-Remove-Selector'] = options.removeSelector\n  if (options?.timeout !== undefined) headers['X-Timeout'] = String(options.timeout)\n  if (options?.noCache) headers['X-No-Cache'] = 'true'\n\n  return headers\n}\n\nfunction clampMaxResults(maxResults: number): number {\n  return Math.min(Math.max(maxResults, 1), JINA_MAX_RESULTS)\n}\n\nfunction isJinaSearchType(category: string | undefined): category is 'web' | 'images' | 'news' {\n  return category === 'web' || category === 'images' || category === 'news'\n}\n\nfunction mapSearchResult(result: JinaResult): SearchResult {\n  return {\n    url: result.url,\n    title: result.title ?? '',\n    snippet: result.description ?? snippetFrom(result.content) ?? snippetFrom(result.text) ?? '',\n    publishedDate: result.publishedTime,\n    text: result.content ?? result.text,\n    image: firstImage(result.images),\n    metadata: resultMetadata(result),\n  }\n}\n\nfunction mapReadResult(result: JinaResult): ReadResult {\n  return {\n    url: result.url,\n    title: result.title,\n    description: result.description,\n    content: result.content ?? result.text ?? result.html ?? '',\n    text: result.text,\n    html: result.html,\n    publishedDate: result.publishedTime,\n    image: firstImage(result.images),\n    links: stringValues(result.links),\n    images: stringValues(result.images),\n    metadata: resultMetadata(result),\n  }\n}\n\nfunction snippetFrom(text: string | undefined): string | undefined {\n  return text ? text.slice(0, 200) : undefined\n}\n\nfunction firstImage(images: JinaResult['images']): string | undefined {\n  return stringValues(images)?.[0]\n}\n\nfunction stringValues(value: string[] | Record<string, string> | null | undefined): string[] | undefined {\n  const values = Array.isArray(value) ? value : value ? Object.values(value) : []\n  const strings = values.filter(isNonEmptyString)\n  return strings.length > 0 ? strings : undefined\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n  return typeof value === 'string' && value.length > 0\n}\n\nfunction assertJinaSuccess(response: JinaEnvelope, url: string): void {\n  const code = response.code\n  const status = response.status\n  if ((code !== undefined && code >= 400) || (status !== undefined && status >= 40000)) {\n    const statusCode = code !== undefined && code >= 400 ? code : Math.floor((status ?? 50000) / 100)\n    throw new HTTPError(statusCode, url, jinaErrorMessage(response))\n  }\n}\n\nfunction jinaErrorMessage(response: JinaEnvelope): string {\n  if (response.message) return response.message\n  if (response.error) return response.error\n  if (response.detail !== undefined) return JSON.stringify(response.detail)\n  return `Jina API error: code=${response.code ?? 'unknown'} status=${response.status ?? 'unknown'}`\n}\n\nfunction resultMetadata(result: JinaResult): Record<string, unknown> | undefined {\n  const metadata: Record<string, unknown> = result.metadata ? { ...result.metadata } : {}\n\n  if (result.warning) metadata.warning = result.warning\n  if (result.screenshotUrl) metadata.screenshotUrl = result.screenshotUrl\n  if (result.pageshotUrl) metadata.pageshotUrl = result.pageshotUrl\n\n  return Object.keys(metadata).length > 0 ? metadata : undefined\n}\n\nconst factory: ProviderFactory = (config) => new JinaProvider(config)\n\nregister('jina', 'https://s.jina.ai', factory)\n","import type { SearchResult, SearchOptions, SearchProvider, ProviderConfig, ProviderFactory } from '../core/types.ts'\nimport { defaultClient } from '../core/client.ts'\nimport type { Client } from '../core/client.ts'\nimport { AuthError, normalizeError } from '../core/errors.ts'\nimport { register } from '../core/registry.ts'\n\ninterface TavilySearchRequest {\n  api_key: string\n  query: string\n  max_results?: number\n  search_depth?: 'basic' | 'advanced'\n  include_answer?: boolean\n  include_raw_content?: boolean\n  include_domains?: string[]\n  exclude_domains?: string[]\n}\n\ninterface TavilyResult {\n  title: string\n  url: string\n  content: string\n  score: number\n  published_date?: string\n  raw_content?: string\n}\n\ninterface TavilySearchResponse {\n  results: TavilyResult[]\n  answer?: string\n  query: string\n}\n\nclass TavilyProvider implements SearchProvider {\n  private readonly client: Client\n  private readonly baseURL: string\n  private readonly apiKey: string\n\n  constructor(config: ProviderConfig) {\n    if (!config.apiKey) {\n      throw new AuthError('Missing API key for Tavily. Set TAVILY_API_KEY', 'tavily')\n    }\n\n    this.client = defaultClient()\n    this.baseURL = config.baseURL ?? 'https://api.tavily.com'\n    this.apiKey = config.apiKey\n  }\n\n  name(): string {\n    return 'tavily'\n  }\n\n  async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {\n    const body = {\n      api_key: this.apiKey,\n      query,\n      max_results: options?.maxResults ?? 10,\n      search_depth: 'basic',\n      include_answer: false,\n      include_raw_content: false,\n      include_domains: options?.includeDomains,\n      exclude_domains: options?.excludeDomains,\n    } satisfies TavilySearchRequest\n\n    try {\n      const url = `${this.baseURL}/search`\n      const response = await this.client.postJSON<TavilySearchResponse>(url, body)\n      return response.results.map((result, index) => mapResult(result, response.answer, index === 0))\n    }\n    catch (error) {\n      throw normalizeError(error, 'tavily')\n    }\n  }\n}\n\nfunction mapResult(result: TavilyResult, answer: string | undefined, isFirst: boolean): SearchResult {\n  return {\n    url: result.url,\n    title: result.title,\n    snippet: result.content,\n    score: result.score,\n    publishedDate: result.published_date,\n    text: result.raw_content,\n    summary: isFirst && answer ? answer : undefined,\n  }\n}\n\nconst factory: ProviderFactory = (config) => new TavilyProvider(config)\n\nregister('tavily', 'https://api.tavily.com', factory)\n","import type { SearchResult, SearchOptions, SearchProvider, ProviderConfig, ProviderFactory } from '../core/types.ts'\nimport { defaultClient } from '../core/client.ts'\nimport type { Client } from '../core/client.ts'\nimport { AuthError, normalizeError } from '../core/errors.ts'\nimport { register } from '../core/registry.ts'\n\ninterface SerpApiResult {\n  position: number\n  title: string\n  link: string\n  snippet: string\n  displayed_link?: string\n  favicon?: string\n  date?: string\n  source?: string\n  thumbnail?: string\n}\n\ninterface SerpApiSearchResponse {\n  search_metadata: {\n    id: string\n    status: string\n  }\n  organic_results?: SerpApiResult[]\n}\n\nclass SerpApiProvider implements SearchProvider {\n  private readonly client: Client\n  private readonly baseURL: string\n  private readonly apiKey: string\n\n  constructor(config: ProviderConfig) {\n    if (!config.apiKey) {\n      throw new AuthError('Missing API key for SerpAPI. Set SERPAPI_API_KEY', 'serpapi')\n    }\n\n    this.client = defaultClient()\n    this.baseURL = config.baseURL ?? 'https://serpapi.com'\n    this.apiKey = config.apiKey\n  }\n\n  name(): string {\n    return 'serpapi'\n  }\n\n  async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {\n    try {\n      const url = `${this.baseURL}/search?engine=google&q=${encodeURIComponent(query)}&api_key=${this.apiKey}&num=${options?.maxResults ?? 10}`\n      const response = await this.client.getJSON<SerpApiSearchResponse>(url)\n      return (response.organic_results ?? []).map(mapResult)\n    }\n    catch (error) {\n      throw normalizeError(error, 'serpapi')\n    }\n  }\n}\n\nfunction mapResult(result: SerpApiResult): SearchResult {\n  return {\n    url: result.link,\n    title: result.title,\n    snippet: result.snippet,\n    favicon: result.favicon,\n    publishedDate: result.date,\n    image: result.thumbnail,\n    metadata: {\n      position: result.position,\n      source: result.source,\n      displayedLink: result.displayed_link,\n    },\n  }\n}\n\nconst factory: ProviderFactory = (config) => new SerpApiProvider(config)\n\nregister('serpapi', 'https://serpapi.com', factory)\n","import type { SearchResult, SearchOptions, SearchProvider, ProviderConfig, ProviderFactory } from '../core/types.ts'\nimport { defaultClient } from '../core/client.ts'\nimport type { Client } from '../core/client.ts'\nimport { AskwebError, AuthError, RateLimitError, normalizeError } from '../core/errors.ts'\nimport { register } from '../core/registry.ts'\n\ninterface SerpBaseSearchRequest {\n  q: string\n  hl?: string\n  gl?: string\n  page?: number\n}\n\ninterface SerpBaseResult {\n  rank?: number\n  position?: number\n  title?: string\n  link?: string\n  url?: string\n  source_url?: string\n  display_url?: string\n  display_link?: string\n  snippet?: string\n  date?: string\n  published_at?: string\n  icon?: string\n  image_url?: string\n  thumbnail_url?: string\n  thumbnail?: string\n  source?: string\n  domain?: string\n  time?: string\n  duration?: string\n}\n\ninterface SerpBaseSearchResponse {\n  status: number\n  error?: string\n  request_id: string\n  elapsed_ms: number\n  credits_charged: number\n  search_type: string\n  query?: string\n  organic?: SerpBaseResult[]\n  images?: SerpBaseResult[]\n  news?: SerpBaseResult[]\n  videos?: SerpBaseResult[]\n}\n\nconst SERPBASE_MAX_RESULTS = 20\n\nclass SerpBaseProvider implements SearchProvider {\n  private readonly client: Client\n  private readonly baseURL: string\n  private readonly apiKey: string\n\n  constructor(config: ProviderConfig) {\n    if (!config.apiKey) {\n      throw new AuthError('Missing API key for SerpBase. Set SERPBASE_API_KEY', 'serpbase')\n    }\n\n    this.client = defaultClient()\n    this.baseURL = config.baseURL ?? 'https://api.serpbase.dev'\n    this.apiKey = config.apiKey\n  }\n\n  name(): string {\n    return 'serpbase'\n  }\n\n  async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {\n    const endpoint = endpointForCategory(options?.category)\n    const body = {\n      q: query,\n      hl: 'en',\n      gl: 'us',\n      page: 1,\n    } satisfies SerpBaseSearchRequest\n\n    try {\n      const url = `${this.baseURL}${endpoint}`\n      const headers = { 'X-API-Key': this.apiKey }\n      const response = await this.client.postJSON<SerpBaseSearchResponse>(url, body, headers)\n      assertSerpBaseSuccess(response)\n      return resultsForResponse(response)\n        .slice(0, clampMaxResults(options?.maxResults ?? 10))\n        .map(result => mapResult(result, response))\n    }\n    catch (error) {\n      throw normalizeError(error, 'serpbase')\n    }\n  }\n}\n\nfunction endpointForCategory(category: string | undefined): '/google/search' | '/google/images' | '/google/news' | '/google/videos' {\n  switch (category) {\n    case 'images':\n    case 'image':\n      return '/google/images'\n    case 'news':\n      return '/google/news'\n    case 'videos':\n    case 'video':\n      return '/google/videos'\n    default:\n      return '/google/search'\n  }\n}\n\nfunction clampMaxResults(maxResults: number): number {\n  return Math.min(Math.max(maxResults, 1), SERPBASE_MAX_RESULTS)\n}\n\nfunction assertSerpBaseSuccess(response: SerpBaseSearchResponse): void {\n  if (response.status === 0) return\n\n  const message = response.error ?? `SerpBase API error: status=${response.status}`\n  switch (response.status) {\n    case 1001:\n      throw new AuthError(`Authentication failed: ${message}`, 'serpbase')\n    case 1029:\n      throw new RateLimitError(60)\n    case 1020:\n      throw new AskwebError(`SerpBase insufficient credits: ${message}`)\n    default:\n      throw new AskwebError(`SerpBase API error ${response.status}: ${message}`)\n  }\n}\n\nfunction resultsForResponse(response: SerpBaseSearchResponse): SerpBaseResult[] {\n  switch (response.search_type) {\n    case 'images':\n      return response.images ?? []\n    case 'news':\n      return response.news ?? []\n    case 'videos':\n      return response.videos ?? []\n    default:\n      return response.organic ?? []\n  }\n}\n\nfunction mapResult(result: SerpBaseResult, response: SerpBaseSearchResponse): SearchResult {\n  const url = result.url ?? result.link ?? result.source_url ?? result.image_url ?? ''\n  return {\n    url,\n    title: result.title ?? result.source ?? result.domain ?? '',\n    snippet: result.snippet ?? '',\n    publishedDate: result.published_at ?? result.date ?? result.time,\n    image: result.image_url ?? result.thumbnail_url ?? result.thumbnail,\n    favicon: result.icon,\n    metadata: {\n      position: result.position ?? result.rank,\n      rank: result.rank,\n      displayUrl: result.display_url,\n      displayLink: result.display_link,\n      sourceUrl: result.source_url,\n      source: result.source,\n      domain: result.domain,\n      duration: result.duration,\n      searchType: response.search_type,\n      requestId: response.request_id,\n      elapsedMs: response.elapsed_ms,\n      creditsCharged: response.credits_charged,\n    },\n  }\n}\n\nconst factory: ProviderFactory = (config) => new SerpBaseProvider(config)\n\nregister('serpbase', 'https://api.serpbase.dev', factory)\n","import type { SearchResult, SearchOptions, SearchProvider, ProviderConfig, ProviderFactory } from '../core/types.ts'\nimport { defaultClient } from '../core/client.ts'\nimport type { Client } from '../core/client.ts'\nimport { normalizeError } from '../core/errors.ts'\nimport { register } from '../core/registry.ts'\n\ninterface SearXNGResult {\n  title: string\n  url: string\n  content: string\n  engine: string\n  engines: string[]\n  score: number\n  category: string\n  publishedDate?: string\n  img_src?: string\n  thumbnail?: string\n}\n\ninterface SearXNGSearchResponse {\n  results: SearXNGResult[]\n  number_of_results?: number\n  query: string\n}\n\nconst SEARXNG_PROBE_TIMEOUT_MS = 2000\n\nclass SearXNGProvider implements SearchProvider {\n  private readonly client: Client\n  private readonly baseURL: string\n\n  constructor(config: ProviderConfig) {\n    this.client = defaultClient()\n    this.baseURL = config.baseURL ?? 'http://localhost:8080'\n  }\n\n  name(): string {\n    return 'searxng'\n  }\n\n  /**\n   * Quick reachability probe for self-hosted instances. Returns false instead\n   * of throwing so {@link searchAll} can skip an unreachable instance silently.\n   * Treats any HTTP response (even 4xx) as reachable — the host is up.\n   * Uses a <=2s timeout so a dead endpoint does not stall fan-out.\n   */\n  async isAvailable(): Promise<boolean> {\n    const controller = new AbortController()\n    const timer = setTimeout(() => controller.abort(), SEARXNG_PROBE_TIMEOUT_MS)\n    try {\n      const response = await fetch(this.baseURL, {\n        method: 'GET',\n        signal: controller.signal,\n      })\n      // Any HTTP status means the host responded; treat as reachable.\n      return typeof response.status === 'number'\n    }\n    catch {\n      return false\n    }\n    finally {\n      clearTimeout(timer)\n    }\n  }\n\n  async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {\n    try {\n      const params = new URLSearchParams({\n        q: query,\n        format: 'json',\n        pageno: '1',\n      })\n\n      if (options?.category) {\n        params.append('categories', options.category)\n      }\n\n      const url = `${this.baseURL}/search?${params.toString()}`\n      const response = await this.client.getJSON<SearXNGSearchResponse>(url)\n\n      let results = response.results.map(mapResult)\n\n      if (options?.maxResults) {\n        results = results.slice(0, options.maxResults)\n      }\n\n      return results\n    }\n    catch (error) {\n      throw normalizeError(error, 'searxng')\n    }\n  }\n}\n\nfunction mapResult(result: SearXNGResult): SearchResult {\n  return {\n    url: result.url,\n    title: result.title,\n    snippet: result.content,\n    score: result.score,\n    publishedDate: result.publishedDate,\n    image: result.img_src ?? result.thumbnail,\n    metadata: {\n      engine: result.engine,\n      engines: result.engines,\n      category: result.category,\n    },\n  }\n}\n\nconst factory: ProviderFactory = (config) => new SearXNGProvider(config)\n\nregister('searxng', 'http://localhost:8080', factory)\n"],"mappings":"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"}