UNPKG

@graphql-hive/plugin-opentelemetry

Version:
3,292 lines 123 kB
import { isRetryExecutionRequest, getRetryInfo, Logger } from '@graphql-hive/gateway-runtime';
import { getHeadersObj } from '@graphql-mesh/utils';
import { isAsyncIterable, getOperationASTFromDocument, fakePromise } from '@graphql-tools/utils';
import { unfakePromise } from '@whatwg-node/promise-helpers';
import { hive } from './api.js';
import { SEMATTRS_HIVE_GRAPHQL_OPERATION_HASH, SEMATTRS_GRAPHQL_OPERATION_TYPE, SEMATTRS_GRAPHQL_OPERATION_NAME, SEMATTRS_HIVE_GATEWAY_OPERATION_SUBGRAPH_NAMES, SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT, SEMATTRS_HIVE_GRAPHQL_ERROR_CODES, SEMATTRS_GRAPHQL_DOCUMENT, SEMATTRS_IS_HIVE_SUBGRAPH_EXECUTION, SEMATTRS_HIVE_GATEWAY_UPSTREAM_SUBGRAPH_NAME, SEMATTRS_IS_HIVE_GRAPHQL_OPERATION, SEMATTRS_IS_HIVE_REQUEST, SEMATTRS_HIVE_REQUEST_ID } from './attributes.js';
import { trace, SpanStatusCode, context, ROOT_CONTEXT, SpanKind, DiagLogLevel, diag, propagation } from '@opentelemetry/api';
import { hashOperation } from '@graphql-hive/core';
import { defaultPrintFn } from '@graphql-mesh/transport-common';
import { SEMATTRS_HTTP_METHOD, SEMATTRS_HTTP_URL, SEMATTRS_NET_HOST_NAME, SEMATTRS_HTTP_HOST, SEMATTRS_HTTP_ROUTE, SEMATTRS_HTTP_SCHEME, SEMATTRS_HTTP_STATUS_CODE, SEMATTRS_EXCEPTION_STACKTRACE, SEMATTRS_EXCEPTION_MESSAGE, SEMATTRS_EXCEPTION_TYPE, SEMATTRS_HTTP_USER_AGENT, SEMATTRS_HTTP_CLIENT_IP } from '@opentelemetry/semantic-conventions';
import { printSchema, TypeInfo } from 'graphql';
import { ExportResultCode } from '@opentelemetry/core';

class OtelContextStack {
  #root;
  #current;
  constructor(root) {
    this.#root = { ctx: root };
    this.#current = this.#root;
  }
  get current() {
    return this.#current.ctx;
  }
  get root() {
    return this.#root.ctx;
  }
  push = (ctx) => {
    this.#current = { ctx, previous: this.#current };
  };
  pop = () => {
    this.#current = this.#current.previous ?? this.#root;
  };
  toString() {
    let node = this.#current;
    const names = [];
    while (node != void 0) {
      names.push(trace.getSpan(node.ctx).name);
      node = node.previous;
    }
    return names.join(" -> ");
  }
}

function withState(pluginFactory) {
  const states = {};
  function getProp(scope, key) {
    return {
      get() {
        if (!states[scope]) states[scope] = /* @__PURE__ */ new WeakMap();
        let value = states[scope].get(key);
        if (!value) states[scope].set(key, value = {});
        return value;
      },
      enumerable: true
    };
  }
  function getState(payload) {
    let { executionRequest, context, request } = payload;
    const state = {};
    const defineState = (scope, key) => Object.defineProperty(state, scope, getProp(scope, key));
    if (executionRequest) {
      defineState("forSubgraphExecution", executionRequest);
      if (executionRequest.context?.params) context = executionRequest.context;
    }
    if (context) {
      defineState("forOperation", context);
      if (context.request) request = context.request;
    }
    if (request) {
      defineState("forRequest", request);
    }
    return state;
  }
  function addStateGetters(src) {
    const result = {};
    const properties = Object.entries(Object.getOwnPropertyDescriptors(src));
    for (const [hookName, descriptor] of properties) {
      const hook = descriptor.value;
      if (typeof hook !== "function") {
        descriptor.get &&= () => src[hookName];
        descriptor.set &&= (value) => {
          src[hookName] = value;
        };
        Object.defineProperty(result, hookName, descriptor);
      } else {
        result[hookName] = {
          [hook.name](payload, ...args) {
            return hook(
              {
                ...payload,
                get state() {
                  return getState(payload);
                }
              },
              ...args
            );
          }
        }[hook.name];
      }
    }
    return result;
  }
  const plugin = pluginFactory(getState);
  const pluginWithState = addStateGetters(plugin);
  pluginWithState.instrumentation = addStateGetters(plugin.instrumentation);
  return pluginWithState;
}
function getMostSpecificState(state = {}) {
  const { forOperation, forRequest, forSubgraphExecution } = state;
  return forSubgraphExecution ?? forOperation ?? forRequest;
}

function createHttpSpan(input) {
  const { url, request, tracer } = input;
  const span = tracer.startSpan(
    `${request.method || "GET"} ${url.pathname}`,
    {
      attributes: {
        [SEMATTRS_HTTP_METHOD]: request.method || "GET",
        [SEMATTRS_HTTP_URL]: request.url,
        [SEMATTRS_HTTP_ROUTE]: url.pathname,
        [SEMATTRS_HTTP_SCHEME]: url.protocol,
        [SEMATTRS_NET_HOST_NAME]: url.hostname || url.host || request.headers.get("host") || "localhost",
        [SEMATTRS_HTTP_HOST]: url.host || request.headers.get("host") || void 0,
        [SEMATTRS_HTTP_CLIENT_IP]: request.headers.get("x-forwarded-for")?.split(",")[0],
        [SEMATTRS_HTTP_USER_AGENT]: request.headers.get("user-agent") || void 0,
        "hive.client.name": request.headers.get("graphql-client-name") || request.headers.get("x-graphql-client-name") || void 0,
        "hive.client.version": request.headers.get("graphql-client-version") || request.headers.get("x-graphql-client-version") || void 0,
        [SEMATTRS_IS_HIVE_REQUEST]: true
      },
      kind: SpanKind.SERVER
    },
    input.ctx
  );
  return {
    ctx: trace.setSpan(input.ctx, span)
  };
}
function setResponseAttributes(ctx, response) {
  const span = trace.getSpan(ctx);
  if (span) {
    span.setAttribute(SEMATTRS_HTTP_STATUS_CODE, response.status);
    span.setAttribute(
      "gateway.cache.response_cache",
      response.status === 304 && response.headers.get("ETag") ? "hit" : "miss"
    );
    span.setStatus({
      code: response.ok ? SpanStatusCode.OK : SpanStatusCode.ERROR,
      message: response.ok ? void 0 : response.statusText
    });
  }
}
function createGraphQLSpan(input) {
  const span = input.tracer.startSpan(
    `graphql.operation`,
    {
      kind: SpanKind.INTERNAL,
      attributes: { [SEMATTRS_IS_HIVE_GRAPHQL_OPERATION]: true }
    },
    input.ctx
  );
  return trace.setSpan(input.ctx, span);
}
function setParamsAttributes(input) {
  const { ctx, params } = input;
  const span = trace.getSpan(ctx);
  if (!span) {
    return;
  }
  span.setAttribute(SEMATTRS_GRAPHQL_DOCUMENT, params.query ?? "<undefined>");
  if (params.operationName) {
    span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_NAME, params.operationName);
  }
}
const typeInfos = /* @__PURE__ */ new WeakMap();
const defaultOperationHashingFn = (input) => {
  if (!typeInfos.has(input.schema)) {
    typeInfos.set(input.schema, new TypeInfo(input.schema));
  }
  const typeInfo = typeInfos.get(input.schema);
  return hashOperation({
    documentNode: input.document,
    operationName: input.operationName ?? null,
    schema: input.schema,
    variables: null,
    // Unstable feature, not using it for now
    typeInfo
  });
};
function setDocumentAttributesOnOperationSpan(input) {
  const { ctx, document } = input;
  const span = trace.getSpan(ctx);
  if (span) {
    span.setAttribute(SEMATTRS_GRAPHQL_DOCUMENT, defaultPrintFn(document));
    const operation = getOperationFromDocument(document, input.operationName);
    if (operation) {
      span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_TYPE, operation.operation);
      const operationName = operation.name?.value;
      if (operationName) {
        span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_NAME, operationName);
        span.updateName(`graphql.operation ${operationName}`);
      }
    }
  }
}
function createGraphqlContextBuildingSpan(input) {
  const span = input.tracer.startSpan(
    "graphql.context",
    { kind: SpanKind.INTERNAL },
    input.ctx
  );
  return trace.setSpan(input.ctx, span);
}
function createGraphQLParseSpan(input) {
  const span = input.tracer.startSpan(
    "graphql.parse",
    {
      kind: SpanKind.INTERNAL
    },
    input.ctx
  );
  return trace.setSpan(input.ctx, span);
}
function setGraphQLParseAttributes(input) {
  const span = trace.getSpan(input.ctx);
  if (!span) {
    return;
  }
  if (input.query) {
    span.setAttribute(SEMATTRS_GRAPHQL_DOCUMENT, input.query);
  }
  if (input.result instanceof Error) {
    span.setAttribute(SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT, 1);
  } else {
    const document = input.result;
    const operation = getOperationFromDocument(document, input.operationName);
    if (operation) {
      span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_TYPE, operation.operation);
      const operationName = operation.name?.value;
      if (operationName) {
        span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_NAME, operationName);
      }
    }
  }
}
function createGraphQLValidateSpan(input) {
  const span = input.tracer.startSpan(
    "graphql.validate",
    { kind: SpanKind.INTERNAL },
    input.ctx
  );
  return trace.setSpan(input.ctx, span);
}
function setGraphQLValidateAttributes(input) {
  const { result, ctx, document } = input;
  const span = trace.getSpan(ctx);
  if (!span) {
    return;
  }
  const operation = getOperationFromDocument(document, input.operationName);
  if (operation) {
    const operationName = operation.name?.value;
    span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_TYPE, operation.operation);
    if (operationName) {
      span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_NAME, operationName);
    }
  }
  const errors = Array.isArray(result) ? result : [];
  if (result instanceof Error) {
    errors.push(result);
  }
  if (errors.length > 0) {
    span.setAttribute(SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT, result.length);
    span.setStatus({
      code: SpanStatusCode.ERROR,
      message: result.map((e) => e.message).join(", ")
    });
    const codes = [];
    for (const error of result) {
      if (error.extensions?.code) {
        codes.push(`${error.extensions.code}`);
      }
      span.recordException(error);
    }
    span.setAttribute(SEMATTRS_HIVE_GRAPHQL_ERROR_CODES, codes);
  }
}
function createGraphQLExecuteSpan(input) {
  const span = input.tracer.startSpan(
    "graphql.execute",
    { kind: SpanKind.INTERNAL },
    input.ctx
  );
  return trace.setSpan(input.ctx, span);
}
function setGraphQLExecutionAttributes(input) {
  const {
    ctx,
    args,
    hashOperationFn = defaultOperationHashingFn,
    operationCtx
  } = input;
  const operationSpan = trace.getSpan(operationCtx);
  if (operationSpan) {
    const hash = hashOperationFn?.({ ...args });
    if (hash) {
      operationSpan.setAttribute(SEMATTRS_HIVE_GRAPHQL_OPERATION_HASH, hash);
    }
  }
  const span = trace.getSpan(ctx);
  if (!span) {
    return;
  }
  const operation = getOperationFromDocument(
    args.document,
    args.operationName
  );
  span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_TYPE, operation.operation);
  const operationName = operation.name?.value;
  if (operationName) {
    span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_NAME, operationName);
  }
}
function setGraphQLExecutionResultAttributes(input) {
  const { ctx, result } = input;
  const span = trace.getSpan(ctx);
  if (!span) {
    return;
  }
  if (input.subgraphNames?.length) {
    span.setAttribute(
      SEMATTRS_HIVE_GATEWAY_OPERATION_SUBGRAPH_NAMES,
      input.subgraphNames
    );
  }
  if (!isAsyncIterable(result) && // FIXME: Handle async iterable too
  result.errors && result.errors.length > 0) {
    span.setAttribute(SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT, result.errors.length);
    span.setStatus({
      code: SpanStatusCode.ERROR,
      message: result.errors.map((e) => e.message).join(", ")
    });
    const codes = [];
    for (const error of result.errors) {
      span.recordException(error);
      if (error.extensions?.["code"]) {
        codes.push(`${error.extensions["code"]}`);
      }
    }
    if (codes.length > 0) {
      span.setAttribute(SEMATTRS_HIVE_GRAPHQL_ERROR_CODES, codes);
    }
  }
}
function createSubgraphExecuteSpan(input) {
  const operation = getOperationASTFromDocument(
    input.executionRequest.document,
    input.executionRequest.operationName
  );
  const span = input.tracer.startSpan(
    `subgraph.execute (${input.subgraphName})`,
    {
      attributes: {
        [SEMATTRS_GRAPHQL_OPERATION_NAME]: operation.name?.value,
        [SEMATTRS_GRAPHQL_DOCUMENT]: defaultPrintFn(
          input.executionRequest.document
        ),
        [SEMATTRS_GRAPHQL_OPERATION_TYPE]: operation.operation,
        [SEMATTRS_HIVE_GATEWAY_UPSTREAM_SUBGRAPH_NAME]: input.subgraphName,
        [SEMATTRS_IS_HIVE_SUBGRAPH_EXECUTION]: true
      },
      kind: SpanKind.CLIENT
    },
    input.ctx
  );
  return trace.setSpan(input.ctx, span);
}
function createUpstreamHttpFetchSpan(input) {
  const span = input.tracer.startSpan(
    "http.fetch",
    {
      attributes: {},
      kind: SpanKind.CLIENT
    },
    input.ctx
  );
  return trace.setSpan(input.ctx, span);
}
function setUpstreamFetchAttributes(input) {
  const { ctx, url, options: fetchOptions } = input;
  const span = trace.getSpan(ctx);
  if (!span) {
    return;
  }
  const urlObj = new URL(input.url);
  span.setAttribute(SEMATTRS_HTTP_METHOD, fetchOptions.method ?? "GET");
  span.setAttribute(SEMATTRS_HTTP_URL, url);
  span.setAttribute(SEMATTRS_NET_HOST_NAME, urlObj.hostname);
  span.setAttribute(SEMATTRS_HTTP_HOST, urlObj.host);
  span.setAttribute(SEMATTRS_HTTP_ROUTE, urlObj.pathname);
  span.setAttribute(SEMATTRS_HTTP_SCHEME, urlObj.protocol);
  if (input.executionRequest && isRetryExecutionRequest(input.executionRequest)) {
    const { attempt } = getRetryInfo(input.executionRequest);
    if (attempt > 0) {
      span.setAttribute("http.request.resend_count", attempt);
    }
  }
}
function setUpstreamFetchResponseAttributes(input) {
  const { ctx, response } = input;
  const span = trace.getSpan(ctx);
  if (!span) {
    return;
  }
  span.setAttribute(SEMATTRS_HTTP_STATUS_CODE, response.status);
  span.setStatus({
    code: response.ok ? SpanStatusCode.OK : SpanStatusCode.ERROR,
    message: response.ok ? void 0 : response.statusText
  });
}
function recordCacheEvent(event, payload) {
  trace.getActiveSpan()?.addEvent("gateway.cache." + event, {
    "gateway.cache.key": payload.key,
    "gateway.cache.ttl": payload.ttl
  });
}
function recordCacheError(action, error, payload) {
  trace.getActiveSpan()?.addEvent("gateway.cache.error", {
    "gateway.cache.key": payload.key,
    "gateway.cache.ttl": payload.ttl,
    "gateway.cache.action": action,
    [SEMATTRS_EXCEPTION_TYPE]: "code" in error ? error.code : error.message,
    [SEMATTRS_EXCEPTION_MESSAGE]: error.message,
    [SEMATTRS_EXCEPTION_STACKTRACE]: error.stack
  });
}
const responseCacheSymbol = Symbol.for("servedFromResponseCache");
function setExecutionResultAttributes(input) {
  const span = trace.getSpan(input.ctx);
  if (input.result && span) {
    span.setAttribute(
      "gateway.cache.response_cache",
      input.result[responseCacheSymbol] ? "hit" : "miss"
    );
  }
}
function createSchemaLoadingSpan(inputs) {
  const span = inputs.tracer.startSpan(
    "gateway.schema",
    { attributes: { "gateway.schema.changed": false } },
    inputs.ctx
  );
  const currentContext = context.active();
  if (currentContext !== inputs.ctx) {
    const currentSpan = trace.getActiveSpan();
    currentSpan?.addLink({ context: span.spanContext() });
  }
  return trace.setSpan(ROOT_CONTEXT, span);
}
function setSchemaAttributes(inputs) {
  const span = trace.getActiveSpan();
  if (!span) {
    return;
  }
  span.setAttribute("gateway.schema.changed", true);
  span.setAttribute("graphql.schema", printSchema(inputs.schema));
}
function registerException(ctx, error) {
  const span = ctx && trace.getSpan(ctx);
  if (!span) {
    return;
  }
  const message = error?.message?.toString() ?? error?.toString();
  span.setStatus({ code: SpanStatusCode.ERROR, message });
  span.recordException(error);
}
const operationByDocument = /* @__PURE__ */ new WeakMap();
const getOperationFromDocument = (document, operationName) => {
  let operation = operationByDocument.get(document)?.get(operationName ?? null);
  if (operation) {
    return operation;
  }
  try {
    operation = getOperationASTFromDocument(
      document,
      operationName || void 0
    );
  } catch {
  }
  let operationNameMap = operationByDocument.get(document);
  if (!operationNameMap) {
    operationByDocument.set(document, operationNameMap = /* @__PURE__ */ new Map());
  }
  operationNameMap.set(operationName ?? null, operation);
  return operation;
};

