import { _optionalChain } from '@sentry/utils/esm/buildPolyfills';
import { getCurrentHub, isSentryRequestUrl, getDynamicSamplingContextFromClient } from '@sentry/core';
import { LRUMap, dynamicRequire, dynamicSamplingContextToSentryBaggageHeader, generateSentryTraceHeader, parseUrl, stringMatchesSomePattern, getSanitizedUrlString } from '@sentry/utils';
import { NODE_VERSION } from '../../nodeVersion.js';

var ChannelName;(function (ChannelName) {
  // https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md#undicirequestcreate
  const RequestCreate = 'undici:request:create'; ChannelName["RequestCreate"] = RequestCreate;
  const RequestEnd = 'undici:request:headers'; ChannelName["RequestEnd"] = RequestEnd;
  const RequestError = 'undici:request:error'; ChannelName["RequestError"] = RequestError;
})(ChannelName || (ChannelName = {}));

// Please note that you cannot use `console.log` to debug the callbacks registered to the `diagnostics_channel` API.
// To debug, you can use `writeFileSync` to write to a file:
// https://nodejs.org/api/async_hooks.html#printing-in-asynchook-callbacks
//
// import { writeFileSync } from 'fs';
// import { format } from 'util';
//
// function debug(...args: any): void {
//   // Use a function like this one when debugging inside an AsyncHook callback
//   // @ts-expect-error any
//   writeFileSync('log.out', `${format(...args)}\n`, { flag: 'a' });
// }

/**
 * Instruments outgoing HTTP requests made with the `undici` package via
 * Node's `diagnostics_channel` API.
 *
 * Supports Undici 4.7.0 or higher.
 *
 * Requires Node 16.17.0 or higher.
 */
class Undici  {
  /**
   * @inheritDoc
   */
   static __initStatic() {this.id = 'Undici';}

  /**
   * @inheritDoc
   */
   __init() {this.name = Undici.id;}

    __init2() {this._createSpanUrlMap = new LRUMap(100);}
    __init3() {this._headersUrlMap = new LRUMap(100);}

   constructor(_options = {}) {Undici.prototype.__init.call(this);Undici.prototype.__init2.call(this);Undici.prototype.__init3.call(this);Undici.prototype.__init4.call(this);Undici.prototype.__init5.call(this);Undici.prototype.__init6.call(this);
    this._options = {
      breadcrumbs: _options.breadcrumbs === undefined ? true : _options.breadcrumbs,
      shouldCreateSpanForRequest: _options.shouldCreateSpanForRequest,
    };
  }

  /**
   * @inheritDoc
   */
   setupOnce(_addGlobalEventProcessor) {
    // Requires Node 16+ to use the diagnostics_channel API.
    if (NODE_VERSION.major && NODE_VERSION.major < 16) {
      return;
    }

    let ds;
    try {
      // eslint-disable-next-line @typescript-eslint/no-var-requires
      ds = dynamicRequire(module, 'diagnostics_channel') ;
    } catch (e) {
      // no-op
    }

    if (!ds || !ds.subscribe) {
      return;
    }

    // https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md
    ds.subscribe(ChannelName.RequestCreate, this._onRequestCreate);
    ds.subscribe(ChannelName.RequestEnd, this._onRequestEnd);
    ds.subscribe(ChannelName.RequestError, this._onRequestError);
  }

  /** Helper that wraps shouldCreateSpanForRequest option */
   _shouldCreateSpan(url) {
    if (this._options.shouldCreateSpanForRequest === undefined) {
      return true;
    }

    const cachedDecision = this._createSpanUrlMap.get(url);
    if (cachedDecision !== undefined) {
      return cachedDecision;
    }

    const decision = this._options.shouldCreateSpanForRequest(url);
    this._createSpanUrlMap.set(url, decision);
    return decision;
  }

