UNPKG

autotel

Version:
1,792 lines 57.8 kB
import { createTraceContext, getContextStorage, getActiveContextWithBaggage } from './chunk-SR35DG5A.js';
import { setSpanName } from './chunk-B3ZHLLMP.js';
import { validateEvent, runInOperationContext } from './chunk-WD4RP6IV.js';
import { getOrCreateCorrelationId } from './chunk-USSL3D6L.js';
import { getValidationConfig, isInitialized, warnIfNotInitialized, getConfig as getConfig$1, getEventsConfig, getLogger, getSdk } from './chunk-7RV6L24E.js';
import { AlwaysSampler } from './chunk-XND7WBVX.js';
import { getConfig } from './chunk-J5QENANM.js';
import { trace, SpanStatusCode, context, propagation } from '@opentelemetry/api';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';

// src/rate-limiter.ts
var TokenBucketRateLimiter = class {
  tokens;
  maxTokens;
  refillRate;
  // tokens per millisecond
  lastRefill;
  constructor(config) {
    this.maxTokens = config.burstCapacity || config.maxEventsPerSecond * 2;
    this.tokens = this.maxTokens;
    this.refillRate = config.maxEventsPerSecond / 1e3;
    this.lastRefill = Date.now();
  }
  /**
   * Try to consume a token (allow an event)
   * Returns true if allowed, false if rate limit exceeded
   */
  tryConsume(count = 1) {
    this.refill();
    if (this.tokens >= count) {
      this.tokens -= count;
      return true;
    }
    return false;
  }
  /**
   * Wait until a token is available (async rate limiting)
   * Returns a promise that resolves when the event can be processed
   */
  async waitForToken(count = 1) {
    this.refill();
    if (this.tokens >= count) {
      this.tokens -= count;
      return;
    }
    const tokensNeeded = count - this.tokens;
    const waitMs = Math.ceil(tokensNeeded / this.refillRate);
    await new Promise((resolve) => setTimeout(resolve, waitMs));
    return this.waitForToken(count);
  }
  /**
   * Refill tokens based on elapsed time
   */
  refill() {
    const now = Date.now();
    const elapsed = now - this.lastRefill;
    const tokensToAdd = elapsed * this.refillRate;
    this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
    this.lastRefill = now;
  }
  /**
   * Get current available tokens (for testing/debugging)
   */
  getAvailableTokens() {
    this.refill();
    return Math.floor(this.tokens);
  }
  /**
   * Reset the rate limiter (for testing)
   */
  reset() {
    this.tokens = this.maxTokens;
    this.lastRefill = Date.now();
  }
};