function getEnvStr(key, opts = {}) {
  const globalThat = opts.globalThis ?? globalThis;
  let variable = globalThat.process?.env?.[key] || // @ts-expect-error can exist in wrangler and maybe other runtimes
  globalThat.env?.[key] || // @ts-expect-error can exist in deno
  globalThat.Deno?.env?.get(key) || // @ts-expect-error could be
  globalThat[key];
  if (variable != null) {
    variable += "";
  } else {
    variable = void 0;
  }
  return variable?.trim();
}
function getEnvBool(key, opts = {}) {
  return strToBool(getEnvStr(key, opts));
}
function strToBool(str) {
  return ["1", "t", "true", "y", "yes", "on", "enabled"].includes(
    (str || "").toLowerCase()
  );
}

function isContextManagerCompatibleWithAsync() {
  const symbol = Symbol();
  const root = context.active();
  return context.with(root.setValue(symbol, true), () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(context.active().getValue(symbol) || false);
      });
    });
  });
}
const logLevelMap = {
  ALL: [DiagLogLevel.ALL, "trace"],
  VERBOSE: [DiagLogLevel.VERBOSE, "trace"],
  DEBUG: [DiagLogLevel.DEBUG, "debug"],
  INFO: [DiagLogLevel.INFO, "info"],
  WARN: [DiagLogLevel.WARN, "warn"],
  ERROR: [DiagLogLevel.ERROR, "error"],
  NONE: [DiagLogLevel.NONE, null]
};
function diagLogLevelFromEnv() {
  const value = getEnvStr("OTEL_LOG_LEVEL");
  if (value == null) {
    return void 0;
  }
  const resolvedLogLevel = logLevelMap[value.toUpperCase()];
  if (resolvedLogLevel == null) {
    diag.warn(
      `Unknown log level "${value}", expected one of ${Object.keys(logLevelMap)}, using default`
    );
    return logLevelMap["INFO"];
  }
  return resolvedLogLevel;
}