   __init4() {this._onRequestCreate = (message) => {
    const hub = getCurrentHub();
    if (!hub.getIntegration(Undici)) {
      return;
    }

    const { request } = message ;

    const stringUrl = request.origin ? request.origin.toString() + request.path : request.path;

    if (isSentryRequestUrl(stringUrl, hub) || request.__sentry_span__ !== undefined) {
      return;
    }

    const client = hub.getClient();
    if (!client) {
      return;
    }

    const clientOptions = client.getOptions();
    const scope = hub.getScope();

    const parentSpan = scope.getSpan();

    const span = this._shouldCreateSpan(stringUrl) ? createRequestSpan(parentSpan, request, stringUrl) : undefined;
    if (span) {
      request.__sentry_span__ = span;
    }

    const shouldAttachTraceData = (url) => {
      if (clientOptions.tracePropagationTargets === undefined) {
        return true;
      }

      const cachedDecision = this._headersUrlMap.get(url);
      if (cachedDecision !== undefined) {
        return cachedDecision;
      }

      const decision = stringMatchesSomePattern(url, clientOptions.tracePropagationTargets);
      this._headersUrlMap.set(url, decision);
      return decision;
    };

    if (shouldAttachTraceData(stringUrl)) {
      if (span) {
        const dynamicSamplingContext = _optionalChain([span, 'optionalAccess', _4 => _4.transaction, 'optionalAccess', _5 => _5.getDynamicSamplingContext, 'call', _6 => _6()]);
        const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);

        setHeadersOnRequest(request, span.toTraceparent(), sentryBaggageHeader);
      } else {
        const { traceId, sampled, dsc } = scope.getPropagationContext();
        const sentryTrace = generateSentryTraceHeader(traceId, undefined, sampled);
        const dynamicSamplingContext = dsc || getDynamicSamplingContextFromClient(traceId, client, scope);
        const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);
        setHeadersOnRequest(request, sentryTrace, sentryBaggageHeader);
      }
    }
  };}

   __init5() {this._onRequestEnd = (message) => {
    const hub = getCurrentHub();
    if (!hub.getIntegration(Undici)) {
      return;
    }

    const { request, response } = message ;

    const stringUrl = request.origin ? request.origin.toString() + request.path : request.path;

    if (isSentryRequestUrl(stringUrl, hub)) {
      return;
    }

    const span = request.__sentry_span__;
    if (span) {
      span.setHttpStatus(response.statusCode);
      span.finish();
    }

    if (this._options.breadcrumbs) {
      hub.addBreadcrumb(
        {
          category: 'http',
          data: {
            method: request.method,
            status_code: response.statusCode,
            url: stringUrl,
          },
          type: 'http',
        },
        {
          event: 'response',
          request,
          response,
        },
      );
    }
  };}

   __init6() {this._onRequestError = (message) => {
    const hub = getCurrentHub();
    if (!hub.getIntegration(Undici)) {
      return;
    }

    const { request } = message ;

    const stringUrl = request.origin ? request.origin.toString() + request.path : request.path;

    if (isSentryRequestUrl(stringUrl, hub)) {
      return;
    }

    const span = request.__sentry_span__;
    if (span) {
      span.setStatus('internal_error');
      span.finish();
    }

    if (this._options.breadcrumbs) {
      hub.addBreadcrumb(
        {
          category: 'http',
          data: {
            method: request.method,
            url: stringUrl,
          },
          level: 'error',
          type: 'http',
        },
        {
          event: 'error',
          request,
        },
      );
    }
  };}
}Undici.__initStatic();

function setHeadersOnRequest(
  request,
  sentryTrace,
  sentryBaggageHeader,
) {
  if (request.__sentry_has_headers__) {
    return;
  }

  request.addHeader('sentry-trace', sentryTrace);
  if (sentryBaggageHeader) {
    request.addHeader('baggage', sentryBaggageHeader);
  }

  request.__sentry_has_headers__ = true;
}

function createRequestSpan(
  activeSpan,
  request,
  stringUrl,
) {
  const url = parseUrl(stringUrl);

  const method = request.method || 'GET';
  const data = {
    'http.method': method,
  };
  if (url.search) {
    data['http.query'] = url.search;
  }
  if (url.hash) {
    data['http.fragment'] = url.hash;
  }
  return _optionalChain([activeSpan, 'optionalAccess', _7 => _7.startChild, 'call', _8 => _8({
    op: 'http.client',
    origin: 'auto.http.node.undici',
    description: `${method} ${getSanitizedUrlString(url)}`,
    data,
  })]);
}

export { ChannelName, Undici };
//# sourceMappingURL=index.js.map
