{"version":3,"file":"httpClient.cjs","names":["message: string | undefined","requestId: string | undefined","id: string | undefined","responseBody: string | Record<string, any> | undefined","headers: Headers | undefined","initFetch","attemptErrors: Error[]","timeoutId: ReturnType<typeof setTimeout> | undefined","deepMergeObjects","sleep","elapsed","aggregate"],"sources":["../../src/network/httpClient.ts"],"sourcesContent":["import { deepMergeObjects, sleep } from '../helpers.js'\nimport type { HttpRetryOptions } from '../types.js'\nimport { initFetch } from './iso-fetch.js'\nimport type { Headers } from './types.js'\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'\n\nexport class HttpError extends Error {\n  readonly method: HttpMethod\n  readonly status: number\n  readonly url: string\n  readonly id?: string\n  readonly requestId?: string\n  readonly responseBody?: string | Record<string, unknown>\n  readonly responseHeaders?: Headers\n\n  constructor(\n    {\n      message,\n      method,\n      id,\n      requestId,\n      responseBody,\n      url,\n      status,\n      headers,\n    }: {\n      message: string\n      method: HttpMethod\n      url: string\n      status: number\n      id?: string | undefined\n      requestId?: string | undefined\n      responseBody?: string | Record<string, unknown>\n      headers?: Headers\n    },\n    options?: { cause?: unknown }\n  ) {\n    super(message, options)\n    this.name = 'HttpError'\n    this.method = method\n    this.url = url\n    this.status = status\n    this.id = id\n    this.requestId = requestId\n    this.responseBody = responseBody\n    this.responseHeaders = headers\n  }\n}\n\nasync function createHttpError(\n  method: HttpMethod,\n  url: string | URL,\n  response: Response,\n  options?: { cause?: unknown }\n) {\n  let message: string | undefined\n  let requestId: string | undefined\n  let id: string | undefined\n  let responseBody: string | Record<string, any> | undefined\n  let headers: Headers | undefined\n  try {\n    id = response.headers.get('x-aipi-call-id') ?? undefined\n    headers = Object.fromEntries(response.headers.entries())\n    responseBody = await response.text()\n    try {\n      responseBody = JSON.parse(responseBody) as Record<string, any>\n      requestId = responseBody?.['request_id']\n      message = responseBody?.['message']\n    } catch {\n      // noop\n    }\n  } catch {\n    // noop\n  }\n\n  const messageParts = [\n    message || response.statusText || 'An error occurred',\n    requestId || id,\n    response.status.toString(),\n    `${method} ${new URL(url).pathname}`,\n  ]\n  return new HttpError(\n    {\n      method,\n      message: messageParts.filter(Boolean).join(' | '),\n      url: url.toString(),\n      status: response.status,\n      id,\n      requestId,\n      responseBody,\n      headers,\n    },\n    options\n  )\n}\n\nexport class TimeoutError extends Error {\n  readonly timeout: number\n  constructor(message: string, timeout: number, options?: { cause?: unknown }) {\n    super(message, options)\n    this.name = 'TimeoutError'\n    this.timeout = timeout\n  }\n}\n\ntype RequestOptions = Omit<RequestInit, 'method' | 'headers'> & {\n  headers?: Headers\n  /** When true, the successful response is the raw `fetch` `Response` (no JSON parsing). */\n  rawResponse?: boolean\n  /** Override the client default HTTP timeout for this request (milliseconds). */\n  requestTimeout?: number\n}\n\nexport type HttpClientOptions = {\n  baseUrl: string | URL\n  headers?: Headers\n  queryParams?: Record<string, string>\n  retry: Required<HttpRetryOptions>\n  timeout: number\n}\n\nfunction matchesStatus(status: number, list: (number | [number, number])[]): boolean {\n  for (const item of list) {\n    if (typeof item === 'number') {\n      if (status === item) return true\n    } else {\n      const [start, end] = item\n      if (status >= start && status <= end) return true\n    }\n  }\n  return false\n}\n\nfunction isAbortError(error: unknown): boolean {\n  if (!error || typeof error !== 'object') return false\n  if ('name' in error && (error as any).name === 'AbortError') return true\n  // In some environments, fetch abort rejects with a DOMException named 'AbortError'\n  try {\n    if (\n      typeof DOMException !== 'undefined' &&\n      error instanceof DOMException &&\n      error.name === 'AbortError'\n    ) {\n      return true\n    }\n  } catch {\n    // ignore if DOMException is not available\n  }\n  return false\n}\n\nexport class HttpClient {\n  private baseUrl: string | URL\n  private defaultHeaders?: Headers\n  private defaultQueryParams?: Record<string, string>\n\n  private retry: Required<HttpRetryOptions>\n  private timeout: number\n  private fetchPromise: Promise<typeof fetch>\n\n  constructor(options: HttpClientOptions) {\n    this.baseUrl = options.baseUrl\n    this.defaultHeaders = options.headers\n    this.defaultQueryParams = options.queryParams\n    this.retry = options.retry\n    this.timeout = options.timeout\n\n    // Ensure maxAttempts, maxDelay and timeout are non-negative integers\n    this.retry.maxAttempts = Math.max(0, Math.floor(this.retry.maxAttempts))\n    this.timeout = Math.max(0, Math.floor(this.timeout))\n\n    this.fetchPromise = initFetch()\n  }\n\n  async get<ResponseType = Response>(\n    url: string | URL,\n    init: RequestOptions = {}\n  ): Promise<ResponseType> {\n    return this.request('GET', url, init)\n  }\n\n  async post<ResponseType = Response>(\n    url: string | URL,\n    init: RequestOptions = {}\n  ): Promise<ResponseType> {\n    return this.request('POST', url, init)\n  }\n\n  async put<ResponseType = Response>(\n    url: string | URL,\n    init: RequestOptions = {}\n  ): Promise<ResponseType> {\n    return this.request('PUT', url, init)\n  }\n\n  async delete<ResponseType = Response>(\n    url: string | URL,\n    init: RequestOptions = {}\n  ): Promise<ResponseType> {\n    return this.request('DELETE', url, init)\n  }\n\n  async request<ResponseType>(\n    method: HttpMethod,\n    url: string | URL,\n    init: RequestOptions = {}\n  ): Promise<ResponseType> {\n    url = new URL(url, this.baseUrl)\n    if (this.defaultQueryParams) {\n      for (const [key, value] of Object.entries(this.defaultQueryParams)) {\n        // Only set default param if it's not already present in the URL\n        if (!url.searchParams.has(key)) {\n          url.searchParams.set(key, value)\n        }\n      }\n    }\n\n    const { signal: userSignal, headers, rawResponse, requestTimeout, ...rest } = init\n    const effectiveTimeout =\n      requestTimeout !== undefined ? Math.max(0, Math.floor(requestTimeout)) : this.timeout\n\n    const overallStart = Date.now()\n    const attemptErrors: Error[] = []\n\n    let attempt = 0\n    const limit = this.retry.maxAttempts\n\n    while (true) {\n      attempt += 1\n\n      // Prepare AbortController that combines user signal and timeout\n      const controller = new AbortController()\n      const onUserAbort = () => controller.abort((userSignal as any)?.reason)\n      let timeoutId: ReturnType<typeof setTimeout> | undefined\n      let timedOut = false\n\n      try {\n        if (userSignal) {\n          if (userSignal.aborted) {\n            // Respect user pre-aborted signal\n            throw new Error('Request aborted by the provided AbortSignal', {\n              cause: (userSignal as any).reason,\n            })\n          }\n          userSignal.addEventListener('abort', onUserAbort, { once: true })\n        }\n\n        if (effectiveTimeout > 0) {\n          timeoutId = setTimeout(() => {\n            timedOut = true\n            controller.abort(\n              new TimeoutError(`Request timed out after ${effectiveTimeout}ms`, effectiveTimeout)\n            )\n          }, effectiveTimeout)\n        }\n\n        const selectedFetch = await this.fetchPromise\n        const response = await selectedFetch(url, {\n          ...rest,\n          method,\n          headers: this.defaultHeaders ? deepMergeObjects(this.defaultHeaders, headers) : headers,\n          signal: controller.signal,\n        })\n\n        // Clear timeout on successful resolution\n        if (timeoutId) clearTimeout(timeoutId)\n        if (userSignal) userSignal.removeEventListener('abort', onUserAbort)\n\n        if (!response.ok) {\n          const httpErr = await createHttpError(method, url, response)\n\n          // Retry only if status is retryable and attempts remain\n          // When limit is 0, retry unlimited times\n          // When limit is 1, no retries (only initial attempt)\n          // When limit is 2, 1 retry (initial + 1 retry)\n          const shouldRetry = limit === 0 ? true : attempt < limit\n          if (shouldRetry && matchesStatus(response.status, this.retry.statusCodes)) {\n            attemptErrors.push(httpErr)\n            const delayMs = this.retry.delay(attempt)\n            await sleep(delayMs)\n            continue\n          }\n\n          // Not retryable or attempts exhausted -> throw and handle in catch\n          throw httpErr\n        }\n\n        if (rawResponse) {\n          return response as ResponseType\n        }\n\n        if (response.headers.get('content-type')?.includes('application/json')) {\n          return (await response.json()) as ResponseType\n        }\n        return response as ResponseType\n      } catch (err) {\n        // Clear timers and listeners\n        if (timeoutId) clearTimeout(timeoutId)\n        if (userSignal) userSignal.removeEventListener('abort', onUserAbort)\n\n        if (timedOut) {\n          // No retry after timeout\n          const elapsed = Date.now() - overallStart\n          const timeoutError = new TimeoutError(\n            `Request timed out after ${effectiveTimeout}ms on attempt ${attempt} (duration=${elapsed}ms) for ${method} ${url}`,\n            effectiveTimeout,\n            { cause: err }\n          )\n          throw timeoutError\n        }\n\n        if (isAbortError(err) || (userSignal && userSignal.aborted)) {\n          // User abort should be clear and not retried\n          const elapsed = Date.now() - overallStart\n          const abortErr = new Error(\n            `Request aborted by the provided AbortSignal after ${elapsed}ms for ${method} ${url}`,\n            { cause: err }\n          )\n          throw abortErr\n        }\n\n        // Other errors (HTTP or network) — decide retry policy here\n        const asError = err instanceof Error ? err : new Error(String(err))\n\n        if (asError instanceof HttpError) {\n          const retryable = matchesStatus(asError.status, this.retry.statusCodes)\n          // When limit is 0, retry unlimited times\n          // When limit is 1, no retries (only initial attempt)\n          // When limit is 2, 1 retry (initial + 1 retry)\n          const shouldRetry = limit === 0 ? true : attempt < limit\n          if (retryable && shouldRetry) {\n            attemptErrors.push(asError)\n            await sleep(this.retry.delay(attempt))\n            continue\n          }\n\n          if (attemptErrors.length > 0) {\n            attemptErrors.push(asError)\n            const elapsed = Date.now() - overallStart\n            const aggregate = new AggregateError(attemptErrors, 'All retry attempts failed')\n            const finalError = new Error(\n              `HTTP request failed after ${attempt} attempts over ${elapsed}ms for ${method} ${url}`,\n              { cause: aggregate }\n            )\n            throw finalError\n          }\n\n          // No prior attempts, not retryable -> surface the HttpError\n          throw asError\n        }\n\n        // Non-HTTP (e.g., network) error\n        // When limit is 0, retry unlimited times\n        // When limit is 1, no retries (only initial attempt)\n        // When limit is 2, 1 retry (initial + 1 retry)\n        const shouldRetry = limit === 0 ? true : attempt < limit\n        if (shouldRetry) {\n          attemptErrors.push(asError)\n          await sleep(this.retry.delay(attempt))\n          continue\n        }\n\n        // Attempts exhausted — throw aggregated error with attempts and duration\n        attemptErrors.push(asError)\n        const elapsed = Date.now() - overallStart\n        const aggregate = new AggregateError(attemptErrors, 'All retry attempts failed')\n        const finalError = new Error(\n          `HTTP request failed after ${attempt} attempts over ${elapsed}ms for ${method} ${url}`,\n          { cause: aggregate }\n        )\n        throw finalError\n      }\n    }\n  }\n}\n"],"mappings":"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"}