// src/event-queue.ts
var DEFAULT_CONFIG = {
  maxSize: 5e4,
  batchSize: 100,
  flushInterval: 1e4,
  maxRetries: 3,
  rateLimit: {
    maxEventsPerSecond: 100,
    burstCapacity: 200
  }
};
function getSubscriberName(subscriber) {
  if (subscriber.name) {
    return subscriber.name.toLowerCase();
  }
  const className = subscriber.constructor?.name || "unknown";
  return className.replace(/Subscriber$/i, "").toLowerCase();
}
var EventQueue = class {
  queue = [];
  flushTimer = null;
  config;
  subscribers;
  rateLimiter;
  flushPromise = null;
  isShuttingDown = false;
  // Metrics
  metrics = null;
  // Observable callback cleanup functions
  observableCleanups = [];
  // Subscriber health tracking (for observable gauges)
  subscriberHealthy = /* @__PURE__ */ new Map();
  constructor(subscribers, config) {
    this.subscribers = subscribers;
    this.config = { ...DEFAULT_CONFIG, ...config };
    this.rateLimiter = this.config.rateLimit ? new TokenBucketRateLimiter(this.config.rateLimit) : null;
    for (const subscriber of subscribers) {
      const name = getSubscriberName(subscriber);
      this.subscriberHealthy.set(name, true);
    }
    this.initMetrics();
  }
  /**
   * Initialize OTel metrics for queue observability
   */
  initMetrics() {
    const runtimeConfig = getConfig();
    const meter = runtimeConfig.meter;
    const queueSize = meter.createObservableGauge(
      "autotel.event_delivery.queue.size",
      {
        description: "Current number of events in the delivery queue",
        unit: "count"
      }
    );
    const queueSizeCallback = (observableResult) => {
      observableResult.observe(this.queue.length);
    };
    queueSize.addCallback(queueSizeCallback);
    this.observableCleanups.push(
      () => queueSize.removeCallback(queueSizeCallback)
    );
    const oldestAge = meter.createObservableGauge(
      "autotel.event_delivery.queue.oldest_age_ms",
      {
        description: "Age of the oldest event in the queue in milliseconds",
        unit: "ms"
      }
    );
    const oldestAgeCallback = (observableResult) => {
      if (this.queue.length > 0) {
        const oldest = this.queue[0];
        const ageMs = Date.now() - oldest.timestamp;
        observableResult.observe(ageMs);
      } else {
        observableResult.observe(0);
      }
    };
    oldestAge.addCallback(oldestAgeCallback);
    this.observableCleanups.push(
      () => oldestAge.removeCallback(oldestAgeCallback)
    );
    const delivered = meter.createCounter(
      "autotel.event_delivery.queue.delivered",
      {
        description: "Number of events successfully delivered to subscribers",
        unit: "count"
      }
    );
    const failed = meter.createCounter("autotel.event_delivery.queue.failed", {
      description: "Number of events that failed delivery after all retry attempts",
      unit: "count"
    });
    const dropped = meter.createCounter(
      "autotel.event_delivery.queue.dropped",
      {
        description: "Number of events dropped from the queue",
        unit: "count"
      }
    );
    const latency = meter.createHistogram(
      "autotel.event_delivery.queue.latency_ms",
      {
        description: "Event delivery latency from enqueue to successful send",
        unit: "ms"
      }
    );
    const subscriberHealth = meter.createObservableGauge(
      "autotel.event_delivery.subscriber.health",
      {
        description: "Subscriber health status (1=healthy, 0=unhealthy)",
        unit: "1"
      }
    );
    const subscriberHealthCallback = (observableResult) => {
      for (const [subscriberName, isHealthy] of this.subscriberHealthy) {
        observableResult.observe(isHealthy ? 1 : 0, {
          subscriber: subscriberName
        });
      }
    };
    subscriberHealth.addCallback(subscriberHealthCallback);
    this.observableCleanups.push(
      () => subscriberHealth.removeCallback(subscriberHealthCallback)
    );
    this.metrics = {
      queueSize,
      oldestAge,
      delivered,
      failed,
      dropped,
      latency,
      subscriberHealth
    };
  }
  /**
   * Record a dropped event with reason and emit debug breadcrumb
   */
  recordDropped(reason, event, subscriberName) {
    const attrs = { reason };
    if (subscriberName) {
      attrs.subscriber = subscriberName;
    }
    this.metrics?.dropped.add(1, attrs);
    const logLevel = reason === "payload_invalid" ? "error" : "warn";
    const logger = getLogger();
    if (logLevel === "error") {
      logger.error(
        {
          eventName: event?.name,
          subscriber: subscriberName,
          reason,
          correlationId: event?._correlationId,
          traceId: event?._traceId
        },
        `[autotel] Event dropped: ${reason}`
      );
    } else {
      logger.warn(
        {
          eventName: event?.name,
          subscriber: subscriberName,
          reason,
          correlationId: event?._correlationId,
          traceId: event?._traceId
        },
        `[autotel] Event dropped: ${reason}`
      );
    }
  }
  /**
   * Record permanent delivery failure (after all retries exhausted)
   * Increments failed counter and logs error
   */
  recordFailed(event, subscriberName, error) {
    this.metrics?.failed.add(1, { subscriber: subscriberName });
    this.subscriberHealthy.set(subscriberName, false);
    getLogger().error(
      {
        eventName: event.name,
        subscriber: subscriberName,
        correlationId: event._correlationId,
        traceId: event._traceId,
        err: error
      },
      `[autotel] Event delivery failed after all retries`
    );
  }
  /**
   * Mark subscriber as unhealthy on transient failure (without incrementing failed counter)
   * Used during retry attempts - only recordFailed should increment the counter
   */
  markSubscriberUnhealthy(subscriberName) {
    this.subscriberHealthy.set(subscriberName, false);
  }
  /**
   * Record successful delivery
   */
  recordDelivered(event, subscriberName, startTime) {
    const latencyMs = Date.now() - startTime;
    this.metrics?.delivered.add(1, { subscriber: subscriberName });
    this.metrics?.latency.record(latencyMs, { subscriber: subscriberName });
    this.subscriberHealthy.set(subscriberName, true);
  }
  /**
   * Enqueue an event for sending
   *
   * Backpressure policy:
   * - Drops oldest event and logs warning if queue is full (same behavior in all environments)
   */
  enqueue(event) {
    if (this.isShuttingDown) {
      this.recordDropped("shutdown", event);
      return;
    }
    if (this.queue.length >= this.config.maxSize) {
      const droppedEvent = this.queue.shift();
      this.recordDropped("rate_limit", droppedEvent);
      getLogger().warn(
        {
          droppedEvent: droppedEvent?.name
        },
        `[autotel] Events queue full (${this.config.maxSize} events). Dropping oldest event. Events are being produced faster than they can be sent. Check your subscribers or reduce tracking frequency.`
      );
    }
    const enrichedEvent = {
      ...event,
      _correlationId: event._correlationId || getOrCreateCorrelationId()
    };
    this.queue.push(enrichedEvent);
    this.scheduleBatchFlush();
  }
  /**
   * Schedule a batch flush if not already scheduled
   */
  scheduleBatchFlush() {
    if (this.flushTimer || this.flushPromise) return;
    this.flushTimer = setTimeout(() => {
      this.flushTimer = null;
      void this.flushBatch();
    }, this.config.flushInterval);
  }
  /**
   * Flush a batch of events
   * Uses promise-based concurrency control to prevent race conditions
   */
  async flushBatch() {
    if (this.queue.length === 0) return;
    if (this.flushPromise) {
      await this.flushPromise;
      return;
    }
    this.flushPromise = this.doFlushBatch();
    try {
      await this.flushPromise;
    } finally {
      this.flushPromise = null;
      if (this.queue.length > 0) {
        this.scheduleBatchFlush();
      }
    }
  }
  /**
   * Internal flush implementation
   */
  async doFlushBatch() {
    const batch = this.queue.splice(0, this.config.batchSize);
    await this.sendWithRetry(batch, this.config.maxRetries);
  }
  /**
   * Send events with exponential backoff retry
   * Tracks per-event, per-subscriber failures so failed counter reflects actual failed deliveries.
   * On retry, only failed (event, subscriber) pairs are re-sent to avoid double-counting delivered.
   */
  async sendWithRetry(events, retriesLeft, subscribersByEventIndex) {
    const failedDeliveries = await this.sendToSubscribers(
      events,
      subscribersByEventIndex
    );
    if (failedDeliveries.length > 0) {
      if (retriesLeft > 0) {
        const failedEventIndices = new Set(
          failedDeliveries.map((f) => f.eventIndex)
        );
        const failedEventIndicesOrdered = [...failedEventIndices].sort(
          (a, b) => a - b
        );
        const eventsToRetry = failedEventIndicesOrdered.map(
          (i) => events[i]
        );
        const failedSubscribersByRetryIndex = /* @__PURE__ */ new Map();
        for (let j = 0; j < failedEventIndicesOrdered.length; j++) {
          const origIndex = failedEventIndicesOrdered[j];
          const set = /* @__PURE__ */ new Set();
          for (const { eventIndex, subscriberName } of failedDeliveries) {
            if (eventIndex === origIndex) set.add(subscriberName);
          }
          failedSubscribersByRetryIndex.set(j, set);
        }
        const delay = Math.pow(2, this.config.maxRetries - retriesLeft) * 1e3;
        await new Promise((resolve) => setTimeout(resolve, delay));
        return this.sendWithRetry(
          eventsToRetry,
          retriesLeft - 1,
          failedSubscribersByRetryIndex
        );
      } else {
        for (const { eventIndex, subscriberName, error } of failedDeliveries) {
          const event = events[eventIndex];
          if (event) this.recordFailed(event, subscriberName, error);
        }
        const failedSubscriberNames = [
          ...new Set(failedDeliveries.map((f) => f.subscriberName))
        ];
        getLogger().error(
          {
            failedSubscribers: failedSubscriberNames,
            retriesAttempted: this.config.maxRetries
          },
          "[autotel] Failed to send events after retries"
        );
      }
    }
  }
  /**
   * Send events to configured subscribers with rate limiting and metrics.
   * When subscribersByEventIndex is provided (retry path), only those subscribers are tried per event.
   * Returns per-event, per-subscriber failures (empty if all succeeded).
   */
  async sendToSubscribers(events, subscribersByEventIndex) {
    const failedDeliveries = [];
    const sendOne = async (event, eventIndex) => {
      const subscriberNames = subscribersByEventIndex?.get(eventIndex);
      const failures = await this.sendEventToSubscribers(
        event,
        subscriberNames ?? void 0
      );
      for (const failure of failures) {
        failedDeliveries.push({
          eventIndex,
          subscriberName: failure.subscriberName,
          error: failure.error
        });
      }
    };
    if (!this.rateLimiter) {
      for (let i = 0; i < events.length; i++) {
        const event = events[i];
        if (event) await sendOne(event, i);
      }
      return failedDeliveries;
    }
    for (let i = 0; i < events.length; i++) {
      await this.rateLimiter.waitForToken();
      const event = events[i];
      if (event) await sendOne(event, i);
    }
    return failedDeliveries;
  }
  /**
   * Send a single event to subscribers.
   * - When subscriberNames is undefined (initial attempt): send to all subscribers.
   * - When subscriberNames is provided (retry): send only to those subscribers (never re-send to healthy ones).
   * Returns list of subscribers that failed (empty if all succeeded).
   */
  async sendEventToSubscribers(event, subscriberNames) {
    const startTime = event.timestamp;
    const failures = [];
    const subscribersToTry = subscriberNames === void 0 ? this.subscribers : this.subscribers.filter(
      (s) => subscriberNames.has(getSubscriberName(s))
    );
    const results = await Promise.allSettled(
      subscribersToTry.map(async (subscriber) => {
        const subscriberName = getSubscriberName(subscriber);
        try {
          await subscriber.trackEvent(event.name, event.attributes, {
            autotel: event.autotel
          });
          this.recordDelivered(event, subscriberName, startTime);
          return { subscriberName, success: true };
        } catch (error) {
          this.markSubscriberUnhealthy(subscriberName);
          return {
            subscriberName,
            success: false,
            error: error instanceof Error ? error : void 0
          };
        }
      })
    );
    for (const result of results) {
      if (result.status === "fulfilled" && !result.value.success) {
        failures.push({
          subscriberName: result.value.subscriberName,
          error: result.value.error
        });
      }
    }
    return failures;
  }
  /**
   * Flush all remaining events. Queue remains usable after flush (e.g. for
   * auto-flush at root span end). Use shutdown() when tearing down the queue.
   */
  async flush() {
    if (this.flushTimer) {
      clearTimeout(this.flushTimer);
      this.flushTimer = null;
    }
    if (this.flushPromise) {
      await this.flushPromise;
    }
    while (this.queue.length > 0) {
      await this.doFlushBatch();
    }
  }
  /**
   * Flush remaining events and permanently disable the queue (reject new events).
   * Use for process/SDK shutdown; use flush() for periodic or span-end drain.
   */
  async shutdown() {
    this.isShuttingDown = true;
    await this.flush();
  }
  /**
   * Cleanup observable metric callbacks to prevent memory leaks
   * Call this when destroying the EventQueue instance
   */
  cleanup() {
    for (const cleanupFn of this.observableCleanups) {
      try {
        cleanupFn();
      } catch {
      }
    }
    this.observableCleanups = [];
  }
  /**
   * Get queue size (for testing/debugging)
   */
  size() {
    return this.queue.length;
  }
  /**
   * Get subscriber health status (for testing/debugging)
   */
  getSubscriberHealth() {
    return new Map(this.subscriberHealthy);
  }
  /**
   * Check if a specific subscriber is healthy
   */
  isSubscriberHealthy(subscriberName) {
    return this.subscriberHealthy.get(subscriberName.toLowerCase()) ?? true;
  }
  /**
   * Manually mark a subscriber as healthy or unhealthy
   * (used for circuit breaker integration)
   */
  setSubscriberHealth(subscriberName, healthy) {
    this.subscriberHealthy.set(subscriberName.toLowerCase(), healthy);
  }
};