const initializationTime = "performance" in globalThis ? performance.now() : void 0;
const ignoredRequests = /* @__PURE__ */ new WeakSet();
const otelCtxForRequestId = /* @__PURE__ */ new Map();
const HeadersTextMapGetter = {
  keys(carrier) {
    return [...carrier.keys()];
  },
  get(carrier, key) {
    return carrier.get(key) || void 0;
  }
};
function useOpenTelemetry(options) {
  const inheritContext = options.inheritContext ?? true;
  const propagateContext = options.propagateContext ?? true;
  let useContextManager;
  let tracer;
  let traces;
  let initSpan;
  let pluginLogger = options.log && options.log.child("[OpenTelemetry] ");
  pluginLogger?.info("Enabled");
  function isParentEnabled(state) {
    const parentState = getMostSpecificState(state);
    return !parentState || !!parentState.otel;
  }
  function getContext(state) {
    const specificState = getMostSpecificState(state)?.otel;
    if (initSpan && !specificState) {
      return initSpan;
    }
    if (useContextManager) {
      return context.active();
    }
    return specificState?.current ?? ROOT_CONTEXT;
  }
  let preparation$ = init();
  preparation$.then(() => {
    preparation$ = fakePromise();
  });
  async function init() {
    if (options.useContextManager !== false && !await isContextManagerCompatibleWithAsync()) {
      useContextManager = false;
      if (options.useContextManager === true) {
        throw new Error(
          "[OTEL] Context Manager usage is enabled, but the registered one is not compatible with async calls. Please use another context manager, such as `AsyncLocalStorageContextManager`."
        );
      }
    } else {
      useContextManager = options.useContextManager ?? true;
    }
    pluginLogger?.info("Initializing");
    tracer = traces?.tracer ?? trace.getTracer("gateway");
    traces = resolveTracesConfig(options, useContextManager, pluginLogger);
    initSpan = trace.setSpan(
      context.active(),
      tracer.startSpan("gateway.initialization", {
        startTime: initializationTime
      })
    );
  }
  const plugin = withState((getState) => {
    hive.setPluginUtils({
      get tracer() {
        return tracer;
      },
      getActiveContext: (matcher) => getContext(getState(matcher)),
      getHttpContext: (request) => getState({ request }).forRequest.otel?.root,
      getOperationContext: (context2) => getState({ context: context2 }).forOperation.otel?.root,
      getExecutionRequestContext: (executionRequest) => getState({ executionRequest }).forSubgraphExecution.otel?.root,
      ignoreRequest: (request) => ignoredRequests.add(request)
    });
    return {
      get tracer() {
        return tracer;
      },
      instrumentation: {
        request({ state: { forRequest }, request }, wrapped) {
          return unfakePromise(
            preparation$.then(() => {
              if (!traces || !shouldTrace(traces.spans?.http, { request, ignoredRequests })) {
                return wrapped();
              }
              const url = getURL(request);
              const ctx = inheritContext ? propagation.extract(
                context.active(),
                request.headers,
                HeadersTextMapGetter
              ) : context.active();
              forRequest.otel = new OtelContextStack(
                createHttpSpan({ ctx, request, tracer, url }).ctx
              );
              if (useContextManager) {
                wrapped = context.bind(forRequest.otel.current, wrapped);
              }
              return wrapped();
            }).catch((error) => {
              registerException(forRequest.otel?.current, error);
              throw error;
            }).finally(() => {
              const ctx = forRequest.otel?.root;
              ctx && trace.getSpan(ctx)?.end();
            })
          );
        },
        operation({ context: gqlCtx, state: { forOperation, ...parentState } }, wrapped) {
          if (!traces || !isParentEnabled(parentState) || !shouldTrace(traces.spans?.graphql, { context: gqlCtx })) {
            return wrapped();
          }
          return unfakePromise(
            preparation$.then(() => {
              const ctx = getContext(parentState);
              forOperation.otel = new OtelContextStack(
                createGraphQLSpan({ tracer, ctx })
              );
              if (useContextManager) {
                wrapped = context.bind(forOperation.otel.current, wrapped);
              }
              return fakePromise().then(wrapped).catch((err) => {
                registerException(forOperation.otel?.current, err);
                throw err;
              }).finally(
                () => trace.getSpan(forOperation.otel.current)?.end()
              );
            })
          );
        },
        context({ state, context: gqlCtx }, wrapped) {
          if (!traces || !isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlContextBuilding, {
            context: gqlCtx
          })) {
            return wrapped();
          }
          const { forOperation } = state;
          const ctx = getContext(state);
          forOperation.otel.push(
            createGraphqlContextBuildingSpan({ ctx, tracer })
          );
          if (useContextManager) {
            wrapped = context.bind(forOperation.otel.current, wrapped);
          }
          try {
            wrapped();
          } catch (err) {
            registerException(forOperation.otel?.current, err);
            throw err;
          } finally {
            trace.getSpan(forOperation.otel.current)?.end();
            forOperation.otel.pop();
          }
        },
        parse({ state, context: gqlCtx }, wrapped) {
          if (!traces || !isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlParse, { context: gqlCtx })) {
            return wrapped();
          }
          const ctx = getContext(state);
          const { forOperation } = state;
          forOperation.otel.push(createGraphQLParseSpan({ ctx, tracer }));
          if (useContextManager) {
            wrapped = context.bind(forOperation.otel.current, wrapped);
          }
          try {
            wrapped();
          } catch (err) {
            registerException(forOperation.otel.current, err);
            throw err;
          } finally {
            trace.getSpan(forOperation.otel.current)?.end();
            forOperation.otel.pop();
          }
        },
        validate({ state, context: gqlCtx }, wrapped) {
          if (!traces || !isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlValidate, { context: gqlCtx })) {
            return wrapped();
          }
          const { forOperation } = state;
          forOperation.otel.push(
            createGraphQLValidateSpan({ ctx: getContext(state), tracer })
          );
          if (useContextManager) {
            wrapped = context.bind(forOperation.otel.current, wrapped);
          }
          try {
            wrapped();
          } catch (err) {
            registerException(forOperation.otel?.current, err);
            throw err;
          } finally {
            trace.getSpan(forOperation.otel.current)?.end();
            forOperation.otel.pop();
          }
        },
        execute({ state, context: gqlCtx }, wrapped) {
          if (!traces || !isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlExecute, { context: gqlCtx })) {
            state.forOperation.skipExecuteSpan = true;
            return wrapped();
          }
          const ctx = getContext(state);
          const { forOperation } = state;
          forOperation.otel?.push(createGraphQLExecuteSpan({ ctx, tracer }));
          if (useContextManager) {
            wrapped = context.bind(forOperation.otel.current, wrapped);
          }
          return unfakePromise(
            fakePromise().then(wrapped).catch((err) => {
              registerException(forOperation.otel.current, err);
              throw err;
            }).finally(() => {
              trace.getSpan(forOperation.otel.current)?.end();
              forOperation.otel.pop();
            })
          );
        },
        subgraphExecute({
          state: { forSubgraphExecution, ...parentState },
          executionRequest,
          subgraphName
        }, wrapped) {
          const isIntrospection = !executionRequest.context.params;
          if (!traces || !isParentEnabled(parentState) || parentState.forOperation?.skipExecuteSpan || !shouldTrace(
            isIntrospection ? traces.spans?.schema : traces.spans?.subgraphExecute,
            {
              subgraphName,
              executionRequest
            }
          )) {
            return wrapped();
          }
          const parentContext = isIntrospection ? context.active() : getContext(parentState);
          forSubgraphExecution.otel = new OtelContextStack(
            createSubgraphExecuteSpan({
              ctx: parentContext,
              tracer,
              executionRequest,
              subgraphName
            })
          );
          if (useContextManager) {
            wrapped = context.bind(forSubgraphExecution.otel.current, wrapped);
          }
          return unfakePromise(
            fakePromise().then(wrapped).catch((err) => {
              registerException(forSubgraphExecution.otel.current, err);
              throw err;
            }).finally(() => {
              trace.getSpan(forSubgraphExecution.otel.current)?.end();
              forSubgraphExecution.otel.pop();
            })
          );
        },
        fetch({ state, executionRequest }, wrapped) {
          if (isRetryExecutionRequest(executionRequest)) {
            state = getState(getRetryInfo(executionRequest));
          }
          return unfakePromise(
            preparation$.then(() => {
              if (!traces || !isParentEnabled(state) || !shouldTrace(traces.spans?.upstreamFetch, { executionRequest })) {
                return wrapped();
              }
              const { forSubgraphExecution } = state;
              const ctx = createUpstreamHttpFetchSpan({
                ctx: getContext(state),
                tracer
              });
              forSubgraphExecution?.otel.push(ctx);
              if (useContextManager) {
                wrapped = context.bind(ctx, wrapped);
              }
              return fakePromise().then(wrapped).catch((err) => {
                registerException(ctx, err);
                throw err;
              }).finally(() => {
                trace.getSpan(ctx)?.end();
                forSubgraphExecution?.otel.pop();
              });
            })
          );
        },
        schema(_, wrapped) {
          return unfakePromise(
            preparation$.then(() => {
              if (!traces || !shouldTrace(traces.spans?.schema, null)) {
                return wrapped();
              }
              const ctx = createSchemaLoadingSpan({
                ctx: initSpan ?? ROOT_CONTEXT,
                tracer
              });
              return fakePromise().then(() => context.with(ctx, wrapped)).catch((err) => {
                trace.getSpan(ctx)?.recordException(err);
              }).finally(() => {
                trace.getSpan(ctx)?.end();
              });
            })
          );
        }
      },
      onYogaInit({ yoga }) {
        pluginLogger ??= new Logger({
          writers: [
            {
              write(level, attrs, msg) {
                level = level === "trace" ? "debug" : level;
                yoga.logger[level](msg, attrs);
              }
            }
          ]
        }).child("[OpenTelemetry] ");
        pluginLogger.debug(
          `Context manager is ${useContextManager ? "enabled" : "disabled"}`
        );
      },
      onRequest({ state, serverContext }) {
        if (!traces) return;
        const requestId = (
          // TODO: serverContext.log will not be available in Yoga, this will be fixed when Hive Logger is integrated into Yoga
          serverContext.log?.attrs?.[
            // @ts-expect-error even if the attrs is an array this will work
            "requestId"
          ]
        );
        if (typeof requestId === "string") {
          const httpCtx = state.forRequest.otel?.root;
          const httpSpan = httpCtx && trace.getSpan(httpCtx);
          httpSpan?.setAttribute(SEMATTRS_HIVE_REQUEST_ID, requestId);
        }
        if (!useContextManager && typeof requestId === "string") {
          otelCtxForRequestId.set(requestId, getContext(state));
        }
      },
      onEnveloped({ state, extendContext }) {
        extendContext({
          openTelemetry: {
            tracer,
            getHttpContext: (request) => {
              const { forRequest } = request ? getState({ request }) : state;
              return forRequest.otel?.root;
            },
            getOperationContext: (context2) => {
              const { forOperation } = context2 ? getState({ context: context2 }) : state;
              return forOperation.otel?.root;
            },
            getExecutionRequestContext: (executionRequest) => {
              return getState({ executionRequest }).forSubgraphExecution.otel?.root;
            },
            getActiveContext: (contextMatcher) => getContext(contextMatcher ? getState(contextMatcher) : state)
          }
        });
      },
      onCacheGet: (payload) => traces && shouldTrace(traces.events?.cache, { key: payload.key, action: "read" }) ? {
        onCacheMiss: () => recordCacheEvent("miss", payload),
        onCacheHit: () => recordCacheEvent("hit", payload),
        onCacheGetError: ({ error }) => recordCacheError("read", error, payload)
      } : void 0,
      onCacheSet: (payload) => traces && shouldTrace(traces.events?.cache, { key: payload.key, action: "write" }) ? {
        onCacheSetDone: () => recordCacheEvent("write", payload),
        onCacheSetError: ({ error }) => recordCacheError("write", error, payload)
      } : void 0,
      onResponse({ response, state, serverContext }) {
        if (traces && state.forRequest.otel) {
          setResponseAttributes(state.forRequest.otel.root, response);
        }
        if (!useContextManager) {
          const requestId = (
            // TODO: serverContext.log will not be available in Yoga, this will be fixed when Hive Logger is integrated into Yoga
            serverContext.log?.attrs?.[
              // @ts-expect-error even if the attrs is an array this will work
              "requestId"
            ]
          );
          if (typeof requestId === "string") {
            otelCtxForRequestId.delete(requestId);
          }
        }
      },
      onParams({ state, context: gqlCtx, params }) {
        if (!traces || !isParentEnabled(state) || !shouldTrace(traces.spans?.graphql, { context: gqlCtx })) {
          return;
        }
        const ctx = getContext(state);
        setParamsAttributes({ ctx, params });
      },
      onExecutionResult({ result, context: gqlCtx, state }) {
        if (!traces || !isParentEnabled(state) || !shouldTrace(traces.spans?.graphql, { context: gqlCtx })) {
          return;
        }
        setExecutionResultAttributes({ ctx: getContext(state), result });
      },
      onParse({ state, context: gqlCtx }) {
        if (!traces || !isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlParse, { context: gqlCtx })) {
          return;
        }
        return ({ result }) => {
          setGraphQLParseAttributes({
            ctx: getContext(state),
            operationName: gqlCtx.params.operationName,
            query: gqlCtx.params.query?.trim(),
            result
          });
          if (!(result instanceof Error)) {
            setDocumentAttributesOnOperationSpan({
              ctx: state.forOperation.otel.root,
              document: result,
              operationName: gqlCtx.params.operationName
            });
          }
        };
      },
      onValidate({ state, context: gqlCtx, params }) {
        if (!traces || !isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlValidate, { context: gqlCtx })) {
          return;
        }
        return ({ result }) => {
          setGraphQLValidateAttributes({
            ctx: getContext(state),
            result,
            document: params.documentAST,
            operationName: gqlCtx.params.operationName
          });
        };
      },
      onExecute({ state, args }) {
        if (state.forOperation.skipExecuteSpan) {
          return;
        }
        const ctx = getContext(state);
        setGraphQLExecutionAttributes({
          ctx,
          operationCtx: state.forOperation.otel.root,
          args,
          hashOperationFn: options.hashOperation
        });
        state.forOperation.subgraphNames = [];
        return {
          onExecuteDone({ result }) {
            setGraphQLExecutionResultAttributes({
              ctx,
              result,
              subgraphNames: state.forOperation.subgraphNames
            });
          }
        };
      },
      onSubgraphExecute({ subgraphName, state }) {
        state.forOperation?.subgraphNames?.push(subgraphName);
      },
      onFetch(payload) {
        const { url, setFetchFn, fetchFn, executionRequest } = payload;
        let { state } = payload;
        if (executionRequest && isRetryExecutionRequest(executionRequest)) {
          state = getState(getRetryInfo(executionRequest));
        }
        if (propagateContext) {
          setFetchFn((url2, options2, ...args) => {
            const reqHeaders = getHeadersObj(options2?.headers || {});
            propagation.inject(getContext(state), reqHeaders);
            return fetchFn(url2, { ...options2, headers: reqHeaders }, ...args);
          });
        }
        if (!traces || !isParentEnabled(state) || !shouldTrace(traces.spans?.upstreamFetch, { executionRequest })) {
          return;
        }
        const ctx = getContext(state);
        setUpstreamFetchAttributes({
          ctx,
          url,
          options: payload.options,
          executionRequest
        });
        return ({ response }) => {
          setUpstreamFetchResponseAttributes({ ctx, response });
        };
      },
      onSchemaChange(payload) {
        if (initSpan) {
          trace.getSpan(initSpan)?.end();
          initSpan = null;
        }
        if (!traces || !shouldTrace(traces?.spans?.schema, null)) {
          setSchemaAttributes(payload);
        }
      },
      onDispose() {
        if (options.flushOnDispose !== false) {
          const flushMethod = options.flushOnDispose ?? "forceFlush";
          const provider = trace.getTracerProvider();
          if (flushMethod in provider && typeof provider[flushMethod] === "function") {
            return provider[flushMethod]();
          }
        }
      }
    };
  });
  plugin.getActiveContext = hive.getActiveContext;
  plugin.getHttpContext = hive.getHttpContext;
  plugin.getOperationContext = hive.getOperationContext;
  plugin.getExecutionRequestContext = hive.getExecutionRequestContext;
  plugin.ignoreRequest = hive.ignoreRequest;
  Object.defineProperty(plugin, "tracer", {
    enumerable: true,
    get: () => tracer
  });
  return plugin;
}
function shouldTrace(value, args) {
  if (value == null) {
    return true;
  }
  if (typeof value === "function") {
    return value(args);
  }
  return value;
}
function getURL(request) {
  if ("parsedUrl" in request) {
    return request.parsedUrl;
  }
  return new URL(request.url, "http://localhost");
}
const defaultHttpFilter = ({ request }) => {
  if (ignoredRequests.has(request)) {
    return false;
  }
  return true;
};
function resolveTracesConfig(options, useContextManager, log) {
  if (options.traces === false) {
    return void 0;
  }
  let traces = typeof options.traces === "object" ? options.traces : {};
  traces.spans ??= {};
  if ((traces.spans.http ?? true) === true) {
    traces.spans = { ...traces.spans, http: defaultHttpFilter };
  }
  if (!useContextManager) {
    if (traces.spans.schema) {
      log?.warn(
        "Schema loading spans are disabled because no context manager is available"
      );
    }
    traces.spans.schema = false;
  }
  return traces;
}

function getDefaultExportFromCjs (x) {
	return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}

var opossum = {exports: {}};

