{"version":3,"file":"errors2.mjs","names":[],"sources":["../../src/core/errors.ts"],"sourcesContent":["/** Base error for all askweb operations. */\nexport class AskwebError extends Error {\n  constructor(message: string, options?: ErrorOptions) {\n    super(message, options)\n    this.name = 'AskwebError'\n  }\n}\n\n/** Non-auth HTTP error with status code, URL, and response body. */\nexport class HTTPError extends AskwebError {\n  readonly statusCode: number\n  readonly url: string\n  readonly body: string\n\n  constructor(statusCode: number, url: string, body: string) {\n    super(`HTTP ${statusCode}: ${url}`)\n    this.name = 'HTTPError'\n    this.statusCode = statusCode\n    this.url = url\n    this.body = body\n  }\n\n  isNotFound(): boolean {\n    return this.statusCode === 404\n  }\n\n  isRateLimit(): boolean {\n    return this.statusCode === 429\n  }\n\n  isServerError(): boolean {\n    return this.statusCode >= 500\n  }\n}\n\n/** Thrown when a provider rejects the API key (HTTP 401). */\nexport class AuthError extends AskwebError {\n  readonly provider: string\n\n  constructor(message: string, provider: string) {\n    super(message)\n    this.name = 'AuthError'\n    this.provider = provider\n  }\n}\n\n/** Thrown on HTTP 429. Check {@link retryAfter} for seconds until retry. */\nexport class RateLimitError extends AskwebError {\n  readonly retryAfter: number\n\n  constructor(retryAfter: number) {\n    super(`Rate limited. Retry after ${retryAfter}s`)\n    this.name = 'RateLimitError'\n    this.retryAfter = retryAfter\n  }\n}\n\n/** Thrown when {@link create} is called with an unregistered provider name. */\nexport class UnknownProviderError extends AskwebError {\n  readonly provider: string\n\n  constructor(provider: string) {\n    super(`Unknown provider: ${provider}`)\n    this.name = 'UnknownProviderError'\n    this.provider = provider\n  }\n}\n\n/** Thrown when the search query is empty or whitespace-only. */\nexport class EmptyQueryError extends AskwebError {\n  constructor() {\n    super('Search query cannot be empty')\n    this.name = 'EmptyQueryError'\n  }\n}\n\n/** Thrown when the read URL is empty or whitespace-only. */\nexport class EmptyUrlError extends AskwebError {\n  constructor() {\n    super('Read URL cannot be empty')\n    this.name = 'EmptyUrlError'\n  }\n}\n\n/** Thrown when a provider does not implement the read capability. */\nexport class ReadNotSupportedError extends AskwebError {\n  readonly provider: string\n\n  constructor(provider: string) {\n    super(`Provider does not support read: ${provider}`)\n    this.name = 'ReadNotSupportedError'\n    this.provider = provider\n  }\n}\n\n/** Thrown when no provider can be selected from env or registry. */\nexport class NoProviderConfiguredError extends AskwebError {\n  constructor() {\n    super('No web search provider configured. Set an API key env var or register a provider.')\n    this.name = 'NoProviderConfiguredError'\n  }\n}\n\n/** Thrown when providers are configured but none are currently reachable. */\nexport class NoProviderAvailableError extends AskwebError {\n  readonly providers: readonly string[]\n\n  constructor(providers: readonly string[]) {\n    const providerList = providers.length > 0 ? providers.join(', ') : 'unknown'\n    super(`No configured web search provider is currently reachable: ${providerList}`)\n    this.name = 'NoProviderAvailableError'\n    this.providers = providers\n  }\n}\n\n/** Thrown when a date filter string is not valid ISO 8601 or the range is reversed. */\nexport class InvalidDateFilterError extends AskwebError {\n  readonly field: string\n  readonly value: string\n  readonly reason: string\n\n  constructor(field: string, value: string, reason: string) {\n    super(`Invalid date filter ${field}=\"${value}\": ${reason}`)\n    this.name = 'InvalidDateFilterError'\n    this.field = field\n    this.value = value\n    this.reason = reason\n  }\n}\n\nconst ISO_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:\\d{2}))?$/\nconst HAS_OFFSET_RE = /Z|[+-]\\d{2}:\\d{2}$/\n\nexport function validateDateFilters(startPublishedDate?: string, endPublishedDate?: string): void {\n  for (const [field, value] of [['startPublishedDate', startPublishedDate], ['endPublishedDate', endPublishedDate]] as const) {\n    if (value == null) continue\n    if (!ISO_DATE_RE.test(value)) {\n      throw new InvalidDateFilterError(field, value, 'must be ISO 8601 (e.g. \"2024-01-01\" or \"2024-01-01T00:00:00Z\")')\n    }\n    const hasTime = value.includes('T')\n    if (hasTime && !HAS_OFFSET_RE.test(value)) {\n      throw new InvalidDateFilterError(field, value, 'datetime must include Z or ±HH:mm offset')\n    }\n    const parsed = new Date(value)\n    if (Number.isNaN(parsed.getTime())) {\n      throw new InvalidDateFilterError(field, value, 'not a valid date')\n    }\n    const dateOnly = value.split('T')[0]\n    const [y, m, d] = dateOnly.split('-').map(Number)\n    const probe = new Date(Date.UTC(y, m - 1, d))\n    if (probe.getUTCFullYear() !== y || probe.getUTCMonth() + 1 !== m || probe.getUTCDate() !== d) {\n      throw new InvalidDateFilterError(field, value, 'not a valid calendar date')\n    }\n  }\n\n  if (startPublishedDate != null && endPublishedDate != null) {\n    if (Date.parse(startPublishedDate) > Date.parse(endPublishedDate)) {\n      throw new InvalidDateFilterError('startPublishedDate', startPublishedDate, `start date is after end date \"${endPublishedDate}\"`)\n    }\n  }\n}\n\n/**\n * Convert any caught error into a typed {@link AskwebError} subclass.\n * Maps HTTP status codes to specific error types (401 → AuthError, 429 → RateLimitError).\n */\nexport function normalizeError(error: unknown, provider?: string): AskwebError {\n  if (error instanceof HTTPError && error.statusCode === 401) {\n    return new AuthError(\n      `Authentication failed: ${error.body || 'Invalid or missing API key'}`,\n      provider || 'unknown'\n    )\n  }\n\n  if (error instanceof AskwebError) {\n    return error\n  }\n\n  if (\n    error &&\n    typeof error === 'object' &&\n    'status' in error &&\n    'message' in error\n  ) {\n    const fetchError = error as { status: number; message: string; response?: { headers?: { get: (key: string) => string | null } } }\n    const status = fetchError.status\n    const message = fetchError.message || `HTTP ${status}`\n\n    switch (status) {\n      case 401:\n        return new AuthError(\n          `Authentication failed: ${message}`,\n          provider || 'unknown'\n        )\n      case 404:\n        return new HTTPError(404, '', message)\n      case 429: {\n        const retryAfter = parseRetryAfter(fetchError.response?.headers?.get('Retry-After'))\n        return new RateLimitError(retryAfter)\n      }\n      default:\n        if (status >= 500) {\n          return new HTTPError(status, '', message)\n        }\n        return new AskwebError(message)\n    }\n  }\n\n  if (error instanceof Error) {\n    return new AskwebError(error.message)\n  }\n\n  return new AskwebError(String(error))\n}\n\nexport const DEFAULT_RETRY_AFTER = 60\n\nexport function parseRetryAfter(header: string | null | undefined): number {\n  if (header == null) {\n    return DEFAULT_RETRY_AFTER\n  }\n\n  const trimmed = header.trim()\n  if (!/^\\d+$/.test(trimmed)) {\n    return DEFAULT_RETRY_AFTER\n  }\n\n  const parsed = Number.parseInt(trimmed, 10)\n  if (!Number.isFinite(parsed)) {\n    return DEFAULT_RETRY_AFTER\n  }\n\n  return parsed\n}\n"],"mappings":"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"}