// src/track.ts
var eventsQueue = null;
function buildAutotelContext(span2) {
  const eventsConfig = getEventsConfig();
  const config = getConfig$1();
  const correlationId = getOrCreateCorrelationId();
  if (!eventsConfig?.includeTraceContext) {
    return {
      correlation_id: correlationId
    };
  }
  const autotelContext = {
    correlation_id: correlationId
  };
  const spanContext = span2?.spanContext();
  if (spanContext) {
    autotelContext.trace_id = spanContext.traceId;
    autotelContext.span_id = spanContext.spanId;
    autotelContext.trace_flags = spanContext.traceFlags.toString(16).padStart(2, "0");
    const traceState = spanContext.traceState;
    if (traceState) {
      try {
        if (typeof traceState.serialize === "function") {
          const traceStateStr = traceState.serialize();
          if (traceStateStr) {
            autotelContext.trace_state = traceStateStr;
          }
        }
      } catch {
      }
    }
    if (eventsConfig.traceUrl && config) {
      const traceUrl = eventsConfig.traceUrl({
        traceId: spanContext.traceId,
        spanId: spanContext.spanId,
        correlationId,
        serviceName: config.service,
        environment: config.environment
      });
      if (traceUrl) {
        autotelContext.trace_url = traceUrl;
      }
    }
  } else {
    if (eventsConfig.traceUrl && config) {
      const traceUrl = eventsConfig.traceUrl({
        correlationId,
        serviceName: config.service,
        environment: config.environment
      });
      if (traceUrl) {
        autotelContext.trace_url = traceUrl;
      }
    }
  }
  return autotelContext;
}
function getOrCreateQueue() {
  if (!isInitialized()) {
    warnIfNotInitialized("track()");
    return null;
  }
  if (!eventsQueue) {
    const config = getConfig$1();
    if (!config?.subscribers || config.subscribers.length === 0) {
      return null;
    }
    eventsQueue = new EventQueue(config.subscribers);
  }
  return eventsQueue;
}
function track(event, data) {
  const queue = getOrCreateQueue();
  if (!queue) return;
  const validationConfig = getValidationConfig();
  const validated = validateEvent(event, data, validationConfig || void 0);
  const span2 = trace.getActiveSpan();
  const enrichedData = span2 ? {
    ...validated.attributes,
    traceId: span2.spanContext().traceId,
    spanId: span2.spanContext().spanId
  } : validated.attributes;
  const autotelContext = buildAutotelContext(span2);
  queue.enqueue({
    name: validated.eventName,
    attributes: enrichedData,
    timestamp: Date.now(),
    autotel: autotelContext
  });
}
function getEventQueue() {
  return eventsQueue;
}
function resetEventQueue() {
  eventsQueue = null;
}
var inferenceCache = /* @__PURE__ */ new Map();
var MAX_CACHE_SIZE = 50;
function captureStackTrace() {
  const originalStackTraceLimit = Error.stackTraceLimit;
  Error.stackTraceLimit = 10;
  const err = new Error("Stack trace capture");
  const stack = err.stack || "";
  Error.stackTraceLimit = originalStackTraceLimit;
  return stack;
}
function parseCallLocation(stack) {
  const lines = stack.split("\n");
  let skippedExternalFrame = false;
  for (const line of lines) {
    if (line.includes("variable-name-inference.ts") || line.includes("variable-name-inference.js") || line.includes("functional.ts") || line.includes("functional.js")) {
      continue;
    }
    const match = line.match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/) || line.match(/^.*?([^:]+):(\d+):(\d+)/);
    if (match) {
      let filePath = match[1].trim();
      if (filePath.startsWith("file://")) {
        try {
          filePath = fileURLToPath(filePath);
        } catch {
          continue;
        }
      }
      if (!skippedExternalFrame) {
        skippedExternalFrame = true;
        continue;
      }
      return {
        file: filePath,
        line: Number.parseInt(match[2], 10),
        column: Number.parseInt(match[3], 10)
      };
    }
  }
  return void 0;
}
function readSourceLine(filePath, lineNumber) {
  try {
    if (typeof readFileSync !== "function") {
      return void 0;
    }
    const content = readFileSync(filePath, "utf8");
    const lines = content.split("\n");
    return lines[lineNumber - 1];
  } catch {
    return void 0;
  }
}
function extractVariableName(sourceLine) {
  const trimmed = sourceLine.trim();
  const patterns = [
    // export const varName = anyFunction(
    /export\s+const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/,
    // const varName = anyFunction(
    /const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/,
    // export let varName = anyFunction(
    /export\s+let\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/,
    // let varName = anyFunction(
    /let\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/,
    // export var varName = anyFunction(
    /export\s+var\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/,
    // var varName = anyFunction(
    /var\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/
  ];
  for (const pattern of patterns) {
    const match = trimmed.match(pattern);
    if (match && match[1]) {
      return match[1];
    }
  }
  return void 0;
}
function cacheInference(key, value) {
  if (inferenceCache.size >= MAX_CACHE_SIZE) {
    const firstKey = inferenceCache.keys().next().value;
    if (firstKey) {
      inferenceCache.delete(firstKey);
    }
  }
  inferenceCache.set(key, value);
}
function inferVariableNameFromCallStack() {
  try {
    const stack = captureStackTrace();
    const callLocation = parseCallLocation(stack);
    if (!callLocation) {
      return void 0;
    }
    const cacheKey = `${callLocation.file}:${callLocation.line}`;
    if (inferenceCache.has(cacheKey)) {
      return inferenceCache.get(cacheKey);
    }
    const sourceLine = readSourceLine(callLocation.file, callLocation.line);
    if (!sourceLine) {
      return void 0;
    }
    const variableName = extractVariableName(sourceLine);
    cacheInference(cacheKey, variableName);
    return variableName;
  } catch {
    return void 0;
  }
}