(function (module, exports) {
	(function webpackUniversalModuleDefinition(root, factory) {
		module.exports = factory();
	})(module.exports, () => {
	return /******/ (() => { // webpackBootstrap
	/******/ 	var __webpack_modules__ = ({

	/***/ "./index.js":
	/*!******************!*\
	  !*** ./index.js ***!
	  \******************/
	/***/ ((module, exports, __webpack_require__) => {


	module.exports = __webpack_require__(/*! ./lib/circuit */ "./lib/circuit.js");

	/***/ }),

	/***/ "./lib/cache.js":
	/*!**********************!*\
	  !*** ./lib/cache.js ***!
	  \**********************/
	/***/ ((module, exports) => {

	function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
	function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
	function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
	function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), Object.defineProperty(e, "prototype", { writable: false }), e; }
	function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
	function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return (String )(t); }
	/**
	 * Simple in-memory cache implementation
	 * @class MemoryCache
	 * @property {Map} cache Cache map
	 */
	var MemoryCache = /*#__PURE__*/function () {
	  function MemoryCache(maxEntries) {
	    _classCallCheck(this, MemoryCache);
	    this.cache = new Map();
	    this.maxEntries = maxEntries !== null && maxEntries !== void 0 ? maxEntries : Math.pow(2, 24) - 1; // Max size for Map is 2^24.
	  }

	  /**
	   * Get cache value by key
	   * @param {string} key Cache key
	   * @return {any} Response from cache
	   */
	  return _createClass(MemoryCache, [{
	    key: "get",
	    value: function get(key) {
	      var cached = this.cache.get(key);
	      if (cached) {
	        if (cached.expiresAt > Date.now() || cached.expiresAt === 0) {
	          return cached.value;
	        }
	        this.cache["delete"](key);
	      }
	      return undefined;
	    }

	    /**
	     * Set cache key with value and ttl
	     * @param {string} key Cache key
	     * @param {any} value Value to cache
	     * @param {number} ttl Time to live in milliseconds
	     * @return {void}
	     */
	  }, {
	    key: "set",
	    value: function set(key, value, ttl) {
	      // Evict first entry when at capacity - only when it's a new key.
	      if (this.cache.size === this.maxEntries && this.get(key) === undefined) {
	        this.cache["delete"](this.cache.keys().next().value);
	      }
	      this.cache.set(key, {
	        expiresAt: ttl,
	        value: value
	      });
	    }

	    /**
	     * Delete cache key
	     * @param {string} key Cache key
	     * @return {void}
	     */
	  }, {
	    key: "delete",
	    value: function _delete(key) {
	      this.cache["delete"](key);
	    }

	    /**
	     * Clear cache
	     * @returns {void}
	     */
	  }, {
	    key: "flush",
	    value: function flush() {
	      this.cache.clear();
	    }
	  }]);
	}();
	module.exports = MemoryCache;

	/***/ }),

	/***/ "./lib/circuit.js":
	/*!************************!*\
	  !*** ./lib/circuit.js ***!
	  \************************/
	/***/ ((module, exports, __webpack_require__) => {


	function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
	function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
	function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
	function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
	function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
	function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
	function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
	function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
	function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
	function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: false }), e; }
	function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
	function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return (String )(t); }
	function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
	function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
	function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
	function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
	function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
	function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: true, configurable: true } }), Object.defineProperty(t, "prototype", { writable: false }), e && _setPrototypeOf(t, e); }
	function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
	var EventEmitter = __webpack_require__(/*! events */ "./node_modules/events/events.js");
	var Status = __webpack_require__(/*! ./status */ "./lib/status.js");
	var Semaphore = __webpack_require__(/*! ./semaphore */ "./lib/semaphore.js");
	var MemoryCache = __webpack_require__(/*! ./cache */ "./lib/cache.js");
	var STATE = Symbol('state');
	var OPEN = Symbol('open');
	var CLOSED = Symbol('closed');
	var HALF_OPEN = Symbol('half-open');
	var PENDING_CLOSE = Symbol('pending-close');
	var SHUTDOWN = Symbol('shutdown');
	var FALLBACK_FUNCTION = Symbol('fallback');
	var STATUS = Symbol('status');
	var NAME = Symbol('name');
	var GROUP = Symbol('group');
	var ENABLED = Symbol('Enabled');
	var WARMING_UP = Symbol('warming-up');
	var VOLUME_THRESHOLD = Symbol('volume-threshold');
	var OUR_ERROR = Symbol('our-error');
	var RESET_TIMEOUT = Symbol('reset-timeout');
	var WARMUP_TIMEOUT = Symbol('warmup-timeout');
	var LAST_TIMER_AT = Symbol('last-timer-at');
	var deprecation = "options.maxFailures is deprecated. Please use options.errorThresholdPercentage";

	/**
	 * Constructs a {@link CircuitBreaker}.
	 *
	 * @class CircuitBreaker
	 * @extends EventEmitter
	 * @param {Function} action The action to fire for this {@link CircuitBreaker}
	 * @param {Object} options Options for the {@link CircuitBreaker}
	 * @param {Status} options.status A {@link Status} object that might
	 *   have pre-prime stats
	 * @param {Number} options.timeout The time in milliseconds that action should
	 * be allowed to execute before timing out. Timeout can be disabled by setting
	 * this to `false`. Default 10000 (10 seconds)
	 * @param {Number} options.maxFailures (Deprecated) The number of times the
	 * circuit can fail before opening. Default 10.
	 * @param {Number} options.resetTimeout The time in milliseconds to wait before
	 * setting the breaker to `halfOpen` state, and trying the action again.
	 * Default: 30000 (30 seconds)
	 * @param {Number} options.rollingCountTimeout Sets the duration of the
	 * statistical rolling window, in milliseconds. This is how long Opossum keeps
	 * metrics for the circuit breaker to use and for publishing. Default: 10000
	 * @param {Number} options.rollingCountBuckets Sets the number of buckets the
	 * rolling statistical window is divided into. So, if
	 * options.rollingCountTimeout is 10000, and options.rollingCountBuckets is 10,
	 * then the statistical window will be 1000/1 second snapshots in the
	 * statistical window. Default: 10
	 * @param {String} options.name the circuit name to use when reporting stats.
	 * Default: the name of the function this circuit controls.
	 * @param {boolean} options.rollingPercentilesEnabled This property indicates
	 * whether execution latencies should be tracked and calculated as percentiles.
	 * If they are disabled, all summary statistics (mean, percentiles) are
	 * returned as -1. Default: true
	 * @param {Number} options.capacity the number of concurrent requests allowed.
	 * If the number currently executing function calls is equal to
	 * options.capacity, further calls to `fire()` are rejected until at least one
	 * of the current requests completes. Default: `Number.MAX_SAFE_INTEGER`.
	 * @param {Number} options.errorThresholdPercentage the error percentage at
	 * which to open the circuit and start short-circuiting requests to fallback.
	 * Default: 50
	 * @param {boolean} options.enabled whether this circuit is enabled upon
	 * construction. Default: true
	 * @param {boolean} options.allowWarmUp determines whether to allow failures
	 * without opening the circuit during a brief warmup period (this is the
	 * `rollingCountTimeout` property). Default: false
	 * This can help in situations where no matter what your
	 * `errorThresholdPercentage` is, if the first execution times out or fails,
	 * the circuit immediately opens.
	 * @param {Number} options.volumeThreshold the minimum number of requests within
	 * the rolling statistical window that must exist before the circuit breaker
	 * can open. This is similar to `options.allowWarmUp` in that no matter how many
	 * failures there are, if the number of requests within the statistical window
	 * does not exceed this threshold, the circuit will remain closed. Default: 0
	 * @param {Function} options.errorFilter an optional function that will be
	 * called when the circuit's function fails (returns a rejected Promise). If
	 * this function returns truthy, the circuit's failPure statistics will not be
	 * incremented. This is useful, for example, when you don't want HTTP 404 to
	 * trip the circuit, but still want to handle it as a failure case.
	 * @param {boolean} options.cache whether the return value of the first
	 * successful execution of the circuit's function will be cached. Once a value
	 * has been cached that value will be returned for every subsequent execution:
	 * the cache can be cleared using `clearCache`. (The metrics `cacheHit` and
	 * `cacheMiss` reflect cache activity.) Default: false
	 * @param {Number} options.cacheTTL the time to live for the cache
	 * in milliseconds. Set 0 for infinity cache. Default: 0 (no TTL)
	 * @param {Number} options.cacheSize the max amount of entries in the internal
	 * cache. Only used when cacheTransport is not defined.
	 * Default: max size of JS map (2^24).
	 * @param {Function} options.cacheGetKey function that returns the key to use
	 * when caching the result of the circuit's fire.
	 * Better to use custom one, because `JSON.stringify` is not good
	 * from performance perspective.
	 * Default: `(...args) => JSON.stringify(args)`
	 * @param {CacheTransport} options.cacheTransport custom cache transport
	 * should implement `get`, `set` and `flush` methods.
	 * @param {boolean} options.coalesce  If true, this provides coalescing of
	 * requests to this breaker, in other words: the promise will be cached.
	 * Only one action (with same cache key) is executed at a time, and the other
	 * pending actions wait for the result. Performance will improve when rapidly
	 * firing the circuitbreaker with the same request, especially on a slower
	 * action (e.g. multiple end-users fetching same data from remote).
	 * Will use internal cache only. Can be used in combination with options.cache.
	 * The metrics `coalesceCacheHit` and `coalesceCacheMiss` are available.
	 * Default: false
	 * @param {Number} options.coalesceTTL the time to live for the coalescing
	 * in milliseconds. Set 0 for infinity cache. Default: same as options.timeout
	 * @param {Number} options.coalesceSize the max amount of entries in the
	 * coalescing cache. Default: max size of JS map (2^24).
	 * @param {string[]} options.coalesceResetOn when to reset the coalesce cache.
	 * Options: `error`, `success`, `timeout`. Default: not set, reset using TTL.
	 * @param {AbortController} options.abortController this allows Opossum to
	 * signal upon timeout and properly abort your on going requests instead of
	 * leaving it in the background
	 * @param {boolean} options.enableSnapshots whether to enable the rolling
	 * stats snapshots that opossum emits at the bucketInterval. Disable this
	 * as an optimization if you don't listen to the 'snapshot' event to reduce
	 * the number of timers opossum initiates.
	 * @param {EventEmitter} options.rotateBucketController if you have multiple
	 * breakers in your app, the number of timers across breakers can get costly.
	 * This option allows you to provide an EventEmitter that rotates the buckets
	 * so you can have one global timer in your app. Make sure that you are
	 * emitting a 'rotate' event from this EventEmitter
	 * @param {boolean} options.autoRenewAbortController Automatically recreates
	 * the instance of AbortController whenever the circuit transitions to
	 * 'halfOpen' or 'closed' state. This ensures that new requests are not
	 * impacted by previous signals that were triggered when the circuit was 'open'.
	 * Default: false
	 *
	 *
	 * @fires CircuitBreaker#halfOpen
	 * @fires CircuitBreaker#close
	 * @fires CircuitBreaker#open
	 * @fires CircuitBreaker#fire
	 * @fires CircuitBreaker#cacheHit
	 * @fires CircuitBreaker#cacheMiss
	 * @fires CircuitBreaker#coalesceCacheHit
	 * @fires CircuitBreaker#coalesceCacheMiss
	 * @fires CircuitBreaker#reject
	 * @fires CircuitBreaker#timeout
	 * @fires CircuitBreaker#success
	 * @fires CircuitBreaker#semaphoreLocked
	 * @fires CircuitBreaker#healthCheckFailed
	 * @fires CircuitBreaker#fallback
	 * @fires CircuitBreaker#failure
	 */
	var CircuitBreaker = /*#__PURE__*/function (_EventEmitter) {
	  function CircuitBreaker(action) {
	    var _options$timeout, _options$resetTimeout, _options$errorThresho, _options$rollingCount, _options$rollingCount2, _options$cacheTTL, _options$cacheGetKey, _options$coalesceTTL, _options$coalesceRese;
	    var _this;
	    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
	    _classCallCheck(this, CircuitBreaker);
	    _this = _callSuper(this, CircuitBreaker);
	    _this.options = options;
	    _this.options.timeout = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : 10000;
	    _this.options.resetTimeout = (_options$resetTimeout = options.resetTimeout) !== null && _options$resetTimeout !== void 0 ? _options$resetTimeout : 30000;
	    _this.options.errorThresholdPercentage = (_options$errorThresho = options.errorThresholdPercentage) !== null && _options$errorThresho !== void 0 ? _options$errorThresho : 50;
	    _this.options.rollingCountTimeout = (_options$rollingCount = options.rollingCountTimeout) !== null && _options$rollingCount !== void 0 ? _options$rollingCount : 10000;
	    _this.options.rollingCountBuckets = (_options$rollingCount2 = options.rollingCountBuckets) !== null && _options$rollingCount2 !== void 0 ? _options$rollingCount2 : 10;
	    _this.options.rollingPercentilesEnabled = options.rollingPercentilesEnabled !== false;
	    _this.options.capacity = Number.isInteger(options.capacity) ? options.capacity : Number.MAX_SAFE_INTEGER;
	    _this.options.errorFilter = options.errorFilter || function (_) {
	      return false;
	    };
	    _this.options.cacheTTL = (_options$cacheTTL = options.cacheTTL) !== null && _options$cacheTTL !== void 0 ? _options$cacheTTL : 0;
	    _this.options.cacheGetKey = (_options$cacheGetKey = options.cacheGetKey) !== null && _options$cacheGetKey !== void 0 ? _options$cacheGetKey : function () {
	      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
	        args[_key] = arguments[_key];
	      }
	      return JSON.stringify(args);
	    };
	    _this.options.enableSnapshots = options.enableSnapshots !== false;
	    _this.options.rotateBucketController = options.rotateBucketController;
	    _this.options.coalesce = !!options.coalesce;
	    _this.options.coalesceTTL = (_options$coalesceTTL = options.coalesceTTL) !== null && _options$coalesceTTL !== void 0 ? _options$coalesceTTL : _this.options.timeout;
	    _this.options.coalesceResetOn = ((_options$coalesceRese = options.coalesceResetOn) === null || _options$coalesceRese === void 0 ? void 0 : _options$coalesceRese.filter(function (o) {
	      return ['error', 'success', 'timeout'].includes(o);
	    })) || [];

	    // Set default cache transport if not provided
	    if (_this.options.cache) {
	      if (_this.options.cacheTransport === undefined) {
	        _this.options.cacheTransport = new MemoryCache(options.cacheSize);
	      } else if (_typeof(_this.options.cacheTransport) !== 'object' || !_this.options.cacheTransport.get || !_this.options.cacheTransport.set || !_this.options.cacheTransport.flush) {
	        throw new TypeError('options.cacheTransport should be an object with `get`, `set` and `flush` methods');
	      }
	    }
	    if (_this.options.coalesce) {
	      _this.options.coalesceCache = new MemoryCache(options.coalesceSize);
	    }
	    _this.semaphore = new Semaphore(_this.options.capacity);

	    // check if action is defined
	    if (!action) {
	      throw new TypeError('No action provided. Cannot construct a CircuitBreaker without an invocable action.');
	    }
	    if (options.autoRenewAbortController && !options.abortController) {
	      options.abortController = new AbortController();
	    }
	    if (options.abortController && typeof options.abortController.abort !== 'function') {
	      throw new TypeError('AbortController does not contain `abort()` method');
	    }
	    _this[VOLUME_THRESHOLD] = Number.isInteger(options.volumeThreshold) ? options.volumeThreshold : 0;
	    _this[WARMING_UP] = options.allowWarmUp === true;

	    // The user can pass in a Status object to initialize the Status/stats
	    if (_this.options.status) {
	      // Do a check that this is a Status Object,
	      if (_this.options.status instanceof Status) {
	        _this[STATUS] = _this.options.status;
	      } else {
	        _this[STATUS] = new Status({
	          stats: _this.options.status
	        });
	      }
	    } else {
	      _this[STATUS] = new Status(_this.options);
	    }
	    _this[STATE] = CLOSED;
	    if (options.state) {
	      _this[ENABLED] = options.state.enabled !== false;
	      _this[WARMING_UP] = options.state.warmUp || _this[WARMING_UP];
	      // Closed if nothing is passed in
	      _this[CLOSED] = options.state.closed !== false;
	      // These should be in sync
	      _this[HALF_OPEN] = _this[PENDING_CLOSE] = options.state.halfOpen || false;
	      // Open should be the opposite of closed,
	      // but also the opposite of half_open
	      _this[OPEN] = !_this[CLOSED] && !_this[HALF_OPEN];
	      _this[SHUTDOWN] = options.state.shutdown || false;
	    } else {
	      _this[PENDING_CLOSE] = false;
	      _this[ENABLED] = options.enabled !== false;
	    }
	    _this[FALLBACK_FUNCTION] = null;
	    _this[NAME] = options.name || action.name || nextName();
	    _this[GROUP] = options.group || _this[NAME];
	    if (_this[WARMING_UP]) {
	      var timer = _this[WARMUP_TIMEOUT] = setTimeout(function (_) {
	        return _this[WARMING_UP] = false;
	      }, _this.options.rollingCountTimeout);
	      if (typeof timer.unref === 'function') {
	        timer.unref();
	      }
	    }
	    if (typeof action !== 'function') {
	      _this.action = function (_) {
	        return Promise.resolve(action);
	      };
	    } else _this.action = action;
	    if (options.maxFailures) console.error(deprecation);
	    var increment = function increment(property) {
	      return function (result, runTime) {
	        return _this[STATUS].increment(property, runTime);
	      };
	    };
	    _this.on('success', increment('successes'));
	    _this.on('failure', increment('failures'));
	    _this.on('fallback', increment('fallbacks'));
	    _this.on('timeout', increment('timeouts'));
	    _this.on('fire', increment('fires'));
	    _this.on('reject', increment('rejects'));
	    _this.on('cacheHit', increment('cacheHits'));
	    _this.on('cacheMiss', increment('cacheMisses'));
	    _this.on('coalesceCacheHit', increment('coalesceCacheHits'));
	    _this.on('coalesceCacheMiss', increment('coalesceCacheMisses'));
	    _this.on('open', function (_) {
	      return _this[STATUS].open();
	    });
	    _this.on('close', function (_) {
	      return _this[STATUS].close();
	    });
	    _this.on('semaphoreLocked', increment('semaphoreRejections'));

	    /**
	     * @param {CircuitBreaker} circuit This current circuit
	     * @returns {function(): void} A bound reset callback
	     * @private
	     */
	    function _startTimer(circuit) {
	      circuit[LAST_TIMER_AT] = Date.now();
	      return function (_) {
	        var timer = circuit[RESET_TIMEOUT] = setTimeout(function () {
	          _halfOpen(circuit);
	        }, circuit.options.resetTimeout);
	        if (typeof timer.unref === 'function') {
	          timer.unref();
	        }
	      };
	    }

	    /**
	     * Sets the circuit breaker to half open
	     * @private
	     * @param {CircuitBreaker} circuit The current circuit breaker
	     * @returns {void}
	     */
	    function _halfOpen(circuit) {
	      circuit[STATE] = HALF_OPEN;
	      circuit[PENDING_CLOSE] = true;
	      circuit._renewAbortControllerIfNeeded();
	      /**
	       * Emitted after `options.resetTimeout` has elapsed, allowing for
	       * a single attempt to call the service again. If that attempt is
	       * successful, the circuit will be closed. Otherwise it remains open.
	       *
	       * @event CircuitBreaker#halfOpen
	       * @type {Number} how long the circuit remained open
	       */
	      circuit.emit('halfOpen', circuit.options.resetTimeout);
	    }
	    _this.on('open', _startTimer(_this));
	    _this.on('success', function (_) {
	      if (_this.halfOpen) {
	        _this.close();
	      }
	    });

	    // Prepopulate the State of the Breaker
	    if (_this[SHUTDOWN]) {
	      _this[STATE] = SHUTDOWN;
	      _this.shutdown();
	    } else if (_this[CLOSED]) {
	      _this.close();
	    } else if (_this[OPEN]) {
	      // If the state being passed in is OPEN but more time has elapsed
	      // than the resetTimeout, then we should be in halfOpen state
	      if (_this.options.state.lastTimerAt !== undefined && Date.now() - _this.options.state.lastTimerAt > _this.options.resetTimeout) {
	        _halfOpen(_this);
	      } else {
	        _this.open();
	      }
	    } else if (_this[HALF_OPEN]) {
	      // Not sure if anything needs to be done here
	      _this[STATE] = HALF_OPEN;
	    }
	    return _this;
	  }

	  /**
	   * Renews the abort controller if needed
	   * @private
	   * @returns {void}
	   */
	  _inherits(CircuitBreaker, _EventEmitter);
	  return _createClass(CircuitBreaker, [{
	    key: "_renewAbortControllerIfNeeded",
	    value: function _renewAbortControllerIfNeeded() {
	      if (this.options.autoRenewAbortController && this.options.abortController && this.options.abortController.signal.aborted) {
	        this.options.abortController = new AbortController();
	      }
	    }

	    /**
	     * Closes the breaker, allowing the action to execute again
	     * @fires CircuitBreaker#close
	     * @returns {void}
	     */
	  }, {
	    key: "close",
	    value: function close() {
	      if (this[STATE] !== CLOSED) {
	        if (this[RESET_TIMEOUT]) {
	          clearTimeout(this[RESET_TIMEOUT]);
	        }
	        this[STATE] = CLOSED;
	        this[PENDING_CLOSE] = false;
	        this._renewAbortControllerIfNeeded();
	        /**
	         * Emitted when the breaker is reset allowing the action to execute again
	         * @event CircuitBreaker#close
	         */
	        this.emit('close');
	      }
	    }

	    /**
	     * Opens the breaker. Each time the breaker is fired while the circuit is
	     * opened, a failed Promise is returned, or if any fallback function
	     * has been provided, it is invoked.
	     *
	     * If the breaker is already open this call does nothing.
	     * @fires CircuitBreaker#open
	     * @returns {void}
	     */
	  }, {
	    key: "open",
	    value: function open() {
	      if (this[STATE] !== OPEN) {
	        this[STATE] = OPEN;
	        this[PENDING_CLOSE] = false;
	        /**
	         * Emitted when the breaker opens because the action has
	         * failure percentage greater than `options.errorThresholdPercentage`.
	         * @event CircuitBreaker#open
	         */
	        this.emit('open');
	      }
	    }

	    /**
	     * Shuts down this circuit breaker. All subsequent calls to the
	     * circuit will fail, returning a rejected promise.
	     * @returns {void}
	     */
	  }, {
	    key: "shutdown",
	    value: function shutdown() {
	      /**
	       * Emitted when the circuit breaker has been shut down.
	       * @event CircuitBreaker#shutdown
	       */
	      this.emit('shutdown');
	      this.disable();
	      this.removeAllListeners();
	      if (this[RESET_TIMEOUT]) {
	        clearTimeout(this[RESET_TIMEOUT]);
	      }
	      if (this[WARMUP_TIMEOUT]) {
	        clearTimeout(this[WARMUP_TIMEOUT]);
	      }
	      this.status.shutdown();
	      this[STATE] = SHUTDOWN;

	      // clear cache on shutdown
	      this.clearCache();
	    }

	    /**
	     * Determines if the circuit has been shutdown.
	     * @type {Boolean}
	     */
	  }, {
	    key: "isShutdown",
	    get: function get() {
	      return this[STATE] === SHUTDOWN;
	    }

	    /**
	     * Gets the name of this circuit
	     * @type {String}
	     */
	  }, {
	    key: "name",
	    get: function get() {
	      return this[NAME];
	    }

	    /**
	     * Gets the name of this circuit group
	     * @type {String}
	     */
	  }, {
	    key: "group",
	    get: function get() {
	      return this[GROUP];
	    }

	    /**
	     * Gets whether this circuit is in the `pendingClosed` state
	     * @type {Boolean}
	     */
	  }, {
	    key: "pendingClose",
	    get: function get() {
	      return this[PENDING_CLOSE];
	    }

	    /**
	     * True if the circuit is currently closed. False otherwise.
	     * @type {Boolean}
	     */
	  }, {
	    key: "closed",
	    get: function get() {
	      return this[STATE] === CLOSED;
	    }

	    /**
	     * True if the circuit is currently opened. False otherwise.
	     * @type {Boolean}
	     */
	  }, {
	    key: "opened",
	    get: function get() {
	      return this[STATE] === OPEN;
	    }

	    /**
	     * True if the circuit is currently half opened. False otherwise.
	     * @type {Boolean}
	     */
	  }, {
	    key: "halfOpen",
	    get: function get() {
	      return this[STATE] === HALF_OPEN;
	    }

	    /**
	     * The current {@link Status} of this {@link CircuitBreaker}
	     * @type {Status}
	     */
	  }, {
	    key: "status",
	    get: function get() {
	      return this[STATUS];
	    }

	    /**
	     * Get the current stats for the circuit.
	     * @see Status#stats
	     * @type {Object}
	     */
	  }, {
	    key: "stats",
	    get: function get() {
	      return this[STATUS].stats;
	    }
	  }, {
	    key: "toJSON",
	    value: function toJSON() {
	      return {
	        state: {
	          name: this.name,
	          enabled: this.enabled,
	          closed: this.closed,
	          open: this.opened,
	          halfOpen: this.halfOpen,
	          warmUp: this.warmUp,
	          shutdown: this.isShutdown,
	          lastTimerAt: this[LAST_TIMER_AT]
	        },
	        status: this.status.stats
	      };
	    }

	    /**
	     * Gets whether the circuit is enabled or not
	     * @type {Boolean}
	     */
	  }, {
	    key: "enabled",
	    get: function get() {
	      return this[ENABLED];
	    }

	    /**
	     * Gets whether the circuit is currently in warm up phase
	     * @type {Boolean}
	     */
	  }, {
	    key: "warmUp",
	    get: function get() {
	      return this[WARMING_UP];
	    }

	    /**
	     * Gets the volume threshold for this circuit
	     * @type {Boolean}
	     */
	  }, {
	    key: "volumeThreshold",
	    get: function get() {
	      return this[VOLUME_THRESHOLD];
	    }

	    /**
	     * Provide a fallback function for this {@link CircuitBreaker}. This
	     * function will be executed when the circuit is `fire`d and fails.
	     * It will always be preceded by a `failure` event, and `breaker.fire` returns
	     * a rejected Promise.
	     * @param {Function | CircuitBreaker} func the fallback function to execute
	     * when the breaker has opened or when a timeout or error occurs.
	     * @return {CircuitBreaker} this
	     */
	  }, {
	    key: "fallback",
	    value: function fallback(func) {
	      var fb = func;
	      if (func instanceof CircuitBreaker) {
	        fb = function fb() {
	          return func.fire.apply(func, arguments);
	        };
	      }
	      this[FALLBACK_FUNCTION] = fb;
	      return this;
	    }

	    /**
	     * Execute the action for this circuit. If the action fails or times out, the
	     * returned promise will be rejected. If the action succeeds, the promise will
	     * resolve with the resolved value from action. If a fallback function was
	     * provided, it will be invoked in the event of any failure or timeout.
	     *
	     * Any parameters passed to this function will be proxied to the circuit
	     * function.
	     *
	     * @return {Promise<any>} promise resolves with the circuit function's return
	     * value on success or is rejected on failure of the action. Use isOurError()
	     * to determine if a rejection was a result of the circuit breaker or the
	     * action.
	     *
	     * @fires CircuitBreaker#failure
	     * @fires CircuitBreaker#fallback
	     * @fires CircuitBreaker#fire
	     * @fires CircuitBreaker#reject
	     * @fires CircuitBreaker#success
	     * @fires CircuitBreaker#timeout
	     * @fires CircuitBreaker#semaphoreLocked
	     */
	  }, {
	    key: "fire",
	    value: function fire() {
	      for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
	        args[_key2] = arguments[_key2];
	      }
	      return this.call.apply(this, [this.action].concat(args));
	    }

	    /**
	     * Execute the action for this circuit using `context` as `this`.
	     * If the action fails or times out, the
	     * returned promise will be rejected. If the action succeeds, the promise will
	     * resolve with the resolved value from action. If a fallback function was
	     * provided, it will be invoked in the event of any failure or timeout.
	     *
	     * Any parameters in addition to `context will be passed to the
	     * circuit function.
	     *
	     * @param {any} context the `this` context used for function execution
	     * @param {any} rest the arguments passed to the action
	     *
	     * @return {Promise<any>} promise resolves with the circuit function's return
	     * value on success or is rejected on failure of the action.
	     *
	     * @fires CircuitBreaker#failure
	     * @fires CircuitBreaker#fallback
	     * @fires CircuitBreaker#fire
	     * @fires CircuitBreaker#reject
	     * @fires CircuitBreaker#success
	     * @fires CircuitBreaker#timeout
	     * @fires CircuitBreaker#semaphoreLocked
	     */
	  }, {
	    key: "call",
	    value: function call(context) {
	      var _this2 = this;
	      if (this.isShutdown) {
	        var err = buildError('The circuit has been shutdown.', 'ESHUTDOWN');
	        return Promise.reject(err);
	      }
	      for (var _len3 = arguments.length, rest = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
	        rest[_key3 - 1] = arguments[_key3];
	      }
	      var args = rest.slice();

	      /**
	       * Emitted when the circuit breaker action is executed
	       * @event CircuitBreaker#fire
	       * @type {any} the arguments passed to the fired function
	       */
	      this.emit('fire', args);

	      // Protection, caches and coalesce disabled.
	      if (!this[ENABLED]) {
	        var result = this.action.apply(context, args);
	        return typeof result.then === 'function' ? result : Promise.resolve(result);
	      }

	      // Generate cachekey only when cache and/or coalesce is enabled.
	      var cacheKey = this.options.cache || this.options.coalesce ? this.options.cacheGetKey.apply(this, rest) : '';

	      // If cache is enabled, check if we have a cached value
	      if (this.options.cache) {
	        var cached = this.options.cacheTransport.get(cacheKey);
	        if (cached) {
	          /**
	           * Emitted when the circuit breaker is using the cache
	           * and finds a value.
	           * @event CircuitBreaker#cacheHit
	           */
	          this.emit('cacheHit');
	          return cached;
	        }
	        /**
	         * Emitted when the circuit breaker does not find a value in
	         * the cache, but the cache option is enabled.
	         * @event CircuitBreaker#cacheMiss
	         */
	        this.emit('cacheMiss');
	      }

	      /* When coalesce is enabled, check coalesce cache and return
	       promise, if any. */
	      if (this.options.coalesce) {
	        var cachedCall = this.options.coalesceCache.get(cacheKey);
	        if (cachedCall) {
	          /**
	           * Emitted when the circuit breaker is using coalesce cache
	           * and finds a cached promise.
	           * @event CircuitBreaker#coalesceCacheHit
	           */
	          this.emit('coalesceCacheHit');
	          return cachedCall;
	        }
	        /**
	         * Emitted when the circuit breaker does not find a value in
	         * coalesce cache, but the coalesce option is enabled.
	         * @event CircuitBreaker#coalesceCacheMiss
	         */
	        this.emit('coalesceCacheMiss');
	      }
	      if (!this.closed && !this.pendingClose) {
	        /**
	         * Emitted when the circuit breaker is open and failing fast
	         * @event CircuitBreaker#reject
	         * @type {Error}
	         */
	        var error = buildError('Breaker is open', 'EOPENBREAKER');
	        this.emit('reject', error);
	        return fallback(this, error, args) || Promise.reject(error);
	      }
	      this[PENDING_CLOSE] = false;
	      var timeout;
	      var timeoutError = false;
	      var call = new Promise(function (resolve, reject) {
	        var latencyStartTime = Date.now();
	        if (_this2.semaphore.test()) {
	          if (_this2.options.timeout) {
	            timeout = setTimeout(function () {
	              timeoutError = true;
	              var error = buildError("Timed out after ".concat(_this2.options.timeout, "ms"), 'ETIMEDOUT');
	              var latency = Date.now() - latencyStartTime;
	              _this2.semaphore.release();
	              /**
	               * Emitted when the circuit breaker action takes longer than
	               * `options.timeout`
	               * @event CircuitBreaker#timeout
	               * @type {Error}
	               */
	              _this2.emit('timeout', error, latency, args);
	              handleError(error, _this2, timeout, args, latency, resolve, reject);
	              resetCoalesce(_this2, cacheKey, 'timeout');
	              if (_this2.options.abortController) {
	                _this2.options.abortController.abort();
	              }
	            }, _this2.options.timeout);
	          }
	          try {
	            var _result = _this2.action.apply(context, args);
	            var promise = typeof _result.then === 'function' ? _result : Promise.resolve(_result);
	            promise.then(function (result) {
	              if (!timeoutError) {
	                clearTimeout(timeout);
	                /**
	                 * Emitted when the circuit breaker action succeeds
	                 * @event CircuitBreaker#success
	                 * @type {any} the return value from the circuit
	                 */
	                _this2.emit('success', result, Date.now() - latencyStartTime);
	                resetCoalesce(_this2, cacheKey, 'success');
	                _this2.semaphore.release();
	                resolve(result);
	                if (_this2.options.cache) {
	                  _this2.options.cacheTransport.set(cacheKey, promise, _this2.options.cacheTTL > 0 ? Date.now() + _this2.options.cacheTTL : 0);
	                }
	              }
	            })["catch"](function (error) {
	              if (!timeoutError) {
	                _this2.semaphore.release();
	                var latencyEndTime = Date.now() - latencyStartTime;
	                handleError(error, _this2, timeout, args, latencyEndTime, resolve, reject);
	                resetCoalesce(_this2, cacheKey, 'error');
	              }
	            });
	          } catch (error) {
	            _this2.semaphore.release();
	            var latency = Date.now() - latencyStartTime;
	            handleError(error, _this2, timeout, args, latency, resolve, reject);
	            resetCoalesce(_this2, cacheKey, 'error');
	          }
	        } else {
	          var _latency = Date.now() - latencyStartTime;
	          var _err = buildError('Semaphore locked', 'ESEMLOCKED');
	          /**
	           * Emitted when the rate limit has been reached and there
	           * are no more locks to be obtained.
	           * @event CircuitBreaker#semaphoreLocked
	           * @type {Error}
	           */
	          _this2.emit('semaphoreLocked', _err, _latency);
	          handleError(_err, _this2, timeout, args, _latency, resolve, reject);
	          resetCoalesce(_this2, cacheKey);
	        }
	      });

	      /* When coalesce is enabled, store promise in coalesceCache */
	      if (this.options.coalesce) {
	        this.options.coalesceCache.set(cacheKey, call, this.options.coalesceTTL > 0 ? Date.now() + this.options.coalesceTTL : 0);
	      }
	      return call;
	    }

	    /**
	     * Clears the cache of this {@link CircuitBreaker}
	     * @returns {void}
	     */
	  }, {
	    key: "clearCache",
	    value: function clearCache() {
	      if (this.options.cache) {
	        this.options.cacheTransport.flush();
	      }
	      if (this.options.coalesceCache) {
	        this.options.coalesceCache.flush();
	      }
	    }

	    /**
	     * Provide a health check function to be called periodically. The function
	     * should return a Promise. If the promise is rejected the circuit will open.
	     * This is in addition to the existing circuit behavior as defined by
	     * `options.errorThresholdPercentage` in the constructor. For example, if the
	     * health check function provided here always returns a resolved promise, the
	     * circuit can still trip and open if there are failures exceeding the
	     * configured threshold. The health check function is executed within the
	     * circuit breaker's execution context, so `this` within the function is the
	     * circuit breaker itself.
	     *
	     * @param {Function} func a health check function which returns a promise.
	     * @param {Number} [interval] the amount of time between calls to the health
	     * check function. Default: 5000 (5 seconds)
	     *
	     * @returns {void}
	     *
	     * @fires CircuitBreaker#healthCheckFailed
	     * @throws {TypeError} if `interval` is supplied but not a number
	     */
	  }, {
	    key: "healthCheck",
	    value: function healthCheck(func, interval) {
	      var _this3 = this;
	      interval = interval || 5000;
	      if (typeof func !== 'function') {
	        throw new TypeError('Health check function must be a function');
	      }
	      if (isNaN(interval)) {
	        throw new TypeError('Health check interval must be a number');
	      }
	      var check = function check(_) {
	        func.apply(_this3)["catch"](function (e) {
	          /**
	           * Emitted with the user-supplied health check function
	           * returns a rejected promise.
	           * @event CircuitBreaker#healthCheckFailed
	           * @type {Error}
	           */
	          _this3.emit('healthCheckFailed', e);
	          _this3.open();
	        });
	      };
	      var timer = setInterval(check, interval);
	      if (typeof timer.unref === 'function') {
	        timer.unref();
	      }
	      check();
	    }

	    /**
	     * Enables this circuit. If the circuit is the  disabled
	     * state, it will be re-enabled. If not, this is essentially
	     * a noop.
	     * @returns {void}
	     */
	  }, {
	    key: "enable",
	    value: function enable() {
	      this[ENABLED] = true;
	      this.status.startListeneningForRotateEvent();
	    }

	    /**
	     * Disables this circuit, causing all calls to the circuit's function
	     * to be executed without circuit or fallback protection.
	     * @returns {void}
	     */
	  }, {
	    key: "disable",
	    value: function disable() {
	      this[ENABLED] = false;
	      this.status.removeRotateBucketControllerListener();
	    }

	    /**
	     * Retrieves the current AbortSignal from the abortController, if available.
	     * This signal can be used to monitor ongoing requests.
	     * @returns {AbortSignal|undefined} The AbortSignal if present,
	     * otherwise undefined.
	     */
	  }, {
	    key: "getSignal",
	    value: function getSignal() {
	      if (this.options.abortController && this.options.abortController.signal) {
	        return this.options.abortController.signal;
	      }
	      return undefined;
	    }

	    /**
	     * Retrieves the current AbortController instance.
	     * This controller can be used to manually abort ongoing requests or create
	     * a new signal.
	     * @returns {AbortController|undefined} The AbortController if present,
	     * otherwise undefined.
	     */
	  }, {
	    key: "getAbortController",
	    value: function getAbortController() {
	      return this.options.abortController;
	    }
	  }], [{
	    key: "isOurError",
	    value:
	    /**
	     * Returns true if the provided error was generated here. It will be false
	     * if the error came from the action itself.
	     * @param {Error} error The Error to check.
	     * @returns {Boolean} true if the error was generated here
	     */
	    function isOurError(error) {
	      return !!error[OUR_ERROR];
	    }

	    /**
	    * Create a new Status object,
	    * helpful when you need to prime a breaker with stats
	    * @param {Object} options -
	    * @param {Number} options.rollingCountBuckets number of buckets in the window
	    * @param {Number} options.rollingCountTimeout the duration of the window
	    * @param {Boolean} options.rollingPercentilesEnabled whether to calculate
	    * @param {Object} options.stats user supplied stats
	    * @returns {Status} a new {@link Status} object
	    */
	  }, {
	    key: "newStatus",
	    value: function newStatus(options) {
	      return new Status(options);
	    }
	  }]);
	}(EventEmitter);
	function handleError(error, circuit, timeout, args, latency, resolve, reject) {
	  var _circuit$options;
	  clearTimeout(timeout);
	  if ((_circuit$options = circuit.options).errorFilter.apply(_circuit$options, [error].concat(_toConsumableArray(args)))) {
	    // The error was filtered, so emit 'success'
	    circuit.emit('success', error, latency);
	  } else {
	    // Error was not filtered, so emit 'failure'
	    fail(circuit, error, args, latency);

	    // Only call the fallback function if errorFilter doesn't succeed
	    // If the fallback function succeeds, resolve
	    var fb = fallback(circuit, error, args);
	    if (fb) return resolve(fb);
	  }

	  // In all other cases, reject
	  reject(error);
	}
	function fallback(circuit, err, args) {
	  if (circuit[FALLBACK_FUNCTION]) {
	    try {
	      var result = circuit[FALLBACK_FUNCTION].apply(circuit[FALLBACK_FUNCTION], [].concat(_toConsumableArray(args), [err]));
	      /**
	       * Emitted when the circuit breaker executes a fallback function
	       * @event CircuitBreaker#fallback
	       * @type {any} the return value of the fallback function
	       */
	      circuit.emit('fallback', result, err);
	      if (result instanceof Promise) return result;
	      return Promise.resolve(result);
	    } catch (e) {
	      return Promise.reject(e);
	    }
	  }
	}
	function fail(circuit, err, args, latency) {
	  /**
	   * Emitted when the circuit breaker action fails
	   * @event CircuitBreaker#failure
	   * @type {Error}
	   */
	  circuit.emit('failure', err, latency, args);
	  if (circuit.warmUp) return;

	  // check stats to see if the circuit should be opened
	  var stats = circuit.stats;
	  if (stats.fires < circuit.volumeThreshold && !circuit.halfOpen) return;
	  var errorRate = stats.failures / stats.fires * 100;
	  if (errorRate > circuit.options.errorThresholdPercentage || circuit.halfOpen) {
	    circuit.open();
	  }
	}
	function resetCoalesce(circuit, cacheKey, event) {
	  /**
	   * Reset coalesce cache for this cacheKey, depending on
	   * options.coalesceResetOn set.
	   * @param {@link CircuitBreaker} circuit what circuit is to be cleared
	   * @param {string} cacheKey cache key to clear.
	   * @param {string} event optional, can be `error`, `success`, `timeout`
	   * @returns {void}
	   */
	  if (!event || circuit.options.coalesceResetOn.includes(event)) {
	    var _circuit$options$coal;
	    (_circuit$options$coal = circuit.options.coalesceCache) === null || _circuit$options$coal === void 0 || _circuit$options$coal["delete"](cacheKey);
	  }
	}
	function buildError(msg, code) {
	  var error = new Error(msg);
	  error.code = code;
	  error[OUR_ERROR] = true;
	  return error;
	}

	// http://stackoverflow.com/a/2117523
	var nextName = function nextName() {
	  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
	    var r = Math.random() * 16 | 0;
	    var v = c === 'x' ? r : r & 0x3 | 0x8;
	    return v.toString(16);
	  });
	};
	module.exports = CircuitBreaker;

	/***/ }),

	/***/ "./lib/semaphore.js":
	/*!**************************!*\
	  !*** ./lib/semaphore.js ***!
	  \**************************/
	/***/ ((module, exports) => {


	module.exports = semaphore;
	function semaphore(count) {
	  var resolvers = [];
	  var counter = count;
	  var sem = {
	    take: take,
	    release: release,
	    test: test
	  };
	  Object.defineProperty(sem, 'count', {
	    get: function get(_) {
	      return counter;
	    },
	    enumerable: true
	  });
	  return sem;
	  function take(timeout) {
	    if (counter > 0) {
	      --counter;
	      return Promise.resolve(release);
	    }
	    return new Promise(function (resolve, reject) {
	      resolvers.push(function (_) {
	        --counter;
	        resolve(release);
	      });
	      if (timeout) {
	        setTimeout(function (_) {
	          resolvers.shift();
	          var err = new Error("Timed out after ".concat(timeout, "ms"));
	          err.code = 'ETIMEDOUT';
	          reject(err);
	        }, timeout);
	      }
	    });
	  }
	  function release() {
	    counter++;
	    if (resolvers.length > 0) {
	      resolvers.shift()();
	    }
	  }
	  function test() {
	    if (counter < 1) return false;
	    return take() && true;
	  }
	}

	/***/ }),

	/***/ "./lib/status.js":
	/*!***********************!*\
	  !*** ./lib/status.js ***!
	  \***********************/
	/***/ ((module, exports, __webpack_require__) => {


	function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
	function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
	function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
	function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e; }
	function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
	function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
	function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), Object.defineProperty(e, "prototype", { writable: false }), e; }
	function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
	function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return (String )(t); }
	function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
	function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
	function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
	function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
	function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
	function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: true, configurable: true } }), Object.defineProperty(t, "prototype", { writable: false }), e && _setPrototypeOf(t, e); }
	function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
	var WINDOW = Symbol('window');
	var BUCKETS = Symbol('buckets');
	var TIMEOUT = Symbol('timeout');
	var PERCENTILES = Symbol('percentiles');
	var BUCKET_INTERVAL = Symbol('bucket-interval');
	var SNAPSHOT_INTERVAL = Symbol('snapshot-interval');
	var ROTATE_EVENT_NAME = Symbol('rotate-event-name');
	var EventEmitter = (__webpack_require__(/*! events */ "./node_modules/events/events.js").EventEmitter);

	/**
	 * Tracks execution status for a given {@link CircuitBreaker}.
	 * A Status instance is created for every {@link CircuitBreaker}
	 * and does not typically need to be created by a user.
	 *
	 * A Status instance will listen for all events on the {@link CircuitBreaker}
	 * and track them in a rolling statistical window. The window duration is
	 * determined by the `rollingCountTimeout` option provided to the
	 * {@link CircuitBreaker}. The window consists of an array of Objects,
	 * each representing the counts for a {@link CircuitBreaker}'s events.
	 *
	 * The array's length is determined by the {@link CircuitBreaker}'s
	 * `rollingCountBuckets` option. The duration of each slice of the window
	 * is determined by dividing the `rollingCountTimeout` by
	 * `rollingCountBuckets`.
	 *
	 * @class Status
	 * @extends EventEmitter
	 * @param {Object} options for the status window
	 * @param {Number} options.rollingCountBuckets number of buckets in the window
	 * @param {Number} options.rollingCountTimeout the duration of the window
	 * @param {Boolean} options.rollingPercentilesEnabled whether to calculate
	 * percentiles
	 * @param {Object} options.stats object of previous stats
	 * @example
	 * // Creates a 1 second window consisting of ten time slices,
	 * // each 100ms long.
	 * const circuit = circuitBreaker(fs.readFile,
	 *  { rollingCountBuckets: 10, rollingCountTimeout: 1000});
	 *
	 * // get the cumulative statistics for the last second
	 * circuit.status.stats;
	 *
	 * // get the array of 10, 1 second time slices for the last second
	 * circuit.status.window;
	 * @fires Status#snapshot
	 * @see CircuitBreaker#status
	 */
	var Status = /*#__PURE__*/function (_EventEmitter) {
	  function Status(options) {
	    var _this;
	    _classCallCheck(this, Status);
	    _this = _callSuper(this, Status);

	    // Set up our statistical rolling window
	    _this[BUCKETS] = options.rollingCountBuckets || 10;
	    _this[TIMEOUT] = options.rollingCountTimeout || 10000;
	    _this[WINDOW] = new Array(_this[BUCKETS]);
	    _this[PERCENTILES] = [0.0, 0.25, 0.5, 0.75, 0.9, 0.95, 0.99, 0.995, 1];
	    _this[ROTATE_EVENT_NAME] = 'rotate';

	    // Default this value to true
	    _this.rollingPercentilesEnabled = options.rollingPercentilesEnabled !== false;

	    // Default this value to true
	    _this.enableSnapshots = options.enableSnapshots !== false;

	    // can be undefined
	    _this.rotateBucketController = options.rotateBucketController;
	    _this.rotateBucket = nextBucket(_this[WINDOW]);

	    // prime the window with buckets
	    for (var i = 0; i < _this[BUCKETS]; i++) _this[WINDOW][i] = bucket();
	    var bucketInterval = Math.floor(_this[TIMEOUT] / _this[BUCKETS]);
	    if (_this.rotateBucketController) {
	      // rotate the buckets based on an optional EventEmitter
	      _this.startListeneningForRotateEvent();
	    } else {
	      // or rotate the buckets periodically
	      _this[BUCKET_INTERVAL] = setInterval(_this.rotateBucket, bucketInterval);
	      // No unref() in the browser
	      if (typeof _this[BUCKET_INTERVAL].unref === 'function') {
	        _this[BUCKET_INTERVAL].unref();
	      }
	    }

	    /**
	     * Emitted at each time-slice. Listeners for this
	     * event will receive a cumulative snapshot of the current status window.
	     * @event Status#snapshot
	     * @type {Object}
	     */
	    if (_this.enableSnapshots) {
	      _this[SNAPSHOT_INTERVAL] = setInterval(function (_) {
	        return _this.emit('snapshot', _this.stats);
	      }, bucketInterval);
	      if (typeof _this[SNAPSHOT_INTERVAL].unref === 'function') {
	        _this[SNAPSHOT_INTERVAL].unref();
	      }
	    }
	    if (options.stats) {
	      _this[WINDOW][0] = _objectSpread(_objectSpread({}, bucket()), options.stats);
	    }
	    return _this;
	  }

	  /**
	   * Get the cumulative stats for the current window
	   * @type {Object}
	   */
	  _inherits(Status, _EventEmitter);
	  return _createClass(Status, [{
	    key: "stats",
	    get: function get() {
	      var _this2 = this;
	      var totals = this[WINDOW].reduce(function (acc, val) {
	        if (!val) {
	          return acc;
	        }
	        Object.keys(acc).forEach(function (key) {
	          if (key !== 'latencyTimes' && key !== 'percentiles') {
	            acc[key] += val[key] || 0;
	          }
	        });
	        if (_this2.rollingPercentilesEnabled) {
	          if (val.latencyTimes) {
	            acc.latencyTimes = acc.latencyTimes.concat(val.latencyTimes);
	          }
	        }
	        return acc;
	      }, bucket());
	      if (this.rollingPercentilesEnabled) {
	        // Sort the latencyTimes
	        totals.latencyTimes.sort(function (a, b) {
	          return a - b;
	        });

	        // Get the mean latency
	        // Mean = sum of all values in the array/length of array
	        if (totals.latencyTimes.length) {
	          totals.latencyMean = totals.latencyTimes.reduce(function (a, b) {
	            return a + b;
	          }, 0) / totals.latencyTimes.length;
	        } else {
	          totals.latencyMean = 0;
	        }

	        // Calculate Percentiles
	        this[PERCENTILES].forEach(function (percentile) {
	          totals.percentiles[percentile] = calculatePercentile(percentile, totals.latencyTimes);
	        });
	      } else {
	        totals.latencyMean = -1;
	        this[PERCENTILES].forEach(function (percentile) {
	          totals.percentiles[percentile] = -1;
	        });
	      }
	      return totals;
	    }

	    /**
	     * Gets the stats window as an array of time-sliced objects.
	     * @type {Array}
	     */
	  }, {
	    key: "window",
	    get: function get() {
	      return this[WINDOW].slice();
	    }
	  }, {
	    key: "increment",
	    value: function increment(property, latencyRunTime) {
	      this[WINDOW][0][property]++;
	      if (property === 'successes' || property === 'failures' || property === 'timeouts') {
	        this[WINDOW][0].latencyTimes.push(latencyRunTime || 0);
	      }
	    }
	  }, {
	    key: "open",
	    value: function open() {
	      this[WINDOW][0].isCircuitBreakerOpen = true;
	    }
	  }, {
	    key: "close",
	    value: function close() {
	      this[WINDOW][0].isCircuitBreakerOpen = false;
	    }
	  }, {
	    key: "shutdown",
	    value: function shutdown() {
	      this.removeAllListeners();
	      // interval is not set if rotateBucketController is provided
	      if (this.rotateBucketController === undefined) {
	        clearInterval(this[BUCKET_INTERVAL]);
	      } else {
	        this.removeRotateBucketControllerListener();
	      }
	      if (this.enableSnapshots) {
	        clearInterval(this[SNAPSHOT_INTERVAL]);
	      }
	    }
	  }, {
	    key: "removeRotateBucketControllerListener",
	    value: function removeRotateBucketControllerListener() {
	      if (this.rotateBucketController) {
	        this.rotateBucketController.removeListener(this[ROTATE_EVENT_NAME], this.rotateBucket);
	      }
	    }
	  }, {
	    key: "startListeneningForRotateEvent",
	    value: function startListeneningForRotateEvent() {
	      if (this.rotateBucketController && this.rotateBucketController.listenerCount(this[ROTATE_EVENT_NAME], this.rotateBucket) === 0) {
	        this.rotateBucketController.on(this[ROTATE_EVENT_NAME], this.rotateBucket);
	      }
	    }
	  }]);
	}(EventEmitter);
	var nextBucket = function nextBucket(window) {
	  return function (_) {
	    window.pop();
	    window.unshift(bucket());
	  };
	};
	var bucket = function bucket(_) {
	  return {
	    failures: 0,
	    fallbacks: 0,
	    successes: 0,
	    rejects: 0,
	    fires: 0,
	    timeouts: 0,
	    cacheHits: 0,
	    cacheMisses: 0,
	    coalesceCacheHits: 0,
	    coalesceCacheMisses: 0,
	    semaphoreRejections: 0,
	    percentiles: {},
	    latencyTimes: []
	  };
	};
	function calculatePercentile(percentile, arr) {
	  if (percentile === 0) {
	    return arr[0] || 0;
	  }
	  var idx = Math.ceil(percentile * arr.length);
	  return arr[idx - 1] || 0;
	}
	module.exports = Status;

	/***/ }),

	/***/ "./node_modules/events/events.js":
	/*!***************************************!*\
	  !*** ./node_modules/events/events.js ***!
	  \***************************************/
	/***/ ((module) => {
	// Copyright Joyent, Inc. and other Node contributors.
	//
	// Permission is hereby granted, free of charge, to any person obtaining a
	// copy of this software and associated documentation files (the
	// "Software"), to deal in the Software without restriction, including
	// without limitation the rights to use, copy, modify, merge, publish,
	// distribute, sublicense, and/or sell copies of the Software, and to permit
	// persons to whom the Software is furnished to do so, subject to the
	// following conditions:
	//
	// The above copyright notice and this permission notice shall be included
	// in all copies or substantial portions of the Software.
	//
	// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
	// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
	// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
	// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
	// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
	// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
	// USE OR OTHER DEALINGS IN THE SOFTWARE.



	var R = typeof Reflect === 'object' ? Reflect : null;
	var ReflectApply = R && typeof R.apply === 'function'
	  ? R.apply
	  : function ReflectApply(target, receiver, args) {
	    return Function.prototype.apply.call(target, receiver, args);
	  };

	var ReflectOwnKeys;
	if (R && typeof R.ownKeys === 'function') {
	  ReflectOwnKeys = R.ownKeys;
	} else if (Object.getOwnPropertySymbols) {
	  ReflectOwnKeys = function ReflectOwnKeys(target) {
	    return Object.getOwnPropertyNames(target)
	      .concat(Object.getOwnPropertySymbols(target));
	  };
	} else {
	  ReflectOwnKeys = function ReflectOwnKeys(target) {
	    return Object.getOwnPropertyNames(target);
	  };
	}

	function ProcessEmitWarning(warning) {
	  if (console && console.warn) console.warn(warning);
	}

	var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
	  return value !== value;
	};

	function EventEmitter() {
	  EventEmitter.init.call(this);
	}
	module.exports = EventEmitter;
	module.exports.once = once;

	// Backwards-compat with node 0.10.x
	EventEmitter.EventEmitter = EventEmitter;

	EventEmitter.prototype._events = undefined;
	EventEmitter.prototype._eventsCount = 0;
	EventEmitter.prototype._maxListeners = undefined;

	// By default EventEmitters will print a warning if more than 10 listeners are
	// added to it. This is a useful default which helps finding memory leaks.
	var defaultMaxListeners = 10;

	function checkListener(listener) {
	  if (typeof listener !== 'function') {
	    throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
	  }
	}

	Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
	  enumerable: true,
	  get: function() {
	    return defaultMaxListeners;
	  },
	  set: function(arg) {
	    if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
	      throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
	    }
	    defaultMaxListeners = arg;
	  }
	});

	EventEmitter.init = function() {

	  if (this._events === undefined ||
	      this._events === Object.getPrototypeOf(this)._events) {
	    this._events = Object.create(null);
	    this._eventsCount = 0;
	  }

	  this._maxListeners = this._maxListeners || undefined;
	};

	// Obviously not all Emitters should be limited to 10. This function allows
	// that to be increased. Set to zero for unlimited.
	EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
	  if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
	    throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
	  }
	  this._maxListeners = n;
	  return this;
	};

	function _getMaxListeners(that) {
	  if (that._maxListeners === undefined)
	    return EventEmitter.defaultMaxListeners;
	  return that._maxListeners;
	}

	EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
	  return _getMaxListeners(this);
	};

	EventEmitter.prototype.emit = function emit(type) {
	  var args = [];
	  for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
	  var doError = (type === 'error');

	  var events = this._events;
	  if (events !== undefined)
	    doError = (doError && events.error === undefined);
	  else if (!doError)
	    return false;

	  // If there is no 'error' event listener then throw.
	  if (doError) {
	    var er;
	    if (args.length > 0)
	      er = args[0];
	    if (er instanceof Error) {
	      // Note: The comments on the `throw` lines are intentional, they show
	      // up in Node's output if this results in an unhandled exception.
	      throw er; // Unhandled 'error' event
	    }
	    // At least give some kind of context to the user
	    var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
	    err.context = er;
	    throw err; // Unhandled 'error' event
	  }

	  var handler = events[type];

	  if (handler === undefined)
	    return false;

	  if (typeof handler === 'function') {
	    ReflectApply(handler, this, args);
	  } else {
	    var len = handler.length;
	    var listeners = arrayClone(handler, len);
	    for (var i = 0; i < len; ++i)
	      ReflectApply(listeners[i], this, args);
	  }

	  return true;
	};

	function _addListener(target, type, listener, prepend) {
	  var m;
	  var events;
	  var existing;

	  checkListener(listener);

	  events = target._events;
	  if (events === undefined) {
	    events = target._events = Object.create(null);
	    target._eventsCount = 0;
	  } else {
	    // To avoid recursion in the case that type === "newListener"! Before
	    // adding it to the listeners, first emit "newListener".
	    if (events.newListener !== undefined) {
	      target.emit('newListener', type,
	                  listener.listener ? listener.listener : listener);

	      // Re-assign `events` because a newListener handler could have caused the
	      // this._events to be assigned to a new object
	      events = target._events;
	    }
	    existing = events[type];
	  }

	  if (existing === undefined) {
	    // Optimize the case of one listener. Don't need the extra array object.
	    existing = events[type] = listener;
	    ++target._eventsCount;
	  } else {
	    if (typeof existing === 'function') {
	      // Adding the second element, need to change to array.
	      existing = events[type] =
	        prepend ? [listener, existing] : [existing, listener];
	      // If we've already got an array, just append.
	    } else if (prepend) {
	      existing.unshift(listener);
	    } else {
	      existing.push(listener);
	    }

	    // Check for listener leak
	    m = _getMaxListeners(target);
	    if (m > 0 && existing.length > m && !existing.warned) {
	      existing.warned = true;
	      // No error code for this since it is a Warning
	      // eslint-disable-next-line no-restricted-syntax
	      var w = new Error('Possible EventEmitter memory leak detected. ' +
	                          existing.length + ' ' + String(type) + ' listeners ' +
	                          'added. Use emitter.setMaxListeners() to ' +
	                          'increase limit');
	      w.name = 'MaxListenersExceededWarning';
	      w.emitter = target;
	      w.type = type;
	      w.count = existing.length;
	      ProcessEmitWarning(w);
	    }
	  }

	  return target;
	}

	EventEmitter.prototype.addListener = function addListener(type, listener) {
	  return _addListener(this, type, listener, false);
	};

	EventEmitter.prototype.on = EventEmitter.prototype.addListener;

	EventEmitter.prototype.prependListener =
	    function prependListener(type, listener) {
	      return _addListener(this, type, listener, true);
	    };

	function onceWrapper() {
	  if (!this.fired) {
	    this.target.removeListener(this.type, this.wrapFn);
	    this.fired = true;
	    if (arguments.length === 0)
	      return this.listener.call(this.target);
	    return this.listener.apply(this.target, arguments);
	  }
	}

	function _onceWrap(target, type, listener) {
	  var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
	  var wrapped = onceWrapper.bind(state);
	  wrapped.listener = listener;
	  state.wrapFn = wrapped;
	  return wrapped;
	}

	EventEmitter.prototype.once = function once(type, listener) {
	  checkListener(listener);
	  this.on(type, _onceWrap(this, type, listener));
	  return this;
	};

	EventEmitter.prototype.prependOnceListener =
	    function prependOnceListener(type, listener) {
	      checkListener(listener);
	      this.prependListener(type, _onceWrap(this, type, listener));
	      return this;
	    };

	// Emits a 'removeListener' event if and only if the listener was removed.
	EventEmitter.prototype.removeListener =
	    function removeListener(type, listener) {
	      var list, events, position, i, originalListener;

	      checkListener(listener);

	      events = this._events;
	      if (events === undefined)
	        return this;

	      list = events[type];
	      if (list === undefined)
	        return this;

	      if (list === listener || list.listener === listener) {
	        if (--this._eventsCount === 0)
	          this._events = Object.create(null);
	        else {
	          delete events[type];
	          if (events.removeListener)
	            this.emit('removeListener', type, list.listener || listener);
	        }
	      } else if (typeof list !== 'function') {
	        position = -1;

	        for (i = list.length - 1; i >= 0; i--) {
	          if (list[i] === listener || list[i].listener === listener) {
	            originalListener = list[i].listener;
	            position = i;
	            break;
	          }
	        }

	        if (position < 0)
	          return this;

	        if (position === 0)
	          list.shift();
	        else {
	          spliceOne(list, position);
	        }

	        if (list.length === 1)
	          events[type] = list[0];

	        if (events.removeListener !== undefined)
	          this.emit('removeListener', type, originalListener || listener);
	      }

	      return this;
	    };

	EventEmitter.prototype.off = EventEmitter.prototype.removeListener;

	EventEmitter.prototype.removeAllListeners =
	    function removeAllListeners(type) {
	      var listeners, events, i;

	      events = this._events;
	      if (events === undefined)
	        return this;

	      // not listening for removeListener, no need to emit
	      if (events.removeListener === undefined) {
	        if (arguments.length === 0) {
	          this._events = Object.create(null);
	          this._eventsCount = 0;
	        } else if (events[type] !== undefined) {
	          if (--this._eventsCount === 0)
	            this._events = Object.create(null);
	          else
	            delete events[type];
	        }
	        return this;
	      }

	      // emit removeListener for all listeners on all events
	      if (arguments.length === 0) {
	        var keys = Object.keys(events);
	        var key;
	        for (i = 0; i < keys.length; ++i) {
	          key = keys[i];
	          if (key === 'removeListener') continue;
	          this.removeAllListeners(key);
	        }
	        this.removeAllListeners('removeListener');
	        this._events = Object.create(null);
	        this._eventsCount = 0;
	        return this;
	      }

	      listeners = events[type];

	      if (typeof listeners === 'function') {
	        this.removeListener(type, listeners);
	      } else if (listeners !== undefined) {
	        // LIFO order
	        for (i = listeners.length - 1; i >= 0; i--) {
	          this.removeListener(type, listeners[i]);
	        }
	      }

	      return this;
	    };

	function _listeners(target, type, unwrap) {
	  var events = target._events;

	  if (events === undefined)
	    return [];

	  var evlistener = events[type];
	  if (evlistener === undefined)
	    return [];

	  if (typeof evlistener === 'function')
	    return unwrap ? [evlistener.listener || evlistener] : [evlistener];

	  return unwrap ?
	    unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
	}

	EventEmitter.prototype.listeners = function listeners(type) {
	  return _listeners(this, type, true);
	};

	EventEmitter.prototype.rawListeners = function rawListeners(type) {
	  return _listeners(this, type, false);
	};

	EventEmitter.listenerCount = function(emitter, type) {
	  if (typeof emitter.listenerCount === 'function') {
	    return emitter.listenerCount(type);
	  } else {
	    return listenerCount.call(emitter, type);
	  }
	};

	EventEmitter.prototype.listenerCount = listenerCount;
	function listenerCount(type) {
	  var events = this._events;

	  if (events !== undefined) {
	    var evlistener = events[type];

	    if (typeof evlistener === 'function') {
	      return 1;
	    } else if (evlistener !== undefined) {
	      return evlistener.length;
	    }
	  }

	  return 0;
	}

	EventEmitter.prototype.eventNames = function eventNames() {
	  return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
	};

	function arrayClone(arr, n) {
	  var copy = new Array(n);
	  for (var i = 0; i < n; ++i)
	    copy[i] = arr[i];
	  return copy;
	}

	function spliceOne(list, index) {
	  for (; index + 1 < list.length; index++)
	    list[index] = list[index + 1];
	  list.pop();
	}

	function unwrapListeners(arr) {
	  var ret = new Array(arr.length);
	  for (var i = 0; i < ret.length; ++i) {
	    ret[i] = arr[i].listener || arr[i];
	  }
	  return ret;
	}

	function once(emitter, name) {
	  return new Promise(function (resolve, reject) {
	    function errorListener(err) {
	      emitter.removeListener(name, resolver);
	      reject(err);
	    }

	    function resolver() {
	      if (typeof emitter.removeListener === 'function') {
	        emitter.removeListener('error', errorListener);
	      }
	      resolve([].slice.call(arguments));
	    }
	    eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
	    if (name !== 'error') {
	      addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
	    }
	  });
	}

	function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
	  if (typeof emitter.on === 'function') {
	    eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
	  }
	}

	function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
	  if (typeof emitter.on === 'function') {
	    if (flags.once) {
	      emitter.once(name, listener);
	    } else {
	      emitter.on(name, listener);
	    }
	  } else if (typeof emitter.addEventListener === 'function') {
	    // EventTarget does not have `error` event semantics like Node
	    // EventEmitters, we do not listen for `error` events here.
	    emitter.addEventListener(name, function wrapListener(arg) {
	      // IE does not have builtin `{ once: true }` support so we
	      // have to do it manually.
	      if (flags.once) {
	        emitter.removeEventListener(name, wrapListener);
	      }
	      listener(arg);
	    });
	  } else {
	    throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
	  }
	}


	/***/ })

	/******/ 	});
	/************************************************************************/
	/******/ 	// The module cache
	/******/ 	var __webpack_module_cache__ = {};
	/******/ 	
	/******/ 	// The require function
	/******/ 	function __webpack_require__(moduleId) {
	/******/ 		// Check if module is in cache
	/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
	/******/ 		if (cachedModule !== undefined) {
	/******/ 			return cachedModule.exports;
	/******/ 		}
	/******/ 		// Create a new module (and put it into the cache)
	/******/ 		var module = __webpack_module_cache__[moduleId] = {
	/******/ 			// no module.id needed
	/******/ 			// no module.loaded needed
	/******/ 			exports: {}
	/******/ 		};
	/******/ 	
	/******/ 		// Execute the module function
	/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
	/******/ 	
	/******/ 		// Return the exports of the module
	/******/ 		return module.exports;
	/******/ 	}
	/******/ 	
	/************************************************************************/
	/******/ 	
	/******/ 	// startup
	/******/ 	// Load entry module and return exports
	/******/ 	// This entry module is referenced by other modules so it can't be inlined
	/******/ 	var __webpack_exports__ = __webpack_require__("./index.js");
	/******/ 	
	/******/ 	return __webpack_exports__;
	/******/ })()
	;
	});
	
} (opossum));

