{"version":3,"file":"FetchTransport.cjs","names":[],"sources":["../../../src/transport/FetchTransport/FetchTransport.ts"],"sourcesContent":["import { diag } from '@opentelemetry/api';\nimport type {\n  ExportResponse,\n  IExporterTransport,\n} from '@opentelemetry/otlp-exporter-base';\nimport type { FetchRequestParameters } from './types.ts';\n\n// The fetch spec limits inflight keepalive request bodies to 64KiB total per\n// page context. We use a conservative budget to leave room for third-party\n// scripts that may also use keepalive or sendBeacon.\n// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch\nconst KEEPALIVE_BYTE_BUDGET = 49152; // 48KiB\n\n// Chrome enforces a 9-request concurrent keepalive limit per renderer process.\nconst MAX_KEEPALIVE_REQUESTS = 9;\n\nlet inflightKeepaliveBytes = 0;\nlet inflightKeepaliveCount = 0;\n\n/** @internal for testing only */\nexport function _resetKeepaliveTracking(): void {\n  inflightKeepaliveBytes = 0;\n  inflightKeepaliveCount = 0;\n}\n\nexport class FetchTransport implements IExporterTransport {\n  public constructor(private readonly _config: FetchRequestParameters) {}\n\n  // Embrace endpoints require request bodies to be compressed with gzip\n  private static async _compressRequest(\n    data: Uint8Array<ArrayBuffer>,\n  ): Promise<Uint8Array<ArrayBuffer>> {\n    const stream = new CompressionStream('gzip');\n    const writer = stream.writable.getWriter();\n\n    void writer.write(data);\n    void writer.close();\n\n    const compressedChunks: Uint8Array[] = [];\n    const reader = stream.readable.getReader();\n\n    let done = false;\n    while (!done) {\n      const result = await reader.read();\n\n      if (result.value) {\n        compressedChunks.push(result.value);\n      }\n\n      done = result.done;\n    }\n\n    const compressedData = new Uint8Array(\n      compressedChunks.reduce((acc, chunk) => acc + chunk.length, 0),\n    );\n\n    let offset = 0;\n\n    for (const chunk of compressedChunks) {\n      compressedData.set(chunk, offset);\n      offset += chunk.length;\n    }\n\n    return compressedData;\n  }\n\n  public send(\n    data: Uint8Array<ArrayBuffer>,\n    timeoutMillis: number,\n  ): Promise<ExportResponse> {\n    return this._asyncSend(data, timeoutMillis);\n  }\n\n  public shutdown(): void {\n    // Intentionally left empty, nothing to do.\n  }\n\n  public async _asyncSend(\n    data: Uint8Array<ArrayBuffer>,\n    timeoutMillis: number,\n  ): Promise<ExportResponse> {\n    let request = data;\n    const headers: Record<string, string> = {\n      'Content-Type': 'application/json',\n      ...this._config.headers,\n    };\n\n    // Use AbortSignal.timeout if available, otherwise fallback to AbortController\n    // https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static\n    let signal: AbortSignal;\n    let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n    if ('timeout' in AbortSignal) {\n      // ok to ignore because there is a guard\n      // eslint-disable-next-line baseline-js/use-baseline\n      signal = AbortSignal.timeout(timeoutMillis);\n    } else {\n      const controller = new AbortController();\n      signal = controller.signal;\n      timeoutId = setTimeout(() => {\n        controller.abort(new DOMException('TimeoutError', 'TimeoutError'));\n      }, timeoutMillis);\n    }\n\n    let keepalive = false;\n\n    try {\n      if (this._config.compression === 'gzip') {\n        try {\n          request = await FetchTransport._compressRequest(data);\n        } catch (error) {\n          const compressError =\n            error instanceof Error ? error : new Error(String(error));\n          diag.warn(\n            `Fetch transport gzip compression failed: ${compressError.message}`,\n          );\n          return { status: 'failure', error: compressError };\n        }\n        headers['Content-Encoding'] = 'gzip';\n        headers['Content-Length'] = request.length.toString();\n      }\n\n      const wouldExceedBytes =\n        inflightKeepaliveBytes + request.byteLength > KEEPALIVE_BYTE_BUDGET;\n      const wouldExceedCount = inflightKeepaliveCount >= MAX_KEEPALIVE_REQUESTS;\n      keepalive = !wouldExceedBytes && !wouldExceedCount;\n\n      if (keepalive) {\n        inflightKeepaliveBytes += request.byteLength;\n        inflightKeepaliveCount++;\n      } else {\n        const reason = wouldExceedBytes\n          ? `inflight bytes (${inflightKeepaliveBytes} + ${request.byteLength}) would exceed ${KEEPALIVE_BYTE_BUDGET}B budget`\n          : `concurrent count (${inflightKeepaliveCount}) at limit of ${MAX_KEEPALIVE_REQUESTS}`;\n        diag.debug(`Sending without keepalive: ${reason}`);\n      }\n\n      const response = await fetch(this._config.url, {\n        method: 'POST',\n        keepalive,\n        headers,\n        body: request,\n        signal,\n      });\n\n      if (response.ok) {\n        return { status: 'success' };\n      }\n\n      const error = new Error(`${response.status} Fetch request failed`);\n      const message = `Fetch transport received HTTP ${response.status} (keepalive=${keepalive})`;\n      if (response.status >= 500) {\n        diag.debug(message);\n        return { status: 'retryable', error };\n      }\n      diag.warn(message);\n      return { status: 'failure', error };\n    } catch (error) {\n      const fetchError =\n        error instanceof Error ? error : new Error(String(error));\n      const isRetryable =\n        fetchError instanceof TypeError ||\n        (fetchError instanceof DOMException &&\n          fetchError.name === 'TimeoutError');\n      const message = `Fetch transport failed (keepalive=${keepalive}): ${fetchError.message}`;\n      if (isRetryable) {\n        diag.debug(message);\n      } else {\n        diag.warn(message);\n      }\n      return {\n        status: isRetryable ? 'retryable' : 'failure',\n        error: fetchError,\n      };\n    } finally {\n      if (timeoutId !== undefined) {\n        clearTimeout(timeoutId);\n      }\n      if (keepalive) {\n        inflightKeepaliveBytes -= request.byteLength;\n        inflightKeepaliveCount--;\n      }\n    }\n  }\n}\n"],"mappings":"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"}