@gladiaio/sdk
Version:
Gladia JavaScript/TypeScript SDK
1 lines • 16.5 kB
Source Map (JSON)
{"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'> & { headers?: Headers }\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, ...rest } = init\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 (this.timeout > 0) {\n timeoutId = setTimeout(() => {\n timedOut = true\n controller.abort(\n new TimeoutError(`Request timed out after ${this.timeout}ms`, this.timeout)\n )\n }, this.timeout)\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 (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 ${this.timeout}ms on attempt ${attempt} (duration=${elapsed}ms) for ${method} ${url}`,\n this.timeout,\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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"}