var opossumExports = opossum.exports;
var CircuitBreaker = /*@__PURE__*/getDefaultExportFromCjs(opossumExports);

const defaultCircuitBreakerConfiguration = {
  // each failed fire already represents 5 HTTP retries, so 3 fires = 15 failed HTTP calls, which
  // is a very clear signal. since each "fire" is already 5 retries, 3 fires = strong signal
  // the endpoint is down
  volumeThreshold: 3,
  // tolerates 1 transient failure in 3 before tripping; avoids opening on a single issue
  // any failure rate at or above 50% should open the circuit.
  // a single transient failure out of 3 = 33%, won't trip.
  // 2 out of 3 = 66%, will trip.
  errorThresholdPercentage: 50,
  // after opening, wait 60s before trying again. 30s (default) is too short - if the
  // endpoint is down, it's likely down for at least a minute. no point hammering it sooner.
  // btw, 30s is the BatchSpanProcessor export timeout exactly, which means the circuit could
  // immediately time out again in HALF_OPEN before getting a real result if it were below or at
  // 30s; 60s gives it room to breathe
  resetTimeout: 6e4
};
class CircuitBreakerExporter {
  constructor(_exporter, config = defaultCircuitBreakerConfiguration, log) {
    this._exporter = _exporter;
    const resolvedConfig = {
      ...defaultCircuitBreakerConfiguration,
      ...config
    };
    this.circuitBreaker = new CircuitBreaker(
      (spans) => new Promise((resolve, reject) => {
        this._exporter.export(spans, (result) => {
          if (result.error) {
            reject(result.error);
          } else {
            resolve(result);
          }
        });
      }),
      {
        // the retrying transport in OTEL already respects the BatchSpanProcessor's exportTimeoutMillis
        // deadline (30s), so opossum's own timeout would fire unnecessarely (or mid-retry), causing the retry
        // backoff setTimeouts to keep running in the background and hitting the collector even
        // after opossum has given up on the fire() — defeating the circuit breaker entirely.
        timeout: false,
        // one full failed export cycle takes ~13s: 5s (scheduledDelayMillis) + ~8s (retry backoffs).
        // to have all 3 failures (volumeThreshold) visible in the rolling window simultaneously, it
        // needs to cover at least 3 cycles: 3 × 13s = ~39s. 60s gives comfortable headroom.
        rollingCountTimeout: 6e4,
        ...resolvedConfig
      }
    );
    if (log) {
      log.info(resolvedConfig, "Circuit breaker span exporter configured");
      this.circuitBreaker.on("open", () => {
        const stats = this.circuitBreaker.stats;
        log.error(
          {
            state: "open",
            resetTimeout: resolvedConfig.resetTimeout,
            errorThresholdPercentage: resolvedConfig.errorThresholdPercentage,
            volumeThreshold: resolvedConfig.volumeThreshold,
            stats: {
              fires: stats.fires,
              failures: stats.failures,
              successes: stats.successes,
              rejects: stats.rejects,
              timeouts: stats.timeouts,
              latencyMean: stats.latencyMean
            }
          },
          "Circuit breaker opened: span export failures exceeded threshold, dropping spans until circuit resets"
        );
      });
      this.circuitBreaker.on("halfOpen", (resetTimeout) => {
        const stats = this.circuitBreaker.stats;
        log.warn(
          {
            state: "halfOpen",
            resetTimeout,
            stats: {
              fires: stats.fires,
              failures: stats.failures,
              successes: stats.successes,
              rejects: stats.rejects,
              timeouts: stats.timeouts,
              latencyMean: stats.latencyMean
            }
          },
          "Circuit breaker half-open: probing span exporter before closing"
        );
      });
      this.circuitBreaker.on("close", () => {
        const stats = this.circuitBreaker.stats;
        log.info(
          {
            state: "closed",
            stats: {
              fires: stats.fires,
              failures: stats.failures,
              successes: stats.successes,
              rejects: stats.rejects,
              timeouts: stats.timeouts,
              latencyMean: stats.latencyMean
            }
          },
          "Circuit breaker closed: span exporter recovered, resuming normal export"
        );
      });
      this.circuitBreaker.on("failure", (error, latencyMs) => {
        log.warn(
          {
            state: this.circuitBreaker.opened ? "open" : this.circuitBreaker.pendingClose ? "halfOpen" : "closed",
            latencyMs,
            error
          },
          "Circuit breaker recorded a span export failure"
        );
      });
      this.circuitBreaker.on("reject", () => {
        log.debug(
          { state: "open" },
          "Circuit breaker rejected span export: circuit is open"
        );
      });
    }
    if (this._exporter.forceFlush) {
      this.forceFlush = () => this._exporter.forceFlush();
    }
  }
  circuitBreaker;
  export(spans, resultCallback) {
    this.circuitBreaker.fire(spans).then(resultCallback).catch((error) => {
      if (error?.code === "EOPENBREAKER") {
        return resultCallback({ code: ExportResultCode.SUCCESS });
      }
      return resultCallback({ code: ExportResultCode.FAILED, error });
    });
  }
  forceFlush;
  shutdown() {
    this.circuitBreaker.shutdown();
    return this._exporter.shutdown();
  }
}

export { CircuitBreakerExporter as C, getEnvStr as a, diagLogLevelFromEnv as d, getEnvBool as g, otelCtxForRequestId as o, useOpenTelemetry as u };