{"version":3,"file":"semantic-helpers.cjs","names":["withTracing","SpanKind"],"sources":["../src/semantic-helpers.ts"],"sourcesContent":["/**\n * Semantic convention helpers for OpenTelemetry\n *\n * Pre-configured trace helpers that follow OpenTelemetry semantic conventions\n * for common operation types. Reduces boilerplate and ensures consistency.\n *\n * Based on: https://opentelemetry.io/docs/specs/semconv/\n */\n\nimport { withTracing } from './functional';\nimport { assertTraceFactory } from './trace-factory-validation';\nimport type { TraceContext } from './trace-context';\nimport { SpanKind, type Attributes } from '@opentelemetry/api';\n\ntype SemanticHandler<TArgs extends unknown[], TReturn> = (\n  ...args: TArgs\n) => TReturn | Promise<TReturn>;\n\ntype SemanticFactory<TArgs extends unknown[], TReturn> = (\n  ctx: TraceContext,\n) => SemanticHandler<TArgs, TReturn>;\n\nfunction setConfiguredAttributes(\n  ctx: TraceContext,\n  attributes?: Attributes,\n): void {\n  if (!attributes) return;\n  for (const [key, value] of Object.entries(attributes)) {\n    if (value === undefined || value === null) continue;\n    const attributeValue =\n      typeof value === 'string' ||\n      typeof value === 'number' ||\n      typeof value === 'boolean'\n        ? value\n        : JSON.stringify(value);\n    ctx.setAttribute(key, attributeValue);\n  }\n}\n\n/**\n * Shared tail of `traceDB`/`traceHTTP`/`traceMessaging`: wrap `fn` directly\n * when given, otherwise return the curried factory-acceptor form.\n */\nfunction wrapSemantic<TArgs extends unknown[], TReturn>(\n  helperName: string,\n  name: string,\n  spanKind: SpanKind,\n  configure: (ctx: TraceContext) => void,\n  fn?: SemanticHandler<TArgs, TReturn>,\n):\n  | SemanticHandler<TArgs, TReturn>\n  | (<TFactoryArgs extends unknown[], TFactoryReturn>(\n      factory: SemanticFactory<TFactoryArgs, TFactoryReturn>,\n    ) => SemanticHandler<TFactoryArgs, TFactoryReturn>) {\n  if (fn) {\n    assertTraceFactory(helperName, fn);\n    return withTracing<TArgs, TReturn>({ name, spanKind })(\n      (ctx: TraceContext) => {\n        configure(ctx);\n        return fn;\n      },\n    );\n  }\n  return <TFactoryArgs extends unknown[], TFactoryReturn>(\n    factory: SemanticFactory<TFactoryArgs, TFactoryReturn>,\n  ) => {\n    assertTraceFactory(helperName, factory);\n    return withTracing<TFactoryArgs, TFactoryReturn>({ name, spanKind })(\n      (ctx: TraceContext) => {\n        configure(ctx);\n        const handler = factory(ctx);\n        assertTraceFactory(helperName, handler, 'result');\n        return handler;\n      },\n    );\n  };\n}\n\n/**\n * Configuration for database operations\n *\n * Follows DB semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/database/\n */\nexport interface DBConfig {\n  /** Database system (e.g., 'postgresql', 'mongodb', 'redis') */\n  system: string;\n  /** Operation type (e.g., 'SELECT', 'INSERT', 'find', 'get') */\n  operation?: string;\n  /** Database name */\n  database?: string;\n  /** Collection/table name */\n  collection?: string;\n  /** Low-cardinality query summary used as the preferred span name */\n  querySummary?: string;\n  /** Additional attributes to add to the span */\n  attributes?: Attributes;\n}\n\n/**\n * Configuration for HTTP client operations\n *\n * Follows HTTP semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/http/\n */\nexport interface HTTPConfig {\n  /** HTTP method (e.g., 'GET', 'POST') */\n  method?: string;\n  /** Target URL or URL template */\n  url?: string;\n  /** Low-cardinality server route used in the span name */\n  route?: string;\n  /** Low-cardinality client URL template used in the span name */\n  urlTemplate?: string;\n  /** Additional attributes to add to the span */\n  attributes?: Attributes;\n}\n\n/**\n * Configuration for messaging operations\n *\n * Follows Messaging semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/messaging/\n */\nexport interface MessagingConfig {\n  /** Messaging system (e.g., 'kafka', 'rabbitmq', 'sqs') */\n  system: string;\n  /** Operation type */\n  operation?: 'publish' | 'receive' | 'process';\n  /** Destination name (queue/topic) */\n  destination?: string;\n  /** Additional attributes to add to the span */\n  attributes?: Attributes;\n}\n\n/**\n * Trace database operations with DB semantic conventions\n *\n * Automatically adds standard attributes for database operations:\n * - db.system.name\n * - db.operation.name\n * - db.namespace\n * - db.collection.name (for NoSQL)\n *\n * **Use Cases:**\n * - SQL queries (PostgreSQL, MySQL, SQLite)\n * - NoSQL operations (MongoDB, DynamoDB, Redis)\n * - ORM queries (Prisma, TypeORM, Drizzle)\n *\n * @param config - Database operation configuration\n * @returns Traced function factory with DB attributes\n *\n * @example PostgreSQL query\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { pool } from './db'\n *\n * export const getUser = traceDB({\n *   system: 'postgresql',\n *   operation: 'SELECT',\n *   database: 'app_db',\n *   collection: 'users',\n *   querySummary: 'SELECT users'\n * })(ctx => async (userId: string) => {\n *   const query = 'SELECT * FROM users WHERE id = $1'\n *   const result = await pool.query(query, [userId])\n *   ctx.setAttribute('db.rows_affected', result.rowCount)\n *\n *   return result.rows[0]\n * })\n * ```\n *\n * @example MongoDB with Mongoose\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { User } from './models/User'\n *\n * export const findUsers = traceDB({\n *   system: 'mongodb',\n *   operation: 'find',\n *   database: 'app_db',\n *   collection: 'users'\n * })(ctx => async (filter: object) => {\n *   ctx.setAttribute('db.mongodb.filter', JSON.stringify(filter))\n *\n *   const users = await User.find(filter).limit(100)\n *   ctx.setAttribute('db.response.count', users.length)\n *\n *   return users\n * })\n * ```\n *\n * @example Redis operations\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { redis } from './redis'\n *\n * export const cacheGet = traceDB({\n *   system: 'redis',\n *   operation: 'GET'\n * })(ctx => async (key: string) => {\n *   ctx.setAttribute('db.redis.key', key)\n *\n *   const value = await redis.get(key)\n *   ctx.setAttribute('db.response.cache_hit', value !== null)\n *\n *   return value\n * })\n * ```\n *\n * @example Prisma with detailed query info\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { prisma } from './prisma'\n *\n * export const createPost = traceDB({\n *   system: 'postgresql',\n *   operation: 'INSERT',\n *   database: 'app_db',\n *   collection: 'posts'\n * })(ctx => async (data: { title: string; content: string; authorId: string }) => {\n *   ctx.setAttribute('db.prisma.model', 'Post')\n *   ctx.setAttribute('db.prisma.action', 'create')\n *\n *   const post = await prisma.post.create({ data })\n *\n *   ctx.setAttribute('db.response.id', post.id)\n *   return post\n * })\n * ```\n *\n * @public\n */\nexport function traceDB(\n  config: DBConfig,\n): <TArgs extends unknown[], TReturn>(\n  factory: SemanticFactory<TArgs, TReturn>,\n) => SemanticHandler<TArgs, TReturn>;\nexport function traceDB<TArgs extends unknown[], TReturn>(\n  config: DBConfig,\n  fn: SemanticHandler<TArgs, TReturn>,\n): SemanticHandler<TArgs, TReturn>;\nexport function traceDB<TArgs extends unknown[], TReturn>(\n  config: DBConfig,\n  fn?: SemanticHandler<TArgs, TReturn>,\n):\n  | SemanticHandler<TArgs, TReturn>\n  | (<TFactoryArgs extends unknown[], TFactoryReturn>(\n      factory: SemanticFactory<TFactoryArgs, TFactoryReturn>,\n    ) => SemanticHandler<TFactoryArgs, TFactoryReturn>) {\n  if (!config || typeof config !== 'object') {\n    throw new TypeError('traceDB: config must be an object');\n  }\n  if (typeof config.system !== 'string' || config.system.trim() === '') {\n    throw new TypeError('traceDB: config.system must be a non-empty string');\n  }\n  const target = config.collection ?? config.database;\n  const name =\n    config.querySummary ?? [config.operation, target].filter(Boolean).join(' ');\n  const spanName = name || config.system;\n  const configure = (ctx: TraceContext) => {\n    // Emit current stable names plus legacy aliases for dashboard compatibility.\n    ctx.setAttribute('db.system.name', config.system);\n    ctx.setAttribute('db.system', config.system);\n    if (config.operation) {\n      ctx.setAttribute('db.operation.name', config.operation);\n      ctx.setAttribute('db.operation', config.operation);\n    }\n    if (config.database) {\n      ctx.setAttribute('db.namespace', config.database);\n      ctx.setAttribute('db.name', config.database);\n    }\n    if (config.collection) {\n      ctx.setAttribute('db.collection.name', config.collection);\n    }\n    if (config.querySummary) {\n      ctx.setAttribute('db.query.summary', config.querySummary);\n    }\n    setConfiguredAttributes(ctx, config.attributes);\n  };\n  return wrapSemantic('traceDB', spanName, SpanKind.CLIENT, configure, fn);\n}\n\n/**\n * Trace HTTP client operations with HTTP semantic conventions\n *\n * Automatically adds standard attributes for HTTP requests:\n * - http.request.method\n * - url.full\n *\n * **Use Cases:**\n * - External API calls\n * - Microservice communication\n * - Third-party integrations\n *\n * @param config - HTTP operation configuration\n * @returns Traced function factory with HTTP attributes\n *\n * @example Fetch API\n * ```typescript\n * import { traceHTTP } from 'autotel/semantic-helpers'\n *\n * export const fetchUser = traceHTTP({\n *   method: 'GET',\n *   urlTemplate: '/users/{id}'\n * })(ctx => async (userId: string) => {\n *   const url = `https://api.example.com/users/${userId}`\n *   ctx.setAttribute('url.full', url)\n *\n *   const response = await fetch(url)\n *   ctx.setAttribute('http.response.status_code', response.status)\n *\n *   if (!response.ok) {\n *     ctx.setAttribute('error', true)\n *     throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n *   }\n *\n *   return response.json()\n * })\n * ```\n *\n * @example Axios with retry logic\n * ```typescript\n * import { traceHTTP } from 'autotel/semantic-helpers'\n * import axios from 'axios'\n *\n * export const sendWebhook = traceHTTP({\n *   method: 'POST',\n *   url: 'https://webhook.example.com/events'\n * })(ctx => async (payload: object) => {\n *   let attempts = 0\n *   const maxAttempts = 3\n *\n *   while (attempts < maxAttempts) {\n *     try {\n *       attempts++\n *       ctx.setAttribute('http.request.resend_count', attempts - 1)\n *\n *       const response = await axios.post('https://webhook.example.com/events', payload)\n *       ctx.setAttribute('http.response.status_code', response.status)\n *       return response.data\n *     } catch (error) {\n *       if (attempts >= maxAttempts) throw error\n *       await new Promise(resolve => setTimeout(resolve, 1000 * attempts))\n *     }\n *   }\n * })\n * ```\n *\n * @public\n */\nexport function traceHTTP(\n  config: HTTPConfig,\n): <TArgs extends unknown[], TReturn>(\n  factory: SemanticFactory<TArgs, TReturn>,\n) => SemanticHandler<TArgs, TReturn>;\nexport function traceHTTP<TArgs extends unknown[], TReturn>(\n  config: HTTPConfig,\n  fn: SemanticHandler<TArgs, TReturn>,\n): SemanticHandler<TArgs, TReturn>;\nexport function traceHTTP<TArgs extends unknown[], TReturn>(\n  config: HTTPConfig,\n  fn?: SemanticHandler<TArgs, TReturn>,\n):\n  | SemanticHandler<TArgs, TReturn>\n  | (<TFactoryArgs extends unknown[], TFactoryReturn>(\n      factory: SemanticFactory<TFactoryArgs, TFactoryReturn>,\n    ) => SemanticHandler<TFactoryArgs, TFactoryReturn>) {\n  if (!config || typeof config !== 'object') {\n    throw new TypeError('traceHTTP: config must be an object');\n  }\n  const method = config.method?.toUpperCase() || 'HTTP';\n  const target = config.route ?? config.urlTemplate;\n  const spanName = target ? `${method} ${target}` : method;\n  const configure = (ctx: TraceContext) => {\n    if (config.method) ctx.setAttribute('http.request.method', method);\n    if (config.url) ctx.setAttribute('url.full', config.url);\n    if (config.route) ctx.setAttribute('http.route', config.route);\n    if (config.urlTemplate) {\n      ctx.setAttribute('url.template', config.urlTemplate);\n    }\n    setConfiguredAttributes(ctx, config.attributes);\n  };\n  return wrapSemantic('traceHTTP', spanName, SpanKind.CLIENT, configure, fn);\n}\n\n/**\n * Trace messaging operations with Messaging semantic conventions\n *\n * Automatically adds standard attributes for messaging:\n * - messaging.system\n * - messaging.operation\n * - messaging.destination.name\n *\n * **Use Cases:**\n * - Publishing messages to queues/topics\n * - Consuming messages from queues/topics\n * - Event-driven architectures\n *\n * @param config - Messaging operation configuration\n * @returns Traced function factory with Messaging attributes\n *\n * @example Publishing to Kafka\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { kafka } from './kafka'\n *\n * const producer = kafka.producer()\n *\n * export const publishEvent = traceMessaging({\n *   system: 'kafka',\n *   operation: 'publish',\n *   destination: 'user-events'\n * })(ctx => async (event: { type: string; userId: string; data: object }) => {\n *   ctx.setAttribute('messaging.message.type', event.type)\n *   ctx.setAttribute('messaging.kafka.partition', 0)\n *\n *   await producer.send({\n *     topic: 'user-events',\n *     messages: [\n *       {\n *         key: event.userId,\n *         value: JSON.stringify(event.data)\n *       }\n *     ]\n *   })\n *\n *   ctx.setAttribute('messaging.message.id', event.userId)\n * })\n * ```\n *\n * @example Consuming from RabbitMQ\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { channel } from './rabbitmq'\n *\n * export const processOrder = traceMessaging({\n *   system: 'rabbitmq',\n *   operation: 'process',\n *   destination: 'orders'\n * })(ctx => async (message: { orderId: string; items: object[] }) => {\n *   ctx.setAttribute('messaging.message.id', message.orderId)\n *   ctx.setAttribute('messaging.message.body.size', JSON.stringify(message).length)\n *\n *   // Process order logic\n *   const result = await processOrderInternal(message)\n *\n *   ctx.setAttribute('messaging.operation.result', 'success')\n *   return result\n * })\n * ```\n *\n * @example AWS SQS with batch processing\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { SQS } from '@aws-sdk/client-sqs'\n *\n * const sqs = new SQS()\n *\n * export const sendBatch = traceMessaging({\n *   system: 'aws_sqs',\n *   operation: 'publish',\n *   destination: 'notifications-queue'\n * })(ctx => async (messages: Array<{ id: string; body: object }>) => {\n *   ctx.setAttribute('messaging.batch.message_count', messages.length)\n *\n *   const result = await sqs.sendMessageBatch({\n *     QueueUrl: process.env.QUEUE_URL,\n *     Entries: messages.map(msg => ({\n *       Id: msg.id,\n *       MessageBody: JSON.stringify(msg.body)\n *     }))\n *   })\n *\n *   ctx.setAttribute('messaging.operation.success_count', result.Successful?.length || 0)\n *   ctx.setAttribute('messaging.operation.failed_count', result.Failed?.length || 0)\n *\n *   return result\n * })\n * ```\n *\n * @public\n */\nexport function traceMessaging(\n  config: MessagingConfig,\n): <TArgs extends unknown[], TReturn>(\n  factory: SemanticFactory<TArgs, TReturn>,\n) => SemanticHandler<TArgs, TReturn>;\nexport function traceMessaging<TArgs extends unknown[], TReturn>(\n  config: MessagingConfig,\n  fn: SemanticHandler<TArgs, TReturn>,\n): SemanticHandler<TArgs, TReturn>;\nexport function traceMessaging<TArgs extends unknown[], TReturn>(\n  config: MessagingConfig,\n  fn?: SemanticHandler<TArgs, TReturn>,\n):\n  | SemanticHandler<TArgs, TReturn>\n  | (<TFactoryArgs extends unknown[], TFactoryReturn>(\n      factory: SemanticFactory<TFactoryArgs, TFactoryReturn>,\n    ) => SemanticHandler<TFactoryArgs, TFactoryReturn>) {\n  if (!config || typeof config !== 'object') {\n    throw new TypeError('traceMessaging: config must be an object');\n  }\n  if (typeof config.system !== 'string' || config.system.trim() === '') {\n    throw new TypeError(\n      'traceMessaging: config.system must be a non-empty string',\n    );\n  }\n  const operation = config.operation ?? 'messaging';\n  const spanName = [operation, config.destination].filter(Boolean).join(' ');\n  const operationType = operation === 'publish' ? 'send' : operation;\n  const spanKind =\n    operation === 'publish'\n      ? SpanKind.PRODUCER\n      : operation === 'process'\n        ? SpanKind.CONSUMER\n        : operation === 'receive'\n          ? SpanKind.CLIENT\n          : SpanKind.INTERNAL;\n  const configure = (ctx: TraceContext) => {\n    ctx.setAttribute('messaging.system', config.system);\n    if (config.operation) {\n      ctx.setAttribute('messaging.operation.name', config.operation);\n      ctx.setAttribute('messaging.operation.type', operationType);\n      ctx.setAttribute('messaging.operation', config.operation);\n    }\n    if (config.destination) {\n      ctx.setAttribute('messaging.destination.name', config.destination);\n    }\n    setConfiguredAttributes(ctx, config.attributes);\n  };\n  return wrapSemantic('traceMessaging', spanName, spanKind, configure, fn);\n}\n"],"mappings":"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"}