// src/functional.ts
var FACTORY_NAME_HINTS = /* @__PURE__ */ new Set([
  "ctx",
  "_ctx",
  "context",
  "tracecontext",
  "tracectx"
]);
var TRACE_FACTORY_SET = /* @__PURE__ */ new WeakSet();
var SINGLE_LINE_COMMENT_REGEX = /\/\/.*$/gm;
var MULTI_LINE_COMMENT_REGEX = /\/\*[\s\S]*?\*\//gm;
var PARAM_TOKEN_SANITIZE_REGEX = new RegExp(String.raw`[{}\[\]\s]`, "g");
function markAsTraceFactory(fn) {
  TRACE_FACTORY_SET.add(fn);
}
function hasFactoryMark(fn) {
  return TRACE_FACTORY_SET.has(fn);
}
function sanitizeParameterToken(token) {
  const [firstToken] = token.split("=");
  return (firstToken ?? "").replaceAll(PARAM_TOKEN_SANITIZE_REGEX, "").trim();
}
function getFirstParameterToken(fn) {
  let source = Function.prototype.toString.call(fn);
  source = source.replaceAll(MULTI_LINE_COMMENT_REGEX, "").replaceAll(SINGLE_LINE_COMMENT_REGEX, "").trim();
  const arrowMatch = source.match(
    /^(?:async\s*)?(?:\(([^)]*)\)|([^=()]+))\s*=>/
  );
  if (arrowMatch) {
    const params = (arrowMatch[1] ?? arrowMatch[2] ?? "").split(",");
    const first = params[0]?.trim();
    if (first) {
      return sanitizeParameterToken(first);
    }
    return null;
  }
  const functionMatch = source.match(/^[^(]*\(([^)]*)\)/);
  if (functionMatch) {
    const params = functionMatch[1]?.split(",");
    const first = params?.[0]?.trim();
    if (first) {
      return sanitizeParameterToken(first);
    }
  }
  return null;
}
function looksLikeTraceFactory(fn) {
  if (hasFactoryMark(fn)) {
    return true;
  }
  if (fn.length === 0) {
    if (!isAsyncFunction(fn)) {
      try {
        const result = fn();
        return typeof result === "function";
      } catch {
        return false;
      }
    }
    return false;
  }
  const firstParam = getFirstParameterToken(fn);
  if (!firstParam) {
    return false;
  }
  const normalized = firstParam.toLowerCase();
  if (FACTORY_NAME_HINTS.has(normalized) || normalized.startsWith("ctx") || normalized.startsWith("_ctx") || normalized.startsWith("trace") || normalized.endsWith("ctx") || // Match baseCtx, spanCtx, etc.
  normalized.includes("context")) {
    return true;
  }
  return false;
}
function isFactoryReturningFunction(fnWithCtx) {
  if (isAsyncFunction(fnWithCtx)) {
    return false;
  }
  try {
    const result = fnWithCtx(createDummyCtx());
    return typeof result === "function";
  } catch {
    return false;
  }
}
function isTraceFactoryFunction(fn) {
  if (typeof fn !== "function") {
    return false;
  }
  if (hasFactoryMark(fn)) {
    return true;
  }
  if (looksLikeTraceFactory(fn)) {
    markAsTraceFactory(fn);
    return true;
  }
  return false;
}
function ensureTraceFactory(fnOrFactory) {
  if (isTraceFactoryFunction(fnOrFactory)) {
    return fnOrFactory;
  }
  const plainFn = fnOrFactory;
  const factory = (ctx2) => {
    return plainFn;
  };
  markAsTraceFactory(factory);
  return factory;
}
function wrapFactoryWithTracing(fnOrFactory, options, variableName) {
  const factory = ensureTraceFactory(fnOrFactory);
  const sampleFn = factory(createDummyCtx());
  const innerFunctionName = inferFunctionName(
    sampleFn
  );
  const callStackVariableName = innerFunctionName ? void 0 : inferVariableNameFromCallStack();
  const factoryName = inferFunctionName(factory);
  const effectiveVariableName = variableName || innerFunctionName || callStackVariableName || factoryName;
  const useAsyncWrapper = isAsyncFunction(sampleFn);
  if (useAsyncWrapper) {
    return wrapWithTracing(
      factory,
      options,
      effectiveVariableName
    );
  }
  return wrapWithTracingSync(
    factory,
    options,
    effectiveVariableName
  );
}
var MAX_ERROR_MESSAGE_LENGTH = 500;
function createDummyCtx() {
  return {
    traceId: "",
    spanId: "",
    correlationId: "",
    setAttribute: () => {
    },
    setAttributes: () => {
    },
    setStatus: () => {
    },
    recordException: () => {
    },
    addEvent: () => {
    },
    addLink: () => {
    },
    addLinks: () => {
    },
    updateName: () => {
    },
    isRecording: () => false,
    getBaggage: () => {
    },
    setBaggage: () => "",
    deleteBaggage: () => {
    },
    getAllBaggage: () => /* @__PURE__ */ new Map()
  };
}
function isAsyncFunction(fn) {
  return typeof fn === "function" && fn.constructor?.name === "AsyncFunction";
}
var INSTRUMENTED_SYMBOL = /* @__PURE__ */ Symbol.for("autotel.functional.instrumented");
function hasInstrumentationFlag(value) {
  return (typeof value === "function" || typeof value === "object") && value !== null && Boolean(value[INSTRUMENTED_SYMBOL]);
}
function truncateErrorMessage(message) {
  if (message.length <= MAX_ERROR_MESSAGE_LENGTH) {
    return message;
  }
  return `${message.slice(0, MAX_ERROR_MESSAGE_LENGTH)}... (truncated)`;
}
function inferFunctionName(fn) {
  const displayName = fn.displayName;
  if (displayName) {
    return displayName;
  }
  if (fn.name && fn.name !== "anonymous" && fn.name !== "") {
    return fn.name;
  }
  const source = Function.prototype.toString.call(fn);
  const match = source.match(/function\s+([^(\s]+)/);
  if (match && match[1] && match[1] !== "anonymous") {
    return match[1];
  }
  return void 0;
}
function getSpanName(options, fn, variableName) {
  if (options.name) {
    return options.name;
  }
  let fnName = variableName || inferFunctionName(fn);
  fnName = fnName || "anonymous";
  if (options.serviceName) {
    return `${options.serviceName}.${fnName}`;
  }
  if (fnName && fnName !== "anonymous") {
    return fnName;
  }
  return "unknown";
}
function shouldSkip(key, fn, skip) {
  if (key.startsWith("_")) {
    return true;
  }
  if (!skip || skip.length === 0) {
    return false;
  }
  for (const rule of skip) {
    if (typeof rule === "string" && key === rule) {
      return true;
    } else if (rule instanceof RegExp && rule.test(key)) {
      return true;
    } else if (typeof rule === "function" && rule(key, fn)) {
      return true;
    }
  }
  return false;
}
function getCtxValue() {
  const activeSpan = trace.getActiveSpan();
  if (!activeSpan) return null;
  return createTraceContext(activeSpan);
}
var ctx = new Proxy(
  {},
  {
    get(_target, prop) {
      const ctxValue = getCtxValue();
      if (!ctxValue) {
        return;
      }
      return ctxValue[prop];
    },
    has(_target, prop) {
      const ctxValue = getCtxValue();
      if (!ctxValue) {
        return false;
      }
      return prop in ctxValue;
    },
    ownKeys() {
      const ctxValue = getCtxValue();
      if (!ctxValue) {
        return [];
      }
      return Object.keys(ctxValue);
    },
    getOwnPropertyDescriptor(_target, prop) {
      const ctxValue = getCtxValue();
      if (!ctxValue) {
        return;
      }
      return Object.getOwnPropertyDescriptor(ctxValue, prop);
    }
  }
);
function wrapWithTracing(fnFactory, options, variableName) {
  if (hasInstrumentationFlag(fnFactory)) ;
  const config = getConfig();
  const tracer = config.tracer;
  const meter = config.meter;
  const sampler = options.sampler || new AlwaysSampler();
  const tempFn = fnFactory(createDummyCtx());
  const spanName = getSpanName(options, tempFn, variableName);
  const callCounter = options.withMetrics ? meter.createCounter(`${spanName}.calls`, {
    description: `Call count for ${spanName}`,
    unit: "1"
  }) : void 0;
  const durationHistogram = options.withMetrics ? meter.createHistogram(`${spanName}.duration`, {
    description: `Duration for ${spanName}`,
    unit: "ms"
  }) : void 0;
  const wrappedFunction = async function wrappedFunction2(...args) {
    const samplingContext = {
      operationName: spanName,
      args,
      metadata: {}
    };
    const shouldSample = sampler.shouldSample(samplingContext);
    const needsTailSampling = "needsTailSampling" in sampler && typeof sampler.needsTailSampling === "function" ? sampler.needsTailSampling() : false;
    if (!shouldSample && !needsTailSampling) {
      const fn = fnFactory(createDummyCtx());
      return await fn.call(this, ...args);
    }
    const startTime = performance.now();
    const isRootSpan = options.startNewRoot || trace.getActiveSpan() === void 0;
    const shouldAutoFlush = options.flushOnRootSpanEnd ?? getConfig$1()?.flushOnRootSpanEnd ?? true;
    const shouldAutoFlushSpans = getConfig$1()?.forceFlushOnShutdown ?? false;
    const flushIfNeeded = async () => {
      if (!shouldAutoFlush || !isRootSpan) return;
      try {
        const queue = getEventQueue();
        if (queue && queue.size() > 0) {
          await queue.flush();
        }
        if (shouldAutoFlushSpans) {
          const sdk = getSdk();
          if (sdk) {
            try {
              const sdkAny = sdk;
              if (typeof sdkAny.getTracerProvider === "function") {
                const tracerProvider = sdkAny.getTracerProvider();
                if (tracerProvider && typeof tracerProvider.forceFlush === "function") {
                  await tracerProvider.forceFlush();
                }
              }
            } catch {
            }
          }
        }
      } catch (error) {
        const initConfig = getConfig$1();
        const logger = initConfig?.logger;
        if (logger?.error) {
          logger.error(
            {
              err: error instanceof Error ? error : void 0
            },
            `[autotel] Auto-flush failed${error instanceof Error ? "" : `: ${String(error)}`}`
          );
        }
      }
    };
    const spanOptions = {};
    if (options.startNewRoot) {
      spanOptions.root = true;
    }
    if (options.spanKind !== void 0) {
      spanOptions.kind = options.spanKind;
    }
    return tracer.startActiveSpan(spanName, spanOptions, async (span2) => {
      return runInOperationContext(spanName, async () => {
        let shouldKeepSpan = true;
        setSpanName(span2, spanName);
        const initialContext = context.active();
        const contextStorage = getContextStorage();
        if (!contextStorage.getStore()) {
          contextStorage.enterWith(initialContext);
        }
        const ctxValue = createTraceContext(span2);
        const fn = fnFactory(ctxValue);
        const argsAttributes = options.attributesFromArgs ? options.attributesFromArgs(args) : {};
        const handleTailSampling = (success, duration, error) => {
          if (needsTailSampling && "shouldKeepTrace" in sampler && typeof sampler.shouldKeepTrace === "function") {
            shouldKeepSpan = sampler.shouldKeepTrace(samplingContext, {
              success,
              duration,
              error
            });
            span2.setAttribute("sampling.tail.keep", shouldKeepSpan);
            span2.setAttribute("sampling.tail.evaluated", true);
          }
        };
        const onSuccess = async (result) => {
          const duration = performance.now() - startTime;
          callCounter?.add(1, {
            operation: spanName,
            status: "success"
          });
          durationHistogram?.record(duration, {
            operation: spanName,
            status: "success"
          });
          const resultAttributes = options.attributesFromResult ? options.attributesFromResult(result) : {};
          span2.setStatus({ code: SpanStatusCode.OK });
          span2.setAttributes({
            ...argsAttributes,
            ...resultAttributes,
            "operation.name": spanName,
            "code.function": spanName,
            "operation.duration": duration,
            "operation.success": true
          });
          handleTailSampling(true, duration);
          span2.end();
          await flushIfNeeded();
          return result;
        };
        const onError = async (error) => {
          const duration = performance.now() - startTime;
          callCounter?.add(1, {
            operation: spanName,
            status: "error"
          });
          durationHistogram?.record(duration, {
            operation: spanName,
            status: "error"
          });
          const errorMessage = error instanceof Error ? error.message : "Unknown error";
          const truncatedMessage = truncateErrorMessage(errorMessage);
          span2.setStatus({
            code: SpanStatusCode.ERROR,
            message: truncatedMessage
          });
          span2.setAttributes({
            ...argsAttributes,
            "operation.name": spanName,
            "code.function": spanName,
            "operation.duration": duration,
            "operation.success": false,
            error: true,
            "exception.type": error instanceof Error ? error.constructor.name : "Error",
            "exception.message": truncatedMessage
          });
          if (error instanceof Error && error.stack) {
            span2.setAttribute(
              "exception.stack",
              error.stack.slice(0, MAX_ERROR_MESSAGE_LENGTH)
            );
          }
          span2.recordException(
            error instanceof Error ? error : new Error(String(error))
          );
          handleTailSampling(false, duration, error);
          span2.end();
          await flushIfNeeded();
          throw error;
        };
        try {
          callCounter?.add(1, {
            operation: spanName,
            status: "started"
          });
          const executeWithContext = async () => {
            const currentContext = getActiveContextWithBaggage();
            return context.with(currentContext, async () => {
              return fn.call(this, ...args);
            });
          };
          const result = await executeWithContext();
          return await onSuccess(result);
        } catch (error) {
          await onError(error);
          throw error;
        }
      });
    });
  };
  wrappedFunction[INSTRUMENTED_SYMBOL] = true;
  Object.defineProperty(wrappedFunction, "name", {
    value: tempFn.name || "trace",
    configurable: true
  });
  return wrappedFunction;
}
function wrapWithTracingSync(fnFactory, options, variableName) {
  if (hasInstrumentationFlag(fnFactory)) ;
  const config = getConfig();
  const tracer = config.tracer;
  const meter = config.meter;
  const sampler = options.sampler || new AlwaysSampler();
  const tempFn = fnFactory(createDummyCtx());
  const spanName = getSpanName(options, tempFn, variableName);
  const callCounter = options.withMetrics ? meter.createCounter(`${spanName}.calls`, {
    description: `Call count for ${spanName}`,
    unit: "1"
  }) : void 0;
  const durationHistogram = options.withMetrics ? meter.createHistogram(`${spanName}.duration`, {
    description: `Duration for ${spanName}`,
    unit: "ms"
  }) : void 0;
  function wrappedFunction(...args) {
    const samplingContext = {
      operationName: spanName,
      args,
      metadata: {}
    };
    const shouldSample = sampler.shouldSample(samplingContext);
    const needsTailSampling = "needsTailSampling" in sampler && typeof sampler.needsTailSampling === "function" ? sampler.needsTailSampling() : false;
    if (!shouldSample && !needsTailSampling) {
      const fn = fnFactory(createDummyCtx());
      return fn.call(this, ...args);
    }
    const startTime = performance.now();
    const isRootSpan = options.startNewRoot || trace.getActiveSpan() === void 0;
    const shouldAutoFlush = options.flushOnRootSpanEnd ?? getConfig$1()?.flushOnRootSpanEnd ?? true;
    const shouldAutoFlushSpans = getConfig$1()?.forceFlushOnShutdown ?? false;
    const flushIfNeeded = () => {
      if (!shouldAutoFlush || !isRootSpan) return;
      const queue = getEventQueue();
      if (queue && queue.size() > 0) {
        void queue.flush().catch((error) => {
          const initConfig = getConfig$1();
          const logger = initConfig?.logger;
          if (logger?.error) {
            logger.error(
              {
                err: error instanceof Error ? error : void 0
              },
              `[autotel] Auto-flush failed${error instanceof Error ? "" : `: ${String(error)}`}`
            );
          }
        });
      }
      if (shouldAutoFlushSpans) {
        const sdk = getSdk();
        if (sdk) {
          try {
            const sdkAny = sdk;
            if (typeof sdkAny.getTracerProvider === "function") {
              const tracerProvider = sdkAny.getTracerProvider();
              if (tracerProvider && typeof tracerProvider.forceFlush === "function") {
                void tracerProvider.forceFlush().catch((error) => {
                  const initConfig = getConfig$1();
                  const logger = initConfig?.logger;
                  if (logger?.error) {
                    logger.error(
                      {
                        err: error instanceof Error ? error : void 0
                      },
                      `[autotel] Span flush failed${error instanceof Error ? "" : `: ${String(error)}`}`
                    );
                  }
                });
              }
            }
          } catch {
          }
        }
      }
    };
    const spanOptions = {};
    if (options.startNewRoot) {
      spanOptions.root = true;
    }
    if (options.spanKind !== void 0) {
      spanOptions.kind = options.spanKind;
    }
    return tracer.startActiveSpan(spanName, spanOptions, (span2) => {
      return runInOperationContext(spanName, () => {
        let shouldKeepSpan = true;
        setSpanName(span2, spanName);
        const ctxValue = createTraceContext(span2);
        const fn = fnFactory(ctxValue);
        const argsAttributes = options.attributesFromArgs ? options.attributesFromArgs(args) : {};
        const handleTailSampling = (success, duration, error) => {
          if (needsTailSampling && "shouldKeepTrace" in sampler && typeof sampler.shouldKeepTrace === "function") {
            shouldKeepSpan = sampler.shouldKeepTrace(samplingContext, {
              success,
              duration,
              error
            });
            span2.setAttribute("sampling.tail.keep", shouldKeepSpan);
            span2.setAttribute("sampling.tail.evaluated", true);
          }
        };
        const onSuccess = (result) => {
          const duration = performance.now() - startTime;
          callCounter?.add(1, {
            operation: spanName,
            status: "success"
          });
          durationHistogram?.record(duration, {
            operation: spanName,
            status: "success"
          });
          const resultAttributes = options.attributesFromResult ? options.attributesFromResult(result) : {};
          span2.setStatus({ code: SpanStatusCode.OK });
          span2.setAttributes({
            ...argsAttributes,
            ...resultAttributes,
            "operation.name": spanName,
            "code.function": spanName,
            "operation.duration": duration,
            "operation.success": true
          });
          handleTailSampling(true, duration);
          span2.end();
          void flushIfNeeded();
          return result;
        };
        const onError = (error) => {
          const duration = performance.now() - startTime;
          callCounter?.add(1, {
            operation: spanName,
            status: "error"
          });
          durationHistogram?.record(duration, {
            operation: spanName,
            status: "error"
          });
          const errorMessage = error instanceof Error ? error.message : "Unknown error";
          const truncatedMessage = truncateErrorMessage(errorMessage);
          span2.setStatus({
            code: SpanStatusCode.ERROR,
            message: truncatedMessage
          });
          span2.setAttributes({
            ...argsAttributes,
            "operation.name": spanName,
            "code.function": spanName,
            "operation.duration": duration,
            "operation.success": false,
            error: true,
            "exception.type": error instanceof Error ? error.constructor.name : "Error",
            "exception.message": truncatedMessage
          });
          span2.recordException(
            error instanceof Error ? error : new Error(String(error))
          );
          handleTailSampling(false, duration, error);
          span2.end();
          void flushIfNeeded();
          throw error;
        };
        try {
          callCounter?.add(1, {
            operation: spanName,
            status: "started"
          });
          const result = fn.call(this, ...args);
          if (result instanceof Promise) {
            return result.then(onSuccess, onError);
          }
          return onSuccess(result);
        } catch (error) {
          return onError(error);
        }
      });
    });
  }
  wrappedFunction[INSTRUMENTED_SYMBOL] = true;
  Object.defineProperty(wrappedFunction, "name", {
    value: tempFn.name || "trace",
    configurable: true
  });
  return wrappedFunction;
}
function executeImmediately(fn, options) {
  const config = getConfig();
  const tracer = config.tracer;
  const meter = config.meter;
  const sampler = options.sampler || new AlwaysSampler();
  const spanName = options.name || "anonymous";
  const samplingContext = {
    operationName: spanName,
    args: [],
    metadata: {}
  };
  const shouldSample = sampler.shouldSample(samplingContext);
  const needsTailSampling = "needsTailSampling" in sampler && typeof sampler.needsTailSampling === "function" ? sampler.needsTailSampling() : false;
  if (!shouldSample && !needsTailSampling) {
    return fn(createDummyCtx());
  }
  const startTime = performance.now();
  const isRootSpan = options.startNewRoot || trace.getActiveSpan() === void 0;
  const shouldAutoFlush = options.flushOnRootSpanEnd ?? getConfig$1()?.flushOnRootSpanEnd ?? true;
  const shouldAutoFlushSpans = getConfig$1()?.forceFlushOnShutdown ?? false;
  const callCounter = options.withMetrics ? meter.createCounter(`${spanName}.calls`, {
    description: `Call count for ${spanName}`,
    unit: "1"
  }) : void 0;
  const durationHistogram = options.withMetrics ? meter.createHistogram(`${spanName}.duration`, {
    description: `Duration for ${spanName}`,
    unit: "ms"
  }) : void 0;
  const flushIfNeeded = async () => {
    if (!shouldAutoFlush || !isRootSpan) return;
    try {
      const queue = getEventQueue();
      if (queue && queue.size() > 0) {
        await queue.flush();
      }
      if (shouldAutoFlushSpans) {
        const sdk = getSdk();
        if (sdk) {
          try {
            const sdkAny = sdk;
            if (typeof sdkAny.getTracerProvider === "function") {
              const tracerProvider = sdkAny.getTracerProvider();
              if (tracerProvider && typeof tracerProvider.forceFlush === "function") {
                await tracerProvider.forceFlush();
              }
            }
          } catch {
          }
        }
      }
    } catch (error) {
      const initConfig = getConfig$1();
      const logger = initConfig?.logger;
      if (logger?.error) {
        logger.error(
          {
            err: error instanceof Error ? error : void 0
          },
          `[autotel] Auto-flush failed${error instanceof Error ? "" : `: ${String(error)}`}`
        );
      }
    }
  };
  const spanOptions = {};
  if (options.startNewRoot) {
    spanOptions.root = true;
  }
  if (options.spanKind !== void 0) {
    spanOptions.kind = options.spanKind;
  }
  return tracer.startActiveSpan(spanName, spanOptions, (span2) => {
    return runInOperationContext(spanName, () => {
      let shouldKeepSpan = true;
      setSpanName(span2, spanName);
      const ctxValue = createTraceContext(span2);
      const handleTailSampling = (success, duration, error) => {
        if (needsTailSampling && "shouldKeepTrace" in sampler && typeof sampler.shouldKeepTrace === "function") {
          shouldKeepSpan = sampler.shouldKeepTrace(samplingContext, {
            success,
            duration,
            error
          });
          span2.setAttribute("sampling.tail.keep", shouldKeepSpan);
          span2.setAttribute("sampling.tail.evaluated", true);
        }
      };
      const onSuccessSync = (result) => {
        const duration = performance.now() - startTime;
        callCounter?.add(1, {
          operation: spanName,
          status: "success"
        });
        durationHistogram?.record(duration, {
          operation: spanName,
          status: "success"
        });
        span2.setStatus({ code: SpanStatusCode.OK });
        span2.setAttributes({
          "operation.name": spanName,
          "code.function": spanName,
          "operation.duration": duration,
          "operation.success": true
        });
        handleTailSampling(true, duration);
        span2.end();
        void flushIfNeeded();
        return result;
      };
      const onErrorSync = (error) => {
        const duration = performance.now() - startTime;
        callCounter?.add(1, {
          operation: spanName,
          status: "error"
        });
        durationHistogram?.record(duration, {
          operation: spanName,
          status: "error"
        });
        const errorMessage = error instanceof Error ? error.message : "Unknown error";
        const truncatedMessage = truncateErrorMessage(errorMessage);
        span2.setStatus({
          code: SpanStatusCode.ERROR,
          message: truncatedMessage
        });
        span2.setAttributes({
          "operation.name": spanName,
          "code.function": spanName,
          "operation.duration": duration,
          "operation.success": false,
          error: true,
          "exception.type": error instanceof Error ? error.constructor.name : "Error",
          "exception.message": truncatedMessage
        });
        if (error instanceof Error && error.stack) {
          span2.setAttribute(
            "exception.stack",
            error.stack.slice(0, MAX_ERROR_MESSAGE_LENGTH)
          );
        }
        span2.recordException(
          error instanceof Error ? error : new Error(String(error))
        );
        handleTailSampling(false, duration, error);
        span2.end();
        void flushIfNeeded();
        throw error;
      };
      const onSuccessAsync = async (result) => {
        const duration = performance.now() - startTime;
        callCounter?.add(1, {
          operation: spanName,
          status: "success"
        });
        durationHistogram?.record(duration, {
          operation: spanName,
          status: "success"
        });
        span2.setStatus({ code: SpanStatusCode.OK });
        span2.setAttributes({
          "operation.name": spanName,
          "code.function": spanName,
          "operation.duration": duration,
          "operation.success": true
        });
        handleTailSampling(true, duration);
        span2.end();
        await flushIfNeeded();
        return result;
      };
      const onErrorAsync = async (error) => {
        const duration = performance.now() - startTime;
        callCounter?.add(1, {
          operation: spanName,
          status: "error"
        });
        durationHistogram?.record(duration, {
          operation: spanName,
          status: "error"
        });
        const errorMessage = error instanceof Error ? error.message : "Unknown error";
        const truncatedMessage = truncateErrorMessage(errorMessage);
        span2.setStatus({
          code: SpanStatusCode.ERROR,
          message: truncatedMessage
        });
        span2.setAttributes({
          "operation.name": spanName,
          "code.function": spanName,
          "operation.duration": duration,
          "operation.success": false,
          error: true,
          "exception.type": error instanceof Error ? error.constructor.name : "Error",
          "exception.message": truncatedMessage
        });
        if (error instanceof Error && error.stack) {
          span2.setAttribute(
            "exception.stack",
            error.stack.slice(0, MAX_ERROR_MESSAGE_LENGTH)
          );
        }
        span2.recordException(
          error instanceof Error ? error : new Error(String(error))
        );
        handleTailSampling(false, duration, error);
        span2.end();
        await flushIfNeeded();
        throw error;
      };
      try {
        callCounter?.add(1, {
          operation: spanName,
          status: "started"
        });
        const result = fn(ctxValue);
        if (result instanceof Promise) {
          return result.then(onSuccessAsync, onErrorAsync);
        }
        return onSuccessSync(result);
      } catch (error) {
        return onErrorSync(error);
      }
    });
  });
}
function trace2(fnOrNameOrOptions, maybeFn) {
  if (typeof fnOrNameOrOptions === "function") {
    if (looksLikeTraceFactory(fnOrNameOrOptions) && !isFactoryReturningFunction(
      fnOrNameOrOptions
    )) {
      return executeImmediately(
        fnOrNameOrOptions,
        {}
      );
    }
    return wrapFactoryWithTracing(
      fnOrNameOrOptions,
      {}
    );
  }
  if (typeof fnOrNameOrOptions === "string") {
    if (!maybeFn) {
      throw new Error("trace(name, fn): fn is required");
    }
    if (looksLikeTraceFactory(maybeFn) && !isFactoryReturningFunction(maybeFn)) {
      return executeImmediately(
        maybeFn,
        { name: fnOrNameOrOptions }
      );
    }
    return wrapFactoryWithTracing(
      maybeFn,
      { name: fnOrNameOrOptions }
    );
  }
  if (!maybeFn) {
    throw new Error("trace(options, fn): fn is required");
  }
  if (looksLikeTraceFactory(maybeFn) && !isFactoryReturningFunction(maybeFn)) {
    return executeImmediately(
      maybeFn,
      fnOrNameOrOptions
    );
  }
  return wrapFactoryWithTracing(
    maybeFn,
    fnOrNameOrOptions
  );
}
function withTracing(options = {}) {
  return (fnFactory) => wrapFactoryWithTracing(fnFactory, options);
}
function instrument(options) {
  const { functions, ...tracingOptions } = options;
  const instrumented = {};
  for (const key of Object.keys(functions)) {
    const typedKey = key;
    const fn = functions[typedKey];
    if (!fn || typeof fn !== "function") {
      instrumented[typedKey] = fn;
      continue;
    }
    if (shouldSkip(key, fn, tracingOptions.skip)) {
      instrumented[typedKey] = fn;
      continue;
    }
    const fnOptions = {
      ...tracingOptions,
      ...tracingOptions.overrides?.[key],
      // If no explicit name, use key as function name
      name: tracingOptions.overrides?.[key]?.name
    };
    const boundFn = fn.bind(functions);
    const fnFactory = (ctx2) => {
      return boundFn;
    };
    instrumented[typedKey] = wrapFactoryWithTracing(
      fnFactory,
      fnOptions,
      key
    );
  }
  return instrumented;
}
function span(options, fn) {
  const config = getConfig();
  const tracer = config.tracer;
  const { name, attributes } = options;
  const executeSpan = (span2) => {
    return runInOperationContext(name, () => {
      try {
        if (attributes) {
          for (const [key, value] of Object.entries(attributes)) {
            span2.setAttribute(key, value);
          }
        }
        const result2 = fn(span2);
        if (result2 instanceof Promise) {
          return result2.then((resolved) => {
            span2.setStatus({ code: SpanStatusCode.OK });
            span2.end();
            return resolved;
          }).catch((error) => {
            const errorMessage = error instanceof Error ? error.message.slice(0, MAX_ERROR_MESSAGE_LENGTH) : String(error).slice(0, MAX_ERROR_MESSAGE_LENGTH);
            span2.setAttribute("error.message", errorMessage);
            span2.setStatus({
              code: SpanStatusCode.ERROR,
              message: errorMessage
            });
            span2.recordException(
              error instanceof Error ? error : new Error(String(error))
            );
            span2.end();
            throw error;
          });
        } else {
          span2.setStatus({ code: SpanStatusCode.OK });
          span2.end();
          return result2;
        }
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message.slice(0, MAX_ERROR_MESSAGE_LENGTH) : String(error).slice(0, MAX_ERROR_MESSAGE_LENGTH);
        span2.setAttribute("error.message", errorMessage);
        span2.setStatus({
          code: SpanStatusCode.ERROR,
          message: errorMessage
        });
        span2.recordException(
          error instanceof Error ? error : new Error(String(error))
        );
        span2.end();
        throw error;
      }
    });
  };
  const result = tracer.startActiveSpan(name, executeSpan);
  if (result instanceof Promise) {
    return result;
  }
  return result;
}
async function withNewContext(options) {
  const { fn } = options;
  const config = getConfig();
  const tracer = config.tracer;
  return tracer.startActiveSpan("root", { root: true }, async (span2) => {
    try {
      const result = await fn();
      span2.setStatus({ code: SpanStatusCode.OK });
      return result;
    } catch (error) {
      span2.recordException(
        error instanceof Error ? error : new Error(String(error))
      );
      span2.setStatus({ code: SpanStatusCode.ERROR });
      throw error;
    } finally {
      span2.end();
    }
  });
}
function withBaggage(options) {
  const { baggage: baggageEntries, fn } = options;
  const currentContext = context.active();
  let updatedBaggage = propagation.getBaggage(currentContext) ?? propagation.createBaggage();
  for (const [key, value] of Object.entries(baggageEntries)) {
    updatedBaggage = updatedBaggage.setEntry(key, { value });
  }
  const newContext = propagation.setBaggage(currentContext, updatedBaggage);
  const ctxStorage = getContextStorage();
  const previousStored = ctxStorage.getStore();
  const baggageEnrichedStored = previousStored ? propagation.setBaggage(previousStored, updatedBaggage) : newContext;
  const result = previousStored ? ctxStorage.run(baggageEnrichedStored, () => context.with(newContext, fn)) : context.with(newContext, fn);
  if (result instanceof Promise) {
    return result.then(
      (value) => {
        if (previousStored) {
          return ctxStorage.run(previousStored, () => value);
        }
        return value;
      },
      (error) => {
        if (previousStored) {
          return ctxStorage.run(previousStored, () => {
            throw error;
          });
        }
        throw error;
      }
    );
  }
  return result;
}

export { ctx, getEventQueue, instrument, resetEventQueue, span, trace2 as trace, track, withBaggage, withNewContext, withTracing };
//# sourceMappingURL=chunk-JDV3LYOI.js.map
//# sourceMappingURL=chunk-JDV3LYOI.js.map