{"version":3,"file":"httpRequest.mjs","names":[],"sources":["../../src/transport/httpRequest.ts"],"sourcesContent":["import type { EndpointBuilder } from '../domain/configuration'\nimport type { Context } from '../tools/serialisation/context'\nimport { fetch } from '../browser/fetch'\nimport { monitor, monitorError } from '../tools/monitor'\nimport type { RawError } from '../domain/error/error.types'\nimport { Observable } from '../tools/observable'\nimport { ONE_KIBI_BYTE } from '../tools/utils/byteUtils'\nimport { newRetryState, sendWithRetryStrategy } from './sendWithRetryStrategy'\n\n/**\n * beacon payload max queue size implementation is 64kb\n * ensure that we leave room for logs, rum and potential other users\n */\nexport const RECOMMENDED_REQUEST_BYTES_LIMIT = 16 * ONE_KIBI_BYTE\n\n/**\n * Use POST request without content type to:\n * - avoid CORS preflight requests\n * - allow usage of sendBeacon\n *\n * multiple elements are sent separated by \\n in order\n * to be parsed correctly without content type header\n */\n\nexport interface HttpRequest<Body extends Payload = Payload> {\n  observable: Observable<HttpRequestEvent<Body>>\n  send(this: void, payload: Body): void\n  sendOnExit(this: void, payload: Body): void\n}\n\nexport interface HttpResponse extends Context {\n  status: number\n  type?: ResponseType\n}\n\nexport interface BandwidthStats {\n  ongoingByteCount: number\n  ongoingRequestCount: number\n}\n\nexport type HttpRequestEvent<Body extends Payload = Payload> =\n  | {\n      // A request to send the given payload failed. (We may retry.)\n      type: 'failure'\n      bandwidth: BandwidthStats\n      payload: Body\n    }\n  | {\n      // The given payload was discarded because the request queue is full.\n      type: 'queue-full'\n      bandwidth: BandwidthStats\n      payload: Body\n    }\n  | {\n      // A request to send the given payload succeeded.\n      type: 'success'\n      bandwidth: BandwidthStats\n      payload: Body\n    }\n\nexport interface Payload {\n  data: string | FormData | Blob\n  bytesCount: number\n  retry?: RetryInfo\n  encoding?: 'deflate'\n}\n\nexport interface RetryInfo {\n  count: number\n  lastFailureStatus: number\n}\n\nexport function createHttpRequest<Body extends Payload = Payload>(\n  endpointBuilders: EndpointBuilder[],\n  reportError: (error: RawError) => void,\n  bytesLimit: number = RECOMMENDED_REQUEST_BYTES_LIMIT\n): HttpRequest<Body> {\n  const observable = new Observable<HttpRequestEvent<Body>>()\n  const retryState = newRetryState<Body>()\n\n  return {\n    observable,\n    send: (payload: Body) => {\n      for (const endpointBuilder of endpointBuilders) {\n        sendWithRetryStrategy(\n          payload,\n          retryState,\n          (payload, onResponse) => {\n            fetchStrategy(endpointBuilder, payload, onResponse)\n          },\n          endpointBuilder.trackType,\n          reportError,\n          observable\n        )\n      }\n    },\n    /**\n     * Since fetch keepalive behaves like regular fetch on Firefox,\n     * keep using sendBeaconStrategy on exit\n     */\n    sendOnExit: (payload: Body) => {\n      for (const endpointBuilder of endpointBuilders) {\n        sendBeaconStrategy(endpointBuilder, bytesLimit, payload)\n      }\n    },\n  }\n}\n\nfunction sendBeaconStrategy(endpointBuilder: EndpointBuilder, bytesLimit: number, payload: Payload) {\n  const canUseBeacon = payload.bytesCount < bytesLimit\n  if (canUseBeacon) {\n    try {\n      const beaconUrl = endpointBuilder.build('beacon', payload)\n      const isQueued = navigator.sendBeacon(beaconUrl, payload.data)\n\n      if (isQueued) {\n        return\n      }\n    } catch (e) {\n      reportBeaconError(e)\n    }\n  }\n\n  fetchStrategy(endpointBuilder, payload)\n}\n\nlet hasReportedBeaconError = false\n\nfunction reportBeaconError(e: unknown) {\n  if (!hasReportedBeaconError) {\n    hasReportedBeaconError = true\n    monitorError(e)\n  }\n}\n\nexport function fetchStrategy(\n  endpointBuilder: EndpointBuilder,\n  payload: Payload,\n  onResponse?: (r: HttpResponse) => void\n) {\n  const fetchUrl = endpointBuilder.build('fetch', payload)\n\n  fetch(fetchUrl, { method: 'POST', body: payload.data, mode: 'cors' })\n    .then(monitor((response: Response) => onResponse?.({ status: response.status, type: response.type })))\n    .catch(monitor(() => onResponse?.({ status: 0 })))\n}\n"],"mappings":";;;;;;;;;;AAaA,MAAa,kCAAkC,KAAK;AA2DpD,SAAgB,kBACd,kBACA,aACA,aAAqB,iCACF;CACnB,MAAM,aAAa,IAAI,WAAmC;CAC1D,MAAM,aAAa,cAAoB;CAEvC,OAAO;EACL;EACA,OAAO,YAAkB;GACvB,KAAK,MAAM,mBAAmB,kBAC5B,sBACE,SACA,aACC,SAAS,eAAe;IACvB,cAAc,iBAAiB,SAAS,UAAU;GACpD,GACA,gBAAgB,WAChB,aACA,UACF;EAEJ;;;;;EAKA,aAAa,YAAkB;GAC7B,KAAK,MAAM,mBAAmB,kBAC5B,mBAAmB,iBAAiB,YAAY,OAAO;EAE3D;CACF;AACF;AAEA,SAAS,mBAAmB,iBAAkC,YAAoB,SAAkB;CAElG,IADqB,QAAQ,aAAa,YAExC,IAAI;EACF,MAAM,YAAY,gBAAgB,MAAM,UAAU,OAAO;EAGzD,IAFiB,UAAU,WAAW,WAAW,QAAQ,IAE9C,GACT;CAEJ,SAAS,GAAG;EACV,kBAAkB,CAAC;CACrB;CAGF,cAAc,iBAAiB,OAAO;AACxC;AAEA,IAAI,yBAAyB;AAE7B,SAAS,kBAAkB,GAAY;CACrC,IAAI,CAAC,wBAAwB;EAC3B,yBAAyB;EACzB,aAAa,CAAC;CAChB;AACF;AAEA,SAAgB,cACd,iBACA,SACA,YACA;CAGA,MAFiB,gBAAgB,MAAM,SAAS,OAEnC,GAAG;EAAE,QAAQ;EAAQ,MAAM,QAAQ;EAAM,MAAM;CAAO,CAAC,CAAC,CAClE,KAAK,SAAS,aAAuB,aAAa;EAAE,QAAQ,SAAS;EAAQ,MAAM,SAAS;CAAK,CAAC,CAAC,CAAC,CAAC,CACrG,MAAM,cAAc,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AACrD"}