@sentry/server-utils
Advanced tools
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const attributes = require('@sentry/conventions/attributes'); | ||
| const op = require('@sentry/conventions/op'); | ||
| const core = require('@sentry/core'); | ||
| const tracingChannel = require('../tracing-channel.js'); | ||
| const utils = require('./utils.js'); | ||
| const GRAPHQL_DC_CHANNEL_PARSE = "graphql:parse"; | ||
| const GRAPHQL_DC_CHANNEL_VALIDATE = "graphql:validate"; | ||
| const GRAPHQL_DC_CHANNEL_EXECUTE = "graphql:execute"; | ||
| const GRAPHQL_DC_CHANNEL_SUBSCRIBE = "graphql:subscribe"; | ||
| const GRAPHQL_DC_CHANNEL_RESOLVE = "graphql:resolve"; | ||
| const ORIGIN = "auto.graphql.diagnostic_channel"; | ||
| const SPAN_NAME_PARSE = "graphql.parse"; | ||
| const SPAN_NAME_VALIDATE = "graphql.validate"; | ||
| const SPAN_NAME_EXECUTE = "graphql.execute"; | ||
| const SPAN_NAME_SUBSCRIBE = "graphql.subscribe"; | ||
| const SPAN_NAME_RESOLVE = "graphql.resolve"; | ||
| const GRAPHQL_FIELD_NAME = "graphql.field.name"; | ||
| const GRAPHQL_FIELD_PATH = "graphql.field.path"; | ||
| const GRAPHQL_FIELD_TYPE = "graphql.field.type"; | ||
| const GRAPHQL_PARENT_NAME = "graphql.parent.name"; | ||
| function subscribeGraphqlDiagnosticChannels(tracingChannel, options = {}) { | ||
| const ignoreResolveSpans = options.ignoreResolveSpans !== false; | ||
| const ignoreTrivialResolveSpans = options.ignoreTrivialResolveSpans !== false; | ||
| const useOperationNameForRootSpan = options.useOperationNameForRootSpan !== false; | ||
| setupParseChannel(tracingChannel); | ||
| setupValidateChannel(tracingChannel); | ||
| setupOperationChannel(tracingChannel, GRAPHQL_DC_CHANNEL_EXECUTE, SPAN_NAME_EXECUTE, useOperationNameForRootSpan); | ||
| setupOperationChannel(tracingChannel, GRAPHQL_DC_CHANNEL_SUBSCRIBE, SPAN_NAME_SUBSCRIBE, useOperationNameForRootSpan); | ||
| if (!ignoreResolveSpans) { | ||
| setupResolveChannel(tracingChannel, ignoreTrivialResolveSpans); | ||
| } | ||
| } | ||
| function setupParseChannel(tracingChannel$1) { | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel$1(GRAPHQL_DC_CHANNEL_PARSE), | ||
| () => core.startInactiveSpan({ | ||
| name: SPAN_NAME_PARSE, | ||
| attributes: { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op.WEB_SERVER_GRAPHQL_SPAN_OP | ||
| } | ||
| }) | ||
| ); | ||
| } | ||
| function setupValidateChannel(tracingChannel$1) { | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel$1(GRAPHQL_DC_CHANNEL_VALIDATE), | ||
| (data) => { | ||
| const document = utils.redactGraphqlDocument(data.document); | ||
| return core.startInactiveSpan({ | ||
| name: SPAN_NAME_VALIDATE, | ||
| attributes: { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op.WEB_SERVER_GRAPHQL_SPAN_OP, | ||
| [attributes.GRAPHQL_DOCUMENT]: document | ||
| } | ||
| }); | ||
| }, | ||
| { | ||
| beforeSpanEnd: (span, data) => { | ||
| if (Array.isArray(data.result) && data.result.length > 0) { | ||
| span.setStatus({ code: core.SPAN_STATUS_ERROR, message: "invalid_argument" }); | ||
| } | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function setupOperationChannel(tracingChannel$1, channelName, fallbackName, useOperationNameForRootSpan) { | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel$1(channelName), | ||
| (data) => { | ||
| const document = utils.redactGraphqlDocument(data.document); | ||
| const span = core.startInactiveSpan({ | ||
| name: utils.getOperationSpanName(data.operationType, data.operationName, fallbackName), | ||
| attributes: { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op.WEB_SERVER_GRAPHQL_SPAN_OP, | ||
| [attributes.GRAPHQL_OPERATION_TYPE]: data.operationType, | ||
| [attributes.GRAPHQL_OPERATION_NAME]: data.operationName || void 0, | ||
| [attributes.GRAPHQL_DOCUMENT]: document | ||
| } | ||
| }); | ||
| if (useOperationNameForRootSpan && data.operationType) { | ||
| utils.renameRootSpanWithOperation(span, data.operationType, data.operationName); | ||
| } | ||
| return span; | ||
| }, | ||
| { | ||
| beforeSpanEnd: (span, data) => { | ||
| if (utils.hasResultErrors(data.result)) { | ||
| span.setStatus({ code: core.SPAN_STATUS_ERROR, message: "internal_error" }); | ||
| } | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function setupResolveChannel(tracingChannel$1, ignoreTrivialResolveSpans) { | ||
| tracingChannel.bindTracingChannelToSpan(tracingChannel$1(GRAPHQL_DC_CHANNEL_RESOLVE), (data) => { | ||
| if (ignoreTrivialResolveSpans && data.isDefaultResolver) { | ||
| return void 0; | ||
| } | ||
| return core.startInactiveSpan({ | ||
| name: `${SPAN_NAME_RESOLVE} ${data.fieldPath}`, | ||
| attributes: { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op.WEB_SERVER_GRAPHQL_SPAN_OP, | ||
| [GRAPHQL_FIELD_NAME]: data.fieldName, | ||
| [GRAPHQL_FIELD_PATH]: data.fieldPath, | ||
| [GRAPHQL_FIELD_TYPE]: data.fieldType, | ||
| [GRAPHQL_PARENT_NAME]: data.parentType | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| exports.GRAPHQL_DC_CHANNEL_EXECUTE = GRAPHQL_DC_CHANNEL_EXECUTE; | ||
| exports.GRAPHQL_DC_CHANNEL_PARSE = GRAPHQL_DC_CHANNEL_PARSE; | ||
| exports.GRAPHQL_DC_CHANNEL_RESOLVE = GRAPHQL_DC_CHANNEL_RESOLVE; | ||
| exports.GRAPHQL_DC_CHANNEL_SUBSCRIBE = GRAPHQL_DC_CHANNEL_SUBSCRIBE; | ||
| exports.GRAPHQL_DC_CHANNEL_VALIDATE = GRAPHQL_DC_CHANNEL_VALIDATE; | ||
| exports.subscribeGraphqlDiagnosticChannels = subscribeGraphqlDiagnosticChannels; | ||
| //# sourceMappingURL=graphql-dc-subscriber.js.map |
| {"version":3,"file":"graphql-dc-subscriber.js","sources":["../../../src/graphql/graphql-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport { GRAPHQL_DOCUMENT, GRAPHQL_OPERATION_NAME, GRAPHQL_OPERATION_TYPE } from '@sentry/conventions/attributes';\nimport { WEB_SERVER_GRAPHQL_SPAN_OP } from '@sentry/conventions/op';\nimport {\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n} from '@sentry/core';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\nimport type { GraphqlDocumentNode } from './utils';\nimport { getOperationSpanName, hasResultErrors, redactGraphqlDocument, renameRootSpanWithOperation } from './utils';\n\n// Channel names published by graphql >= 17.0.0 (see graphql-js `src/diagnostics.ts`).\n// Hardcoded so the subscriber does not have to import graphql — the channels just\n// have to be subscribed to before the user's graphql code publishes.\nexport const GRAPHQL_DC_CHANNEL_PARSE = 'graphql:parse';\nexport const GRAPHQL_DC_CHANNEL_VALIDATE = 'graphql:validate';\nexport const GRAPHQL_DC_CHANNEL_EXECUTE = 'graphql:execute';\nexport const GRAPHQL_DC_CHANNEL_SUBSCRIBE = 'graphql:subscribe';\nexport const GRAPHQL_DC_CHANNEL_RESOLVE = 'graphql:resolve';\n\nconst ORIGIN = 'auto.graphql.diagnostic_channel';\n\nconst SPAN_NAME_PARSE = 'graphql.parse';\nconst SPAN_NAME_VALIDATE = 'graphql.validate';\nconst SPAN_NAME_EXECUTE = 'graphql.execute';\nconst SPAN_NAME_SUBSCRIBE = 'graphql.subscribe';\nconst SPAN_NAME_RESOLVE = 'graphql.resolve';\n\n// Field-level attributes for resolver spans. Not in `@sentry/conventions`; these match the keys the\n// vendored OTel instrumentation emits so there is no drift between the two paths.\nconst GRAPHQL_FIELD_NAME = 'graphql.field.name';\nconst GRAPHQL_FIELD_PATH = 'graphql.field.path';\nconst GRAPHQL_FIELD_TYPE = 'graphql.field.type';\nconst GRAPHQL_PARENT_NAME = 'graphql.parent.name';\n\n/** Context published on the sync-only `graphql:parse` channel. */\nexport interface GraphqlParseData {\n source: string | { body?: string };\n result?: GraphqlDocumentNode;\n error?: unknown;\n}\n\n/** Context published on the sync-only `graphql:validate` channel. */\nexport interface GraphqlValidateData {\n document: GraphqlDocumentNode;\n /** Validation errors returned by validation; an empty array means the document is valid. */\n result?: ReadonlyArray<unknown>;\n error?: unknown;\n}\n\n/**\n * Context published on the `graphql:execute` and `graphql:subscribe` channels.\n *\n * `result` carries an `ExecutionResult` (or, for subscriptions, an async generator); GraphQL errors\n * collected during execution surface on `result.errors` rather than as the channel's `error`\n * lifecycle event, which only fires on an abrupt throw.\n */\nexport interface GraphqlOperationData {\n document: GraphqlDocumentNode;\n operationName?: string;\n operationType?: string;\n result?: unknown;\n error?: unknown;\n}\n\n/**\n * Context published on the per-field `graphql:resolve` channel.\n *\n * A resolver throw or rejection publishes the `error` lifecycle event here; the same failure also\n * surfaces in the enclosing execution result.\n */\nexport interface GraphqlResolveData {\n fieldName: string;\n parentType: string;\n fieldType: string;\n fieldPath: string;\n /** Whether the field is handled by graphql's default property resolver (vs. a user resolver). */\n isDefaultResolver: boolean;\n alias?: string;\n args?: unknown;\n result?: unknown;\n error?: unknown;\n}\n\n/** Options controlling which graphql channels the subscriber emits spans for. */\nexport interface GraphqlDiagnosticChannelsOptions {\n /**\n * Do not create spans for resolvers. Resolver spans are per-field and can be very high volume.\n * Defaults to `true`.\n */\n ignoreResolveSpans?: boolean;\n\n /**\n * When resolver spans are enabled, do not create them for graphql's default property resolver\n * (fields without a user-defined resolver), which are rarely interesting. Defaults to `true`.\n */\n ignoreTrivialResolveSpans?: boolean;\n\n /**\n * Rename the enclosing root span to include the operation name(s), e.g.\n * `GET /graphql` -> `GET /graphql (query GetUser)`. Defaults to `true`.\n */\n useOperationNameForRootSpan?: boolean;\n}\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Node passes `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type GraphqlTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/**\n * Subscribe Sentry span handlers to graphql's diagnostics-channel events\n * (`graphql:parse`, `:validate`, `:execute`, `:subscribe`), published by graphql >= 17.0.0.\n *\n * On older graphql versions the channels are never published to, so the subscribers are inert —\n * there is no double-instrumentation against the vendored OTel patcher, which is gated to `< 17`.\n *\n * The per-field `graphql:resolve` channel is only subscribed when `ignoreResolveSpans` is `false`:\n * resolver spans are per-field and can be extremely high-volume, so they are off by default (matching\n * the legacy OTel path). When enabled, `ignoreTrivialResolveSpans` (default `true`) additionally skips\n * graphql's default property resolver.\n */\nexport function subscribeGraphqlDiagnosticChannels(\n tracingChannel: GraphqlTracingChannelFactory,\n options: GraphqlDiagnosticChannelsOptions = {},\n): void {\n const ignoreResolveSpans = options.ignoreResolveSpans !== false;\n const ignoreTrivialResolveSpans = options.ignoreTrivialResolveSpans !== false;\n const useOperationNameForRootSpan = options.useOperationNameForRootSpan !== false;\n\n setupParseChannel(tracingChannel);\n setupValidateChannel(tracingChannel);\n setupOperationChannel(tracingChannel, GRAPHQL_DC_CHANNEL_EXECUTE, SPAN_NAME_EXECUTE, useOperationNameForRootSpan);\n setupOperationChannel(tracingChannel, GRAPHQL_DC_CHANNEL_SUBSCRIBE, SPAN_NAME_SUBSCRIBE, useOperationNameForRootSpan);\n\n if (!ignoreResolveSpans) {\n setupResolveChannel(tracingChannel, ignoreTrivialResolveSpans);\n }\n}\n\nfunction setupParseChannel(tracingChannel: GraphqlTracingChannelFactory): void {\n bindTracingChannelToSpan(tracingChannel<GraphqlParseData>(GRAPHQL_DC_CHANNEL_PARSE), () =>\n startInactiveSpan({\n name: SPAN_NAME_PARSE,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n },\n }),\n );\n}\n\nfunction setupValidateChannel(tracingChannel: GraphqlTracingChannelFactory): void {\n bindTracingChannelToSpan(\n tracingChannel<GraphqlValidateData>(GRAPHQL_DC_CHANNEL_VALIDATE),\n data => {\n const document = redactGraphqlDocument(data.document);\n\n return startInactiveSpan({\n name: SPAN_NAME_VALIDATE,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n [GRAPHQL_DOCUMENT]: document,\n },\n });\n },\n {\n beforeSpanEnd: (span, data) => {\n // Validation completes normally even when it returns errors, so flag the span here.\n if (Array.isArray(data.result) && data.result.length > 0) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'invalid_argument' });\n }\n },\n },\n );\n}\n\nfunction setupOperationChannel(\n tracingChannel: GraphqlTracingChannelFactory,\n channelName: string,\n fallbackName: string,\n useOperationNameForRootSpan: boolean,\n): void {\n bindTracingChannelToSpan(\n tracingChannel<GraphqlOperationData>(channelName),\n data => {\n const document = redactGraphqlDocument(data.document);\n\n const span = startInactiveSpan({\n name: getOperationSpanName(data.operationType, data.operationName, fallbackName),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n [GRAPHQL_OPERATION_TYPE]: data.operationType,\n [GRAPHQL_OPERATION_NAME]: data.operationName || undefined,\n [GRAPHQL_DOCUMENT]: document,\n },\n });\n\n if (useOperationNameForRootSpan && data.operationType) {\n renameRootSpanWithOperation(span, data.operationType, data.operationName);\n }\n\n return span;\n },\n {\n beforeSpanEnd: (span, data) => {\n // GraphQL errors are returned on `result.errors`, not as a thrown error, so flag the span here.\n if (hasResultErrors(data.result)) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n }\n },\n },\n );\n}\n\nfunction setupResolveChannel(tracingChannel: GraphqlTracingChannelFactory, ignoreTrivialResolveSpans: boolean): void {\n bindTracingChannelToSpan(tracingChannel<GraphqlResolveData>(GRAPHQL_DC_CHANNEL_RESOLVE), data => {\n // Returning `undefined` opts this field out: no span is created and the active context is left\n // untouched, so the field still resolves under its parent span.\n if (ignoreTrivialResolveSpans && data.isDefaultResolver) {\n return undefined;\n }\n\n return startInactiveSpan({\n name: `${SPAN_NAME_RESOLVE} ${data.fieldPath}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n [GRAPHQL_FIELD_NAME]: data.fieldName,\n [GRAPHQL_FIELD_PATH]: data.fieldPath,\n [GRAPHQL_FIELD_TYPE]: data.fieldType,\n [GRAPHQL_PARENT_NAME]: data.parentType,\n },\n });\n });\n}\n"],"names":["tracingChannel","bindTracingChannelToSpan","startInactiveSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","WEB_SERVER_GRAPHQL_SPAN_OP","redactGraphqlDocument","GRAPHQL_DOCUMENT","SPAN_STATUS_ERROR","getOperationSpanName","GRAPHQL_OPERATION_TYPE","GRAPHQL_OPERATION_NAME","renameRootSpanWithOperation","hasResultErrors"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const graphqlDcSubscriber = require('./graphql-dc-subscriber.js'); | ||
| const _graphqlIntegration = ((options = {}) => { | ||
| return { | ||
| name: "Graphql", | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| core.waitForTracingChannelBinding(() => { | ||
| graphqlDcSubscriber.subscribeGraphqlDiagnosticChannels(diagnosticsChannel.tracingChannel, options); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const graphqlIntegration = core.defineIntegration(_graphqlIntegration); | ||
| exports.graphqlIntegration = graphqlIntegration; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../src/graphql/index.ts"],"sourcesContent":["import { defineIntegration, type IntegrationFn, waitForTracingChannelBinding } from '@sentry/core';\nimport * as dc from 'node:diagnostics_channel';\nimport { type GraphqlDiagnosticChannelsOptions, subscribeGraphqlDiagnosticChannels } from './graphql-dc-subscriber';\n\nconst _graphqlIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {\n return {\n name: 'Graphql' as const,\n setupOnce() {\n // Bail on Node <= 18.18.0, where `tracingChannel` does not exist.\n if (!dc.tracingChannel) {\n return;\n }\n\n // Subscribe to graphql's native tracing channels (graphql >= 17).\n // This is a no-op on versions that don't publish to the channels, so it is always safe to call.\n waitForTracingChannelBinding(() => {\n subscribeGraphqlDiagnosticChannels(dc.tracingChannel, options);\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the [graphql](https://www.npmjs.com/package/graphql) library via its native\n * `node:diagnostics_channel` tracing channels (graphql >= 17).\n *\n * On older graphql versions the channels are never published to, so this integration is inert and\n * the vendored OTel instrumentation (gated to `< 17`) handles instrumentation instead.\n */\nexport const graphqlIntegration = defineIntegration(_graphqlIntegration);\n"],"names":["dc","waitForTracingChannelBinding","subscribeGraphqlDiagnosticChannels","defineIntegration"],"mappings":";;;;;;AAIA,MAAM,mBAAA,IAAuB,CAAC,OAAA,GAA4C,EAAC,KAAM;AAC/E,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,SAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAACA,mBAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAIA,MAAAC,iCAAA,CAA6B,MAAM;AACjC,QAAAC,sDAAA,CAAmCF,kBAAA,CAAG,gBAAgB,OAAO,CAAA;AAAA,MAC/D,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AASO,MAAM,kBAAA,GAAqBG,uBAAkB,mBAAmB;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const attributes = require('@sentry/conventions/attributes'); | ||
| const core = require('@sentry/core'); | ||
| const ORIGINAL_DESCRIPTION_ATTRIBUTE = "original-description"; | ||
| const REDACTED_LITERAL_KINDS = /* @__PURE__ */ new Set(["Int", "Float", "String", "BlockString"]); | ||
| function renameRootSpanWithOperation(span, operationType, operationName) { | ||
| const rootSpan = core.getRootSpan(span); | ||
| if (rootSpan === span) { | ||
| return; | ||
| } | ||
| const rootSpanJson = core.spanToJSON(rootSpan); | ||
| const newOperation = operationName ? `${operationType} ${operationName}` : operationType; | ||
| const existingOperations = rootSpanJson.data[attributes.SENTRY_GRAPHQL_OPERATION]; | ||
| let operations; | ||
| if (Array.isArray(existingOperations)) { | ||
| operations = [...existingOperations, newOperation]; | ||
| } else if (typeof existingOperations === "string") { | ||
| operations = [existingOperations, newOperation]; | ||
| } else { | ||
| operations = newOperation; | ||
| } | ||
| rootSpan.setAttribute(attributes.SENTRY_GRAPHQL_OPERATION, operations); | ||
| const originalDescription = rootSpanJson.data[ORIGINAL_DESCRIPTION_ATTRIBUTE] ?? rootSpanJson.description; | ||
| if (!rootSpanJson.data[ORIGINAL_DESCRIPTION_ATTRIBUTE]) { | ||
| rootSpan.setAttribute(ORIGINAL_DESCRIPTION_ATTRIBUTE, originalDescription); | ||
| } | ||
| rootSpan.updateName(`${originalDescription} (${getGraphqlOperationNamesFromAttribute(operations)})`); | ||
| } | ||
| function getGraphqlOperationNamesFromAttribute(attr) { | ||
| if (Array.isArray(attr)) { | ||
| const sorted = attr.slice().sort(); | ||
| if (sorted.length <= 5) { | ||
| return sorted.join(", "); | ||
| } | ||
| return `${sorted.slice(0, 5).join(", ")}, +${sorted.length - 5}`; | ||
| } | ||
| return attr; | ||
| } | ||
| function getOperationSpanName(operationType, operationName, fallbackName) { | ||
| if (operationType && operationName) { | ||
| return `${operationType} ${operationName}`; | ||
| } | ||
| if (operationType) { | ||
| return operationType; | ||
| } | ||
| return fallbackName; | ||
| } | ||
| function hasResultErrors(result) { | ||
| if (core.isObjectLike(result) && "errors" in result) { | ||
| const errors = result.errors; | ||
| return Array.isArray(errors) && errors.length > 0; | ||
| } | ||
| return false; | ||
| } | ||
| function redactGraphqlDocument(document) { | ||
| const loc = document?.loc; | ||
| const body = loc?.source?.body; | ||
| if (typeof body !== "string" || !loc?.startToken) { | ||
| return void 0; | ||
| } | ||
| try { | ||
| const ranges = []; | ||
| for (let token = loc.startToken; token; token = token.next) { | ||
| if (REDACTED_LITERAL_KINDS.has(token.kind)) { | ||
| ranges.push({ start: token.start, end: token.end, kind: token.kind }); | ||
| } | ||
| } | ||
| let out = body; | ||
| for (let i = ranges.length - 1; i >= 0; i--) { | ||
| const { start, end, kind } = ranges[i]; | ||
| const replacement = kind === "String" || kind === "BlockString" ? '"*"' : "*"; | ||
| out = out.slice(0, start) + replacement + out.slice(end); | ||
| } | ||
| return out; | ||
| } catch { | ||
| return void 0; | ||
| } | ||
| } | ||
| exports.getOperationSpanName = getOperationSpanName; | ||
| exports.hasResultErrors = hasResultErrors; | ||
| exports.redactGraphqlDocument = redactGraphqlDocument; | ||
| exports.renameRootSpanWithOperation = renameRootSpanWithOperation; | ||
| //# sourceMappingURL=utils.js.map |
| {"version":3,"file":"utils.js","sources":["../../../src/graphql/utils.ts"],"sourcesContent":["import { SENTRY_GRAPHQL_OPERATION } from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport { isObjectLike, getRootSpan, spanToJSON } from '@sentry/core';\n\n// Same key the OTel path uses, so renames stay consistent across both.\nconst ORIGINAL_DESCRIPTION_ATTRIBUTE = 'original-description';\n\n// graphql-js token kinds whose values may carry user data (literal arguments). We\n// replace them in the serialized document so raw inline values can never reach\n// `graphql.document`. Mirrors the legacy OTel instrumentation's redaction set.\nconst REDACTED_LITERAL_KINDS = new Set(['Int', 'Float', 'String', 'BlockString']);\n\n/** Minimal shape of a graphql-js lexer token, enough to locate literal spans for redaction. */\ninterface GraphqlToken {\n kind: string;\n start: number;\n end: number;\n next?: GraphqlToken | null;\n}\n\n/** Minimal shape of a parsed graphql-js `DocumentNode`, enough to read its source and tokens. */\nexport interface GraphqlDocumentNode {\n loc?: {\n startToken?: GraphqlToken;\n source?: { body?: string };\n };\n}\n\n/**\n * Rename the enclosing root span to include the operation name(s), e.g. `GET /graphql (query GetUser)`.\n * Mirrors the legacy OTel `useOperationNameForRootSpan` behavior; `parseSpanDescription` reads the same\n * `sentry.graphql.operation` attribute on the OTel export path.\n */\nexport function renameRootSpanWithOperation(span: Span, operationType: string, operationName?: string): void {\n const rootSpan = getRootSpan(span);\n // Nothing to rename if the operation span is itself the root (graphql ran outside any span).\n if (rootSpan === span) {\n return;\n }\n\n const rootSpanJson = spanToJSON(rootSpan);\n\n const newOperation = operationName ? `${operationType} ${operationName}` : operationType;\n\n // A single operation is stored as a string, multiple as an array.\n const existingOperations = rootSpanJson.data[SENTRY_GRAPHQL_OPERATION];\n let operations: string | string[];\n if (Array.isArray(existingOperations)) {\n operations = [...(existingOperations as string[]), newOperation];\n } else if (typeof existingOperations === 'string') {\n operations = [existingOperations, newOperation];\n } else {\n operations = newOperation;\n }\n rootSpan.setAttribute(SENTRY_GRAPHQL_OPERATION, operations);\n\n // Keep the pre-rename name so repeated renames don't compound.\n const originalDescription =\n (rootSpanJson.data[ORIGINAL_DESCRIPTION_ATTRIBUTE] as string | undefined) ?? rootSpanJson.description;\n if (!rootSpanJson.data[ORIGINAL_DESCRIPTION_ATTRIBUTE]) {\n rootSpan.setAttribute(ORIGINAL_DESCRIPTION_ATTRIBUTE, originalDescription);\n }\n\n rootSpan.updateName(`${originalDescription} (${getGraphqlOperationNamesFromAttribute(operations)})`);\n}\n\n/** Format the accumulated operations for the root span name: up to 5 sorted names, then `+N`. */\nfunction getGraphqlOperationNamesFromAttribute(attr: string | string[]): string {\n if (Array.isArray(attr)) {\n // oxlint-disable-next-line typescript/require-array-sort-compare\n const sorted = attr.slice().sort();\n if (sorted.length <= 5) {\n return sorted.join(', ');\n }\n\n return `${sorted.slice(0, 5).join(', ')}, +${sorted.length - 5}`;\n }\n\n return attr;\n}\n\n/**\n * Span name follows the GraphQL semantic conventions: `<operation.type> <operation.name>` when both\n * are available, `<operation.type>` when only the type is, otherwise a static fallback.\n */\nexport function getOperationSpanName(\n operationType: string | undefined,\n operationName: string | undefined,\n fallbackName: string,\n): string {\n if (operationType && operationName) {\n return `${operationType} ${operationName}`;\n }\n if (operationType) {\n return operationType;\n }\n\n return fallbackName;\n}\n\n/** Whether a graphql execution result carries GraphQL errors (returned on `result.errors`). */\nexport function hasResultErrors(result: unknown): boolean {\n if (isObjectLike(result) && 'errors' in result) {\n const errors = (result as { errors?: unknown }).errors;\n\n return Array.isArray(errors) && errors.length > 0;\n }\n\n return false;\n}\n\n/**\n * Serialize a parsed document into `graphql.document` while redacting every literal argument value:\n * the original source text is preserved verbatim except that string/number literal spans are\n * replaced (`\"foo\"` -> `\"*\"`, `42` -> `*`). graphql does not sanitize its channel payload, so this\n * prevents raw inline values (potential PII) from leaving the process. Variable values are never\n * included. Returns `undefined` (rather than throwing) on anything it cannot serialize.\n */\nexport function redactGraphqlDocument(document: GraphqlDocumentNode | undefined): string | undefined {\n const loc = document?.loc;\n const body = loc?.source?.body;\n if (typeof body !== 'string' || !loc?.startToken) {\n return undefined;\n }\n\n try {\n // Collect literal token spans, then splice them out back-to-front so earlier offsets stay valid.\n const ranges: Array<{ start: number; end: number; kind: string }> = [];\n for (let token: GraphqlToken | null | undefined = loc.startToken; token; token = token.next) {\n if (REDACTED_LITERAL_KINDS.has(token.kind)) {\n ranges.push({ start: token.start, end: token.end, kind: token.kind });\n }\n }\n\n let out = body;\n // Reverse index loop (not `for...of`) so we splice back-to-front; `i` is bounded by the loop, so\n // `ranges[i]` is always present and the `!` just satisfies `noUncheckedIndexedAccess`.\n for (let i = ranges.length - 1; i >= 0; i--) {\n const { start, end, kind } = ranges[i]!;\n const replacement = kind === 'String' || kind === 'BlockString' ? '\"*\"' : '*';\n out = out.slice(0, start) + replacement + out.slice(end);\n }\n\n return out;\n } catch {\n return undefined;\n }\n}\n"],"names":["getRootSpan","spanToJSON","SENTRY_GRAPHQL_OPERATION","isObjectLike"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const core = require('@sentry/core'); | ||
| const attributes = require('@sentry/conventions/attributes'); | ||
| const debugBuild = require('../../debug-build.js'); | ||
| const channels = require('../../orchestrion/channels.js'); | ||
| const tracingChannel = require('../../tracing-channel.js'); | ||
| const INTEGRATION_NAME = "Amqplib"; | ||
| const PUBLISHER_ORIGIN = "auto.amqplib.orchestrion.publisher"; | ||
| const CONSUMER_ORIGIN = "auto.amqplib.orchestrion.consumer"; | ||
| const ATTR_MESSAGING_OPERATION = "messaging.operation"; | ||
| const ATTR_MESSAGING_DESTINATION = "messaging.destination"; | ||
| const ATTR_MESSAGING_DESTINATION_KIND = "messaging.destination_kind"; | ||
| const ATTR_MESSAGING_RABBITMQ_ROUTING_KEY = "messaging.rabbitmq.routing_key"; | ||
| const ATTR_MESSAGING_PROTOCOL = "messaging.protocol"; | ||
| const ATTR_MESSAGING_PROTOCOL_VERSION_LEGACY = "messaging.protocol_version"; | ||
| const ATTR_MESSAGING_URL = "messaging.url"; | ||
| const ATTR_MESSAGING_MESSAGE_ID = "messaging.message_id"; | ||
| const ATTR_MESSAGING_CONVERSATION_ID_LEGACY = "messaging.conversation_id"; | ||
| const ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY = "messaging.rabbitmq.destination.routing_key"; | ||
| const ATTR_MESSAGING_CONVERSATION_ID = "messaging.message.conversation_id"; | ||
| const MESSAGING_DESTINATION_KIND_VALUE_TOPIC = "topic"; | ||
| const MESSAGING_OPERATION_VALUE_PROCESS = "process"; | ||
| const MESSAGING_OPERATION_VALUE_SEND = "send"; | ||
| const CONSUME_TIMEOUT_MS = 1e3 * 60; | ||
| const END_OP = { | ||
| Ack: "ack", | ||
| AckAll: "ackAll", | ||
| Reject: "reject", | ||
| Nack: "nack", | ||
| NackAll: "nackAll", | ||
| ChannelClosed: "channel closed", | ||
| ChannelError: "channel error", | ||
| InstrumentationTimeout: "instrumentation timeout" | ||
| }; | ||
| const MESSAGE_STORED_SPAN = /* @__PURE__ */ Symbol("sentry.amqplib.message.stored-span"); | ||
| const CHANNEL_SPANS_NOT_ENDED = /* @__PURE__ */ Symbol("sentry.amqplib.channel.spans-not-ended"); | ||
| const CHANNEL_CONSUME_TIMEOUT_TIMER = /* @__PURE__ */ Symbol("sentry.amqplib.channel.consume-timeout-timer"); | ||
| const CHANNEL_CONSUMER_INFO = /* @__PURE__ */ Symbol("sentry.amqplib.channel.consumer-info"); | ||
| const CHANNEL_IS_CONFIRM_PUBLISHING = /* @__PURE__ */ Symbol("sentry.amqplib.channel.is-confirm-publishing"); | ||
| const CONNECTION_ATTRIBUTES = /* @__PURE__ */ Symbol("sentry.amqplib.connection.attributes"); | ||
| const NOOP = () => { | ||
| }; | ||
| let subscribed = false; | ||
| const _amqplibChannelIntegration = (() => { | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel || subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| debugBuild.DEBUG_BUILD && core.debug.log("[orchestrion:amqplib] subscribing to amqplib tracing channels"); | ||
| core.waitForTracingChannelBinding(() => { | ||
| subscribeConnect(); | ||
| subscribePublish(); | ||
| subscribeConfirmPublish(); | ||
| subscribeConsume(); | ||
| subscribeDispatch(); | ||
| subscribeSettle(); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| function subscribePublish() { | ||
| tracingChannel.bindTracingChannelToSpan(diagnosticsChannel.tracingChannel(channels.CHANNELS.AMQPLIB_PUBLISH), (data) => { | ||
| if (data.self?.[CHANNEL_IS_CONFIRM_PUBLISHING]) { | ||
| return void 0; | ||
| } | ||
| return startPublishSpan(data); | ||
| }); | ||
| } | ||
| function subscribeConfirmPublish() { | ||
| const channel = diagnosticsChannel.tracingChannel(channels.CHANNELS.AMQPLIB_CONFIRM_PUBLISH); | ||
| tracingChannel.bindTracingChannelToSpan(channel, (data) => { | ||
| if (data.self) { | ||
| data.self[CHANNEL_IS_CONFIRM_PUBLISHING] = true; | ||
| } | ||
| return startPublishSpan(data); | ||
| }); | ||
| channel.end.subscribe((message) => { | ||
| const self = message.self; | ||
| if (self) { | ||
| self[CHANNEL_IS_CONFIRM_PUBLISHING] = false; | ||
| } | ||
| }); | ||
| } | ||
| function subscribeConsume() { | ||
| const channel = diagnosticsChannel.tracingChannel(channels.CHANNELS.AMQPLIB_CONSUME); | ||
| channel.start.subscribe(NOOP); | ||
| channel.asyncEnd.subscribe((message) => { | ||
| const data = message; | ||
| const consumerChannel = data.self; | ||
| const result = data.result; | ||
| const consumerTag = result?.consumerTag; | ||
| if (!consumerChannel || !consumerTag) { | ||
| return; | ||
| } | ||
| ensureChannelState(consumerChannel); | ||
| const queueArg = data.arguments[0]; | ||
| const queue = typeof queueArg === "string" ? queueArg : "<unknown>"; | ||
| const options = data.arguments[2]; | ||
| consumerChannel[CHANNEL_CONSUMER_INFO]?.set(consumerTag, { noAck: !!options?.noAck, queue }); | ||
| }); | ||
| } | ||
| function subscribeDispatch() { | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.AMQPLIB_DISPATCH), | ||
| (data) => { | ||
| const channel = data.self; | ||
| const fields = data.arguments[0]; | ||
| const msg = data.arguments[1]; | ||
| if (!channel || !msg) { | ||
| return void 0; | ||
| } | ||
| ensureChannelState(channel); | ||
| const info = fields?.consumerTag ? channel[CHANNEL_CONSUMER_INFO]?.get(fields.consumerTag) : void 0; | ||
| const queue = info?.queue ?? msg.fields?.routingKey ?? "<unknown>"; | ||
| const noAck = info?.noAck ?? false; | ||
| const headers = msg.properties?.headers; | ||
| const sentryTrace = getHeaderAsString(headers, "sentry-trace"); | ||
| const baggage = getHeaderAsString(headers, "baggage"); | ||
| const span = core.continueTrace({ sentryTrace, baggage }, () => startConsumeSpan(queue, msg, channel)); | ||
| if (!noAck) { | ||
| channel[CHANNEL_SPANS_NOT_ENDED]?.push({ msg, timeOfConsume: core.timestampInSeconds() }); | ||
| msg[MESSAGE_STORED_SPAN] = span; | ||
| } | ||
| data._sentryNoAck = noAck; | ||
| return span; | ||
| }, | ||
| { | ||
| // Manual-ack consumers: the span outlives the dispatch call and is ended by ack/nack/reject | ||
| // (or timeout/close), so take ownership and don't let the helper end it here. noAck consumers | ||
| // have no settle call, so let the helper end the span when dispatch returns. | ||
| deferSpanEnd({ data }) { | ||
| return !data._sentryNoAck; | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function subscribeSettle() { | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.AMQPLIB_ACK).start.subscribe((message) => handleAck(message, false, END_OP.Ack)); | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.AMQPLIB_NACK).start.subscribe((message) => handleAck(message, true, END_OP.Nack)); | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.AMQPLIB_REJECT).start.subscribe((message) => handleAck(message, true, END_OP.Reject)); | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.AMQPLIB_ACK_ALL).start.subscribe((message) => { | ||
| const data = message; | ||
| if (data.self) { | ||
| endAllSpansOnChannel(data.self, false, END_OP.AckAll, void 0); | ||
| } | ||
| }); | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.AMQPLIB_NACK_ALL).start.subscribe((message) => { | ||
| const data = message; | ||
| if (data.self) { | ||
| endAllSpansOnChannel(data.self, true, END_OP.NackAll, data.arguments[0]); | ||
| } | ||
| }); | ||
| } | ||
| function subscribeConnect() { | ||
| const channel = diagnosticsChannel.tracingChannel(channels.CHANNELS.AMQPLIB_CONNECT); | ||
| channel.start.subscribe(NOOP); | ||
| channel.asyncEnd.subscribe((message) => { | ||
| const data = message; | ||
| const conn = data.result; | ||
| if (!conn || typeof conn !== "object") { | ||
| return; | ||
| } | ||
| conn[CONNECTION_ATTRIBUTES] = { | ||
| ...getConnectionAttributesFromUrl(data.arguments?.[0]), | ||
| ...getConnectionAttributesFromServer(conn) | ||
| }; | ||
| }); | ||
| } | ||
| function handleAck(data, isRejected, endOperation) { | ||
| const channel = data.self; | ||
| if (!channel) { | ||
| return; | ||
| } | ||
| const message = data.arguments[0]; | ||
| if (!message) { | ||
| return; | ||
| } | ||
| const allUpToOrRequeue = data.arguments[1]; | ||
| const requeue = data.arguments[2]; | ||
| const requeueResolved = endOperation === END_OP.Reject ? allUpToOrRequeue : requeue; | ||
| const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? []; | ||
| const msgIndex = spansNotEnded.findIndex((msgDetails) => msgDetails.msg === message); | ||
| if (msgIndex < 0) { | ||
| endConsumerSpan(message, isRejected, endOperation, requeueResolved); | ||
| } else if (endOperation !== END_OP.Reject && allUpToOrRequeue) { | ||
| for (let i = 0; i <= msgIndex; i++) { | ||
| endConsumerSpan(spansNotEnded[i].msg, isRejected, endOperation, requeueResolved); | ||
| } | ||
| spansNotEnded.splice(0, msgIndex + 1); | ||
| } else { | ||
| endConsumerSpan(message, isRejected, endOperation, requeueResolved); | ||
| spansNotEnded.splice(msgIndex, 1); | ||
| } | ||
| } | ||
| function ensureChannelState(channel) { | ||
| if (Object.prototype.hasOwnProperty.call(channel, CHANNEL_SPANS_NOT_ENDED)) { | ||
| return; | ||
| } | ||
| channel[CHANNEL_SPANS_NOT_ENDED] = []; | ||
| channel[CHANNEL_CONSUMER_INFO] = /* @__PURE__ */ new Map(); | ||
| const timer = setInterval(() => checkConsumeTimeoutOnChannel(channel), CONSUME_TIMEOUT_MS); | ||
| timer.unref?.(); | ||
| channel[CHANNEL_CONSUME_TIMEOUT_TIMER] = timer; | ||
| if (typeof channel.on === "function") { | ||
| channel.on("close", () => { | ||
| endAllSpansOnChannel(channel, true, END_OP.ChannelClosed, void 0); | ||
| clearConsumeTimeoutTimer(channel); | ||
| }); | ||
| channel.on("error", () => { | ||
| endAllSpansOnChannel(channel, true, END_OP.ChannelError, void 0); | ||
| clearConsumeTimeoutTimer(channel); | ||
| }); | ||
| } | ||
| } | ||
| function clearConsumeTimeoutTimer(channel) { | ||
| const activeTimer = channel[CHANNEL_CONSUME_TIMEOUT_TIMER]; | ||
| if (activeTimer) { | ||
| clearInterval(activeTimer); | ||
| channel[CHANNEL_CONSUME_TIMEOUT_TIMER] = void 0; | ||
| } | ||
| } | ||
| function checkConsumeTimeoutOnChannel(channel) { | ||
| const currentTime = core.timestampInSeconds(); | ||
| const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? []; | ||
| let i; | ||
| for (i = 0; i < spansNotEnded.length; i++) { | ||
| const currMessage = spansNotEnded[i]; | ||
| const timeFromConsumeMs = (currentTime - currMessage.timeOfConsume) * 1e3; | ||
| if (timeFromConsumeMs < CONSUME_TIMEOUT_MS) { | ||
| break; | ||
| } | ||
| endConsumerSpan(currMessage.msg, null, END_OP.InstrumentationTimeout, true); | ||
| } | ||
| spansNotEnded.splice(0, i); | ||
| } | ||
| function endAllSpansOnChannel(channel, isRejected, operation, requeue) { | ||
| const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? []; | ||
| spansNotEnded.forEach((msgDetails) => { | ||
| endConsumerSpan(msgDetails.msg, isRejected, operation, requeue); | ||
| }); | ||
| channel[CHANNEL_SPANS_NOT_ENDED] = []; | ||
| } | ||
| function endConsumerSpan(message, isRejected, operation, requeue) { | ||
| const storedSpan = message[MESSAGE_STORED_SPAN]; | ||
| if (!storedSpan) { | ||
| return; | ||
| } | ||
| if (isRejected !== false) { | ||
| storedSpan.setStatus({ | ||
| code: core.SPAN_STATUS_ERROR, | ||
| message: operation !== END_OP.ChannelClosed && operation !== END_OP.ChannelError ? `${operation} called on message${requeue === true ? " with requeue" : requeue === false ? " without requeue" : ""}` : operation | ||
| }); | ||
| } | ||
| storedSpan.end(); | ||
| message[MESSAGE_STORED_SPAN] = void 0; | ||
| } | ||
| function startPublishSpan(data) { | ||
| const exchangeArg = data.arguments[0]; | ||
| const routingKeyArg = data.arguments[1]; | ||
| const exchange = typeof exchangeArg === "string" ? exchangeArg : ""; | ||
| const routingKey = typeof routingKeyArg === "string" ? routingKeyArg : ""; | ||
| let options = data.arguments[3]; | ||
| const span = core.startInactiveSpan({ | ||
| name: `publish ${normalizeExchange(exchange)}`, | ||
| op: "message", | ||
| kind: core.SPAN_KIND.PRODUCER, | ||
| attributes: { | ||
| ...getStoredConnectionAttributes(data.self), | ||
| [ATTR_MESSAGING_DESTINATION]: exchange, | ||
| // TODO(v11) remove this attribute | ||
| [attributes.MESSAGING_DESTINATION_NAME]: exchange, | ||
| [ATTR_MESSAGING_DESTINATION_KIND]: MESSAGING_DESTINATION_KIND_VALUE_TOPIC, | ||
| // TODO(v11) remove this attribute | ||
| [ATTR_MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey, | ||
| // TODO(v11) remove this attribute | ||
| [ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY]: routingKey, | ||
| [attributes.MESSAGING_OPERATION_TYPE]: MESSAGING_OPERATION_VALUE_SEND, | ||
| [ATTR_MESSAGING_MESSAGE_ID]: options?.messageId, | ||
| // todo(v11) remove this attribute | ||
| [attributes.MESSAGING_MESSAGE_ID]: options?.messageId, | ||
| [ATTR_MESSAGING_CONVERSATION_ID_LEGACY]: options?.correlationId, | ||
| // todo(v11) remove this attribute | ||
| [ATTR_MESSAGING_CONVERSATION_ID]: options?.correlationId, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: PUBLISHER_ORIGIN | ||
| } | ||
| }); | ||
| if (!options || typeof options !== "object") { | ||
| options = {}; | ||
| data.arguments[3] = options; | ||
| } | ||
| const headers = options.headers && typeof options.headers === "object" ? options.headers : options.headers = {}; | ||
| const traceData = core.getTraceData({ span }); | ||
| if (traceData["sentry-trace"]) { | ||
| headers["sentry-trace"] = traceData["sentry-trace"]; | ||
| } | ||
| if (traceData.baggage) { | ||
| headers["baggage"] = traceData.baggage; | ||
| } | ||
| return span; | ||
| } | ||
| function startConsumeSpan(queue, msg, channel) { | ||
| return core.startInactiveSpan({ | ||
| name: `${queue} process`, | ||
| op: "message", | ||
| kind: core.SPAN_KIND.CONSUMER, | ||
| attributes: { | ||
| ...getStoredConnectionAttributes(channel), | ||
| [ATTR_MESSAGING_DESTINATION]: msg.fields?.exchange, | ||
| // TODO(v11) remove this attribute | ||
| [attributes.MESSAGING_DESTINATION_NAME]: msg.fields?.exchange, | ||
| [ATTR_MESSAGING_DESTINATION_KIND]: MESSAGING_DESTINATION_KIND_VALUE_TOPIC, | ||
| // TODO(v11) remove this attribute | ||
| [ATTR_MESSAGING_RABBITMQ_ROUTING_KEY]: msg.fields?.routingKey, | ||
| // TODO(v11) remove this attribute | ||
| [ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY]: msg.fields?.routingKey, | ||
| [ATTR_MESSAGING_OPERATION]: MESSAGING_OPERATION_VALUE_PROCESS, | ||
| // TODO(v11) remove this attribute | ||
| [attributes.MESSAGING_OPERATION_TYPE]: MESSAGING_OPERATION_VALUE_PROCESS, | ||
| [ATTR_MESSAGING_MESSAGE_ID]: msg.properties?.messageId, | ||
| // todo(v11) remove this attribute | ||
| [attributes.MESSAGING_MESSAGE_ID]: msg.properties?.messageId, | ||
| [ATTR_MESSAGING_CONVERSATION_ID_LEGACY]: msg.properties?.correlationId, | ||
| // todo(v11) remove this attribute | ||
| [ATTR_MESSAGING_CONVERSATION_ID]: msg.properties?.correlationId, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: CONSUMER_ORIGIN | ||
| } | ||
| }); | ||
| } | ||
| function getStoredConnectionAttributes(channel) { | ||
| const connection = channel?.connection; | ||
| const stored = connection?.[CONNECTION_ATTRIBUTES]; | ||
| if (stored) { | ||
| return stored; | ||
| } | ||
| const product = connection?.serverProperties?.product ?? connection?.connection?.serverProperties?.product; | ||
| if (typeof product === "string" && product) { | ||
| return { [attributes.MESSAGING_SYSTEM]: product.toLowerCase() }; | ||
| } | ||
| return {}; | ||
| } | ||
| function getConnectionAttributesFromServer(conn) { | ||
| const product = conn.serverProperties?.product ?? conn.connection?.serverProperties?.product; | ||
| if (typeof product === "string" && product) { | ||
| return { [attributes.MESSAGING_SYSTEM]: product.toLowerCase() }; | ||
| } | ||
| return {}; | ||
| } | ||
| function getConnectionAttributesFromUrl(url) { | ||
| const attributes$1 = { | ||
| // The only protocol supported by the instrumented library. | ||
| [ATTR_MESSAGING_PROTOCOL_VERSION_LEGACY]: "0.9.1", | ||
| // TODO(v11): remove this attribute | ||
| [attributes.NETWORK_PROTOCOL_VERSION]: "0.9.1" | ||
| }; | ||
| const resolvedUrl = url || "amqp://localhost"; | ||
| if (typeof resolvedUrl === "object") { | ||
| const connectOptions = resolvedUrl; | ||
| const protocol = getProtocol(connectOptions.protocol); | ||
| const hostname = getHostname(connectOptions.hostname); | ||
| const port = getPort(connectOptions.port, protocol); | ||
| attributes$1[ATTR_MESSAGING_PROTOCOL] = protocol; | ||
| attributes$1[attributes.NETWORK_PROTOCOL_NAME] = protocol; | ||
| attributes$1[attributes.SERVER_ADDRESS] = hostname; | ||
| attributes$1[attributes.SERVER_PORT] = port; | ||
| attributes$1[attributes.NET_PEER_NAME] = hostname; | ||
| attributes$1[attributes.NET_PEER_PORT] = port; | ||
| } else if (typeof resolvedUrl === "string") { | ||
| const censoredUrl = censorPassword(resolvedUrl); | ||
| attributes$1[ATTR_MESSAGING_URL] = censoredUrl; | ||
| attributes$1[attributes.URL_FULL] = censoredUrl; | ||
| try { | ||
| const urlParts = new URL(censoredUrl); | ||
| const protocol = getProtocol(urlParts.protocol); | ||
| const hostname = getHostname(urlParts.hostname); | ||
| const port = getPort(urlParts.port ? parseInt(urlParts.port, 10) : void 0, protocol); | ||
| attributes$1[ATTR_MESSAGING_PROTOCOL] = protocol; | ||
| attributes$1[attributes.NETWORK_PROTOCOL_NAME] = protocol; | ||
| attributes$1[attributes.SERVER_ADDRESS] = hostname; | ||
| attributes$1[attributes.SERVER_PORT] = port; | ||
| attributes$1[attributes.NET_PEER_NAME] = hostname; | ||
| attributes$1[attributes.NET_PEER_PORT] = port; | ||
| } catch { | ||
| } | ||
| } | ||
| return attributes$1; | ||
| } | ||
| function normalizeExchange(exchangeName) { | ||
| return exchangeName !== "" ? exchangeName : "<default>"; | ||
| } | ||
| function censorPassword(url) { | ||
| return url.replace(/:[^:@/]*@/, ":***@"); | ||
| } | ||
| function getPort(portFromUrl, resolvedProtocol) { | ||
| return portFromUrl || (resolvedProtocol === "AMQP" ? 5672 : 5671); | ||
| } | ||
| function getProtocol(protocolFromUrl) { | ||
| const resolvedProtocol = protocolFromUrl || "amqp"; | ||
| const noEndingColon = resolvedProtocol.endsWith(":") ? resolvedProtocol.substring(0, resolvedProtocol.length - 1) : resolvedProtocol; | ||
| return noEndingColon.toUpperCase(); | ||
| } | ||
| function getHostname(hostnameFromUrl) { | ||
| return hostnameFromUrl || "localhost"; | ||
| } | ||
| function getHeaderAsString(headers, key) { | ||
| const value = headers?.[key]; | ||
| if (value == null) { | ||
| return void 0; | ||
| } | ||
| return Array.isArray(value) ? String(value[0]) : String(value); | ||
| } | ||
| const amqplibChannelIntegration = core.defineIntegration(_amqplibChannelIntegration); | ||
| exports.amqplibChannelIntegration = amqplibChannelIntegration; | ||
| //# sourceMappingURL=amqplib.js.map |
| {"version":3,"file":"amqplib.js","sources":["../../../../src/integrations/tracing-channel/amqplib.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Span, SpanAttributes } from '@sentry/core';\nimport {\n continueTrace,\n debug,\n defineIntegration,\n getTraceData,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n timestampInSeconds,\n waitForTracingChannelBinding,\n} from '@sentry/core';\n// eslint-disable-next-line typescript/no-deprecated -- NET_PEER_* emitted alongside SERVER_* for backwards compatibility (TODO(v11): remove)\nimport {\n MESSAGING_SYSTEM,\n MESSAGING_MESSAGE_ID,\n MESSAGING_OPERATION_TYPE,\n MESSAGING_DESTINATION_NAME,\n NET_PEER_NAME,\n NET_PEER_PORT,\n NETWORK_PROTOCOL_NAME,\n NETWORK_PROTOCOL_VERSION,\n SERVER_ADDRESS,\n SERVER_PORT,\n URL_FULL,\n} from '@sentry/conventions/attributes';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, the OTel 'Amqplib' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Amqplib' as const;\n\nconst PUBLISHER_ORIGIN = 'auto.amqplib.orchestrion.publisher';\nconst CONSUMER_ORIGIN = 'auto.amqplib.orchestrion.consumer';\n\n// Legacy messaging semantic-conventions, inlined to keep this integration free of `@opentelemetry/*`\n// deps. These mirror what the vendored OTel amqplib instrumentation has always emitted. We keep\n// emitting them alongside the current `@sentry/conventions` attributes for backwards compatibility.\n// TODO(v11): remove these legacy attributes.\nconst ATTR_MESSAGING_OPERATION = 'messaging.operation';\nconst ATTR_MESSAGING_DESTINATION = 'messaging.destination';\nconst ATTR_MESSAGING_DESTINATION_KIND = 'messaging.destination_kind';\nconst ATTR_MESSAGING_RABBITMQ_ROUTING_KEY = 'messaging.rabbitmq.routing_key';\nconst ATTR_MESSAGING_PROTOCOL = 'messaging.protocol';\nconst ATTR_MESSAGING_PROTOCOL_VERSION_LEGACY = 'messaging.protocol_version';\nconst ATTR_MESSAGING_URL = 'messaging.url';\nconst ATTR_MESSAGING_MESSAGE_ID = 'messaging.message_id';\nconst ATTR_MESSAGING_CONVERSATION_ID_LEGACY = 'messaging.conversation_id';\n\n// TODO(v11): replace with the corresponding attribute from `@sentry/conventions` once it is added there.\nconst ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY = 'messaging.rabbitmq.destination.routing_key';\nconst ATTR_MESSAGING_CONVERSATION_ID = 'messaging.message.conversation_id';\n\nconst MESSAGING_DESTINATION_KIND_VALUE_TOPIC = 'topic';\nconst MESSAGING_OPERATION_VALUE_PROCESS = 'process';\nconst MESSAGING_OPERATION_VALUE_SEND = 'send';\n\n// To prevent reference leaks from un-acked messages, their spans are closed after this timeout. The\n// upstream instrumentation exposed this as the `consumeTimeoutMs` option; the SDK always used the default.\nconst CONSUME_TIMEOUT_MS = 1000 * 60; // 1 minute\n\n// The end-operation labels used in the consumer span's error status message.\nconst END_OP = {\n Ack: 'ack',\n AckAll: 'ackAll',\n Reject: 'reject',\n Nack: 'nack',\n NackAll: 'nackAll',\n ChannelClosed: 'channel closed',\n ChannelError: 'channel error',\n InstrumentationTimeout: 'instrumentation timeout',\n} as const;\ntype EndOp = (typeof END_OP)[keyof typeof END_OP];\n\n// State stashed on the live amqplib objects (mirrors the vendored OTel instrumentation's symbols).\nconst MESSAGE_STORED_SPAN: unique symbol = Symbol('sentry.amqplib.message.stored-span');\nconst CHANNEL_SPANS_NOT_ENDED: unique symbol = Symbol('sentry.amqplib.channel.spans-not-ended');\nconst CHANNEL_CONSUME_TIMEOUT_TIMER: unique symbol = Symbol('sentry.amqplib.channel.consume-timeout-timer');\nconst CHANNEL_CONSUMER_INFO: unique symbol = Symbol('sentry.amqplib.channel.consumer-info');\nconst CHANNEL_IS_CONFIRM_PUBLISHING: unique symbol = Symbol('sentry.amqplib.channel.is-confirm-publishing');\nconst CONNECTION_ATTRIBUTES: unique symbol = Symbol('sentry.amqplib.connection.attributes');\n\ninterface MessageFields {\n deliveryTag?: number;\n exchange?: string;\n routingKey?: string;\n consumerTag?: string;\n}\n\ninterface MessageProperties {\n headers?: Record<string, unknown>;\n messageId?: unknown;\n correlationId?: unknown;\n}\n\ninterface ConsumeMessage {\n fields?: MessageFields;\n properties?: MessageProperties;\n [MESSAGE_STORED_SPAN]?: Span;\n}\n\ninterface PublishOptions {\n headers?: Record<string, unknown>;\n messageId?: unknown;\n correlationId?: unknown;\n}\n\ninterface ConnectionLike {\n serverProperties?: { product?: string };\n // Some amqplib versions nest the low-level connection one level deeper.\n connection?: { serverProperties?: { product?: string } };\n [CONNECTION_ATTRIBUTES]?: SpanAttributes;\n}\n\ninterface ConsumerInfo {\n noAck: boolean;\n queue: string;\n}\n\ninterface ChannelLike {\n connection?: ConnectionLike;\n on?: (event: string, listener: (...args: unknown[]) => void) => unknown;\n [CHANNEL_SPANS_NOT_ENDED]?: { msg: ConsumeMessage; timeOfConsume: number }[];\n [CHANNEL_CONSUME_TIMEOUT_TIMER]?: ReturnType<typeof setInterval>;\n [CHANNEL_CONSUMER_INFO]?: Map<string, ConsumerInfo>;\n [CHANNEL_IS_CONFIRM_PUBLISHING]?: boolean;\n}\n\n/**\n * The shape orchestrion's transform attaches to the tracing-channel `context`. Documented here rather\n * than imported because orchestrion's runtime doesn't export it.\n */\ninterface AmqpChannelContext {\n // The live args array passed to the wrapped call.\n arguments: unknown[];\n self?: ChannelLike;\n result?: unknown;\n error?: unknown;\n}\n\ninterface AmqpDispatchContext extends AmqpChannelContext {\n // Whether the delivering consumer registered with `noAck`; set at span creation so `deferSpanEnd`\n // knows whether the helper should end the span (noAck) or leave it open until ack/nack (manual ack).\n _sentryNoAck?: boolean;\n}\n\ninterface AmqpConnectContext {\n arguments?: unknown[];\n result?: unknown;\n}\n\nconst NOOP = (): void => {};\n\n// Guards against subscribing to the amqplib channels more than once in a process. Core dedupes\n// `setupOnce` by integration *name*, which is not enough here: the Deno SDK wraps this integration\n// under a different name (`DenoAmqplib`) via `extendIntegration`, so adding both would otherwise run\n// the subscribe logic twice and emit duplicate spans for every operation.\nlet subscribed = false;\n\nconst _amqplibChannelIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel || subscribed) {\n return;\n }\n subscribed = true;\n\n DEBUG_BUILD && debug.log('[orchestrion:amqplib] subscribing to amqplib tracing channels');\n\n waitForTracingChannelBinding(() => {\n subscribeConnect();\n subscribePublish();\n subscribeConfirmPublish();\n subscribeConsume();\n subscribeDispatch();\n subscribeSettle();\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Producer span for `Channel.prototype.publish`. Creates a PRODUCER span, injects the trace headers\n * into the publish options, and ends when the (synchronous) publish call returns. Skips the confirm\n * channel's internal `super.publish` call, which is already handled by {@link subscribeConfirmPublish}.\n */\nfunction subscribePublish(): void {\n bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_PUBLISH), data => {\n if (data.self?.[CHANNEL_IS_CONFIRM_PUBLISHING]) {\n return undefined;\n }\n return startPublishSpan(data);\n });\n}\n\n/**\n * Producer span for `ConfirmChannel.prototype.publish`. The span ends on the broker-confirm callback\n * (the channel's trailing `cb` arg, wrapped by orchestrion) rather than synchronously. A synchronous\n * flag on the channel suppresses the base `publish` channel that `super.publish` triggers.\n */\nfunction subscribeConfirmPublish(): void {\n const channel = diagnosticsChannel.tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_CONFIRM_PUBLISH);\n\n bindTracingChannelToSpan(channel, data => {\n if (data.self) {\n data.self[CHANNEL_IS_CONFIRM_PUBLISHING] = true;\n }\n return startPublishSpan(data);\n });\n\n // `super.publish` runs synchronously inside the confirm publish body, so clearing the flag on the\n // synchronous `end` (which fires after the body returns) is enough to guard the base publish.\n channel.end.subscribe(message => {\n const self = (message as AmqpChannelContext).self;\n if (self) {\n self[CHANNEL_IS_CONFIRM_PUBLISHING] = false;\n }\n });\n}\n\n/**\n * Records `consumerTag -> { noAck, queue }` when a consumer is registered, so the per-message\n * dispatch hook can name the span after the queue and know when to end it.\n */\nfunction subscribeConsume(): void {\n const channel = diagnosticsChannel.tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_CONSUME);\n\n // A `start` subscriber is required for orchestrion to wrap `consume` at all.\n channel.start.subscribe(NOOP);\n channel.asyncEnd.subscribe(message => {\n const data = message as AmqpChannelContext;\n const consumerChannel = data.self;\n const result = data.result as { consumerTag?: string } | undefined;\n const consumerTag = result?.consumerTag;\n if (!consumerChannel || !consumerTag) {\n return;\n }\n\n ensureChannelState(consumerChannel);\n const queueArg = data.arguments[0];\n const queue = typeof queueArg === 'string' ? queueArg : '<unknown>';\n const options = data.arguments[2] as { noAck?: boolean } | undefined;\n consumerChannel[CHANNEL_CONSUMER_INFO]?.set(consumerTag, { noAck: !!options?.noAck, queue });\n });\n}\n\n/**\n * Per delivered message (`BaseChannel.prototype.dispatchMessage`): continues the producer's trace,\n * opens a CONSUMER span, and runs the user callback under it. Manual-ack consumers keep the span open\n * until `ack`/`nack`/`reject`/timeout/close; `noAck` consumers end it when dispatch returns.\n */\nfunction subscribeDispatch(): void {\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<AmqpDispatchContext>(CHANNELS.AMQPLIB_DISPATCH),\n data => {\n const channel = data.self;\n const fields = data.arguments[0] as MessageFields | undefined;\n const msg = data.arguments[1] as ConsumeMessage | null | undefined;\n // `message` is null for a consumer-cancel notification: not a real message, so no span.\n if (!channel || !msg) {\n return undefined;\n }\n\n ensureChannelState(channel);\n const info = fields?.consumerTag ? channel[CHANNEL_CONSUMER_INFO]?.get(fields.consumerTag) : undefined;\n const queue = info?.queue ?? msg.fields?.routingKey ?? '<unknown>';\n const noAck = info?.noAck ?? false;\n\n const headers = msg.properties?.headers;\n const sentryTrace = getHeaderAsString(headers, 'sentry-trace');\n const baggage = getHeaderAsString(headers, 'baggage');\n\n // Continue the producer's trace so the consumer span links back to the publishing service.\n const span = continueTrace({ sentryTrace, baggage }, () => startConsumeSpan(queue, msg, channel));\n\n if (!noAck) {\n // Track the message so its span can be ended when the user calls ack/nack/reject (or on\n // timeout/channel close).\n channel[CHANNEL_SPANS_NOT_ENDED]?.push({ msg, timeOfConsume: timestampInSeconds() });\n msg[MESSAGE_STORED_SPAN] = span;\n }\n\n data._sentryNoAck = noAck;\n return span;\n },\n {\n // Manual-ack consumers: the span outlives the dispatch call and is ended by ack/nack/reject\n // (or timeout/close), so take ownership and don't let the helper end it here. noAck consumers\n // have no settle call, so let the helper end the span when dispatch returns.\n deferSpanEnd({ data }) {\n return !data._sentryNoAck;\n },\n },\n );\n}\n\n/** Ends consumer spans when the user acks/nacks/rejects a message or the channel closes/errors. */\nfunction subscribeSettle(): void {\n diagnosticsChannel\n .tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_ACK)\n .start.subscribe(message => handleAck(message as AmqpChannelContext, false, END_OP.Ack));\n diagnosticsChannel\n .tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_NACK)\n .start.subscribe(message => handleAck(message as AmqpChannelContext, true, END_OP.Nack));\n diagnosticsChannel\n .tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_REJECT)\n .start.subscribe(message => handleAck(message as AmqpChannelContext, true, END_OP.Reject));\n diagnosticsChannel.tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_ACK_ALL).start.subscribe(message => {\n const data = message as AmqpChannelContext;\n if (data.self) {\n endAllSpansOnChannel(data.self, false, END_OP.AckAll, undefined);\n }\n });\n diagnosticsChannel.tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_NACK_ALL).start.subscribe(message => {\n const data = message as AmqpChannelContext;\n if (data.self) {\n endAllSpansOnChannel(data.self, true, END_OP.NackAll, data.arguments[0] as boolean | undefined);\n }\n });\n}\n\n/** Captures connection attributes on the connection object for span-time reads via `channel.connection`. */\nfunction subscribeConnect(): void {\n const channel = diagnosticsChannel.tracingChannel<AmqpConnectContext>(CHANNELS.AMQPLIB_CONNECT);\n // A `start` subscriber is required for orchestrion to wrap the callback-style `connect` at all.\n channel.start.subscribe(NOOP);\n channel.asyncEnd.subscribe(message => {\n const data = message as AmqpConnectContext;\n const conn = data.result as ConnectionLike | undefined;\n if (!conn || typeof conn !== 'object') {\n return;\n }\n conn[CONNECTION_ATTRIBUTES] = {\n ...getConnectionAttributesFromUrl(data.arguments?.[0]),\n ...getConnectionAttributesFromServer(conn),\n };\n });\n}\n\nfunction handleAck(data: AmqpChannelContext, isRejected: boolean, endOperation: EndOp): void {\n const channel = data.self;\n if (!channel) {\n return;\n }\n const message = data.arguments[0] as ConsumeMessage | undefined;\n if (!message) {\n return;\n }\n\n // `reject(message, requeue)` carries requeue in arg 1; `ack`/`nack` carry `allUpTo` in arg 1.\n const allUpToOrRequeue = data.arguments[1] as boolean | undefined;\n const requeue = data.arguments[2] as boolean | undefined;\n const requeueResolved = endOperation === END_OP.Reject ? allUpToOrRequeue : requeue;\n\n const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? [];\n const msgIndex = spansNotEnded.findIndex(msgDetails => msgDetails.msg === message);\n if (msgIndex < 0) {\n // Not tracked (e.g. the user acked the same message twice) — end the stored span directly.\n endConsumerSpan(message, isRejected, endOperation, requeueResolved);\n } else if (endOperation !== END_OP.Reject && allUpToOrRequeue) {\n for (let i = 0; i <= msgIndex; i++) {\n endConsumerSpan(spansNotEnded[i]!.msg, isRejected, endOperation, requeueResolved);\n }\n spansNotEnded.splice(0, msgIndex + 1);\n } else {\n endConsumerSpan(message, isRejected, endOperation, requeueResolved);\n spansNotEnded.splice(msgIndex, 1);\n }\n}\n\nfunction ensureChannelState(channel: ChannelLike): void {\n if (Object.prototype.hasOwnProperty.call(channel, CHANNEL_SPANS_NOT_ENDED)) {\n return;\n }\n\n channel[CHANNEL_SPANS_NOT_ENDED] = [];\n channel[CHANNEL_CONSUMER_INFO] = new Map();\n\n const timer = setInterval(() => checkConsumeTimeoutOnChannel(channel), CONSUME_TIMEOUT_MS);\n timer.unref?.();\n channel[CHANNEL_CONSUME_TIMEOUT_TIMER] = timer;\n\n // End outstanding spans and stop the timer when the channel goes away (replaces patching `emit`).\n // amqplib emits 'close' after 'error', but we clear in both to avoid leaking the interval (which\n // pins the channel via its closure) should a version or edge case ever skip the trailing 'close'.\n if (typeof channel.on === 'function') {\n channel.on('close', () => {\n endAllSpansOnChannel(channel, true, END_OP.ChannelClosed, undefined);\n clearConsumeTimeoutTimer(channel);\n });\n channel.on('error', () => {\n endAllSpansOnChannel(channel, true, END_OP.ChannelError, undefined);\n clearConsumeTimeoutTimer(channel);\n });\n }\n}\n\n/** Stops and clears the per-channel consume-timeout interval. Idempotent. */\nfunction clearConsumeTimeoutTimer(channel: ChannelLike): void {\n const activeTimer = channel[CHANNEL_CONSUME_TIMEOUT_TIMER];\n if (activeTimer) {\n clearInterval(activeTimer);\n channel[CHANNEL_CONSUME_TIMEOUT_TIMER] = undefined;\n }\n}\n\nfunction checkConsumeTimeoutOnChannel(channel: ChannelLike): void {\n const currentTime = timestampInSeconds();\n const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? [];\n let i: number;\n for (i = 0; i < spansNotEnded.length; i++) {\n const currMessage = spansNotEnded[i]!;\n const timeFromConsumeMs = (currentTime - currMessage.timeOfConsume) * 1000;\n if (timeFromConsumeMs < CONSUME_TIMEOUT_MS) {\n break;\n }\n endConsumerSpan(currMessage.msg, null, END_OP.InstrumentationTimeout, true);\n }\n spansNotEnded.splice(0, i);\n}\n\nfunction endAllSpansOnChannel(\n channel: ChannelLike,\n isRejected: boolean,\n operation: EndOp,\n requeue: boolean | undefined,\n): void {\n const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? [];\n spansNotEnded.forEach(msgDetails => {\n endConsumerSpan(msgDetails.msg, isRejected, operation, requeue);\n });\n channel[CHANNEL_SPANS_NOT_ENDED] = [];\n}\n\nfunction endConsumerSpan(\n message: ConsumeMessage,\n isRejected: boolean | null,\n operation: EndOp,\n requeue: boolean | undefined,\n): void {\n const storedSpan = message[MESSAGE_STORED_SPAN];\n if (!storedSpan) {\n return;\n }\n if (isRejected !== false) {\n storedSpan.setStatus({\n code: SPAN_STATUS_ERROR,\n message:\n operation !== END_OP.ChannelClosed && operation !== END_OP.ChannelError\n ? `${operation} called on message${\n requeue === true ? ' with requeue' : requeue === false ? ' without requeue' : ''\n }`\n : operation,\n });\n }\n storedSpan.end();\n message[MESSAGE_STORED_SPAN] = undefined;\n}\n\n/** Starts an inactive PRODUCER span and propagates its trace into the publish `options.headers`. */\nfunction startPublishSpan(data: AmqpChannelContext): Span {\n const exchangeArg = data.arguments[0];\n const routingKeyArg = data.arguments[1];\n const exchange = typeof exchangeArg === 'string' ? exchangeArg : '';\n const routingKey = typeof routingKeyArg === 'string' ? routingKeyArg : '';\n let options = data.arguments[3] as PublishOptions | undefined;\n\n const span = startInactiveSpan({\n name: `publish ${normalizeExchange(exchange)}`,\n op: 'message',\n kind: SPAN_KIND.PRODUCER,\n attributes: {\n ...getStoredConnectionAttributes(data.self),\n [ATTR_MESSAGING_DESTINATION]: exchange, // TODO(v11) remove this attribute\n [MESSAGING_DESTINATION_NAME]: exchange,\n [ATTR_MESSAGING_DESTINATION_KIND]: MESSAGING_DESTINATION_KIND_VALUE_TOPIC, // TODO(v11) remove this attribute\n [ATTR_MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey, // TODO(v11) remove this attribute\n [ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY]: routingKey,\n [MESSAGING_OPERATION_TYPE]: MESSAGING_OPERATION_VALUE_SEND,\n [ATTR_MESSAGING_MESSAGE_ID]: options?.messageId as string | undefined, // todo(v11) remove this attribute\n [MESSAGING_MESSAGE_ID]: options?.messageId as string | undefined,\n [ATTR_MESSAGING_CONVERSATION_ID_LEGACY]: options?.correlationId as string | undefined, // todo(v11) remove this attribute\n [ATTR_MESSAGING_CONVERSATION_ID]: options?.correlationId as string | undefined,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: PUBLISHER_ORIGIN,\n },\n });\n\n if (!options || typeof options !== 'object') {\n options = {};\n data.arguments[3] = options;\n }\n const headers = options.headers && typeof options.headers === 'object' ? options.headers : (options.headers = {});\n const traceData = getTraceData({ span });\n if (traceData['sentry-trace']) {\n headers['sentry-trace'] = traceData['sentry-trace'];\n }\n if (traceData.baggage) {\n headers['baggage'] = traceData.baggage;\n }\n\n return span;\n}\n\n/** Starts an inactive CONSUMER (process) span carrying the amqplib messaging attributes. */\nfunction startConsumeSpan(queue: string, msg: ConsumeMessage, channel: ChannelLike): Span {\n return startInactiveSpan({\n name: `${queue} process`,\n op: 'message',\n kind: SPAN_KIND.CONSUMER,\n attributes: {\n ...getStoredConnectionAttributes(channel),\n [ATTR_MESSAGING_DESTINATION]: msg.fields?.exchange, // TODO(v11) remove this attribute\n [MESSAGING_DESTINATION_NAME]: msg.fields?.exchange,\n [ATTR_MESSAGING_DESTINATION_KIND]: MESSAGING_DESTINATION_KIND_VALUE_TOPIC, // TODO(v11) remove this attribute\n [ATTR_MESSAGING_RABBITMQ_ROUTING_KEY]: msg.fields?.routingKey, // TODO(v11) remove this attribute\n [ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY]: msg.fields?.routingKey,\n [ATTR_MESSAGING_OPERATION]: MESSAGING_OPERATION_VALUE_PROCESS, // TODO(v11) remove this attribute\n [MESSAGING_OPERATION_TYPE]: MESSAGING_OPERATION_VALUE_PROCESS,\n [ATTR_MESSAGING_MESSAGE_ID]: msg.properties?.messageId as string | undefined, // todo(v11) remove this attribute\n [MESSAGING_MESSAGE_ID]: msg.properties?.messageId as string | undefined,\n [ATTR_MESSAGING_CONVERSATION_ID_LEGACY]: msg.properties?.correlationId as string | undefined, // todo(v11) remove this attribute\n [ATTR_MESSAGING_CONVERSATION_ID]: msg.properties?.correlationId as string | undefined,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: CONSUMER_ORIGIN,\n },\n });\n}\n\n/**\n * Reads the connection attributes stashed by the `connect` channel, falling back to the live\n * connection's server product so `messaging.system` is populated even when the connection was\n * established before the integration ran.\n */\nfunction getStoredConnectionAttributes(channel: ChannelLike | undefined): SpanAttributes {\n const connection = channel?.connection;\n const stored = connection?.[CONNECTION_ATTRIBUTES];\n if (stored) {\n return stored;\n }\n const product = connection?.serverProperties?.product ?? connection?.connection?.serverProperties?.product;\n if (typeof product === 'string' && product) {\n return { [MESSAGING_SYSTEM]: product.toLowerCase() };\n }\n return {};\n}\n\nfunction getConnectionAttributesFromServer(conn: ConnectionLike): SpanAttributes {\n const product = conn.serverProperties?.product ?? conn.connection?.serverProperties?.product;\n if (typeof product === 'string' && product) {\n return { [MESSAGING_SYSTEM]: product.toLowerCase() };\n }\n return {};\n}\n\nfunction getConnectionAttributesFromUrl(url: unknown): SpanAttributes {\n const attributes: SpanAttributes = {\n // The only protocol supported by the instrumented library.\n [ATTR_MESSAGING_PROTOCOL_VERSION_LEGACY]: '0.9.1', // TODO(v11): remove this attribute\n [NETWORK_PROTOCOL_VERSION]: '0.9.1',\n };\n\n const resolvedUrl = url || 'amqp://localhost';\n if (typeof resolvedUrl === 'object') {\n const connectOptions = resolvedUrl as { protocol?: string; hostname?: string; port?: number };\n const protocol = getProtocol(connectOptions.protocol);\n const hostname = getHostname(connectOptions.hostname);\n const port = getPort(connectOptions.port, protocol);\n\n attributes[ATTR_MESSAGING_PROTOCOL] = protocol; // TODO(v11) remove this attribute\n attributes[NETWORK_PROTOCOL_NAME] = protocol;\n\n attributes[SERVER_ADDRESS] = hostname;\n attributes[SERVER_PORT] = port;\n // TODO(v11): remove deprecated options\n // eslint-disable-next-line typescript/no-deprecated -- emitted alongside SERVER_ADDRESS/SERVER_PORT for backwards compatibility\n attributes[NET_PEER_NAME] = hostname;\n // eslint-disable-next-line typescript/no-deprecated -- emitted alongside SERVER_ADDRESS/SERVER_PORT for backwards compatibility\n attributes[NET_PEER_PORT] = port;\n } else if (typeof resolvedUrl === 'string') {\n const censoredUrl = censorPassword(resolvedUrl);\n attributes[ATTR_MESSAGING_URL] = censoredUrl; // todo(v11) remove this attribute\n attributes[URL_FULL] = censoredUrl;\n\n try {\n const urlParts = new URL(censoredUrl);\n const protocol = getProtocol(urlParts.protocol);\n const hostname = getHostname(urlParts.hostname);\n const port = getPort(urlParts.port ? parseInt(urlParts.port, 10) : undefined, protocol);\n\n attributes[ATTR_MESSAGING_PROTOCOL] = protocol; // TODO(v11) remove this attribute\n attributes[NETWORK_PROTOCOL_NAME] = protocol;\n\n attributes[SERVER_ADDRESS] = hostname;\n attributes[SERVER_PORT] = port;\n // eslint-disable-next-line typescript/no-deprecated -- emitted alongside SERVER_ADDRESS/SERVER_PORT for backwards compatibility\n attributes[NET_PEER_NAME] = hostname;\n // eslint-disable-next-line typescript/no-deprecated -- emitted alongside SERVER_ADDRESS/SERVER_PORT for backwards compatibility\n attributes[NET_PEER_PORT] = port;\n } catch {\n // best-effort: a malformed url simply yields fewer connection attributes\n }\n }\n return attributes;\n}\n\nfunction normalizeExchange(exchangeName: string): string {\n return exchangeName !== '' ? exchangeName : '<default>';\n}\n\nfunction censorPassword(url: string): string {\n return url.replace(/:[^:@/]*@/, ':***@');\n}\n\nfunction getPort(portFromUrl: number | undefined, resolvedProtocol: string): number {\n // Mimics amqplib's own defaulting; the resolved protocol is upper-cased.\n return portFromUrl || (resolvedProtocol === 'AMQP' ? 5672 : 5671);\n}\n\nfunction getProtocol(protocolFromUrl: string | undefined): string {\n const resolvedProtocol = protocolFromUrl || 'amqp';\n const noEndingColon = resolvedProtocol.endsWith(':')\n ? resolvedProtocol.substring(0, resolvedProtocol.length - 1)\n : resolvedProtocol;\n return noEndingColon.toUpperCase();\n}\n\nfunction getHostname(hostnameFromUrl: string | undefined): string {\n // An empty hostname is forwarded to `net`, which defaults it to localhost.\n return hostnameFromUrl || 'localhost';\n}\n\nfunction getHeaderAsString(headers: Record<string, unknown> | undefined, key: string): string | undefined {\n const value = headers?.[key];\n if (value == null) {\n return undefined;\n }\n return Array.isArray(value) ? String(value[0]) : String(value);\n}\n\n/**\n * EXPERIMENTAL: orchestrion-driven `amqplib` integration.\n *\n * Subscribes to the `orchestrion:amqplib:*` diagnostics_channels that the orchestrion code transform\n * injects into `amqplib`'s channel/connection methods. Requires the orchestrion runtime hook or\n * bundler plugin to be active.\n */\nexport const amqplibChannelIntegration = defineIntegration(_amqplibChannelIntegration);\n"],"names":["DEBUG_BUILD","debug","waitForTracingChannelBinding","bindTracingChannelToSpan","CHANNELS","continueTrace","timestampInSeconds","SPAN_STATUS_ERROR","startInactiveSpan","SPAN_KIND","MESSAGING_DESTINATION_NAME","MESSAGING_OPERATION_TYPE","MESSAGING_MESSAGE_ID","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","getTraceData","MESSAGING_SYSTEM","attributes","NETWORK_PROTOCOL_VERSION","NETWORK_PROTOCOL_NAME","SERVER_ADDRESS","SERVER_PORT","NET_PEER_NAME","NET_PEER_PORT","URL_FULL","defineIntegration"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const core = require('@sentry/core'); | ||
| const instrumentation = require('./instrumentation.js'); | ||
| const INTEGRATION_NAME = "Express"; | ||
| const _expressChannelIntegration = ((options = {}) => { | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| core.waitForTracingChannelBinding(() => { | ||
| instrumentation.instrumentExpress(options, diagnosticsChannel.tracingChannel); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const expressChannelIntegration = core.defineIntegration(_expressChannelIntegration); | ||
| exports.expressChannelIntegration = expressChannelIntegration; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing-channel/express/index.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, waitForTracingChannelBinding } from '@sentry/core';\nimport type { ExpressIntegrationOptions } from './types';\nimport { instrumentExpress } from './instrumentation';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, the OTel 'Express' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Express' as const;\n\nconst _expressChannelIntegration = ((options: ExpressIntegrationOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n instrumentExpress(options, diagnosticsChannel.tracingChannel);\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * EXPERIMENTAL — orchestrion-driven Express integration.\n *\n * Subscribes to the `orchestrion:express:handle` (Express v4) and\n * `orchestrion:router:handle` (Express v5, via the `router` package)\n * diagnostics_channels that the orchestrion code transform injects into the\n * routing layer's request handler (`Layer.prototype.handle_request` /\n * `handleRequest`). One span is opened per layer invocation — producing the\n * same spans as the OTel Express instrumentation.\n *\n * Requires the orchestrion runtime hook or bundler plugin to be active — wire\n * that up via `experimentalUseDiagnosticsChannelInjection()`.\n */\nexport const expressChannelIntegration = defineIntegration(_expressChannelIntegration);\n"],"names":["waitForTracingChannelBinding","instrumentExpress","defineIntegration"],"mappings":";;;;;;AAQA,MAAM,gBAAA,GAAmB,SAAA;AAEzB,MAAM,0BAAA,IAA8B,CAAC,OAAA,GAAqC,EAAC,KAAM;AAC/E,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,mBAAmB,cAAA,EAAgB;AACtC,QAAA;AAAA,MACF;AAEA,MAAAA,iCAAA,CAA6B,MAAM;AACjC,QAAAC,iCAAA,CAAkB,OAAA,EAAS,mBAAmB,cAAc,CAAA;AAAA,MAC9D,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAeO,MAAM,yBAAA,GAA4BC,uBAAkB,0BAA0B;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const attributes = require('@sentry/conventions/attributes'); | ||
| const core = require('@sentry/core'); | ||
| const debugBuild = require('../../../debug-build.js'); | ||
| const channels = require('../../../orchestrion/channels.js'); | ||
| const tracingChannel = require('../../../tracing-channel.js'); | ||
| const route = require('./route.js'); | ||
| const ORIGIN = "auto.http.express"; | ||
| const ATTR_EXPRESS_NAME = "express.name"; | ||
| const ATTR_EXPRESS_TYPE = "express.type"; | ||
| const NOOP = () => { | ||
| }; | ||
| let _isInstrumented = false; | ||
| function instrumentExpress(options, tracingChannel$1) { | ||
| if (_isInstrumented) { | ||
| return; | ||
| } | ||
| _isInstrumented = true; | ||
| for (const channelName of [ | ||
| channels.CHANNELS.EXPRESS_ROUTE, | ||
| channels.CHANNELS.EXPRESS_USE, | ||
| channels.CHANNELS.ROUTER_ROUTE, | ||
| channels.CHANNELS.ROUTER_USE | ||
| ]) { | ||
| tracingChannel$1(channelName).subscribe({ | ||
| start: NOOP, | ||
| asyncStart: NOOP, | ||
| asyncEnd: NOOP, | ||
| error: NOOP, | ||
| end: captureRegisteredLayerPath | ||
| }); | ||
| } | ||
| for (const channelName of [channels.CHANNELS.EXPRESS_HANDLE, channels.CHANNELS.ROUTER_HANDLE]) { | ||
| debugBuild.DEBUG_BUILD && core.debug.log(`[orchestrion:express] subscribing to channel "${channelName}"`); | ||
| const channel = tracingChannel$1(channelName); | ||
| tracingChannel.bindTracingChannelToSpan(channel, (data) => getSpanForLayer(data, options), { | ||
| beforeSpanEnd(_span, data) { | ||
| data._sentryCleanup?.(); | ||
| } | ||
| }); | ||
| channel.subscribe({ | ||
| start: NOOP, | ||
| asyncEnd: NOOP, | ||
| end: NOOP, | ||
| error: NOOP, | ||
| asyncStart: popLayerPathForLayer | ||
| }); | ||
| } | ||
| } | ||
| function captureRegisteredLayerPath(data) { | ||
| const stack = data.self?.stack; | ||
| if (!Array.isArray(stack)) { | ||
| return; | ||
| } | ||
| const layer = stack[stack.length - 1]; | ||
| if (layer) { | ||
| route.setLayerRegisteredPath(layer, route.getLayerPath(data.arguments ?? [])); | ||
| } | ||
| } | ||
| function popLayerPathForLayer(data) { | ||
| if (!data._sentryStoredLayer) { | ||
| return; | ||
| } | ||
| data._sentryStoredLayer = false; | ||
| const req = data.arguments?.[0]; | ||
| if (req) { | ||
| route.popLayerPath(req); | ||
| } | ||
| } | ||
| function getSpanForLayer(data, options) { | ||
| const layer = data.self; | ||
| const args = data.arguments; | ||
| if (!layer || !Array.isArray(args)) { | ||
| return void 0; | ||
| } | ||
| if (layer.handle?.length === 4) { | ||
| return void 0; | ||
| } | ||
| if (layer.method && !layer.route) { | ||
| return void 0; | ||
| } | ||
| const req = args[0]; | ||
| const res = args[1]; | ||
| if (!req) { | ||
| return void 0; | ||
| } | ||
| if (!core.getActiveSpan()) { | ||
| return void 0; | ||
| } | ||
| const type = getLayerType(layer); | ||
| const registeredPath = route.getLayerRegisteredPath(layer); | ||
| if (registeredPath != null) { | ||
| route.pushLayerPath(req, registeredPath); | ||
| data._sentryStoredLayer = true; | ||
| } | ||
| const constructedRoute = type === "request_handler" ? route.getConstructedRoute(req) : void 0; | ||
| const matchedRoute = type === "request_handler" && constructedRoute != null ? route.getActualMatchedRoute(req, constructedRoute) : void 0; | ||
| const name = type === "request_handler" ? constructedRoute || "request handler" : type === "router" ? layer.path ?? "/" : layer.name ?? "<anonymous>"; | ||
| if (matchedRoute) { | ||
| setHttpServerSpanRoute(matchedRoute); | ||
| } | ||
| if (type === "request_handler" && constructedRoute) { | ||
| const isolationScope = core.getIsolationScope(); | ||
| if (isolationScope !== core.getDefaultIsolationScope()) { | ||
| const method = typeof req.method === "string" ? req.method.toUpperCase() : "GET"; | ||
| isolationScope.setTransactionName(`${method} ${constructedRoute}`); | ||
| } else { | ||
| debugBuild.DEBUG_BUILD && core.debug.warn( | ||
| "[orchestrion:express] Isolation scope is still default isolation scope - skipping transaction name" | ||
| ); | ||
| } | ||
| } | ||
| if (isLayerIgnored(name, type, options)) { | ||
| return void 0; | ||
| } | ||
| const span = core.startInactiveSpan({ | ||
| name, | ||
| attributes: { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.express`, | ||
| [ATTR_EXPRESS_NAME]: name, | ||
| [ATTR_EXPRESS_TYPE]: type, | ||
| ...matchedRoute ? { [attributes.HTTP_ROUTE]: matchedRoute } : {} | ||
| } | ||
| }); | ||
| if (res && typeof res.once === "function") { | ||
| const onFinish = () => { | ||
| span.end(); | ||
| }; | ||
| res.once("finish", onFinish); | ||
| data._sentryCleanup = () => res.removeListener("finish", onFinish); | ||
| } | ||
| return span; | ||
| } | ||
| function getLayerType(layer) { | ||
| if (layer.name === "router") { | ||
| return "router"; | ||
| } | ||
| if (layer.name === "bound dispatch" || layer.name === "handle") { | ||
| return "request_handler"; | ||
| } | ||
| return "middleware"; | ||
| } | ||
| function setHttpServerSpanRoute(route) { | ||
| const activeSpan = core.getActiveSpan(); | ||
| const rootSpan = activeSpan && core.getRootSpan(activeSpan); | ||
| if (!rootSpan) { | ||
| return; | ||
| } | ||
| if (core.spanToJSON(rootSpan).data[core.SEMANTIC_ATTRIBUTE_SENTRY_OP] !== "http.server") { | ||
| return; | ||
| } | ||
| rootSpan.setAttribute(attributes.HTTP_ROUTE, route); | ||
| } | ||
| function isLayerIgnored(name, type, options) { | ||
| const { ignoreLayers, ignoreLayersType } = options; | ||
| if (Array.isArray(ignoreLayersType) && ignoreLayersType.includes(type)) { | ||
| return true; | ||
| } | ||
| if (!Array.isArray(ignoreLayers)) { | ||
| return false; | ||
| } | ||
| try { | ||
| return core.stringMatchesSomePattern(name, ignoreLayers, true); | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
| exports.instrumentExpress = instrumentExpress; | ||
| //# sourceMappingURL=instrumentation.js.map |
| {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing-channel/express/instrumentation.ts"],"sourcesContent":["import type * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { HTTP_ROUTE } from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport {\n debug,\n getActiveSpan,\n getDefaultIsolationScope,\n getIsolationScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n spanToJSON,\n startInactiveSpan,\n stringMatchesSomePattern,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../../debug-build';\nimport { CHANNELS } from '../../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../../tracing-channel';\nimport {\n getActualMatchedRoute,\n getConstructedRoute,\n getLayerPath,\n getLayerRegisteredPath,\n popLayerPath,\n pushLayerPath,\n setLayerRegisteredPath,\n} from './route';\nimport type {\n ExpressIntegrationOptions,\n ExpressLayer,\n ExpressLayerType,\n ExpressRequest,\n ExpressResponse,\n HandleChannelContext,\n RegistrationChannelContext,\n} from './types';\n\nconst ORIGIN = 'auto.http.express';\n\n// `express.name`/`express.type` are Sentry-internal Express attributes (not part\n// of `@sentry/conventions`); kept in sync with `@sentry/core`'s OTel-derived\n// Express integration so the emitted spans are identical across both code paths.\nconst ATTR_EXPRESS_NAME = 'express.name';\nconst ATTR_EXPRESS_TYPE = 'express.type';\n\nconst NOOP = (): void => {};\n\nlet _isInstrumented = false;\n\nexport function instrumentExpress(\n options: ExpressIntegrationOptions,\n tracingChannel: typeof diagnosticsChannel.tracingChannel,\n): void {\n if (_isInstrumented) {\n return;\n }\n _isInstrumented = true;\n\n // Record each layer's registered path *pattern* as it is registered, so the\n // matched route can be reconstructed with its parameters intact at request\n // time. Only the `end` event matters (the layer is on the router's stack by\n // then); the others are required by the subscriber type, so no-op them.\n for (const channelName of [\n CHANNELS.EXPRESS_ROUTE,\n CHANNELS.EXPRESS_USE,\n CHANNELS.ROUTER_ROUTE,\n CHANNELS.ROUTER_USE,\n ]) {\n tracingChannel<RegistrationChannelContext>(channelName).subscribe({\n start: NOOP,\n asyncStart: NOOP,\n asyncEnd: NOOP,\n error: NOOP,\n end: captureRegisteredLayerPath,\n });\n }\n\n for (const channelName of [CHANNELS.EXPRESS_HANDLE, CHANNELS.ROUTER_HANDLE]) {\n DEBUG_BUILD && debug.log(`[orchestrion:express] subscribing to channel \"${channelName}\"`);\n\n const channel = tracingChannel<HandleChannelContext>(channelName);\n\n bindTracingChannelToSpan(channel, data => getSpanForLayer(data, options), {\n beforeSpanEnd(_span, data) {\n data._sentryCleanup?.();\n },\n });\n\n // Pop the layer path when the layer hands off via `next`. `asyncStart` fires\n // when `next` is called and *before* the downstream layer runs, so the\n // per-request path chain reflects only the current chain when each layer\n // reconstructs its route. Only `asyncStart` is relevant here.\n channel.subscribe({\n start: NOOP,\n asyncEnd: NOOP,\n end: NOOP,\n error: NOOP,\n asyncStart: popLayerPathForLayer,\n });\n }\n}\n\n/** Record the freshly-registered layer's path pattern from a `route`/`use` call. */\nfunction captureRegisteredLayerPath(data: RegistrationChannelContext): void {\n const stack = data.self?.stack;\n if (!Array.isArray(stack)) {\n return;\n }\n const layer = stack[stack.length - 1];\n if (layer) {\n setLayerRegisteredPath(layer, getLayerPath(data.arguments ?? []));\n }\n}\n\n/** Pop the path a layer pushed once it hands control onward via `next`. */\nfunction popLayerPathForLayer(data: HandleChannelContext): void {\n if (!data._sentryStoredLayer) {\n return;\n }\n // Clear the marker first so a layer that (incorrectly) calls `next` more than\n // once can't pop again and take a parent's entry off the stack with it.\n data._sentryStoredLayer = false;\n const req = data.arguments?.[0] as ExpressRequest | undefined;\n if (req) {\n popLayerPath(req);\n }\n}\n\n/**\n * Open a span for one layer invocation. Returns `undefined` to opt the layer\n * out (error handlers, or a layer with no active parent trace) — the helper\n * then leaves the active context untouched.\n */\nfunction getSpanForLayer(data: HandleChannelContext, options: ExpressIntegrationOptions): Span | undefined {\n const layer = data.self;\n const args = data.arguments;\n if (!layer || !Array.isArray(args)) {\n return undefined;\n }\n\n // Express only treats a 4-arg handler as an error handler and skips it in\n // the normal request pipeline; match the OTel integration and don't trace it.\n if (layer.handle?.length === 4) {\n return undefined;\n }\n\n // A Route dispatches to its handlers via the same `handle_request` method,\n // but those inner layers already run inside the route-dispatch layer's\n // `request_handler` span. They carry `.method` (and no `.route`); skip them\n // so we emit one span per route, matching the OTel Express integration.\n if (layer.method && !layer.route) {\n return undefined;\n }\n\n const req = args[0] as ExpressRequest | undefined;\n const res = args[1] as ExpressResponse | undefined;\n if (!req) {\n return undefined;\n }\n\n // No active parent span means this request is being ignored (unsampled /\n // filtered), so don't open a span\n if (!getActiveSpan()) {\n return undefined;\n }\n\n const type = getLayerType(layer);\n\n // Push this layer's registered path onto the request's chain so a\n // `request_handler` can reconstruct the full route with parameters intact\n // (`req.baseUrl` only exposes the *resolved* mount prefix). The matching pop\n // happens on `asyncStart` when the layer hands off via `next`.\n const registeredPath = getLayerRegisteredPath(layer);\n if (registeredPath != null) {\n pushLayerPath(req, registeredPath);\n data._sentryStoredLayer = true;\n }\n\n // `constructedRoute` (the full registered pattern) names the span/transaction;\n // `matchedRoute` (validated against the request URL) is the `http.route`.\n const constructedRoute = type === 'request_handler' ? getConstructedRoute(req) : undefined;\n const matchedRoute =\n type === 'request_handler' && constructedRoute != null ? getActualMatchedRoute(req, constructedRoute) : undefined;\n\n const name =\n type === 'request_handler'\n ? constructedRoute || 'request handler'\n : type === 'router'\n ? (layer.path ?? '/')\n : (layer.name ?? '<anonymous>');\n\n // Propagate the route to the root `http.server` span *before* the ignore\n // check, so the transaction is still named even when the layer's own span is\n // ignored — matches the OTel Express integration's `onRouteResolved` timing.\n if (matchedRoute) {\n setHttpServerSpanRoute(matchedRoute);\n }\n\n if (type === 'request_handler' && constructedRoute) {\n const isolationScope = getIsolationScope();\n if (isolationScope !== getDefaultIsolationScope()) {\n const method = typeof req.method === 'string' ? req.method.toUpperCase() : 'GET';\n isolationScope.setTransactionName(`${method} ${constructedRoute}`);\n } else {\n DEBUG_BUILD &&\n debug.warn(\n '[orchestrion:express] Isolation scope is still default isolation scope - skipping transaction name',\n );\n }\n }\n\n // Honor `ignoreLayers`/`ignoreLayersType`: skip the span for matching layers.\n // We intentionally do NOT pop the pushed path here (unlike OTel Express, which\n // pops on ignore): the path is still popped on `asyncStart` when the layer\n // calls `next`, so a following sibling isn't polluted, while an ignored\n // *router* keeps its mount prefix on the stack for the sub-stack it dispatches\n // — so routes under an ignored router stay correct. The only entries that\n // never pop come from layers that end the response without `next()`, and those\n // sit on a per-request store that is discarded when the request ends.\n if (isLayerIgnored(name, type, options)) {\n return undefined;\n }\n\n const span = startInactiveSpan({\n name,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.express`,\n [ATTR_EXPRESS_NAME]: name,\n [ATTR_EXPRESS_TYPE]: type,\n ...(matchedRoute ? { [HTTP_ROUTE]: matchedRoute } : {}),\n },\n });\n\n // A layer that sends the response (route handlers, typically) never calls\n // `next`, so the channel's `asyncEnd` never fires. End on the response's\n // `finish` in that case. When `next` *is* called, the helper ends the span\n // (via `asyncEnd`) and `beforeSpanEnd` removes this now-redundant listener.\n if (res && typeof res.once === 'function') {\n const onFinish = (): void => {\n span.end();\n };\n res.once('finish', onFinish);\n data._sentryCleanup = () => res.removeListener('finish', onFinish);\n }\n\n return span;\n}\n\nfunction getLayerType(layer: ExpressLayer): ExpressLayerType {\n if (layer.name === 'router') {\n return 'router';\n }\n // `bound dispatch` (v4) / `handle` — the route-dispatch layer created by `router.route()`.\n if (layer.name === 'bound dispatch' || layer.name === 'handle') {\n return 'request_handler';\n }\n return 'middleware';\n}\n\n/**\n * Propagate the resolved route to the root `http.server` span so the\n * transaction gets a parameterized `http.route`. Mirrors `@sentry/node`'s\n * `setHttpServerSpanRouteAttribute`; inlined to keep this package free of\n * `@sentry/node` deps. No-op unless the root span is an `http.server` span.\n */\nfunction setHttpServerSpanRoute(route: string): void {\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n if (!rootSpan) {\n return;\n }\n if (spanToJSON(rootSpan).data[SEMANTIC_ATTRIBUTE_SENTRY_OP] !== 'http.server') {\n return;\n }\n rootSpan.setAttribute(HTTP_ROUTE, route);\n}\n\n/**\n * Whether a layer should be skipped per the `ignoreLayers`/`ignoreLayersType`\n * options. Matches `@sentry/core`'s Express `isLayerIgnored`: `ignoreLayersType`\n * filters by layer type, `ignoreLayers` matches the layer's name against\n * string/RegExp/predicate patterns (exact string match).\n */\nfunction isLayerIgnored(name: string, type: ExpressLayerType, options: ExpressIntegrationOptions): boolean {\n const { ignoreLayers, ignoreLayersType } = options;\n\n if (Array.isArray(ignoreLayersType) && ignoreLayersType.includes(type)) {\n return true;\n }\n\n if (!Array.isArray(ignoreLayers)) {\n return false;\n }\n\n try {\n return stringMatchesSomePattern(name, ignoreLayers, true);\n } catch {\n return false;\n }\n}\n"],"names":["tracingChannel","CHANNELS","DEBUG_BUILD","debug","bindTracingChannelToSpan","setLayerRegisteredPath","getLayerPath","popLayerPath","getActiveSpan","getLayerRegisteredPath","pushLayerPath","getConstructedRoute","getActualMatchedRoute","getIsolationScope","getDefaultIsolationScope","startInactiveSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","HTTP_ROUTE","getRootSpan","spanToJSON","stringMatchesSomePattern"],"mappings":";;;;;;;;;AAqCA,MAAM,MAAA,GAAS,mBAAA;AAKf,MAAM,iBAAA,GAAoB,cAAA;AAC1B,MAAM,iBAAA,GAAoB,cAAA;AAE1B,MAAM,OAAO,MAAY;AAAC,CAAA;AAE1B,IAAI,eAAA,GAAkB,KAAA;AAEf,SAAS,iBAAA,CACd,SACAA,gBAAA,EACM;AACN,EAAA,IAAI,eAAA,EAAiB;AACnB,IAAA;AAAA,EACF;AACA,EAAA,eAAA,GAAkB,IAAA;AAMlB,EAAA,KAAA,MAAW,WAAA,IAAe;AAAA,IACxBC,iBAAA,CAAS,aAAA;AAAA,IACTA,iBAAA,CAAS,WAAA;AAAA,IACTA,iBAAA,CAAS,YAAA;AAAA,IACTA,iBAAA,CAAS;AAAA,GACX,EAAG;AACD,IAAAD,gBAAA,CAA2C,WAAW,EAAE,SAAA,CAAU;AAAA,MAChE,KAAA,EAAO,IAAA;AAAA,MACP,UAAA,EAAY,IAAA;AAAA,MACZ,QAAA,EAAU,IAAA;AAAA,MACV,KAAA,EAAO,IAAA;AAAA,MACP,GAAA,EAAK;AAAA,KACN,CAAA;AAAA,EACH;AAEA,EAAA,KAAA,MAAW,eAAe,CAACC,iBAAA,CAAS,cAAA,EAAgBA,iBAAA,CAAS,aAAa,CAAA,EAAG;AAC3E,IAAAC,sBAAA,IAAeC,UAAA,CAAM,GAAA,CAAI,CAAA,8CAAA,EAAiD,WAAW,CAAA,CAAA,CAAG,CAAA;AAExF,IAAA,MAAM,OAAA,GAAUH,iBAAqC,WAAW,CAAA;AAEhE,IAAAI,uCAAA,CAAyB,OAAA,EAAS,CAAA,IAAA,KAAQ,eAAA,CAAgB,IAAA,EAAM,OAAO,CAAA,EAAG;AAAA,MACxE,aAAA,CAAc,OAAO,IAAA,EAAM;AACzB,QAAA,IAAA,CAAK,cAAA,IAAiB;AAAA,MACxB;AAAA,KACD,CAAA;AAMD,IAAA,OAAA,CAAQ,SAAA,CAAU;AAAA,MAChB,KAAA,EAAO,IAAA;AAAA,MACP,QAAA,EAAU,IAAA;AAAA,MACV,GAAA,EAAK,IAAA;AAAA,MACL,KAAA,EAAO,IAAA;AAAA,MACP,UAAA,EAAY;AAAA,KACb,CAAA;AAAA,EACH;AACF;AAGA,SAAS,2BAA2B,IAAA,EAAwC;AAC1E,EAAA,MAAM,KAAA,GAAQ,KAAK,IAAA,EAAM,KAAA;AACzB,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACzB,IAAA;AAAA,EACF;AACA,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA;AACpC,EAAA,IAAI,KAAA,EAAO;AACT,IAAAC,4BAAA,CAAuB,OAAOC,kBAAA,CAAa,IAAA,CAAK,SAAA,IAAa,EAAE,CAAC,CAAA;AAAA,EAClE;AACF;AAGA,SAAS,qBAAqB,IAAA,EAAkC;AAC9D,EAAA,IAAI,CAAC,KAAK,kBAAA,EAAoB;AAC5B,IAAA;AAAA,EACF;AAGA,EAAA,IAAA,CAAK,kBAAA,GAAqB,KAAA;AAC1B,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,SAAA,GAAY,CAAC,CAAA;AAC9B,EAAA,IAAI,GAAA,EAAK;AACP,IAAAC,kBAAA,CAAa,GAAG,CAAA;AAAA,EAClB;AACF;AAOA,SAAS,eAAA,CAAgB,MAA4B,OAAA,EAAsD;AACzG,EAAA,MAAM,QAAQ,IAAA,CAAK,IAAA;AACnB,EAAA,MAAM,OAAO,IAAA,CAAK,SAAA;AAClB,EAAA,IAAI,CAAC,KAAA,IAAS,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AAClC,IAAA,OAAO,MAAA;AAAA,EACT;AAIA,EAAA,IAAI,KAAA,CAAM,MAAA,EAAQ,MAAA,KAAW,CAAA,EAAG;AAC9B,IAAA,OAAO,MAAA;AAAA,EACT;AAMA,EAAA,IAAI,KAAA,CAAM,MAAA,IAAU,CAAC,KAAA,CAAM,KAAA,EAAO;AAChC,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAA,GAAM,KAAK,CAAC,CAAA;AAClB,EAAA,MAAM,GAAA,GAAM,KAAK,CAAC,CAAA;AAClB,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,OAAO,MAAA;AAAA,EACT;AAIA,EAAA,IAAI,CAACC,oBAAc,EAAG;AACpB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,IAAA,GAAO,aAAa,KAAK,CAAA;AAM/B,EAAA,MAAM,cAAA,GAAiBC,6BAAuB,KAAK,CAAA;AACnD,EAAA,IAAI,kBAAkB,IAAA,EAAM;AAC1B,IAAAC,mBAAA,CAAc,KAAK,cAAc,CAAA;AACjC,IAAA,IAAA,CAAK,kBAAA,GAAqB,IAAA;AAAA,EAC5B;AAIA,EAAA,MAAM,gBAAA,GAAmB,IAAA,KAAS,iBAAA,GAAoBC,yBAAA,CAAoB,GAAG,CAAA,GAAI,MAAA;AACjF,EAAA,MAAM,YAAA,GACJ,SAAS,iBAAA,IAAqB,gBAAA,IAAoB,OAAOC,2BAAA,CAAsB,GAAA,EAAK,gBAAgB,CAAA,GAAI,MAAA;AAE1G,EAAA,MAAM,IAAA,GACJ,IAAA,KAAS,iBAAA,GACL,gBAAA,IAAoB,iBAAA,GACpB,IAAA,KAAS,QAAA,GACN,KAAA,CAAM,IAAA,IAAQ,GAAA,GACd,KAAA,CAAM,IAAA,IAAQ,aAAA;AAKvB,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,sBAAA,CAAuB,YAAY,CAAA;AAAA,EACrC;AAEA,EAAA,IAAI,IAAA,KAAS,qBAAqB,gBAAA,EAAkB;AAClD,IAAA,MAAM,iBAAiBC,sBAAA,EAAkB;AACzC,IAAA,IAAI,cAAA,KAAmBC,+BAAyB,EAAG;AACjD,MAAA,MAAM,MAAA,GAAS,OAAO,GAAA,CAAI,MAAA,KAAW,WAAW,GAAA,CAAI,MAAA,CAAO,aAAY,GAAI,KAAA;AAC3E,MAAA,cAAA,CAAe,kBAAA,CAAmB,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,gBAAgB,CAAA,CAAE,CAAA;AAAA,IACnE,CAAA,MAAO;AACL,MAAAZ,sBAAA,IACEC,UAAA,CAAM,IAAA;AAAA,QACJ;AAAA,OACF;AAAA,IACJ;AAAA,EACF;AAUA,EAAA,IAAI,cAAA,CAAe,IAAA,EAAM,IAAA,EAAM,OAAO,CAAA,EAAG;AACvC,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAOY,sBAAA,CAAkB;AAAA,IAC7B,IAAA;AAAA,IACA,UAAA,EAAY;AAAA,MACV,CAACC,qCAAgC,GAAG,MAAA;AAAA,MACpC,CAACC,iCAA4B,GAAG,CAAA,EAAG,IAAI,CAAA,QAAA,CAAA;AAAA,MACvC,CAAC,iBAAiB,GAAG,IAAA;AAAA,MACrB,CAAC,iBAAiB,GAAG,IAAA;AAAA,MACrB,GAAI,eAAe,EAAE,CAACC,qBAAU,GAAG,YAAA,KAAiB;AAAC;AACvD,GACD,CAAA;AAMD,EAAA,IAAI,GAAA,IAAO,OAAO,GAAA,CAAI,IAAA,KAAS,UAAA,EAAY;AACzC,IAAA,MAAM,WAAW,MAAY;AAC3B,MAAA,IAAA,CAAK,GAAA,EAAI;AAAA,IACX,CAAA;AACA,IAAA,GAAA,CAAI,IAAA,CAAK,UAAU,QAAQ,CAAA;AAC3B,IAAA,IAAA,CAAK,cAAA,GAAiB,MAAM,GAAA,CAAI,cAAA,CAAe,UAAU,QAAQ,CAAA;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,aAAa,KAAA,EAAuC;AAC3D,EAAA,IAAI,KAAA,CAAM,SAAS,QAAA,EAAU;AAC3B,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,IAAI,KAAA,CAAM,IAAA,KAAS,gBAAA,IAAoB,KAAA,CAAM,SAAS,QAAA,EAAU;AAC9D,IAAA,OAAO,iBAAA;AAAA,EACT;AACA,EAAA,OAAO,YAAA;AACT;AAQA,SAAS,uBAAuB,KAAA,EAAqB;AACnD,EAAA,MAAM,aAAaV,kBAAA,EAAc;AACjC,EAAA,MAAM,QAAA,GAAW,UAAA,IAAcW,gBAAA,CAAY,UAAU,CAAA;AACrD,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA;AAAA,EACF;AACA,EAAA,IAAIC,gBAAW,QAAQ,CAAA,CAAE,IAAA,CAAKH,iCAA4B,MAAM,aAAA,EAAe;AAC7E,IAAA;AAAA,EACF;AACA,EAAA,QAAA,CAAS,YAAA,CAAaC,uBAAY,KAAK,CAAA;AACzC;AAQA,SAAS,cAAA,CAAe,IAAA,EAAc,IAAA,EAAwB,OAAA,EAA6C;AACzG,EAAA,MAAM,EAAE,YAAA,EAAc,gBAAA,EAAiB,GAAI,OAAA;AAE3C,EAAA,IAAI,MAAM,OAAA,CAAQ,gBAAgB,KAAK,gBAAA,CAAiB,QAAA,CAAS,IAAI,CAAA,EAAG;AACtE,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,YAAY,CAAA,EAAG;AAChC,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI;AACF,IAAA,OAAOG,6BAAA,CAAyB,IAAA,EAAM,YAAA,EAAc,IAAI,CAAA;AAAA,EAC1D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const layerRegisteredPaths = /* @__PURE__ */ new WeakMap(); | ||
| function setLayerRegisteredPath(layer, path) { | ||
| layerRegisteredPaths.set(layer, path); | ||
| } | ||
| function getLayerRegisteredPath(layer) { | ||
| return layerRegisteredPaths.get(layer); | ||
| } | ||
| const requestLayerPaths = /* @__PURE__ */ new WeakMap(); | ||
| function getStore(req) { | ||
| let store = requestLayerPaths.get(req); | ||
| if (!store) { | ||
| store = []; | ||
| requestLayerPaths.set(req, store); | ||
| } | ||
| return store; | ||
| } | ||
| function pushLayerPath(req, path) { | ||
| getStore(req).push(path); | ||
| } | ||
| function popLayerPath(req) { | ||
| getStore(req).pop(); | ||
| } | ||
| function getLayerPath(args) { | ||
| const firstArg = args[0]; | ||
| if (Array.isArray(firstArg)) { | ||
| return firstArg.map((segment) => extractLayerPathSegment(segment) ?? "").join(","); | ||
| } | ||
| return extractLayerPathSegment(firstArg); | ||
| } | ||
| function extractLayerPathSegment(segment) { | ||
| return typeof segment === "string" ? segment : segment instanceof RegExp || typeof segment === "number" ? String(segment) : void 0; | ||
| } | ||
| function getConstructedRoute(req) { | ||
| const layersStore = getStore(req); | ||
| let constructedRoute = ""; | ||
| for (const path of layersStore) { | ||
| if (path === "/" || path === "/*") { | ||
| continue; | ||
| } | ||
| constructedRoute += !constructedRoute || constructedRoute.endsWith("/") ? path : `/${path}`; | ||
| } | ||
| return constructedRoute.replace(/\/{2,}/g, "/"); | ||
| } | ||
| function getActualMatchedRoute(req, constructedRoute) { | ||
| const layersStore = getStore(req); | ||
| if (layersStore.length === 0) { | ||
| return void 0; | ||
| } | ||
| const originalUrl = typeof req.originalUrl === "string" ? req.originalUrl : ""; | ||
| if (layersStore.every((path) => path === "/")) { | ||
| return originalUrl === "/" ? "/" : void 0; | ||
| } | ||
| if (constructedRoute === "*") { | ||
| return constructedRoute; | ||
| } | ||
| if (constructedRoute.includes("/") && (constructedRoute.includes(",") || constructedRoute.includes("\\") || constructedRoute.includes("*") || constructedRoute.includes("["))) { | ||
| return constructedRoute; | ||
| } | ||
| const normalizedRoute = constructedRoute.startsWith("/") ? constructedRoute : `/${constructedRoute}`; | ||
| const isValidRoute = normalizedRoute.length > 0 && (originalUrl === normalizedRoute || originalUrl.startsWith(normalizedRoute) || isRoutePattern(normalizedRoute)); | ||
| return isValidRoute ? normalizedRoute : void 0; | ||
| } | ||
| function isRoutePattern(route) { | ||
| return route.includes(":") || route.includes("*"); | ||
| } | ||
| exports.getActualMatchedRoute = getActualMatchedRoute; | ||
| exports.getConstructedRoute = getConstructedRoute; | ||
| exports.getLayerPath = getLayerPath; | ||
| exports.getLayerRegisteredPath = getLayerRegisteredPath; | ||
| exports.popLayerPath = popLayerPath; | ||
| exports.pushLayerPath = pushLayerPath; | ||
| exports.setLayerRegisteredPath = setLayerRegisteredPath; | ||
| //# sourceMappingURL=route.js.map |
| {"version":3,"file":"route.js","sources":["../../../../../src/integrations/tracing-channel/express/route.ts"],"sourcesContent":["import type { ExpressLayer, ExpressRequest } from './types';\n\n/**\n * Registered path *pattern* per routing `Layer`, captured when the layer is\n * registered via `Router.prototype.route`/`.use`. `undefined` means the layer\n * was registered without an explicit path (e.g. `app.use(mw)`), so it does not\n * contribute to the reconstructed route.\n *\n * This is the piece `req.baseUrl` can't provide: at request time `req.baseUrl`\n * holds the *resolved* mount prefix (`/api/v1`), whereas we want the registered\n * pattern (`/api/:version`).\n */\nconst layerRegisteredPaths = new WeakMap<ExpressLayer, string | undefined>();\n\n/** Record the path pattern a layer was registered with. */\nexport function setLayerRegisteredPath(layer: ExpressLayer, path: string | undefined): void {\n layerRegisteredPaths.set(layer, path);\n}\n\n/** Read the path pattern a layer was registered with, if any. */\nexport function getLayerRegisteredPath(layer: ExpressLayer): string | undefined {\n return layerRegisteredPaths.get(layer);\n}\n\n/**\n * Per-request ordered stack of the registered path patterns of the layers\n * currently on the matched chain. Layers push on entry and pop when they hand\n * off via `next`, so at any point it reflects the path from the app root down\n * to the currently-executing layer. `WeakMap` so entries are released with the\n * request.\n */\nconst requestLayerPaths = new WeakMap<ExpressRequest, string[]>();\n\nfunction getStore(req: ExpressRequest): string[] {\n let store = requestLayerPaths.get(req);\n if (!store) {\n store = [];\n requestLayerPaths.set(req, store);\n }\n return store;\n}\n\n/** Push a layer's registered path onto the request's chain. */\nexport function pushLayerPath(req: ExpressRequest, path: string): void {\n getStore(req).push(path);\n}\n\n/** Pop the most recently pushed layer path off the request's chain. */\nexport function popLayerPath(req: ExpressRequest): void {\n getStore(req).pop();\n}\n\n/**\n * The path pattern a `route`/`use` call registered, derived from its arguments.\n * A leading string/RegExp/number path becomes the pattern (arrays are joined\n * with `,`); a bare handler function yields `undefined`. Kept in sync with\n * `@sentry/core`'s Express `getLayerPath`.\n */\nexport function getLayerPath(args: unknown[]): string | undefined {\n const firstArg = args[0];\n if (Array.isArray(firstArg)) {\n return firstArg.map(segment => extractLayerPathSegment(segment) ?? '').join(',');\n }\n return extractLayerPathSegment(firstArg);\n}\n\nfunction extractLayerPathSegment(segment: unknown): string | undefined {\n return typeof segment === 'string'\n ? segment\n : segment instanceof RegExp || typeof segment === 'number'\n ? String(segment)\n : undefined;\n}\n\n/**\n * Concatenate the stored layer paths into the full route pattern (parameters\n * preserved), e.g. `/api/:version/user`. Mirrors `@sentry/core`.\n */\nexport function getConstructedRoute(req: ExpressRequest): string {\n const layersStore = getStore(req);\n\n let constructedRoute = '';\n for (const path of layersStore) {\n if (path === '/' || path === '/*') {\n continue;\n }\n constructedRoute += !constructedRoute || constructedRoute.endsWith('/') ? path : `/${path}`;\n }\n\n return constructedRoute.replace(/\\/{2,}/g, '/');\n}\n\n/**\n * Validate the constructed route against the request URL, returning it only\n * when it plausibly corresponds to a real match (otherwise `undefined`). Mirrors\n * `@sentry/core`'s `getActualMatchedRoute` — used for the `http.route` attribute.\n */\nexport function getActualMatchedRoute(req: ExpressRequest, constructedRoute: string): string | undefined {\n const layersStore = getStore(req);\n\n if (layersStore.length === 0) {\n return undefined;\n }\n\n const originalUrl = typeof req.originalUrl === 'string' ? req.originalUrl : '';\n\n // The layer store also includes root paths in case a non-existing url was requested.\n if (layersStore.every(path => path === '/')) {\n return originalUrl === '/' ? '/' : undefined;\n }\n\n if (constructedRoute === '*') {\n return constructedRoute;\n }\n\n // For RegExp routes or route arrays, return the constructed route as-is.\n if (\n constructedRoute.includes('/') &&\n (constructedRoute.includes(',') ||\n constructedRoute.includes('\\\\') ||\n constructedRoute.includes('*') ||\n constructedRoute.includes('['))\n ) {\n return constructedRoute;\n }\n\n const normalizedRoute = constructedRoute.startsWith('/') ? constructedRoute : `/${constructedRoute}`;\n\n const isValidRoute =\n normalizedRoute.length > 0 &&\n (originalUrl === normalizedRoute || originalUrl.startsWith(normalizedRoute) || isRoutePattern(normalizedRoute));\n\n return isValidRoute ? normalizedRoute : undefined;\n}\n\n/** Whether a route contains parameter/wildcard patterns (`:id`, `*`). */\nfunction isRoutePattern(route: string): boolean {\n return route.includes(':') || route.includes('*');\n}\n"],"names":[],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const core = require('@sentry/core'); | ||
| const debugBuild = require('../../debug-build.js'); | ||
| const channels = require('../../orchestrion/channels.js'); | ||
| const tracingChannel = require('../../tracing-channel.js'); | ||
| const INTEGRATION_NAME = "Google_GenAI"; | ||
| const ORIGIN = "auto.ai.orchestrion.google_genai"; | ||
| const INSTRUMENTED_CHANNELS = [ | ||
| { channel: channels.CHANNELS.GOOGLE_GENAI_GENERATE_CONTENT, operation: "generate_content" }, | ||
| { channel: channels.CHANNELS.GOOGLE_GENAI_EMBED_CONTENT, operation: "embeddings" }, | ||
| { channel: channels.CHANNELS.GOOGLE_GENAI_CHAT, operation: "chat" } | ||
| ]; | ||
| let subscribed = false; | ||
| const _googleGenAIChannelIntegration = ((options = {}) => { | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel || subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| core.waitForTracingChannelBinding(() => { | ||
| for (const { channel, operation } of INSTRUMENTED_CHANNELS) { | ||
| debugBuild.DEBUG_BUILD && core.debug.log(`[orchestrion:google-genai] subscribing to channel "${channel}"`); | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(channel), | ||
| (data) => createGenAiSpan(data, operation, options), | ||
| { | ||
| beforeSpanEnd: (span, data) => { | ||
| if (operation !== "embeddings") { | ||
| core.addGoogleGenAIResponseAttributes( | ||
| span, | ||
| data.result, | ||
| core.resolveAIRecordingOptions(options).recordOutputs | ||
| ); | ||
| } | ||
| }, | ||
| deferSpanEnd: ({ span, data }) => wrapStreamResult(span, data, options) | ||
| } | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| function createGenAiSpan(data, operation, options) { | ||
| if (core._INTERNAL_shouldSkipAiProviderWrapping(INTEGRATION_NAME)) { | ||
| return void 0; | ||
| } | ||
| if (operation !== "chat") { | ||
| const activeSpan = core.getActiveSpan(); | ||
| if (activeSpan) { | ||
| const { op, origin } = core.spanToJSON(activeSpan); | ||
| if (origin === ORIGIN && op === "gen_ai.chat") { | ||
| return void 0; | ||
| } | ||
| } | ||
| } | ||
| const args = data.arguments ?? []; | ||
| const params = args[0]; | ||
| const { recordInputs } = core.resolveAIRecordingOptions(options); | ||
| const enableTruncation = core.shouldEnableTruncation(options.enableTruncation); | ||
| const attributes = core.extractGoogleGenAIRequestAttributes(operation, params, data.self); | ||
| const model = attributes[core.GEN_AI_REQUEST_MODEL_ATTRIBUTE] || "unknown"; | ||
| attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN; | ||
| const span = core.startInactiveSpan({ | ||
| name: `${operation} ${model}`, | ||
| op: `gen_ai.${operation}`, | ||
| attributes | ||
| }); | ||
| if (recordInputs && params) { | ||
| core.addGoogleGenAIRequestAttributes(span, params, operation, enableTruncation); | ||
| } | ||
| return span; | ||
| } | ||
| function isAsyncIterable(value) { | ||
| return !!value && typeof value[Symbol.asyncIterator] === "function"; | ||
| } | ||
| function wrapStreamResult(span, data, options) { | ||
| const result = data.result; | ||
| if (!isAsyncIterable(result)) { | ||
| return false; | ||
| } | ||
| const { recordOutputs } = core.resolveAIRecordingOptions(options); | ||
| const iterate = result[Symbol.asyncIterator].bind(result); | ||
| const instrumented = core.instrumentGoogleGenAIStream({ [Symbol.asyncIterator]: iterate }, span, recordOutputs ?? false); | ||
| result[Symbol.asyncIterator] = () => instrumented; | ||
| return true; | ||
| } | ||
| const googleGenAIChannelIntegration = core.defineIntegration(_googleGenAIChannelIntegration); | ||
| exports.googleGenAIChannelIntegration = googleGenAIChannelIntegration; | ||
| //# sourceMappingURL=google-genai.js.map |
| {"version":3,"file":"google-genai.js","sources":["../../../../src/integrations/tracing-channel/google-genai.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { GoogleGenAIOptions, GoogleGenAIResponse, IntegrationFn, Span } from '@sentry/core';\nimport {\n _INTERNAL_shouldSkipAiProviderWrapping,\n addGoogleGenAIRequestAttributes,\n addGoogleGenAIResponseAttributes,\n debug,\n defineIntegration,\n extractGoogleGenAIRequestAttributes,\n GEN_AI_REQUEST_MODEL_ATTRIBUTE,\n getActiveSpan,\n instrumentGoogleGenAIStream,\n resolveAIRecordingOptions,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n spanToJSON,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Same name as the OTel integration by design: when enabled, the OTel 'Google_GenAI'\n// integration is dropped from the default set (see the Node opt-in loader).\nconst INTEGRATION_NAME = 'Google_GenAI' as const;\n\n// Distinct from the proxy's `auto.ai.google_genai` so spans from the orchestrion path\n// are attributable separately from the OTel/proxy one.\nconst ORIGIN = 'auto.ai.orchestrion.google_genai';\n\n// Each instrumented method maps to the gen_ai operation its span reports.\nconst INSTRUMENTED_CHANNELS = [\n { channel: CHANNELS.GOOGLE_GENAI_GENERATE_CONTENT, operation: 'generate_content' },\n { channel: CHANNELS.GOOGLE_GENAI_EMBED_CONTENT, operation: 'embeddings' },\n { channel: CHANNELS.GOOGLE_GENAI_CHAT, operation: 'chat' },\n] as const;\n\ninterface GoogleGenAIChannelContext {\n arguments: unknown[];\n // The transform stashes the call's `this` here, which chat methods need since the model lives on\n // the `Chat` instance (`this.model`/`this.modelVersion`), not in the call arguments.\n self?: unknown;\n result?: unknown;\n}\n\nlet subscribed = false;\n\nconst _googleGenAIChannelIntegration = ((options: GoogleGenAIOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19, and a second `init()` would double-subscribe.\n if (!diagnosticsChannel.tracingChannel || subscribed) {\n return;\n }\n subscribed = true;\n\n // `bindTracingChannelToSpan` needs the async-context binding that `initOpenTelemetry()` registers\n // after `setupOnce` runs, so wait for it before subscribing.\n waitForTracingChannelBinding(() => {\n for (const { channel, operation } of INSTRUMENTED_CHANNELS) {\n DEBUG_BUILD && debug.log(`[orchestrion:google-genai] subscribing to channel \"${channel}\"`);\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<GoogleGenAIChannelContext>(channel),\n data => createGenAiSpan(data, operation, options),\n {\n beforeSpanEnd: (span, data) => {\n // Embeddings responses carry no content attributes.\n if (operation !== 'embeddings') {\n addGoogleGenAIResponseAttributes(\n span,\n data.result as GoogleGenAIResponse,\n resolveAIRecordingOptions(options).recordOutputs,\n );\n }\n },\n deferSpanEnd: ({ span, data }) => wrapStreamResult(span, data, options),\n },\n );\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Build the span for an instrumented call.\n * Returning `undefined` opts the payload out so no span is opened.\n */\nfunction createGenAiSpan(\n data: GoogleGenAIChannelContext,\n operation: string,\n options: GoogleGenAIOptions,\n): Span | undefined {\n // When another provider (e.g. LangChain) is driving the SDK, it records the spans itself and marks this\n // provider as skipped; skip here to avoid double spans.\n if (_INTERNAL_shouldSkipAiProviderWrapping(INTEGRATION_NAME)) {\n return undefined;\n }\n\n // `chat.sendMessage()`/`sendMessageStream()` internally call `Models.generateContent(Stream)`, which\n // publishes the `generate-content` channel while the chat span is active. Skip that nested event so a\n // chat call yields a single `gen_ai.chat` span instead of a chat span wrapping a generate_content one.\n if (operation !== 'chat') {\n const activeSpan = getActiveSpan();\n if (activeSpan) {\n const { op, origin } = spanToJSON(activeSpan);\n if (origin === ORIGIN && op === 'gen_ai.chat') {\n return undefined;\n }\n }\n }\n\n const args = data.arguments ?? [];\n const params = args[0] as Record<string, unknown> | undefined;\n\n const { recordInputs } = resolveAIRecordingOptions(options);\n const enableTruncation = shouldEnableTruncation(options.enableTruncation);\n\n const attributes = extractGoogleGenAIRequestAttributes(operation, params, data.self);\n const model = (attributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] as string) || 'unknown';\n attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN;\n\n const span = startInactiveSpan({\n name: `${operation} ${model}`,\n op: `gen_ai.${operation}`,\n attributes,\n });\n\n if (recordInputs && params) {\n addGoogleGenAIRequestAttributes(span, params, operation, enableTruncation);\n }\n\n return span;\n}\n\ntype AsyncIterableStream = { [Symbol.asyncIterator]: () => AsyncIterator<unknown> };\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterableStream {\n return !!value && typeof (value as AsyncIterableStream)[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * Only the streaming methods (`generateContentStream`/`sendMessageStream`) resolve to an async iterable.\n * For a stream we patch `result[Symbol.asyncIterator]` in place so `instrumentGoogleGenAIStream` ends the\n * span when iteration finishes.\n */\nfunction wrapStreamResult(span: Span, data: GoogleGenAIChannelContext, options: GoogleGenAIOptions): boolean {\n const result = data.result;\n if (!isAsyncIterable(result)) {\n return false;\n }\n\n const { recordOutputs } = resolveAIRecordingOptions(options);\n const iterate = result[Symbol.asyncIterator].bind(result);\n const instrumented = instrumentGoogleGenAIStream({ [Symbol.asyncIterator]: iterate }, span, recordOutputs ?? false);\n result[Symbol.asyncIterator] = () => instrumented;\n\n return true;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven Google GenAI integration. Subscribes to the\n * `orchestrion:@google/genai:*` diagnostics_channels injected into the SDK's `Models`\n * (`generateContent`/`generateContentStream`/`embedContent`) and `Chat`\n * (`sendMessage`/`sendMessageStream`) methods, so it requires the orchestrion runtime hook or\n * bundler plugin.\n */\nexport const googleGenAIChannelIntegration = defineIntegration(_googleGenAIChannelIntegration);\n"],"names":["CHANNELS","waitForTracingChannelBinding","DEBUG_BUILD","debug","bindTracingChannelToSpan","addGoogleGenAIResponseAttributes","resolveAIRecordingOptions","_INTERNAL_shouldSkipAiProviderWrapping","getActiveSpan","spanToJSON","shouldEnableTruncation","extractGoogleGenAIRequestAttributes","GEN_AI_REQUEST_MODEL_ATTRIBUTE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startInactiveSpan","addGoogleGenAIRequestAttributes","instrumentGoogleGenAIStream","defineIntegration"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const ORIGIN = "auto.graphql.diagnostic_channel"; | ||
| const SPAN_NAME_PARSE = "graphql.parse"; | ||
| const SPAN_NAME_VALIDATE = "graphql.validate"; | ||
| const SPAN_NAME_EXECUTE = "graphql.execute"; | ||
| const SPAN_NAME_RESOLVE = "graphql.resolve"; | ||
| const GRAPHQL_FIELD_NAME = "graphql.field.name"; | ||
| const GRAPHQL_FIELD_PATH = "graphql.field.path"; | ||
| const GRAPHQL_FIELD_TYPE = "graphql.field.type"; | ||
| const GRAPHQL_PARENT_NAME = "graphql.parent.name"; | ||
| const GRAPHQL_DATA_SYMBOL = /* @__PURE__ */ Symbol.for("opentelemetry.graphql_data"); | ||
| const GRAPHQL_PATCHED_SYMBOL = /* @__PURE__ */ Symbol.for("opentelemetry.patched"); | ||
| exports.GRAPHQL_DATA_SYMBOL = GRAPHQL_DATA_SYMBOL; | ||
| exports.GRAPHQL_FIELD_NAME = GRAPHQL_FIELD_NAME; | ||
| exports.GRAPHQL_FIELD_PATH = GRAPHQL_FIELD_PATH; | ||
| exports.GRAPHQL_FIELD_TYPE = GRAPHQL_FIELD_TYPE; | ||
| exports.GRAPHQL_PARENT_NAME = GRAPHQL_PARENT_NAME; | ||
| exports.GRAPHQL_PATCHED_SYMBOL = GRAPHQL_PATCHED_SYMBOL; | ||
| exports.ORIGIN = ORIGIN; | ||
| exports.SPAN_NAME_EXECUTE = SPAN_NAME_EXECUTE; | ||
| exports.SPAN_NAME_PARSE = SPAN_NAME_PARSE; | ||
| exports.SPAN_NAME_RESOLVE = SPAN_NAME_RESOLVE; | ||
| exports.SPAN_NAME_VALIDATE = SPAN_NAME_VALIDATE; | ||
| //# sourceMappingURL=constants.js.map |
| {"version":3,"file":"constants.js","sources":["../../../../../src/integrations/tracing-channel/graphql/constants.ts"],"sourcesContent":["/*\n * These mirror the constants in `@sentry/server-utils`'s native graphql subscriber\n * (`src/graphql/graphql-dc-subscriber.ts`) so the orchestrion path (graphql v14–16) and the native\n * `diagnostics_channel` path (graphql >= 17) emit identical spans — same origin, span names and\n * field-attribute keys. `graphql.document`/`graphql.operation.*` and the span `op` come from\n * `@sentry/conventions` directly and are imported where used.\n */\n\nexport const ORIGIN = 'auto.graphql.diagnostic_channel';\n\nexport const SPAN_NAME_PARSE = 'graphql.parse';\nexport const SPAN_NAME_VALIDATE = 'graphql.validate';\nexport const SPAN_NAME_EXECUTE = 'graphql.execute';\nexport const SPAN_NAME_RESOLVE = 'graphql.resolve';\n\n// Field-level resolver-span attributes; not in `@sentry/conventions`.\nexport const GRAPHQL_FIELD_NAME = 'graphql.field.name';\nexport const GRAPHQL_FIELD_PATH = 'graphql.field.path';\nexport const GRAPHQL_FIELD_TYPE = 'graphql.field.type';\nexport const GRAPHQL_PARENT_NAME = 'graphql.parent.name';\n\n// `Symbol.for` keys shared with any co-resident OTel graphql instrumentation on purpose: the paths are\n// mutually exclusive at runtime, and reusing the key keeps nested-execute detection and resolver\n// parenting consistent if both ever load.\nexport const GRAPHQL_DATA_SYMBOL = Symbol.for('opentelemetry.graphql_data');\nexport const GRAPHQL_PATCHED_SYMBOL = Symbol.for('opentelemetry.patched');\n"],"names":[],"mappings":";;AAQO,MAAM,MAAA,GAAS;AAEf,MAAM,eAAA,GAAkB;AACxB,MAAM,kBAAA,GAAqB;AAC3B,MAAM,iBAAA,GAAoB;AAC1B,MAAM,iBAAA,GAAoB;AAG1B,MAAM,kBAAA,GAAqB;AAC3B,MAAM,kBAAA,GAAqB;AAC3B,MAAM,kBAAA,GAAqB;AAC3B,MAAM,mBAAA,GAAsB;AAK5B,MAAM,mBAAA,mBAAsB,MAAA,CAAO,GAAA,CAAI,4BAA4B;AACnE,MAAM,sBAAA,mBAAyB,MAAA,CAAO,GAAA,CAAI,uBAAuB;;;;;;;;;;;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const core = require('@sentry/core'); | ||
| const debugBuild = require('../../../debug-build.js'); | ||
| const index = require('../../../graphql/index.js'); | ||
| const channels = require('../../../orchestrion/channels.js'); | ||
| const tracingChannel = require('../../../tracing-channel.js'); | ||
| const spans = require('./spans.js'); | ||
| const INTEGRATION_NAME = "Graphql"; | ||
| function getOptionsWithDefaults(options) { | ||
| return { | ||
| ignoreResolveSpans: options.ignoreResolveSpans !== false, | ||
| ignoreTrivialResolveSpans: options.ignoreTrivialResolveSpans !== false, | ||
| useOperationNameForRootSpan: options.useOperationNameForRootSpan !== false | ||
| }; | ||
| } | ||
| function safe(fn) { | ||
| try { | ||
| return fn(); | ||
| } catch (error) { | ||
| debugBuild.DEBUG_BUILD && core.debug.warn("[orchestrion:graphql] error building span", error); | ||
| return void 0; | ||
| } | ||
| } | ||
| const _graphqlChannelIntegration = ((options = {}) => { | ||
| const config = getOptionsWithDefaults(options); | ||
| const getConfig = () => config; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| core.waitForTracingChannelBinding(() => { | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.GRAPHQL_PARSE), | ||
| () => safe(() => spans.startParseSpan()) | ||
| ); | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.GRAPHQL_VALIDATE), | ||
| (data) => safe(() => spans.startValidateSpan(data.arguments[1])), | ||
| { beforeSpanEnd: (span, data) => void safe(() => spans.finalizeValidateSpan(span, data.result)) } | ||
| ); | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.GRAPHQL_EXECUTE), | ||
| (data) => safe(() => spans.startExecuteSpan(data.arguments, data.self, config, getConfig)), | ||
| { beforeSpanEnd: (span, data) => void safe(() => spans.finalizeExecuteSpan(span, data.result)) } | ||
| ); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const graphqlChannelIntegration = core.defineIntegration(_graphqlChannelIntegration); | ||
| const graphqlDiagnosticsChannelIntegration = (options) => { | ||
| const orchestrion = graphqlChannelIntegration(options); | ||
| return core.extendIntegration(index.graphqlIntegration(options), { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce: () => orchestrion.setupOnce?.() | ||
| }); | ||
| }; | ||
| exports.graphqlChannelIntegration = graphqlChannelIntegration; | ||
| exports.graphqlDiagnosticsChannelIntegration = graphqlDiagnosticsChannelIntegration; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing-channel/graphql/index.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';\nimport { DEBUG_BUILD } from '../../../debug-build';\nimport { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql';\nimport type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber';\nimport { CHANNELS } from '../../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../../tracing-channel';\nimport {\n finalizeExecuteSpan,\n finalizeValidateSpan,\n startExecuteSpan,\n startParseSpan,\n startValidateSpan,\n} from './spans';\nimport type { GraphqlResolvedConfig } from './types';\n\n// Same name as the OTel/native integration by design, so enabling injection swaps this in for it.\nconst INTEGRATION_NAME = 'Graphql' as const;\n\n// The context orchestrion's transform attaches to each channel: `arguments` is the live args of the\n// wrapped call, `result` the settled return value.\ninterface GraphqlChannelContext {\n arguments: unknown[];\n self?: unknown;\n result?: unknown;\n error?: unknown;\n}\n\nfunction getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): GraphqlResolvedConfig {\n return {\n ignoreResolveSpans: options.ignoreResolveSpans !== false,\n ignoreTrivialResolveSpans: options.ignoreTrivialResolveSpans !== false,\n useOperationNameForRootSpan: options.useOperationNameForRootSpan !== false,\n };\n}\n\n/**\n * Runs a span-building callback so a throw inside it can never break the user's graphql call: these\n * run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan`\n * producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call.\n */\nfunction safe<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch (error) {\n DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error);\n return undefined;\n }\n}\n\nconst _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {\n const config = getOptionsWithDefaults(options);\n const getConfig = (): GraphqlResolvedConfig => config;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_PARSE), () =>\n safe(() => startParseSpan()),\n );\n\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_VALIDATE),\n data => safe(() => startValidateSpan(data.arguments[1])),\n { beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) },\n );\n\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_EXECUTE),\n data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),\n { beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) },\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * EXPERIMENTAL — orchestrion-driven graphql integration for graphql v14–16 (v17 publishes native\n * `diagnostics_channel` events handled by `@sentry/server-utils`'s graphql integration instead).\n *\n * Subscribes to the `orchestrion:graphql:{parse,validate,execute}` channels the orchestrion code\n * transform injects into `graphql`'s `language/parser.js`, `validation/validate.js` and\n * `execution/execute.js`, emitting spans identical to the native path. Requires the orchestrion\n * runtime hook or bundler plugin — wire it up via `experimentalUseDiagnosticsChannelInjection()`.\n *\n * @experimental\n */\nexport const graphqlChannelIntegration = defineIntegration(_graphqlChannelIntegration);\n\n/**\n * The complete graphql diagnostics-channel integration: the native subscriber (graphql v17) composed\n * with the orchestrion subscriber (v14–16), so opting into injection instruments every supported\n * version via diagnostics channels without the OTel patcher. Reuses the OTel `Graphql` name so\n * enabling injection swaps this in for it.\n */\nexport const graphqlDiagnosticsChannelIntegration = (options?: GraphqlDiagnosticChannelsOptions) => {\n const orchestrion = graphqlChannelIntegration(options);\n return extendIntegration(graphqlNativeIntegration(options), {\n name: INTEGRATION_NAME,\n setupOnce: () => orchestrion.setupOnce?.(),\n });\n};\n"],"names":["DEBUG_BUILD","debug","waitForTracingChannelBinding","bindTracingChannelToSpan","CHANNELS","startParseSpan","startValidateSpan","finalizeValidateSpan","startExecuteSpan","finalizeExecuteSpan","defineIntegration","extendIntegration","graphqlNativeIntegration"],"mappings":";;;;;;;;;;AAkBA,MAAM,gBAAA,GAAmB,SAAA;AAWzB,SAAS,uBAAuB,OAAA,EAAkE;AAChG,EAAA,OAAO;AAAA,IACL,kBAAA,EAAoB,QAAQ,kBAAA,KAAuB,KAAA;AAAA,IACnD,yBAAA,EAA2B,QAAQ,yBAAA,KAA8B,KAAA;AAAA,IACjE,2BAAA,EAA6B,QAAQ,2BAAA,KAAgC;AAAA,GACvE;AACF;AAOA,SAAS,KAAQ,EAAA,EAA4B;AAC3C,EAAA,IAAI;AACF,IAAA,OAAO,EAAA,EAAG;AAAA,EACZ,SAAS,KAAA,EAAO;AACd,IAAAA,sBAAA,IAAeC,UAAA,CAAM,IAAA,CAAK,2CAAA,EAA6C,KAAK,CAAA;AAC5E,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAEA,MAAM,0BAAA,IAA8B,CAAC,OAAA,GAA4C,EAAC,KAAM;AACtF,EAAA,MAAM,MAAA,GAAS,uBAAuB,OAAO,CAAA;AAC7C,EAAA,MAAM,YAAY,MAA6B,MAAA;AAE/C,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,IAAI,CAAC,mBAAmB,cAAA,EAAgB;AACtC,QAAA;AAAA,MACF;AAEA,MAAAC,iCAAA,CAA6B,MAAM;AACjC,QAAAC,uCAAA;AAAA,UAAyB,kBAAA,CAAmB,cAAA,CAAsCC,iBAAA,CAAS,aAAa,CAAA;AAAA,UAAG,MACzG,IAAA,CAAK,MAAMC,oBAAA,EAAgB;AAAA,SAC7B;AAEA,QAAAF,uCAAA;AAAA,UACE,kBAAA,CAAmB,cAAA,CAAsCC,iBAAA,CAAS,gBAAgB,CAAA;AAAA,UAClF,CAAA,IAAA,KAAQ,KAAK,MAAME,uBAAA,CAAkB,KAAK,SAAA,CAAU,CAAC,CAAC,CAAC,CAAA;AAAA,UACvD,EAAE,aAAA,EAAe,CAAC,IAAA,EAAM,IAAA,KAAS,KAAK,IAAA,CAAK,MAAMC,0BAAA,CAAqB,IAAA,EAAM,IAAA,CAAK,MAAM,CAAC,CAAA;AAAE,SAC5F;AAEA,QAAAJ,uCAAA;AAAA,UACE,kBAAA,CAAmB,cAAA,CAAsCC,iBAAA,CAAS,eAAe,CAAA;AAAA,UACjF,CAAA,IAAA,KAAQ,IAAA,CAAK,MAAMI,sBAAA,CAAiB,IAAA,CAAK,WAAW,IAAA,CAAK,IAAA,EAAM,MAAA,EAAQ,SAAS,CAAC,CAAA;AAAA,UACjF,EAAE,aAAA,EAAe,CAAC,IAAA,EAAM,IAAA,KAAS,KAAK,IAAA,CAAK,MAAMC,yBAAA,CAAoB,IAAA,EAAM,IAAA,CAAK,MAAM,CAAC,CAAA;AAAE,SAC3F;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAaO,MAAM,yBAAA,GAA4BC,uBAAkB,0BAA0B;AAQ9E,MAAM,oCAAA,GAAuC,CAAC,OAAA,KAA+C;AAClG,EAAA,MAAM,WAAA,GAAc,0BAA0B,OAAO,CAAA;AACrD,EAAA,OAAOC,sBAAA,CAAkBC,wBAAA,CAAyB,OAAO,CAAA,EAAG;AAAA,IAC1D,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,EAAW,MAAM,WAAA,CAAY,SAAA;AAAY,GAC1C,CAAA;AACH;;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const op = require('@sentry/conventions/op'); | ||
| const core = require('@sentry/core'); | ||
| const constants = require('./constants.js'); | ||
| function isPromise(value) { | ||
| return typeof value?.then === "function"; | ||
| } | ||
| function wrapFields(type, getConfig) { | ||
| if (!type || type[constants.GRAPHQL_PATCHED_SYMBOL]) { | ||
| return; | ||
| } | ||
| type[constants.GRAPHQL_PATCHED_SYMBOL] = true; | ||
| const fields = type.getFields(); | ||
| Object.keys(fields).forEach((key) => { | ||
| const field = fields[key]; | ||
| if (!field) { | ||
| return; | ||
| } | ||
| if (field.resolve) { | ||
| field.resolve = wrapFieldResolver(getConfig, field.resolve); | ||
| } | ||
| if (field.type) { | ||
| for (const unwrappedType of unwrapType(field.type)) { | ||
| wrapFields(unwrappedType, getConfig); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| function wrapFieldResolver(getConfig, fieldResolver, isDefaultResolver = false) { | ||
| if (typeof fieldResolver !== "function" || fieldResolver[constants.GRAPHQL_PATCHED_SYMBOL]) { | ||
| return fieldResolver; | ||
| } | ||
| function wrappedFieldResolver(source, args, rawContextValue, info) { | ||
| if (!fieldResolver) { | ||
| return void 0; | ||
| } | ||
| const contextValue = rawContextValue ?? {}; | ||
| const config = getConfig(); | ||
| if (config.ignoreTrivialResolveSpans && isDefaultResolver && (core.isObjectLike(source) || typeof source === "function")) { | ||
| const property = source[info.fieldName]; | ||
| if (typeof property !== "function") { | ||
| return fieldResolver.call(this, source, args, contextValue, info); | ||
| } | ||
| } | ||
| if (!contextValue[constants.GRAPHQL_DATA_SYMBOL]) { | ||
| return fieldResolver.call(this, source, args, contextValue, info); | ||
| } | ||
| const path = pathToArray(info.path); | ||
| const { field, spanAdded } = createFieldIfNotExists(contextValue, info, path); | ||
| const span = field.span; | ||
| return core.withActiveSpan(span, () => { | ||
| try { | ||
| const res = fieldResolver.call(this, source, args, contextValue, info); | ||
| if (isPromise(res)) { | ||
| return res.then( | ||
| (r) => { | ||
| endResolveSpan(span, spanAdded); | ||
| return r; | ||
| }, | ||
| (err) => { | ||
| endResolveSpan(span, spanAdded, err); | ||
| throw err; | ||
| } | ||
| ); | ||
| } | ||
| endResolveSpan(span, spanAdded); | ||
| return res; | ||
| } catch (err) { | ||
| endResolveSpan(span, spanAdded, err); | ||
| throw err; | ||
| } | ||
| }); | ||
| } | ||
| wrappedFieldResolver[constants.GRAPHQL_PATCHED_SYMBOL] = true; | ||
| return wrappedFieldResolver; | ||
| } | ||
| function endResolveSpan(span, shouldEndSpan, error) { | ||
| if (!shouldEndSpan) { | ||
| return; | ||
| } | ||
| if (error) { | ||
| span.setStatus({ code: core.SPAN_STATUS_ERROR, message: error.message }); | ||
| } | ||
| span.end(); | ||
| } | ||
| function createFieldIfNotExists(contextValue, info, path) { | ||
| const existing = getField(contextValue, path); | ||
| if (existing) { | ||
| return { field: existing, spanAdded: false }; | ||
| } | ||
| const field = { span: createResolverSpan(info, path, getParentFieldSpan(contextValue, path)) }; | ||
| addField(contextValue, path, field); | ||
| return { field, spanAdded: true }; | ||
| } | ||
| function createResolverSpan(info, path, parentSpan) { | ||
| const attributes = { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: constants.ORIGIN, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op.WEB_SERVER_GRAPHQL_SPAN_OP, | ||
| [constants.GRAPHQL_FIELD_NAME]: info.fieldName, | ||
| [constants.GRAPHQL_FIELD_PATH]: path.join("."), | ||
| [constants.GRAPHQL_FIELD_TYPE]: info.returnType.toString(), | ||
| [constants.GRAPHQL_PARENT_NAME]: info.parentType.name | ||
| }; | ||
| return core.startInactiveSpan({ name: `${constants.SPAN_NAME_RESOLVE} ${path.join(".")}`, attributes, parentSpan }); | ||
| } | ||
| function addField(contextValue, path, field) { | ||
| const data = contextValue[constants.GRAPHQL_DATA_SYMBOL]; | ||
| if (data) { | ||
| data.fields[path.join(".")] = field; | ||
| } | ||
| } | ||
| function getField(contextValue, path) { | ||
| return contextValue[constants.GRAPHQL_DATA_SYMBOL]?.fields[path.join(".")]; | ||
| } | ||
| function getParentFieldSpan(contextValue, path) { | ||
| for (let i = path.length - 1; i > 0; i--) { | ||
| const field = getField(contextValue, path.slice(0, i)); | ||
| if (field) { | ||
| return field.span; | ||
| } | ||
| } | ||
| return contextValue[constants.GRAPHQL_DATA_SYMBOL]?.span; | ||
| } | ||
| function pathToArray(path) { | ||
| const flattened = []; | ||
| let curr = path; | ||
| while (curr) { | ||
| flattened.push(String(curr.key)); | ||
| curr = curr.prev; | ||
| } | ||
| return flattened.reverse(); | ||
| } | ||
| function unwrapType(type) { | ||
| if ("ofType" in type && type.ofType) { | ||
| return unwrapType(type.ofType); | ||
| } | ||
| if (isGraphQLUnionType(type)) { | ||
| return type.getTypes(); | ||
| } | ||
| if (isGraphQLObjectType(type)) { | ||
| return [type]; | ||
| } | ||
| return []; | ||
| } | ||
| function isGraphQLUnionType(type) { | ||
| return "getTypes" in type && typeof type.getTypes === "function"; | ||
| } | ||
| function isGraphQLObjectType(type) { | ||
| return "getFields" in type && typeof type.getFields === "function"; | ||
| } | ||
| function getOperation(document, operationName) { | ||
| const definitions = document?.definitions; | ||
| if (!definitions || !Array.isArray(definitions)) { | ||
| return void 0; | ||
| } | ||
| const isOperation = (def) => !!def?.operation && ["query", "mutation", "subscription"].indexOf(def.operation) !== -1; | ||
| if (operationName) { | ||
| return definitions.filter(isOperation).find((def) => operationName === def?.name?.value); | ||
| } | ||
| return definitions.find(isOperation); | ||
| } | ||
| exports.getOperation = getOperation; | ||
| exports.wrapFieldResolver = wrapFieldResolver; | ||
| exports.wrapFields = wrapFields; | ||
| //# sourceMappingURL=resolvers.js.map |
| {"version":3,"file":"resolvers.js","sources":["../../../../../src/integrations/tracing-channel/graphql/resolvers.ts"],"sourcesContent":["/*\n * Resolver-span wrapping for the orchestrion graphql path. graphql v14–16 publishes no per-field\n * `resolve` channel (unlike v17's native one), so the schema's field resolvers are swapped for\n * span-creating proxies under the execute channel's `start` (the \"consumer trick\" — the transform\n * can't target user resolvers, but `execute` receives the schema, so we mutate it before the wrapped\n * call runs). Resolver spans use the same origin/op/field attributes as the native subscriber.\n */\n\nimport { WEB_SERVER_GRAPHQL_SPAN_OP } from '@sentry/conventions/op';\nimport type { Span, SpanAttributes } from '@sentry/core';\nimport {\n isObjectLike,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n withActiveSpan,\n} from '@sentry/core';\nimport {\n GRAPHQL_DATA_SYMBOL,\n GRAPHQL_FIELD_NAME,\n GRAPHQL_FIELD_PATH,\n GRAPHQL_FIELD_TYPE,\n GRAPHQL_PARENT_NAME,\n GRAPHQL_PATCHED_SYMBOL,\n ORIGIN,\n SPAN_NAME_RESOLVE,\n} from './constants';\nimport type {\n DefinitionNode,\n DocumentNode,\n GraphQLFieldResolver,\n GraphQLObjectType,\n GraphQLOutputType,\n GraphQLPath,\n GraphQLResolveInfo,\n GraphQLType,\n GraphQLUnionType,\n GraphqlResolvedConfig,\n Maybe,\n ObjectWithGraphQLData,\n Patched,\n} from './types';\n\nfunction isPromise(value: unknown): value is Promise<unknown> {\n return typeof (value as { then?: unknown } | undefined)?.then === 'function';\n}\n\n/**\n * Walks the query/mutation type tree and swaps each field's `resolve` for a span-creating proxy.\n * Idempotent per type via {@link GRAPHQL_PATCHED_SYMBOL}.\n */\nexport function wrapFields(type: Maybe<GraphQLObjectType & Patched>, getConfig: () => GraphqlResolvedConfig): void {\n if (!type || type[GRAPHQL_PATCHED_SYMBOL]) {\n return;\n }\n\n type[GRAPHQL_PATCHED_SYMBOL] = true;\n const fields = type.getFields();\n\n Object.keys(fields).forEach(key => {\n const field = fields[key];\n if (!field) {\n return;\n }\n\n if (field.resolve) {\n field.resolve = wrapFieldResolver(getConfig, field.resolve);\n }\n\n if (field.type) {\n for (const unwrappedType of unwrapType(field.type)) {\n wrapFields(unwrappedType, getConfig);\n }\n }\n });\n}\n\nexport function wrapFieldResolver(\n getConfig: () => GraphqlResolvedConfig,\n fieldResolver: Maybe<GraphQLFieldResolver & Patched>,\n isDefaultResolver = false,\n): GraphQLFieldResolver & Patched {\n // Return the resolver untouched if it isn't a function or is already a wrapped one — the wrapper we\n // return is marked with `GRAPHQL_PATCHED_SYMBOL` below precisely so it can be detected here. (The\n // guard must test the incoming `fieldResolver`, not the freshly-hoisted `wrappedFieldResolver`.)\n if (typeof fieldResolver !== 'function' || fieldResolver[GRAPHQL_PATCHED_SYMBOL]) {\n return fieldResolver as GraphQLFieldResolver;\n }\n\n function wrappedFieldResolver(\n this: unknown,\n source: unknown,\n args: unknown,\n rawContextValue: unknown,\n info: GraphQLResolveInfo,\n ): unknown {\n if (!fieldResolver) {\n return undefined;\n }\n\n const contextValue = (rawContextValue ?? {}) as ObjectWithGraphQLData;\n const config = getConfig();\n\n // Mirror graphql's own \"trivial resolver\" check: a default resolver that just reads a\n // non-function property is not worth a span.\n if (\n config.ignoreTrivialResolveSpans &&\n isDefaultResolver &&\n (isObjectLike(source) || typeof source === 'function')\n ) {\n const property = (source as Record<string, unknown>)[info.fieldName];\n if (typeof property !== 'function') {\n return fieldResolver.call(this, source, args, contextValue, info);\n }\n }\n\n if (!contextValue[GRAPHQL_DATA_SYMBOL]) {\n return fieldResolver.call(this, source, args, contextValue, info);\n }\n\n const path = pathToArray(info.path);\n const { field, spanAdded } = createFieldIfNotExists(contextValue, info, path);\n const span = field.span;\n\n return withActiveSpan(span, () => {\n try {\n const res = fieldResolver.call(this, source, args, contextValue, info);\n if (isPromise(res)) {\n return res.then(\n r => {\n endResolveSpan(span, spanAdded);\n return r;\n },\n (err: Error) => {\n endResolveSpan(span, spanAdded, err);\n throw err;\n },\n );\n }\n endResolveSpan(span, spanAdded);\n return res;\n } catch (err) {\n endResolveSpan(span, spanAdded, err as Error);\n throw err;\n }\n });\n }\n\n (wrappedFieldResolver as Patched)[GRAPHQL_PATCHED_SYMBOL] = true;\n return wrappedFieldResolver;\n}\n\nfunction endResolveSpan(span: Span, shouldEndSpan: boolean, error?: Error): void {\n if (!shouldEndSpan) {\n return;\n }\n if (error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: error.message });\n }\n span.end();\n}\n\nfunction createFieldIfNotExists(\n contextValue: ObjectWithGraphQLData,\n info: GraphQLResolveInfo,\n path: string[],\n): { field: { span: Span }; spanAdded: boolean } {\n const existing = getField(contextValue, path);\n if (existing) {\n return { field: existing, spanAdded: false };\n }\n\n const field = { span: createResolverSpan(info, path, getParentFieldSpan(contextValue, path)) };\n addField(contextValue, path, field);\n return { field, spanAdded: true };\n}\n\nfunction createResolverSpan(info: GraphQLResolveInfo, path: string[], parentSpan?: Span): Span {\n const attributes: SpanAttributes = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n [GRAPHQL_FIELD_NAME]: info.fieldName,\n [GRAPHQL_FIELD_PATH]: path.join('.'),\n [GRAPHQL_FIELD_TYPE]: info.returnType.toString(),\n [GRAPHQL_PARENT_NAME]: info.parentType.name,\n };\n\n return startInactiveSpan({ name: `${SPAN_NAME_RESOLVE} ${path.join('.')}`, attributes, parentSpan });\n}\n\nfunction addField(contextValue: ObjectWithGraphQLData, path: string[], field: { span: Span }): void {\n const data = contextValue[GRAPHQL_DATA_SYMBOL];\n if (data) {\n data.fields[path.join('.')] = field;\n }\n}\n\nfunction getField(contextValue: ObjectWithGraphQLData, path: string[]): { span: Span } | undefined {\n return contextValue[GRAPHQL_DATA_SYMBOL]?.fields[path.join('.')];\n}\n\nfunction getParentFieldSpan(contextValue: ObjectWithGraphQLData, path: string[]): Span | undefined {\n for (let i = path.length - 1; i > 0; i--) {\n const field = getField(contextValue, path.slice(0, i));\n if (field) {\n return field.span;\n }\n }\n return contextValue[GRAPHQL_DATA_SYMBOL]?.span;\n}\n\nfunction pathToArray(path: GraphQLPath): string[] {\n const flattened: string[] = [];\n let curr: GraphQLPath | undefined = path;\n while (curr) {\n flattened.push(String(curr.key));\n curr = curr.prev;\n }\n return flattened.reverse();\n}\n\nfunction unwrapType(type: GraphQLOutputType): readonly (GraphQLObjectType & Patched)[] {\n // The structural index signature widens `ofType` to `unknown`, so narrow it back explicitly.\n if ('ofType' in type && type.ofType) {\n return unwrapType(type.ofType as GraphQLOutputType);\n }\n if (isGraphQLUnionType(type)) {\n return type.getTypes();\n }\n if (isGraphQLObjectType(type)) {\n return [type];\n }\n return [];\n}\n\nfunction isGraphQLUnionType(type: GraphQLType): type is GraphQLUnionType {\n return 'getTypes' in type && typeof type.getTypes === 'function';\n}\n\nfunction isGraphQLObjectType(type: GraphQLType): type is GraphQLObjectType {\n return 'getFields' in type && typeof (type as GraphQLObjectType).getFields === 'function';\n}\n\n/**\n * Returns the operation definition for `operationName` (or the first operation) from a parsed\n * document, or `undefined` for schema documents / when no operation is present.\n */\nexport function getOperation(document: DocumentNode, operationName?: Maybe<string>): DefinitionNode | undefined {\n const definitions: readonly DefinitionNode[] | undefined = document?.definitions;\n if (!definitions || !Array.isArray(definitions)) {\n return undefined;\n }\n\n const isOperation = (def: DefinitionNode): boolean =>\n !!def?.operation && ['query', 'mutation', 'subscription'].indexOf(def.operation) !== -1;\n\n if (operationName) {\n return definitions.filter(isOperation).find((def: DefinitionNode) => operationName === def?.name?.value);\n }\n return definitions.find(isOperation);\n}\n"],"names":["GRAPHQL_PATCHED_SYMBOL","isObjectLike","GRAPHQL_DATA_SYMBOL","withActiveSpan","SPAN_STATUS_ERROR","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","WEB_SERVER_GRAPHQL_SPAN_OP","GRAPHQL_FIELD_NAME","GRAPHQL_FIELD_PATH","GRAPHQL_FIELD_TYPE","GRAPHQL_PARENT_NAME","startInactiveSpan","SPAN_NAME_RESOLVE"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const attributes = require('@sentry/conventions/attributes'); | ||
| const op = require('@sentry/conventions/op'); | ||
| const core = require('@sentry/core'); | ||
| const utils = require('../../../graphql/utils.js'); | ||
| const constants = require('./constants.js'); | ||
| const resolvers = require('./resolvers.js'); | ||
| const BASE_ATTRIBUTES = { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: constants.ORIGIN, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op.WEB_SERVER_GRAPHQL_SPAN_OP | ||
| }; | ||
| function startParseSpan() { | ||
| return core.startInactiveSpan({ name: constants.SPAN_NAME_PARSE, attributes: { ...BASE_ATTRIBUTES } }); | ||
| } | ||
| function startValidateSpan(documentAST) { | ||
| return core.startInactiveSpan({ | ||
| name: constants.SPAN_NAME_VALIDATE, | ||
| attributes: { ...BASE_ATTRIBUTES, [attributes.GRAPHQL_DOCUMENT]: utils.redactGraphqlDocument(documentAST) } | ||
| }); | ||
| } | ||
| function finalizeValidateSpan(span, result) { | ||
| if (Array.isArray(result) && result.length > 0) { | ||
| span.setStatus({ code: core.SPAN_STATUS_ERROR, message: "invalid_argument" }); | ||
| } | ||
| } | ||
| function normalizeExecuteArgs(argsArray) { | ||
| if (argsArray.length >= 2) { | ||
| return { | ||
| schema: argsArray[0 /* SCHEMA */], | ||
| document: argsArray[1 /* DOCUMENT */], | ||
| contextValue: argsArray[3 /* CONTEXT_VALUE */] ?? {}, | ||
| operationName: argsArray[5 /* OPERATION_NAME */], | ||
| fieldResolver: argsArray[6 /* FIELD_RESOLVER */], | ||
| writeBack: (contextValue, fieldResolver) => { | ||
| argsArray[3 /* CONTEXT_VALUE */] = contextValue; | ||
| argsArray[6 /* FIELD_RESOLVER */] = fieldResolver; | ||
| } | ||
| }; | ||
| } | ||
| const obj = argsArray[0] ?? {}; | ||
| return { | ||
| schema: obj.schema, | ||
| document: obj.document, | ||
| contextValue: obj.contextValue ?? {}, | ||
| operationName: obj.operationName, | ||
| fieldResolver: obj.fieldResolver, | ||
| writeBack: (contextValue, fieldResolver) => { | ||
| obj.contextValue = contextValue; | ||
| obj.fieldResolver = fieldResolver; | ||
| } | ||
| }; | ||
| } | ||
| function startExecuteSpan(argsArray, self, config, getConfig) { | ||
| const args = normalizeExecuteArgs(argsArray); | ||
| const { schema, document } = args; | ||
| let { contextValue, fieldResolver } = args; | ||
| const alreadyInstrumented = !!contextValue[constants.GRAPHQL_DATA_SYMBOL]; | ||
| if (!config.ignoreResolveSpans && !alreadyInstrumented) { | ||
| const isUsingDefaultResolver = fieldResolver == null; | ||
| const defaultFieldResolver = self?.defaultFieldResolver; | ||
| const fieldResolverForExecute = fieldResolver ?? defaultFieldResolver; | ||
| if (fieldResolverForExecute) { | ||
| fieldResolver = resolvers.wrapFieldResolver(getConfig, fieldResolverForExecute, isUsingDefaultResolver); | ||
| } | ||
| if (schema) { | ||
| resolvers.wrapFields(schema.getQueryType(), getConfig); | ||
| resolvers.wrapFields(schema.getMutationType(), getConfig); | ||
| } | ||
| } | ||
| const operation = resolvers.getOperation(document, args.operationName); | ||
| const operationType = operation?.operation; | ||
| const operationName = operation?.name?.value ?? args.operationName ?? void 0; | ||
| const span = core.startInactiveSpan({ | ||
| name: utils.getOperationSpanName(operationType, operationName || void 0, constants.SPAN_NAME_EXECUTE), | ||
| attributes: { | ||
| ...BASE_ATTRIBUTES, | ||
| [attributes.GRAPHQL_OPERATION_TYPE]: operationType, | ||
| [attributes.GRAPHQL_OPERATION_NAME]: operationName || void 0, | ||
| [attributes.GRAPHQL_DOCUMENT]: utils.redactGraphqlDocument(document) | ||
| } | ||
| }); | ||
| if (config.useOperationNameForRootSpan && operationType) { | ||
| utils.renameRootSpanWithOperation(span, operationType, operationName || void 0); | ||
| } | ||
| contextValue[constants.GRAPHQL_DATA_SYMBOL] = { source: document, span, fields: {} }; | ||
| args.writeBack(contextValue, fieldResolver); | ||
| return span; | ||
| } | ||
| function finalizeExecuteSpan(span, result) { | ||
| if (utils.hasResultErrors(result)) { | ||
| span.setStatus({ code: core.SPAN_STATUS_ERROR, message: "internal_error" }); | ||
| } | ||
| } | ||
| exports.finalizeExecuteSpan = finalizeExecuteSpan; | ||
| exports.finalizeValidateSpan = finalizeValidateSpan; | ||
| exports.startExecuteSpan = startExecuteSpan; | ||
| exports.startParseSpan = startParseSpan; | ||
| exports.startValidateSpan = startValidateSpan; | ||
| //# sourceMappingURL=spans.js.map |
| {"version":3,"file":"spans.js","sources":["../../../../../src/integrations/tracing-channel/graphql/spans.ts"],"sourcesContent":["/*\n * Span builders for the orchestrion graphql channels (v14–16). They emit the same spans as the native\n * `diagnostics_channel` subscriber (`../../../graphql/graphql-dc-subscriber.ts`, graphql >= 17) by\n * reusing its `utils` and conventions — only the data source differs: here the payloads are the raw\n * arguments of the injected `parse`/`validate`/`execute` calls rather than graphql's native events.\n */\n\nimport { GRAPHQL_DOCUMENT, GRAPHQL_OPERATION_NAME, GRAPHQL_OPERATION_TYPE } from '@sentry/conventions/attributes';\nimport { WEB_SERVER_GRAPHQL_SPAN_OP } from '@sentry/conventions/op';\nimport type { Span } from '@sentry/core';\nimport {\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n} from '@sentry/core';\nimport type { GraphqlDocumentNode } from '../../../graphql/utils';\nimport {\n getOperationSpanName,\n hasResultErrors,\n redactGraphqlDocument,\n renameRootSpanWithOperation,\n} from '../../../graphql/utils';\nimport { GRAPHQL_DATA_SYMBOL, ORIGIN, SPAN_NAME_EXECUTE, SPAN_NAME_PARSE, SPAN_NAME_VALIDATE } from './constants';\nimport { getOperation, wrapFields, wrapFieldResolver } from './resolvers';\nimport type {\n DocumentNode,\n GraphQLFieldResolver,\n GraphQLSchema,\n GraphqlResolvedConfig,\n Maybe,\n ObjectWithGraphQLData,\n} from './types';\n\nconst BASE_ATTRIBUTES = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n} as const;\n\nexport function startParseSpan(): Span {\n return startInactiveSpan({ name: SPAN_NAME_PARSE, attributes: { ...BASE_ATTRIBUTES } });\n}\n\n/** `documentAST` is the 2nd argument to `validate(schema, documentAST, …)`. */\nexport function startValidateSpan(documentAST: unknown): Span {\n return startInactiveSpan({\n name: SPAN_NAME_VALIDATE,\n attributes: { ...BASE_ATTRIBUTES, [GRAPHQL_DOCUMENT]: redactGraphqlDocument(documentAST as GraphqlDocumentNode) },\n });\n}\n\n/** `result` is validation's return value: a (possibly empty) array of errors. */\nexport function finalizeValidateSpan(span: Span, result: unknown): void {\n if (Array.isArray(result) && result.length > 0) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'invalid_argument' });\n }\n}\n\n/** Positional slots of a `graphql.execute(schema, document, …)` call (v14/v15 legacy signature). */\nconst enum ExecuteArg {\n SCHEMA = 0,\n DOCUMENT = 1,\n CONTEXT_VALUE = 3,\n OPERATION_NAME = 5,\n FIELD_RESOLVER = 6,\n}\n\ninterface NormalizedExecuteArgs {\n schema?: GraphQLSchema;\n document?: DocumentNode;\n contextValue: ObjectWithGraphQLData;\n operationName?: Maybe<string>;\n fieldResolver?: Maybe<GraphQLFieldResolver>;\n /** Writes `contextValue`/`fieldResolver` mutations back to the live channel `arguments`. */\n writeBack: (contextValue: ObjectWithGraphQLData, fieldResolver: Maybe<GraphQLFieldResolver>) => void;\n}\n\n/**\n * `execute` accepts either a single `ExecutionArgs` object (modern callers, always in v16) or\n * positional args (v14/v15). Both are normalized here; `writeBack` puts mutations onto the correct\n * slot so they reach the real call.\n */\nfunction normalizeExecuteArgs(argsArray: unknown[]): NormalizedExecuteArgs {\n if (argsArray.length >= 2) {\n return {\n schema: argsArray[ExecuteArg.SCHEMA] as GraphQLSchema | undefined,\n document: argsArray[ExecuteArg.DOCUMENT] as DocumentNode | undefined,\n contextValue: (argsArray[ExecuteArg.CONTEXT_VALUE] ?? {}) as ObjectWithGraphQLData,\n operationName: argsArray[ExecuteArg.OPERATION_NAME] as Maybe<string>,\n fieldResolver: argsArray[ExecuteArg.FIELD_RESOLVER] as Maybe<GraphQLFieldResolver>,\n writeBack: (contextValue, fieldResolver) => {\n argsArray[ExecuteArg.CONTEXT_VALUE] = contextValue;\n argsArray[ExecuteArg.FIELD_RESOLVER] = fieldResolver;\n },\n };\n }\n\n const obj = (argsArray[0] ?? {}) as {\n schema?: GraphQLSchema;\n document?: DocumentNode;\n contextValue?: unknown;\n operationName?: Maybe<string>;\n fieldResolver?: Maybe<GraphQLFieldResolver>;\n };\n return {\n schema: obj.schema,\n document: obj.document,\n contextValue: (obj.contextValue ?? {}) as ObjectWithGraphQLData,\n operationName: obj.operationName,\n fieldResolver: obj.fieldResolver,\n writeBack: (contextValue, fieldResolver) => {\n obj.contextValue = contextValue;\n obj.fieldResolver = fieldResolver;\n },\n };\n}\n\n/**\n * Opens the execute span and, unless resolver spans are disabled, swaps the schema's field resolvers\n * (and the default field resolver) for span-creating proxies — mutating the live `arguments` in place\n * so the wrapped `execute` call runs with them. Always returns a span; the caller guards against\n * throws (see `safe` in `index.ts`).\n */\nexport function startExecuteSpan(\n argsArray: unknown[],\n self: unknown,\n config: GraphqlResolvedConfig,\n getConfig: () => GraphqlResolvedConfig,\n): Span {\n const args = normalizeExecuteArgs(argsArray);\n const { schema, document } = args;\n let { contextValue, fieldResolver } = args;\n\n // Skip resolver wrapping when disabled or when a parent execute already set up this context\n // (nested execute reusing the same contextValue).\n const alreadyInstrumented = !!contextValue[GRAPHQL_DATA_SYMBOL];\n if (!config.ignoreResolveSpans && !alreadyInstrumented) {\n const isUsingDefaultResolver = fieldResolver == null;\n const defaultFieldResolver = (self as { defaultFieldResolver?: GraphQLFieldResolver } | undefined)\n ?.defaultFieldResolver;\n const fieldResolverForExecute = fieldResolver ?? defaultFieldResolver;\n if (fieldResolverForExecute) {\n fieldResolver = wrapFieldResolver(getConfig, fieldResolverForExecute, isUsingDefaultResolver);\n }\n\n if (schema) {\n wrapFields(schema.getQueryType(), getConfig);\n wrapFields(schema.getMutationType(), getConfig);\n }\n }\n\n // v14–16 channels carry only the raw args, so derive the operation from the document (native v17\n // provides `operationType`/`operationName` on the event directly).\n const operation = getOperation(document as DocumentNode, args.operationName);\n const operationType = operation?.operation;\n const operationName = operation?.name?.value ?? args.operationName ?? undefined;\n\n const span = startInactiveSpan({\n name: getOperationSpanName(operationType, operationName || undefined, SPAN_NAME_EXECUTE),\n attributes: {\n ...BASE_ATTRIBUTES,\n [GRAPHQL_OPERATION_TYPE]: operationType,\n [GRAPHQL_OPERATION_NAME]: operationName || undefined,\n [GRAPHQL_DOCUMENT]: redactGraphqlDocument(document as GraphqlDocumentNode | undefined),\n },\n });\n\n if (config.useOperationNameForRootSpan && operationType) {\n renameRootSpanWithOperation(span, operationType, operationName || undefined);\n }\n\n // The resolver proxies read the execute span (and their own bookkeeping) off this symbol.\n contextValue[GRAPHQL_DATA_SYMBOL] = { source: document, span, fields: {} };\n args.writeBack(contextValue, fieldResolver);\n\n return span;\n}\n\n/** `result` is the settled `ExecutionResult`; GraphQL errors surface on `result.errors`, not a throw. */\nexport function finalizeExecuteSpan(span: Span, result: unknown): void {\n if (hasResultErrors(result)) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n }\n}\n"],"names":["SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","WEB_SERVER_GRAPHQL_SPAN_OP","startInactiveSpan","SPAN_NAME_PARSE","SPAN_NAME_VALIDATE","GRAPHQL_DOCUMENT","redactGraphqlDocument","SPAN_STATUS_ERROR","GRAPHQL_DATA_SYMBOL","wrapFieldResolver","wrapFields","getOperation","getOperationSpanName","SPAN_NAME_EXECUTE","GRAPHQL_OPERATION_TYPE","GRAPHQL_OPERATION_NAME","renameRootSpanWithOperation","hasResultErrors"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const attributes = require('@sentry/conventions/attributes'); | ||
| const core = require('@sentry/core'); | ||
| const debugBuild = require('../../debug-build.js'); | ||
| const channels = require('../../orchestrion/channels.js'); | ||
| const tracingChannel = require('../../tracing-channel.js'); | ||
| const INTEGRATION_NAME = "PostgresJs"; | ||
| const ORIGIN = "auto.db.orchestrion.postgresjs"; | ||
| const DB_RESPONSE_STATUS_CODE = "db.response.status_code"; | ||
| const NOOP = () => { | ||
| }; | ||
| const QUERY_FROM_INSTRUMENTED_SQL = /* @__PURE__ */ Symbol.for("sentry.query.from.instrumented.sql"); | ||
| const QUERY_SPAN = /* @__PURE__ */ Symbol("sentryPostgresJsSpan"); | ||
| const CONNECTION_ATTRS_SET = /* @__PURE__ */ Symbol("sentryPostgresJsConnectionAttrsSet"); | ||
| const SPAN_ENDED = /* @__PURE__ */ Symbol("sentryPostgresJsSpanEnded"); | ||
| const connectionContexts = /* @__PURE__ */ new WeakMap(); | ||
| const endpointRegistry = []; | ||
| function registerEndpoint(context) { | ||
| const alreadyKnown = endpointRegistry.some( | ||
| (e) => e.ATTR_SERVER_ADDRESS === context.ATTR_SERVER_ADDRESS && e.ATTR_SERVER_PORT === context.ATTR_SERVER_PORT && e.ATTR_DB_NAMESPACE === context.ATTR_DB_NAMESPACE | ||
| ); | ||
| if (!alreadyKnown) { | ||
| endpointRegistry.push(context); | ||
| } | ||
| } | ||
| function resolveSingleEndpoint() { | ||
| return endpointRegistry.length === 1 ? endpointRegistry[0] : void 0; | ||
| } | ||
| function recordConnectionFromChannel(message) { | ||
| const connection = message.result; | ||
| const options = message.arguments?.[0]; | ||
| if (!connection || typeof connection !== "object" || !options) { | ||
| return; | ||
| } | ||
| const context = core._INTERNAL_buildPostgresConnectionContext(options); | ||
| connectionContexts.set(connection, context); | ||
| registerEndpoint(context); | ||
| } | ||
| function setConnectionAttributes(span, query, context) { | ||
| const queryRecord = query; | ||
| if (queryRecord[CONNECTION_ATTRS_SET]) { | ||
| return; | ||
| } | ||
| queryRecord[CONNECTION_ATTRS_SET] = true; | ||
| core._INTERNAL_setPostgresConnectionAttributes(span, context); | ||
| } | ||
| function attachConnectionAttributesFromChannel(message) { | ||
| const connection = message.self; | ||
| const query = message.arguments?.[0]; | ||
| if (!connection || !query) { | ||
| return; | ||
| } | ||
| const span = query[QUERY_SPAN]; | ||
| const context = connectionContexts.get(connection); | ||
| if (span && context) { | ||
| setConnectionAttributes(span, query, context); | ||
| } | ||
| } | ||
| function wrapQuerySettlement(data, span, sanitizedSqlQuery) { | ||
| const query = data.self; | ||
| if (!query) { | ||
| return; | ||
| } | ||
| const markEnded = () => { | ||
| data[SPAN_ENDED] = true; | ||
| }; | ||
| const originalResolve = query.resolve; | ||
| if (typeof originalResolve === "function") { | ||
| query.resolve = function(...resolveArgs) { | ||
| markEnded(); | ||
| try { | ||
| const command = resolveArgs[0]?.command; | ||
| core._INTERNAL_setPostgresOperationName(span, sanitizedSqlQuery, command); | ||
| span.end(); | ||
| } catch (e) { | ||
| debugBuild.DEBUG_BUILD && core.debug.error("[orchestrion:postgresjs] error ending span in resolve:", e); | ||
| } | ||
| return originalResolve.apply(this, resolveArgs); | ||
| }; | ||
| } | ||
| const originalReject = query.reject; | ||
| if (typeof originalReject === "function") { | ||
| query.reject = function(...rejectArgs) { | ||
| markEnded(); | ||
| try { | ||
| const err = rejectArgs[0]; | ||
| span.setStatus({ code: core.SPAN_STATUS_ERROR, message: err?.message || "unknown_error" }); | ||
| span.setAttribute(DB_RESPONSE_STATUS_CODE, err?.code || "unknown"); | ||
| span.setAttribute(attributes.ERROR_TYPE, err?.name || "unknown"); | ||
| core._INTERNAL_setPostgresOperationName(span, sanitizedSqlQuery); | ||
| span.end(); | ||
| } catch (e) { | ||
| debugBuild.DEBUG_BUILD && core.debug.error("[orchestrion:postgresjs] error ending span in reject:", e); | ||
| } | ||
| return originalReject.apply(this, rejectArgs); | ||
| }; | ||
| } | ||
| } | ||
| const _postgresJsChannelIntegration = ((options = {}) => { | ||
| const { requireParentSpan, requestHook } = options; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| debugBuild.DEBUG_BUILD && core.debug.log(`[orchestrion:postgresjs] subscribing to "${channels.CHANNELS.POSTGRESJS_HANDLE}"`); | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.POSTGRESJS_CONNECTION).subscribe({ | ||
| start: NOOP, | ||
| asyncStart: NOOP, | ||
| asyncEnd: NOOP, | ||
| error: NOOP, | ||
| end: recordConnectionFromChannel | ||
| }); | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.POSTGRESJS_EXECUTE).subscribe({ | ||
| end: NOOP, | ||
| asyncStart: NOOP, | ||
| asyncEnd: NOOP, | ||
| error: NOOP, | ||
| start: attachConnectionAttributesFromChannel | ||
| }); | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.POSTGRESJS_CONNECT).subscribe({ | ||
| end: NOOP, | ||
| asyncStart: NOOP, | ||
| asyncEnd: NOOP, | ||
| error: NOOP, | ||
| start: attachConnectionAttributesFromChannel | ||
| }); | ||
| core.waitForTracingChannelBinding(() => { | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(channels.CHANNELS.POSTGRESJS_HANDLE), | ||
| (data) => { | ||
| const query = data.self; | ||
| if (!query) { | ||
| return void 0; | ||
| } | ||
| if (query.executed === true || query[QUERY_FROM_INSTRUMENTED_SQL]) { | ||
| return void 0; | ||
| } | ||
| const fullQuery = core._INTERNAL_reconstructPostgresQuery(query.strings); | ||
| const sanitizedSqlQuery = core._INTERNAL_sanitizeSqlQuery(fullQuery); | ||
| const span = core.startInactiveSpan({ | ||
| name: sanitizedSqlQuery || "postgresjs.query", | ||
| op: "db", | ||
| kind: core.SPAN_KIND.CLIENT, | ||
| attributes: { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [attributes.DB_SYSTEM_NAME]: "postgres", | ||
| [attributes.DB_QUERY_TEXT]: sanitizedSqlQuery | ||
| } | ||
| }); | ||
| query[QUERY_SPAN] = span; | ||
| const context = resolveSingleEndpoint(); | ||
| if (context) { | ||
| setConnectionAttributes(span, query, context); | ||
| } | ||
| if (requestHook) { | ||
| try { | ||
| requestHook(span, sanitizedSqlQuery, context); | ||
| } catch (e) { | ||
| span.setAttribute("sentry.hook.error", "requestHook failed"); | ||
| debugBuild.DEBUG_BUILD && core.debug.error("[orchestrion:postgresjs] error in requestHook:", e); | ||
| } | ||
| } | ||
| wrapQuerySettlement(data, span, sanitizedSqlQuery); | ||
| return span; | ||
| }, | ||
| { | ||
| requiresParentSpan: requireParentSpan !== false, | ||
| deferSpanEnd({ data }) { | ||
| if (data[SPAN_ENDED]) { | ||
| return true; | ||
| } | ||
| if ("error" in data) { | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| } | ||
| ); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const postgresJsChannelIntegration = core.defineIntegration(_postgresJsChannelIntegration); | ||
| exports.postgresJsChannelIntegration = postgresJsChannelIntegration; | ||
| //# sourceMappingURL=postgres-js.js.map |
| {"version":3,"file":"postgres-js.js","sources":["../../../../src/integrations/tracing-channel/postgres-js.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { DB_QUERY_TEXT, DB_SYSTEM_NAME, ERROR_TYPE } from '@sentry/conventions/attributes';\nimport type { IntegrationFn, PostgresConnectionContext, Span } from '@sentry/core';\nimport {\n _INTERNAL_buildPostgresConnectionContext,\n _INTERNAL_reconstructPostgresQuery,\n _INTERNAL_sanitizeSqlQuery,\n _INTERNAL_setPostgresConnectionAttributes,\n _INTERNAL_setPostgresOperationName,\n debug,\n defineIntegration,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Same name as the OTel `PostgresJs` integration by design: when this is\n// enabled, the OTel integration of the same name is dropped from the default\n// set (see `experimentalUseDiagnosticsChannelInjection`).\nconst INTEGRATION_NAME = 'PostgresJs' as const;\n\nconst ORIGIN = 'auto.db.orchestrion.postgresjs';\n\n// Not part of `@sentry/conventions`, so we keep it inline (matches the OTel `PostgresJsInstrumentation`).\nconst DB_RESPONSE_STATUS_CODE = 'db.response.status_code';\n\nconst NOOP = (): void => {};\n\n// Same `Symbol.for()` marker the core `instrumentPostgresJsSql` wrapper sets on\n// queries it manually instruments, so we skip them there and never double-span.\nconst QUERY_FROM_INSTRUMENTED_SQL = Symbol.for('sentry.query.from.instrumented.sql');\n// The query span, stashed on the `Query` so the `execute`/`connect` channels can\n// attach connection attributes to it.\nconst QUERY_SPAN = Symbol('sentryPostgresJsSpan');\n// Set once connection attributes are on the span, so the fallback and the\n// `execute`/`connect` channels don't both write them.\nconst CONNECTION_ATTRS_SET = Symbol('sentryPostgresJsConnectionAttrsSet');\n// Set on the channel context once the resolve/reject wrappers have ended the\n// span, so `deferSpanEnd` knows the wrappers own the lifecycle.\nconst SPAN_ENDED = Symbol('sentryPostgresJsSpanEnded');\n\nexport interface PostgresJsChannelIntegrationOptions {\n /**\n * Only create spans when there's already an active parent span. Defaults to\n * `true`, matching the OTel `postgresJsIntegration`.\n */\n requireParentSpan?: boolean;\n /**\n * Hook to modify the query span before the query runs. Receives the span, the\n * sanitized SQL, and (when resolvable) the connection context.\n */\n requestHook?: (span: Span, sanitizedSqlQuery: string, postgresConnectionContext?: PostgresConnectionContext) => void;\n}\n\n/** The `Query` instance postgres.js passes as `self` to `Query.prototype.handle`. */\ninterface PostgresQuery {\n strings?: string[];\n executed?: boolean;\n resolve?: (...args: unknown[]) => unknown;\n reject?: (...args: unknown[]) => unknown;\n}\n\ninterface PostgresJsQueryContext {\n arguments?: unknown[];\n self?: PostgresQuery;\n result?: unknown;\n error?: unknown;\n}\n\n// A connection object -> its resolved context, populated on the `connection`\n// channel and read on the `execute`/`connect` channels (keyed by the same object).\nconst connectionContexts = new WeakMap<object, PostgresConnectionContext>();\n// Distinct endpoints seen so far (value-compared, so N connections to one DB\n// count once). When exactly one endpoint exists — the common case, and the only\n// one the tests exercise — every query resolves to it at handle-start.\nconst endpointRegistry: PostgresConnectionContext[] = [];\n\nfunction registerEndpoint(context: PostgresConnectionContext): void {\n const alreadyKnown = endpointRegistry.some(\n e =>\n e.ATTR_SERVER_ADDRESS === context.ATTR_SERVER_ADDRESS &&\n e.ATTR_SERVER_PORT === context.ATTR_SERVER_PORT &&\n e.ATTR_DB_NAMESPACE === context.ATTR_DB_NAMESPACE,\n );\n if (!alreadyKnown) {\n endpointRegistry.push(context);\n }\n}\n\n/** The single known endpoint, or `undefined` when zero or multiple are known. */\nfunction resolveSingleEndpoint(): PostgresConnectionContext | undefined {\n return endpointRegistry.length === 1 ? endpointRegistry[0] : undefined;\n}\n\n/**\n * Record a connection from the `connection` channel `end` (`result` is the\n * connection object, `arguments[0]` the parsed options), keying its resolved\n * context by the connection object and tracking its endpoint.\n */\nfunction recordConnectionFromChannel(message: PostgresJsQueryContext): void {\n const connection = message.result;\n const options = message.arguments?.[0] as { host?: string[]; port?: number[]; database?: string } | undefined;\n if (!connection || typeof connection !== 'object' || !options) {\n return;\n }\n const context = _INTERNAL_buildPostgresConnectionContext(options);\n connectionContexts.set(connection, context);\n registerEndpoint(context);\n}\n\nfunction setConnectionAttributes(span: Span, query: PostgresQuery, context: PostgresConnectionContext): void {\n const queryRecord = query as Record<symbol, unknown>;\n if (queryRecord[CONNECTION_ATTRS_SET]) {\n return;\n }\n queryRecord[CONNECTION_ATTRS_SET] = true;\n _INTERNAL_setPostgresConnectionAttributes(span, context);\n}\n\n/**\n * Backfill connection attributes onto a query's span from a channel whose `self`\n * is the connection object and `arguments[0]` the query. Shared by the `execute`\n * and `connect` channels; both carry that shape and both resolve the context via\n * the `connectionContexts` WeakMap. Idempotent (guarded inside `setConnectionAttributes`).\n */\nfunction attachConnectionAttributesFromChannel(message: PostgresJsQueryContext): void {\n const connection = message.self as object | undefined;\n const query = message.arguments?.[0] as PostgresQuery | undefined;\n if (!connection || !query) {\n return;\n }\n const span = (query as Record<symbol, unknown>)[QUERY_SPAN] as Span | undefined;\n const context = connectionContexts.get(connection);\n if (span && context) {\n setConnectionAttributes(span, query, context);\n }\n}\n\n/**\n * Wrap `query.resolve`/`query.reject` so the span ends when the query settles.\n *\n * `Query extends Promise` and `async handle()` only dispatches — its promise\n * resolves immediately, long before the query completes. postgres.js signals\n * completion by calling `this.resolve`/`this.reject`, so we own the span end\n * there. Wrapping happens at handle-start because `reject` can fire\n * synchronously during dispatch and `cursor()` reassigns both before executing.\n */\nfunction wrapQuerySettlement(data: PostgresJsQueryContext, span: Span, sanitizedSqlQuery: string): void {\n const query = data.self;\n if (!query) {\n return;\n }\n\n // Claim ownership of ending the span up front, so `deferSpanEnd` defers to the\n // wrapper even if `span.end()` below throws.\n const markEnded = (): void => {\n (data as Record<symbol, unknown>)[SPAN_ENDED] = true;\n };\n\n const originalResolve = query.resolve;\n if (typeof originalResolve === 'function') {\n query.resolve = function (this: unknown, ...resolveArgs: unknown[]): unknown {\n markEnded();\n try {\n const command = (resolveArgs[0] as { command?: string } | undefined)?.command;\n _INTERNAL_setPostgresOperationName(span, sanitizedSqlQuery, command);\n span.end();\n } catch (e) {\n DEBUG_BUILD && debug.error('[orchestrion:postgresjs] error ending span in resolve:', e);\n }\n return originalResolve.apply(this, resolveArgs);\n };\n }\n\n const originalReject = query.reject;\n if (typeof originalReject === 'function') {\n query.reject = function (this: unknown, ...rejectArgs: unknown[]): unknown {\n markEnded();\n try {\n const err = rejectArgs[0] as { message?: string; code?: string; name?: string } | undefined;\n span.setStatus({ code: SPAN_STATUS_ERROR, message: err?.message || 'unknown_error' });\n span.setAttribute(DB_RESPONSE_STATUS_CODE, err?.code || 'unknown');\n span.setAttribute(ERROR_TYPE, err?.name || 'unknown');\n _INTERNAL_setPostgresOperationName(span, sanitizedSqlQuery);\n span.end();\n } catch (e) {\n DEBUG_BUILD && debug.error('[orchestrion:postgresjs] error ending span in reject:', e);\n }\n return originalReject.apply(this, rejectArgs);\n };\n }\n}\n\nconst _postgresJsChannelIntegration = ((options: PostgresJsChannelIntegrationOptions = {}) => {\n const { requireParentSpan, requestHook } = options;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD && debug.log(`[orchestrion:postgresjs] subscribing to \"${CHANNELS.POSTGRESJS_HANDLE}\"`);\n\n // Connection + execute are pure observers (no span, no async binding), so\n // subscribe immediately — factory-time `Connection()` calls happen before\n // `waitForTracingChannelBinding` resolves and must still be recorded.\n diagnosticsChannel.tracingChannel<PostgresJsQueryContext>(CHANNELS.POSTGRESJS_CONNECTION).subscribe({\n start: NOOP,\n asyncStart: NOOP,\n asyncEnd: NOOP,\n error: NOOP,\n end: recordConnectionFromChannel,\n });\n\n // Per-connection attributes for queries reusing an already-open connection\n // (`c.execute(q)`, `self === c`). `execute` is also called bare\n // (`self === undefined`) for the first query on each connection, `fetchState`\n // and `retry`; those miss here (the `connect` channel below covers the first\n // user query, and the single-endpoint fallback covers the common case).\n diagnosticsChannel.tracingChannel<PostgresJsQueryContext>(CHANNELS.POSTGRESJS_EXECUTE).subscribe({\n end: NOOP,\n asyncStart: NOOP,\n asyncEnd: NOOP,\n error: NOOP,\n start: attachConnectionAttributesFromChannel,\n });\n\n // The connection's `connect(query)` method (`self === c`, `arguments[0]` the\n // query) fires when a fresh connection is opened for a query. That first query\n // is later dispatched via a bare `execute` (no `self`), so this is where it\n // gets its connection attributes in multi-endpoint apps.\n diagnosticsChannel.tracingChannel<PostgresJsQueryContext>(CHANNELS.POSTGRESJS_CONNECT).subscribe({\n end: NOOP,\n asyncStart: NOOP,\n asyncEnd: NOOP,\n error: NOOP,\n start: attachConnectionAttributesFromChannel,\n });\n\n // The span-creating `handle` subscription needs the async-context binding\n // that `initOpenTelemetry()` registers after integration setup.\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan<PostgresJsQueryContext>(\n diagnosticsChannel.tracingChannel<PostgresJsQueryContext>(CHANNELS.POSTGRESJS_HANDLE),\n data => {\n const query = data.self;\n if (!query) {\n return undefined;\n }\n\n // Opt out of re-entrant `handle()` calls (then/catch/finally re-invoke it, guarded by\n // `executed`) and queries already wrapped by the portable `instrumentPostgresJsSql`. The\n // parent-span requirement is applied via `requiresParentSpan` below.\n if (query.executed === true || (query as Record<symbol, unknown>)[QUERY_FROM_INSTRUMENTED_SQL]) {\n return undefined;\n }\n\n const fullQuery = _INTERNAL_reconstructPostgresQuery(query.strings);\n const sanitizedSqlQuery = _INTERNAL_sanitizeSqlQuery(fullQuery);\n\n // `kind: CLIENT` matches the mysql/pg channel subscribers.\n const span = startInactiveSpan({\n name: sanitizedSqlQuery || 'postgresjs.query',\n op: 'db',\n kind: SPAN_KIND.CLIENT,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [DB_SYSTEM_NAME]: 'postgres',\n [DB_QUERY_TEXT]: sanitizedSqlQuery,\n },\n });\n\n // Stash for the `execute`/`connect` channels to attach per-connection attributes.\n (query as Record<symbol, unknown>)[QUERY_SPAN] = span;\n\n // Single-endpoint fallback: resolve context now so `requestHook` has it\n // and the first-query-per-connection (bare `execute`) path still gets attrs.\n const context = resolveSingleEndpoint();\n if (context) {\n setConnectionAttributes(span, query, context);\n }\n\n if (requestHook) {\n try {\n requestHook(span, sanitizedSqlQuery, context);\n } catch (e) {\n span.setAttribute('sentry.hook.error', 'requestHook failed');\n DEBUG_BUILD && debug.error('[orchestrion:postgresjs] error in requestHook:', e);\n }\n }\n\n wrapQuerySettlement(data, span, sanitizedSqlQuery);\n\n return span;\n },\n {\n requiresParentSpan: requireParentSpan !== false,\n deferSpanEnd({ data }) {\n // `handle` is async: its promise settles on dispatch (asyncEnd), long\n // before the query does. The resolve/reject wrappers own the ending.\n if ((data as Record<symbol, unknown>)[SPAN_ENDED]) {\n return true; // wrappers already ended it\n }\n if ('error' in data) {\n return false; // `handle()` itself threw; the error subscriber annotated the span, let the helper end it\n }\n // NOTE: for a cursor consumed as an async iterator, only the first batch\n // reaches `handle` (the `executed` guard blocks the rest), so the span\n // ends on the first batch — a pre-existing flaw kept for parity.\n return true; // query in flight; the wrappers will end the span when it settles\n },\n },\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * EXPERIMENTAL — orchestrion-driven postgres.js (`postgres` v3.x) integration.\n *\n * Subscribes to the `orchestrion:postgres:handle` / `:connection` / `:execute` /\n * `:connect` diagnostics channels injected into postgres.js' `Query.prototype.handle`\n * and `Connection`/`execute`/`connect` (in `src/*` and `cjs/src/*`) and creates db\n * spans matching the OTel `postgresJsIntegration`. Requires the orchestrion runtime\n * hook or bundler plugin.\n */\nexport const postgresJsChannelIntegration = defineIntegration(_postgresJsChannelIntegration);\n"],"names":["_INTERNAL_buildPostgresConnectionContext","_INTERNAL_setPostgresConnectionAttributes","_INTERNAL_setPostgresOperationName","DEBUG_BUILD","debug","SPAN_STATUS_ERROR","ERROR_TYPE","CHANNELS","waitForTracingChannelBinding","bindTracingChannelToSpan","_INTERNAL_reconstructPostgresQuery","_INTERNAL_sanitizeSqlQuery","startInactiveSpan","SPAN_KIND","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","DB_SYSTEM_NAME","DB_QUERY_TEXT","defineIntegration"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const attributes = require('@sentry/conventions/attributes'); | ||
| const core = require('@sentry/core'); | ||
| const debugBuild = require('../../debug-build.js'); | ||
| const channels = require('../../orchestrion/channels.js'); | ||
| const redisStatementSerializer = require('../../redis/redis-statement-serializer.js'); | ||
| const tracingChannel = require('../../tracing-channel.js'); | ||
| const INTEGRATION_NAME = "RedisChannel"; | ||
| const ORIGIN = "auto.db.orchestrion.redis"; | ||
| const ATTR_DB_CONNECTION_STRING = "db.connection_string"; | ||
| const DB_SYSTEM_VALUE_REDIS = "redis"; | ||
| function endSpan(span, err) { | ||
| if (err) { | ||
| span.setStatus({ code: core.SPAN_STATUS_ERROR, message: err instanceof Error ? err.message : String(err) }); | ||
| } | ||
| span.end(); | ||
| } | ||
| function runResponseHook(hook, span, command, args, result) { | ||
| if (!hook) { | ||
| return; | ||
| } | ||
| try { | ||
| hook(span, command, args, result); | ||
| } catch { | ||
| } | ||
| } | ||
| function stripCommandOptions(args) { | ||
| const first = args[0]; | ||
| if (core.isObjectLike(first) && Object.getOwnPropertySymbols(first).length > 0) { | ||
| return args.slice(1); | ||
| } | ||
| return args; | ||
| } | ||
| function removeCredentialsFromConnectionString(url) { | ||
| if (typeof url !== "string" || !url) { | ||
| return void 0; | ||
| } | ||
| try { | ||
| const parsed = new URL(url); | ||
| parsed.searchParams.delete("user_pwd"); | ||
| parsed.username = ""; | ||
| parsed.password = ""; | ||
| return parsed.href; | ||
| } catch { | ||
| return void 0; | ||
| } | ||
| } | ||
| function nodeRedisAttributes(options) { | ||
| return { | ||
| [attributes.DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS, | ||
| [attributes.NET_PEER_NAME]: options?.socket?.host, | ||
| [attributes.NET_PEER_PORT]: options?.socket?.port, | ||
| [ATTR_DB_CONNECTION_STRING]: removeCredentialsFromConnectionString(options?.url), | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN | ||
| }; | ||
| } | ||
| function startCommandSpan(commandName, commandArgs, attributes$1) { | ||
| return core.startInactiveSpan({ | ||
| name: `redis-${commandName}`, | ||
| kind: core.SPAN_KIND.CLIENT, | ||
| attributes: { | ||
| ...attributes$1, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db", | ||
| [attributes.DB_STATEMENT]: redisStatementSerializer.defaultDbStatementSerializer(commandName, commandArgs) | ||
| } | ||
| }); | ||
| } | ||
| function subscribeLegacyRedisCommand(responseHook) { | ||
| const channel = diagnosticsChannel.tracingChannel(channels.CHANNELS.REDIS_COMMAND); | ||
| const noop = () => { | ||
| }; | ||
| channel.subscribe({ | ||
| end: noop, | ||
| asyncStart: noop, | ||
| asyncEnd: noop, | ||
| start(data) { | ||
| const command = data.arguments?.[0]; | ||
| if (!command || typeof command !== "object") { | ||
| return; | ||
| } | ||
| const originalCallback = command.callback; | ||
| if (typeof originalCallback !== "function") { | ||
| return; | ||
| } | ||
| const client = data.self; | ||
| const attributes$1 = { | ||
| [attributes.DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN | ||
| }; | ||
| attributes$1[attributes.NET_PEER_NAME] = client?.connection_options?.host; | ||
| attributes$1[attributes.NET_PEER_PORT] = client?.connection_options?.port; | ||
| if (client?.address) { | ||
| attributes$1[ATTR_DB_CONNECTION_STRING] = `redis://${client.address}`; | ||
| } | ||
| const span = startCommandSpan(command.command, command.args ?? [], attributes$1); | ||
| data._sentrySpan = span; | ||
| const parentSpan = core.getActiveSpan(); | ||
| command.callback = function(err, reply) { | ||
| if (!err) { | ||
| runResponseHook(responseHook, span, command.command, command.args ?? [], reply); | ||
| } | ||
| endSpan(span, err); | ||
| const args = arguments; | ||
| return core.withActiveSpan(parentSpan ?? null, () => originalCallback.apply(this, args)); | ||
| }; | ||
| }, | ||
| error(data) { | ||
| const span = data._sentrySpan; | ||
| if (span) { | ||
| endSpan(span, data.error); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| function bindNodeRedisCommandChannel(channelName, getWireArgs, responseHook) { | ||
| const channel = diagnosticsChannel.tracingChannel(channelName); | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| channel, | ||
| (data) => { | ||
| const wireArgs = getWireArgs(data); | ||
| if (!wireArgs?.length) { | ||
| return void 0; | ||
| } | ||
| const commandName = String(wireArgs[0]); | ||
| const options = data.self?.options; | ||
| return startCommandSpan(commandName, wireArgs.slice(1), nodeRedisAttributes(options)); | ||
| }, | ||
| { | ||
| captureError: false, | ||
| beforeSpanEnd(span, data) { | ||
| if ("error" in data || !responseHook) { | ||
| return; | ||
| } | ||
| const wireArgs = getWireArgs(data); | ||
| if (wireArgs?.length) { | ||
| runResponseHook(responseHook, span, String(wireArgs[0]), wireArgs.slice(1), data.result); | ||
| } | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function getSendCommandArgs(data) { | ||
| const args = data.arguments?.[0]; | ||
| return Array.isArray(args) ? args : void 0; | ||
| } | ||
| function getExecutorArgs(data) { | ||
| const command = data.arguments?.[0]; | ||
| const jsArgs = data.arguments?.[1]; | ||
| if (typeof command?.transformArguments !== "function" || !Array.isArray(jsArgs)) { | ||
| return void 0; | ||
| } | ||
| try { | ||
| return command.transformArguments(...stripCommandOptions(jsArgs)); | ||
| } catch { | ||
| return void 0; | ||
| } | ||
| } | ||
| function bindNodeRedisConnectChannel() { | ||
| const channel = diagnosticsChannel.tracingChannel(channels.CHANNELS.NODE_REDIS_CONNECT); | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| channel, | ||
| (data) => { | ||
| const options = data.self?.options; | ||
| return core.startInactiveSpan({ | ||
| name: "redis-connect", | ||
| kind: core.SPAN_KIND.CLIENT, | ||
| attributes: { ...nodeRedisAttributes(options), [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db" } | ||
| }); | ||
| }, | ||
| { captureError: false } | ||
| ); | ||
| } | ||
| function bindNodeRedisBatchChannel(channelName, getOperation) { | ||
| const channel = diagnosticsChannel.tracingChannel(channelName); | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| channel, | ||
| (data) => { | ||
| const commands = data.arguments?.[0]; | ||
| const size = Array.isArray(commands) ? commands.length : void 0; | ||
| const socket = data.self?.options?.socket; | ||
| return core.startInactiveSpan({ | ||
| name: getOperation(data), | ||
| kind: core.SPAN_KIND.CLIENT, | ||
| attributes: { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db.redis", | ||
| [attributes.DB_SYSTEM_NAME]: DB_SYSTEM_VALUE_REDIS, | ||
| ...size && size > 1 ? { [attributes.DB_OPERATION_BATCH_SIZE]: size } : {}, | ||
| ...socket?.host != null ? { [attributes.SERVER_ADDRESS]: socket.host } : {}, | ||
| ...socket?.port != null ? { [attributes.SERVER_PORT]: socket.port } : {} | ||
| } | ||
| }); | ||
| }, | ||
| { captureError: false } | ||
| ); | ||
| } | ||
| const _redisChannelIntegration = ((options = {}) => { | ||
| const responseHook = options.responseHook; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| debugBuild.DEBUG_BUILD && core.debug.log(`[orchestrion:redis] subscribing to "${channels.CHANNELS.REDIS_COMMAND}" and node-redis channels`); | ||
| subscribeLegacyRedisCommand(responseHook); | ||
| core.waitForTracingChannelBinding(() => { | ||
| bindNodeRedisCommandChannel(channels.CHANNELS.NODE_REDIS_COMMAND, getSendCommandArgs, responseHook); | ||
| bindNodeRedisCommandChannel(channels.CHANNELS.NODE_REDIS_EXECUTOR, getExecutorArgs, responseHook); | ||
| bindNodeRedisConnectChannel(); | ||
| bindNodeRedisBatchChannel(channels.CHANNELS.NODE_REDIS_MULTI, () => "MULTI"); | ||
| bindNodeRedisBatchChannel(channels.CHANNELS.NODE_REDIS_PIPELINE, () => "PIPELINE"); | ||
| bindNodeRedisBatchChannel( | ||
| channels.CHANNELS.NODE_REDIS_BATCH, | ||
| (data) => data.arguments?.[2] !== void 0 ? "MULTI" : "PIPELINE" | ||
| ); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const redisChannelIntegration = core.defineIntegration(_redisChannelIntegration); | ||
| exports.redisChannelIntegration = redisChannelIntegration; | ||
| //# sourceMappingURL=redis.js.map |
| {"version":3,"file":"redis.js","sources":["../../../../src/integrations/tracing-channel/redis.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-deprecated -- we intentionally emit the OLD db/net semconv\n to match `@opentelemetry/instrumentation-redis`. TODO(v11): switch to the non-deprecated\n `db.system.name`/`db.query.text`/`server.address`/`server.port` conventions and drop this disable. */\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport {\n DB_OPERATION_BATCH_SIZE,\n DB_STATEMENT,\n DB_SYSTEM,\n DB_SYSTEM_NAME,\n NET_PEER_NAME,\n NET_PEER_PORT,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport type { IntegrationFn, Span, SpanAttributes } from '@sentry/core';\nimport {\n isObjectLike,\n debug,\n defineIntegration,\n getActiveSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n waitForTracingChannelBinding,\n withActiveSpan,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { defaultDbStatementSerializer } from '../../redis/redis-statement-serializer';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// A distinct name from the composite OTel `Redis` integration — they can't share one, and\n// `Redis` stays in the set for its native diagnostics_channel subscriber (node-redis >=5.12 /\n// ioredis >=5.11). When this integration is active, the OTel `RedisInstrumentation` monkey-patch\n// is fully gated off in the node SDK.\nconst INTEGRATION_NAME = 'RedisChannel' as const;\n\nconst ORIGIN = 'auto.db.orchestrion.redis';\n\n// todo(v11): drop this — it is already covered by host and port.\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst DB_SYSTEM_VALUE_REDIS = 'redis';\n\n/** Mirrors `@opentelemetry/instrumentation-redis`' response hook. Not called for failed commands. */\nexport type RedisResponseHook = (span: Span, command: string, args: Array<string | Buffer>, result: unknown) => void;\n\nexport interface RedisChannelIntegrationOptions {\n responseHook?: RedisResponseHook;\n}\n\n/** Structural type for a node-redis (`@redis/client`) command definition. */\ninterface RedisCommandDefinition {\n transformArguments?: (...args: unknown[]) => Array<string | Buffer>;\n}\n\n/** Structural type for the `command_obj` `redis` v2-v3 passes to `internal_send_command`. */\ninterface LegacyRedisCommand {\n command: string;\n args: Array<string | Buffer>;\n callback?: (err: Error | null | undefined, reply: unknown) => unknown;\n}\n\ninterface LegacyRedisClient {\n connection_options?: { host?: string; port?: number };\n address?: string;\n}\n\ninterface NodeRedisClientOptions {\n socket?: { host?: string; port?: number };\n url?: string;\n}\n\ninterface NodeRedisClient {\n options?: NodeRedisClientOptions;\n}\n\ninterface CommandContext {\n arguments?: unknown[];\n self?: unknown;\n result?: unknown;\n error?: unknown;\n}\n\nfunction endSpan(span: Span, err: unknown): void {\n if (err) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: err instanceof Error ? err.message : String(err) });\n }\n span.end();\n}\n\nfunction runResponseHook(\n hook: RedisResponseHook | undefined,\n span: Span,\n command: string,\n args: Array<string | Buffer>,\n result: unknown,\n): void {\n if (!hook) {\n return;\n }\n try {\n hook(span, command, args, result);\n } catch {\n // never let a user-provided response hook break instrumentation\n }\n}\n\n// Strip a leading `commandOptions(...)` object (tagged with a `Symbol`) before\n// deriving the wire arguments, mirroring `@redis/client`'s `transformCommandArguments`.\nfunction stripCommandOptions(args: unknown[]): unknown[] {\n const first = args[0];\n if (isObjectLike(first) && Object.getOwnPropertySymbols(first).length > 0) {\n return args.slice(1);\n }\n return args;\n}\n\nfunction removeCredentialsFromConnectionString(url: string | undefined): string | undefined {\n if (typeof url !== 'string' || !url) {\n return undefined;\n }\n try {\n const parsed = new URL(url);\n parsed.searchParams.delete('user_pwd');\n parsed.username = '';\n parsed.password = '';\n return parsed.href;\n } catch {\n return undefined;\n }\n}\n\nfunction nodeRedisAttributes(options: NodeRedisClientOptions | undefined): SpanAttributes {\n return {\n [DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS,\n [NET_PEER_NAME]: options?.socket?.host,\n [NET_PEER_PORT]: options?.socket?.port,\n [ATTR_DB_CONNECTION_STRING]: removeCredentialsFromConnectionString(options?.url),\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n };\n}\n\nfunction startCommandSpan(commandName: string, commandArgs: Array<string | Buffer>, attributes: SpanAttributes): Span {\n return startInactiveSpan({\n name: `redis-${commandName}`,\n kind: SPAN_KIND.CLIENT,\n attributes: {\n ...attributes,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',\n [DB_STATEMENT]: defaultDbStatementSerializer(commandName, commandArgs),\n },\n });\n}\n\n// --- redis v2-v3: `RedisClient.prototype.internal_send_command(command_obj)` ---\n\n// Settles via `command_obj.callback`, not the sync return — so instead of\n// `bindTracingChannelToSpan` we open the span in `start`, wrap the callback to end it, and end on `error` for sync throws.\nfunction subscribeLegacyRedisCommand(responseHook: RedisResponseHook | undefined): void {\n const channel = diagnosticsChannel.tracingChannel<CommandContext>(CHANNELS.REDIS_COMMAND);\n const noop = (): void => {};\n channel.subscribe({\n end: noop,\n asyncStart: noop,\n asyncEnd: noop,\n start(data) {\n const command = data.arguments?.[0] as LegacyRedisCommand | undefined;\n if (!command || typeof command !== 'object') {\n return;\n }\n // The span is ended via the wrapped callback (or the sync-throw `error` path). A\n // command with no callback has no completion signal to end it on, so don't open one.\n const originalCallback = command.callback;\n if (typeof originalCallback !== 'function') {\n return;\n }\n const client = data.self as LegacyRedisClient | undefined;\n const attributes: SpanAttributes = {\n [DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n };\n\n attributes[NET_PEER_NAME] = client?.connection_options?.host;\n attributes[NET_PEER_PORT] = client?.connection_options?.port;\n\n if (client?.address) {\n attributes[ATTR_DB_CONNECTION_STRING] = `redis://${client.address}`;\n }\n const span = startCommandSpan(command.command, command.args ?? [], attributes);\n (data as CommandContext & { _sentrySpan?: Span })._sentrySpan = span;\n\n const parentSpan = getActiveSpan();\n command.callback = function (this: unknown, err: Error | null | undefined, reply: unknown) {\n if (!err) {\n runResponseHook(responseHook, span, command.command, command.args ?? [], reply);\n }\n endSpan(span, err);\n // eslint-disable-next-line prefer-rest-params\n const args = arguments as unknown as [Error | null | undefined, unknown];\n return withActiveSpan(parentSpan ?? null, () => originalCallback.apply(this, args));\n };\n },\n error(data) {\n // Synchronous throw: the wrapped callback never fires, so end here instead.\n const span = (data as CommandContext & { _sentrySpan?: Span })._sentrySpan;\n if (span) {\n endSpan(span, data.error);\n }\n },\n });\n}\n\n// --- node-redis v4/v5 (`@redis/client`) ---\n\nfunction bindNodeRedisCommandChannel(\n channelName: string,\n getWireArgs: (data: CommandContext) => Array<string | Buffer> | undefined,\n responseHook: RedisResponseHook | undefined,\n): void {\n const channel = diagnosticsChannel.tracingChannel<CommandContext, CommandContext>(channelName);\n bindTracingChannelToSpan(\n channel,\n data => {\n const wireArgs = getWireArgs(data);\n if (!wireArgs?.length) {\n return undefined;\n }\n const commandName = String(wireArgs[0]);\n const options = (data.self as NodeRedisClient | undefined)?.options;\n return startCommandSpan(commandName, wireArgs.slice(1), nodeRedisAttributes(options));\n },\n {\n captureError: false,\n beforeSpanEnd(span, data) {\n if ('error' in data || !responseHook) {\n return;\n }\n const wireArgs = getWireArgs(data);\n if (wireArgs?.length) {\n runResponseHook(responseHook, span, String(wireArgs[0]), wireArgs.slice(1), data.result);\n }\n },\n },\n );\n}\n\n// `sendCommand(args, options)` — `args` are already the wire arguments.\nfunction getSendCommandArgs(data: CommandContext): Array<string | Buffer> | undefined {\n const args = data.arguments?.[0];\n return Array.isArray(args) ? (args as Array<string | Buffer>) : undefined;\n}\n\n// `commandsExecutor(command, jsArgs)` — derive the wire arguments the same way\n// `@redis/client` does internally, via `command.transformArguments`.\nfunction getExecutorArgs(data: CommandContext): Array<string | Buffer> | undefined {\n const command = data.arguments?.[0] as RedisCommandDefinition | undefined;\n const jsArgs = data.arguments?.[1];\n if (typeof command?.transformArguments !== 'function' || !Array.isArray(jsArgs)) {\n return undefined;\n }\n try {\n return command.transformArguments(...stripCommandOptions(jsArgs));\n } catch {\n return undefined;\n }\n}\n\nfunction bindNodeRedisConnectChannel(): void {\n const channel = diagnosticsChannel.tracingChannel<CommandContext, CommandContext>(CHANNELS.NODE_REDIS_CONNECT);\n bindTracingChannelToSpan(\n channel,\n data => {\n const options = (data.self as NodeRedisClient | undefined)?.options;\n return startInactiveSpan({\n name: 'redis-connect',\n kind: SPAN_KIND.CLIENT,\n attributes: { ...nodeRedisAttributes(options), [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db' },\n });\n },\n { captureError: false },\n );\n}\n\n// Batch (multi/pipeline): one span per `exec`. Batched commands bypass `sendCommand`, so\n// the executor's `ctx.arguments[0]` (the queued commands) gives the batch size. Span shape\n// mirrors the native `node-redis:batch` span (see `redis-dc-subscriber.ts`).\nfunction bindNodeRedisBatchChannel(channelName: string, getOperation: (data: CommandContext) => string): void {\n const channel = diagnosticsChannel.tracingChannel<CommandContext, CommandContext>(channelName);\n bindTracingChannelToSpan(\n channel,\n data => {\n const commands = data.arguments?.[0];\n const size = Array.isArray(commands) ? commands.length : undefined;\n const socket = (data.self as NodeRedisClient | undefined)?.options?.socket;\n return startInactiveSpan({\n name: getOperation(data),\n kind: SPAN_KIND.CLIENT,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [DB_SYSTEM_NAME]: DB_SYSTEM_VALUE_REDIS,\n ...(size && size > 1 ? { [DB_OPERATION_BATCH_SIZE]: size } : {}),\n ...(socket?.host != null ? { [SERVER_ADDRESS]: socket.host } : {}),\n ...(socket?.port != null ? { [SERVER_PORT]: socket.port } : {}),\n },\n });\n },\n { captureError: false },\n );\n}\n\nconst _redisChannelIntegration = ((options: RedisChannelIntegrationOptions = {}) => {\n const responseHook = options.responseHook;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD &&\n debug.log(`[orchestrion:redis] subscribing to \"${CHANNELS.REDIS_COMMAND}\" and node-redis channels`);\n\n // redis v2-v3 uses a nested callback rather than `bindStore`, so it can be\n // subscribed synchronously here.\n subscribeLegacyRedisCommand(responseHook);\n\n waitForTracingChannelBinding(() => {\n bindNodeRedisCommandChannel(CHANNELS.NODE_REDIS_COMMAND, getSendCommandArgs, responseHook);\n bindNodeRedisCommandChannel(CHANNELS.NODE_REDIS_EXECUTOR, getExecutorArgs, responseHook);\n bindNodeRedisConnectChannel();\n bindNodeRedisBatchChannel(CHANNELS.NODE_REDIS_MULTI, () => 'MULTI');\n bindNodeRedisBatchChannel(CHANNELS.NODE_REDIS_PIPELINE, () => 'PIPELINE');\n bindNodeRedisBatchChannel(CHANNELS.NODE_REDIS_BATCH, data =>\n data.arguments?.[2] !== undefined ? 'MULTI' : 'PIPELINE',\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * EXPERIMENTAL — orchestrion-driven redis integration for `redis` v2-v3 and\n * node-redis v4/v5 `<5.12.0` (`@redis/client`). Covers single commands, `connect`,\n * and multi/pipeline batches, fully replacing `@opentelemetry/instrumentation-redis`.\n * Requires the orchestrion runtime hook or bundler plugin.\n */\nexport const redisChannelIntegration = defineIntegration(_redisChannelIntegration);\n"],"names":["SPAN_STATUS_ERROR","isObjectLike","DB_SYSTEM","NET_PEER_NAME","NET_PEER_PORT","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","attributes","startInactiveSpan","SPAN_KIND","SEMANTIC_ATTRIBUTE_SENTRY_OP","DB_STATEMENT","defaultDbStatementSerializer","CHANNELS","getActiveSpan","withActiveSpan","bindTracingChannelToSpan","DB_SYSTEM_NAME","DB_OPERATION_BATCH_SIZE","SERVER_ADDRESS","SERVER_PORT","DEBUG_BUILD","debug","waitForTracingChannelBinding","defineIntegration"],"mappings":";;;;;;;;;;AAqCA,MAAM,gBAAA,GAAmB,cAAA;AAEzB,MAAM,MAAA,GAAS,2BAAA;AAGf,MAAM,yBAAA,GAA4B,sBAAA;AAClC,MAAM,qBAAA,GAAwB,OAAA;AA0C9B,SAAS,OAAA,CAAQ,MAAY,GAAA,EAAoB;AAC/C,EAAA,IAAI,GAAA,EAAK;AACP,IAAA,IAAA,CAAK,SAAA,CAAU,EAAE,IAAA,EAAMA,sBAAA,EAAmB,OAAA,EAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAA,EAAG,CAAA;AAAA,EACvG;AACA,EAAA,IAAA,CAAK,GAAA,EAAI;AACX;AAEA,SAAS,eAAA,CACP,IAAA,EACA,IAAA,EACA,OAAA,EACA,MACA,MAAA,EACM;AACN,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA;AAAA,EACF;AACA,EAAA,IAAI;AACF,IAAA,IAAA,CAAK,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,MAAM,CAAA;AAAA,EAClC,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAIA,SAAS,oBAAoB,IAAA,EAA4B;AACvD,EAAA,MAAM,KAAA,GAAQ,KAAK,CAAC,CAAA;AACpB,EAAA,IAAIC,iBAAA,CAAa,KAAK,CAAA,IAAK,MAAA,CAAO,sBAAsB,KAAK,CAAA,CAAE,SAAS,CAAA,EAAG;AACzE,IAAA,OAAO,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,EACrB;AACA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,sCAAsC,GAAA,EAA6C;AAC1F,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,CAAC,GAAA,EAAK;AACnC,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,GAAG,CAAA;AAC1B,IAAA,MAAA,CAAO,YAAA,CAAa,OAAO,UAAU,CAAA;AACrC,IAAA,MAAA,CAAO,QAAA,GAAW,EAAA;AAClB,IAAA,MAAA,CAAO,QAAA,GAAW,EAAA;AAClB,IAAA,OAAO,MAAA,CAAO,IAAA;AAAA,EAChB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAEA,SAAS,oBAAoB,OAAA,EAA6D;AACxF,EAAA,OAAO;AAAA,IACL,CAACC,oBAAS,GAAG,qBAAA;AAAA,IACb,CAACC,wBAAa,GAAG,OAAA,EAAS,MAAA,EAAQ,IAAA;AAAA,IAClC,CAACC,wBAAa,GAAG,OAAA,EAAS,MAAA,EAAQ,IAAA;AAAA,IAClC,CAAC,yBAAyB,GAAG,qCAAA,CAAsC,SAAS,GAAG,CAAA;AAAA,IAC/E,CAACC,qCAAgC,GAAG;AAAA,GACtC;AACF;AAEA,SAAS,gBAAA,CAAiB,WAAA,EAAqB,WAAA,EAAqCC,YAAA,EAAkC;AACpH,EAAA,OAAOC,sBAAA,CAAkB;AAAA,IACvB,IAAA,EAAM,SAAS,WAAW,CAAA,CAAA;AAAA,IAC1B,MAAMC,cAAA,CAAU,MAAA;AAAA,IAChB,UAAA,EAAY;AAAA,MACV,GAAGF,YAAA;AAAA,MACH,CAACG,iCAA4B,GAAG,IAAA;AAAA,MAChC,CAACC,uBAAY,GAAGC,qDAAA,CAA6B,aAAa,WAAW;AAAA;AACvE,GACD,CAAA;AACH;AAMA,SAAS,4BAA4B,YAAA,EAAmD;AACtF,EAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,cAAA,CAA+BC,iBAAA,CAAS,aAAa,CAAA;AACxF,EAAA,MAAM,OAAO,MAAY;AAAA,EAAC,CAAA;AAC1B,EAAA,OAAA,CAAQ,SAAA,CAAU;AAAA,IAChB,GAAA,EAAK,IAAA;AAAA,IACL,UAAA,EAAY,IAAA;AAAA,IACZ,QAAA,EAAU,IAAA;AAAA,IACV,MAAM,IAAA,EAAM;AACV,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,SAAA,GAAY,CAAC,CAAA;AAClC,MAAA,IAAI,CAAC,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,EAAU;AAC3C,QAAA;AAAA,MACF;AAGA,MAAA,MAAM,mBAAmB,OAAA,CAAQ,QAAA;AACjC,MAAA,IAAI,OAAO,qBAAqB,UAAA,EAAY;AAC1C,QAAA;AAAA,MACF;AACA,MAAA,MAAM,SAAS,IAAA,CAAK,IAAA;AACpB,MAAA,MAAMN,YAAA,GAA6B;AAAA,QACjC,CAACJ,oBAAS,GAAG,qBAAA;AAAA,QACb,CAACG,qCAAgC,GAAG;AAAA,OACtC;AAEA,MAAAC,YAAA,CAAWH,wBAAa,CAAA,GAAI,MAAA,EAAQ,kBAAA,EAAoB,IAAA;AACxD,MAAAG,YAAA,CAAWF,wBAAa,CAAA,GAAI,MAAA,EAAQ,kBAAA,EAAoB,IAAA;AAExD,MAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,QAAAE,YAAA,CAAW,yBAAyB,CAAA,GAAI,CAAA,QAAA,EAAW,MAAA,CAAO,OAAO,CAAA,CAAA;AAAA,MACnE;AACA,MAAA,MAAM,IAAA,GAAO,iBAAiB,OAAA,CAAQ,OAAA,EAAS,QAAQ,IAAA,IAAQ,IAAIA,YAAU,CAAA;AAC7E,MAAC,KAAiD,WAAA,GAAc,IAAA;AAEhE,MAAA,MAAM,aAAaO,kBAAA,EAAc;AACjC,MAAA,OAAA,CAAQ,QAAA,GAAW,SAAyB,GAAA,EAA+B,KAAA,EAAgB;AACzF,QAAA,IAAI,CAAC,GAAA,EAAK;AACR,UAAA,eAAA,CAAgB,YAAA,EAAc,MAAM,OAAA,CAAQ,OAAA,EAAS,QAAQ,IAAA,IAAQ,IAAI,KAAK,CAAA;AAAA,QAChF;AACA,QAAA,OAAA,CAAQ,MAAM,GAAG,CAAA;AAEjB,QAAA,MAAM,IAAA,GAAO,SAAA;AACb,QAAA,OAAOC,mBAAA,CAAe,cAAc,IAAA,EAAM,MAAM,iBAAiB,KAAA,CAAM,IAAA,EAAM,IAAI,CAAC,CAAA;AAAA,MACpF,CAAA;AAAA,IACF,CAAA;AAAA,IACA,MAAM,IAAA,EAAM;AAEV,MAAA,MAAM,OAAQ,IAAA,CAAiD,WAAA;AAC/D,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,OAAA,CAAQ,IAAA,EAAM,KAAK,KAAK,CAAA;AAAA,MAC1B;AAAA,IACF;AAAA,GACD,CAAA;AACH;AAIA,SAAS,2BAAA,CACP,WAAA,EACA,WAAA,EACA,YAAA,EACM;AACN,EAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,cAAA,CAA+C,WAAW,CAAA;AAC7F,EAAAC,uCAAA;AAAA,IACE,OAAA;AAAA,IACA,CAAA,IAAA,KAAQ;AACN,MAAA,MAAM,QAAA,GAAW,YAAY,IAAI,CAAA;AACjC,MAAA,IAAI,CAAC,UAAU,MAAA,EAAQ;AACrB,QAAA,OAAO,MAAA;AAAA,MACT;AACA,MAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAA,CAAS,CAAC,CAAC,CAAA;AACtC,MAAA,MAAM,OAAA,GAAW,KAAK,IAAA,EAAsC,OAAA;AAC5D,MAAA,OAAO,gBAAA,CAAiB,aAAa,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,EAAG,mBAAA,CAAoB,OAAO,CAAC,CAAA;AAAA,IACtF,CAAA;AAAA,IACA;AAAA,MACE,YAAA,EAAc,KAAA;AAAA,MACd,aAAA,CAAc,MAAM,IAAA,EAAM;AACxB,QAAA,IAAI,OAAA,IAAW,IAAA,IAAQ,CAAC,YAAA,EAAc;AACpC,UAAA;AAAA,QACF;AACA,QAAA,MAAM,QAAA,GAAW,YAAY,IAAI,CAAA;AACjC,QAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,UAAA,eAAA,CAAgB,YAAA,EAAc,IAAA,EAAM,MAAA,CAAO,QAAA,CAAS,CAAC,CAAC,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA;AAAA,QACzF;AAAA,MACF;AAAA;AACF,GACF;AACF;AAGA,SAAS,mBAAmB,IAAA,EAA0D;AACpF,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,GAAY,CAAC,CAAA;AAC/B,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,GAAK,IAAA,GAAkC,MAAA;AAClE;AAIA,SAAS,gBAAgB,IAAA,EAA0D;AACjF,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,SAAA,GAAY,CAAC,CAAA;AAClC,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,SAAA,GAAY,CAAC,CAAA;AACjC,EAAA,IAAI,OAAO,SAAS,kBAAA,KAAuB,UAAA,IAAc,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC/E,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,IAAI;AACF,IAAA,OAAO,OAAA,CAAQ,kBAAA,CAAmB,GAAG,mBAAA,CAAoB,MAAM,CAAC,CAAA;AAAA,EAClE,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAEA,SAAS,2BAAA,GAAoC;AAC3C,EAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,cAAA,CAA+CH,iBAAA,CAAS,kBAAkB,CAAA;AAC7G,EAAAG,uCAAA;AAAA,IACE,OAAA;AAAA,IACA,CAAA,IAAA,KAAQ;AACN,MAAA,MAAM,OAAA,GAAW,KAAK,IAAA,EAAsC,OAAA;AAC5D,MAAA,OAAOR,sBAAA,CAAkB;AAAA,QACvB,IAAA,EAAM,eAAA;AAAA,QACN,MAAMC,cAAA,CAAU,MAAA;AAAA,QAChB,UAAA,EAAY,EAAE,GAAG,mBAAA,CAAoB,OAAO,CAAA,EAAG,CAACC,iCAA4B,GAAG,IAAA;AAAK,OACrF,CAAA;AAAA,IACH,CAAA;AAAA,IACA,EAAE,cAAc,KAAA;AAAM,GACxB;AACF;AAKA,SAAS,yBAAA,CAA0B,aAAqB,YAAA,EAAsD;AAC5G,EAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,cAAA,CAA+C,WAAW,CAAA;AAC7F,EAAAM,uCAAA;AAAA,IACE,OAAA;AAAA,IACA,CAAA,IAAA,KAAQ;AACN,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,GAAY,CAAC,CAAA;AACnC,MAAA,MAAM,OAAO,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,GAAI,SAAS,MAAA,GAAS,MAAA;AACzD,MAAA,MAAM,MAAA,GAAU,IAAA,CAAK,IAAA,EAAsC,OAAA,EAAS,MAAA;AACpE,MAAA,OAAOR,sBAAA,CAAkB;AAAA,QACvB,IAAA,EAAM,aAAa,IAAI,CAAA;AAAA,QACvB,MAAMC,cAAA,CAAU,MAAA;AAAA,QAChB,UAAA,EAAY;AAAA,UACV,CAACH,qCAAgC,GAAG,MAAA;AAAA,UACpC,CAACI,iCAA4B,GAAG,UAAA;AAAA,UAChC,CAACO,yBAAc,GAAG,qBAAA;AAAA,UAClB,GAAI,IAAA,IAAQ,IAAA,GAAO,CAAA,GAAI,EAAE,CAACC,kCAAuB,GAAG,IAAA,EAAK,GAAI,EAAC;AAAA,UAC9D,GAAI,MAAA,EAAQ,IAAA,IAAQ,IAAA,GAAO,EAAE,CAACC,yBAAc,GAAG,MAAA,CAAO,IAAA,EAAK,GAAI,EAAC;AAAA,UAChE,GAAI,MAAA,EAAQ,IAAA,IAAQ,IAAA,GAAO,EAAE,CAACC,sBAAW,GAAG,MAAA,CAAO,IAAA,EAAK,GAAI;AAAC;AAC/D,OACD,CAAA;AAAA,IACH,CAAA;AAAA,IACA,EAAE,cAAc,KAAA;AAAM,GACxB;AACF;AAEA,MAAM,wBAAA,IAA4B,CAAC,OAAA,GAA0C,EAAC,KAAM;AAClF,EAAA,MAAM,eAAe,OAAA,CAAQ,YAAA;AAE7B,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,IAAI,CAAC,mBAAmB,cAAA,EAAgB;AACtC,QAAA;AAAA,MACF;AAEA,MAAAC,sBAAA,IACEC,UAAA,CAAM,GAAA,CAAI,CAAA,oCAAA,EAAuCT,iBAAA,CAAS,aAAa,CAAA,yBAAA,CAA2B,CAAA;AAIpG,MAAA,2BAAA,CAA4B,YAAY,CAAA;AAExC,MAAAU,iCAAA,CAA6B,MAAM;AACjC,QAAA,2BAAA,CAA4BV,iBAAA,CAAS,kBAAA,EAAoB,kBAAA,EAAoB,YAAY,CAAA;AACzF,QAAA,2BAAA,CAA4BA,iBAAA,CAAS,mBAAA,EAAqB,eAAA,EAAiB,YAAY,CAAA;AACvF,QAAA,2BAAA,EAA4B;AAC5B,QAAA,yBAAA,CAA0BA,iBAAA,CAAS,gBAAA,EAAkB,MAAM,OAAO,CAAA;AAClE,QAAA,yBAAA,CAA0BA,iBAAA,CAAS,mBAAA,EAAqB,MAAM,UAAU,CAAA;AACxE,QAAA,yBAAA;AAAA,UAA0BA,iBAAA,CAAS,gBAAA;AAAA,UAAkB,UACnD,IAAA,CAAK,SAAA,GAAY,CAAC,CAAA,KAAM,SAAY,OAAA,GAAU;AAAA,SAChD;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAQO,MAAM,uBAAA,GAA0BW,uBAAkB,wBAAwB;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const mysql2DcSubscriber = require('./mysql2-dc-subscriber.js'); | ||
| const _mysql2Integration = (() => { | ||
| return { | ||
| name: "Mysql2", | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| core.waitForTracingChannelBinding(() => { | ||
| mysql2DcSubscriber.subscribeMysql2DiagnosticChannels(diagnosticsChannel.tracingChannel); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const mysql2Integration = core.defineIntegration(_mysql2Integration); | ||
| exports.mysql2Integration = mysql2Integration; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../src/mysql2/index.ts"],"sourcesContent":["import { defineIntegration, type IntegrationFn, waitForTracingChannelBinding } from '@sentry/core';\nimport * as dc from 'node:diagnostics_channel';\nimport { subscribeMysql2DiagnosticChannels } from './mysql2-dc-subscriber';\n\nconst _mysql2Integration = (() => {\n return {\n name: 'Mysql2',\n setupOnce() {\n // Bail on Node <= 18.18.0, where `tracingChannel` does not exist.\n if (!dc.tracingChannel) {\n return;\n }\n\n // Subscribe to mysql2's native tracing channels (mysql2 >= 3.20.0).\n // This is a no-op on versions that don't publish to the channels, so it is always safe to call.\n waitForTracingChannelBinding(() => {\n subscribeMysql2DiagnosticChannels(dc.tracingChannel);\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the [mysql2](https://www.npmjs.com/package/mysql2) library via its native\n * `node:diagnostics_channel` tracing channels (mysql2 >= 3.20.0).\n *\n * On older mysql2 versions the channels are never published to, so this integration is inert and\n * the vendored OTel instrumentation (gated to `< 3.20.0`) handles instrumentation instead.\n */\nexport const mysql2Integration = defineIntegration(_mysql2Integration);\n"],"names":["dc","waitForTracingChannelBinding","subscribeMysql2DiagnosticChannels","defineIntegration"],"mappings":";;;;;;AAIA,MAAM,sBAAsB,MAAM;AAChC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAACA,mBAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAIA,MAAAC,iCAAA,CAA6B,MAAM;AACjC,QAAAC,oDAAA,CAAkCF,mBAAG,cAAc,CAAA;AAAA,MACrD,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AASO,MAAM,iBAAA,GAAoBG,uBAAkB,kBAAkB;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const attributes = require('@sentry/conventions/attributes'); | ||
| const core = require('@sentry/core'); | ||
| const tracingChannel = require('../tracing-channel.js'); | ||
| const MYSQL2_DC_CHANNEL_QUERY = "mysql2:query"; | ||
| const MYSQL2_DC_CHANNEL_EXECUTE = "mysql2:execute"; | ||
| const MYSQL2_DC_CHANNEL_CONNECT = "mysql2:connect"; | ||
| const MYSQL2_DC_CHANNEL_POOL_CONNECT = "mysql2:pool:connect"; | ||
| const ORIGIN = "auto.db.mysql2.diagnostic_channel"; | ||
| const DB_SYSTEM_NAME_VALUE_MYSQL = "mysql"; | ||
| const SQL_OPERATION_RE = /^\s*(\w+)/; | ||
| function subscribeMysql2DiagnosticChannels(tracingChannel) { | ||
| setupQueryChannel(tracingChannel, MYSQL2_DC_CHANNEL_QUERY); | ||
| setupQueryChannel(tracingChannel, MYSQL2_DC_CHANNEL_EXECUTE); | ||
| setupConnectChannel(tracingChannel, MYSQL2_DC_CHANNEL_CONNECT, "mysql2.connect"); | ||
| setupConnectChannel(tracingChannel, MYSQL2_DC_CHANNEL_POOL_CONNECT, "mysql2.pool.connect"); | ||
| } | ||
| function setupQueryChannel(tracingChannel$1, channelName) { | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel$1(channelName), | ||
| (data) => { | ||
| const queryText = data.query ? core._INTERNAL_sanitizeSqlQuery(data.query) : void 0; | ||
| const operation = queryText?.match(SQL_OPERATION_RE)?.[1]?.toUpperCase(); | ||
| return core.startInactiveSpan({ | ||
| name: queryText || "mysql2.query", | ||
| attributes: { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db", | ||
| [attributes.DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MYSQL, | ||
| [attributes.DB_QUERY_TEXT]: queryText, | ||
| [attributes.DB_OPERATION_NAME]: operation, | ||
| [attributes.DB_NAMESPACE]: data.database || void 0, | ||
| [attributes.SERVER_ADDRESS]: data.serverAddress, | ||
| [attributes.SERVER_PORT]: data.serverPort | ||
| } | ||
| }); | ||
| }, | ||
| { requiresParentSpan: true } | ||
| ); | ||
| } | ||
| function setupConnectChannel(tracingChannel$1, channelName, spanName) { | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel$1(channelName), | ||
| (data) => { | ||
| return core.startInactiveSpan({ | ||
| name: spanName, | ||
| attributes: { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db", | ||
| [attributes.DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MYSQL, | ||
| [attributes.DB_NAMESPACE]: data.database || void 0, | ||
| [attributes.SERVER_ADDRESS]: data.serverAddress, | ||
| [attributes.SERVER_PORT]: data.serverPort | ||
| } | ||
| }); | ||
| }, | ||
| { requiresParentSpan: true } | ||
| ); | ||
| } | ||
| exports.MYSQL2_DC_CHANNEL_CONNECT = MYSQL2_DC_CHANNEL_CONNECT; | ||
| exports.MYSQL2_DC_CHANNEL_EXECUTE = MYSQL2_DC_CHANNEL_EXECUTE; | ||
| exports.MYSQL2_DC_CHANNEL_POOL_CONNECT = MYSQL2_DC_CHANNEL_POOL_CONNECT; | ||
| exports.MYSQL2_DC_CHANNEL_QUERY = MYSQL2_DC_CHANNEL_QUERY; | ||
| exports.subscribeMysql2DiagnosticChannels = subscribeMysql2DiagnosticChannels; | ||
| //# sourceMappingURL=mysql2-dc-subscriber.js.map |
| {"version":3,"file":"mysql2-dc-subscriber.js","sources":["../../../src/mysql2/mysql2-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport {\n DB_NAMESPACE,\n DB_OPERATION_NAME,\n DB_QUERY_TEXT,\n DB_SYSTEM_NAME,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport {\n _INTERNAL_sanitizeSqlQuery,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startInactiveSpan,\n} from '@sentry/core';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n// Channel names published by mysql2 >= 3.20.0 (see mysql2 `lib/tracing.js`).\n// Hardcoded so the subscriber does not have to import mysql2 — the channels\n// just have to be subscribed to before the user's mysql2 code publishes.\nexport const MYSQL2_DC_CHANNEL_QUERY = 'mysql2:query';\nexport const MYSQL2_DC_CHANNEL_EXECUTE = 'mysql2:execute';\nexport const MYSQL2_DC_CHANNEL_CONNECT = 'mysql2:connect';\nexport const MYSQL2_DC_CHANNEL_POOL_CONNECT = 'mysql2:pool:connect';\n\nconst ORIGIN = 'auto.db.mysql2.diagnostic_channel';\nconst DB_SYSTEM_NAME_VALUE_MYSQL = 'mysql';\n\n// Leading keyword of a SQL statement (SELECT, INSERT, …) → `db.operation.name`.\nconst SQL_OPERATION_RE = /^\\s*(\\w+)/;\n\n/**\n * Shape of the context object mysql2 >= 3.20.0 publishes on its query/execute\n * tracing channels (see mysql2 `lib/base/connection.js`).\n *\n * Node's `traceCallback`/`tracePromise` mutate this same object with\n * `result`/`error` once the operation settles, which `bindTracingChannelToSpan`\n * reads in its lifecycle handlers — hence both are declared optional here.\n *\n * `query` is the SQL statement. On the `query` channel mysql2 has already\n * inlined `values` into it (`Connection.format`), so it carries raw user data;\n * on the `execute` channel it keeps `?` placeholders. Either way we sanitize it\n * before emitting `db.query.text` and never attach `values`.\n */\nexport interface MySQL2QueryData {\n query?: string;\n values?: unknown;\n database?: string;\n serverAddress?: string;\n /** Absent for unix-socket connections, where `serverAddress` is the socket path. */\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Shape of the context object mysql2 >= 3.20.0 publishes on its\n * `connect`/`pool:connect` channels.\n */\nexport interface MySQL2ConnectData {\n database?: string;\n serverAddress?: string;\n serverPort?: number;\n user?: string;\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Node passes `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type MySQL2TracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/**\n * Subscribe Sentry span handlers to mysql2's diagnostics-channel events\n * (`mysql2:query`, `:execute`, `:connect`, `:pool:connect`), published by\n * mysql2 >= 3.20.0.\n *\n * On older mysql2 versions the channels are never published to, so the\n * subscribers are inert — there is no double-instrumentation against the\n * vendored OTel patcher, which is gated to `< 3.20.0`.\n */\nexport function subscribeMysql2DiagnosticChannels(tracingChannel: MySQL2TracingChannelFactory): void {\n setupQueryChannel(tracingChannel, MYSQL2_DC_CHANNEL_QUERY);\n setupQueryChannel(tracingChannel, MYSQL2_DC_CHANNEL_EXECUTE);\n setupConnectChannel(tracingChannel, MYSQL2_DC_CHANNEL_CONNECT, 'mysql2.connect');\n setupConnectChannel(tracingChannel, MYSQL2_DC_CHANNEL_POOL_CONNECT, 'mysql2.pool.connect');\n}\n\nfunction setupQueryChannel(tracingChannel: MySQL2TracingChannelFactory, channelName: string): void {\n bindTracingChannelToSpan(\n tracingChannel<MySQL2QueryData>(channelName),\n data => {\n // mysql2 does not sanitize its channel payload, so the statement may carry\n // raw user values (on the `query` channel they are inlined). Strip every\n // literal before it leaves the process; `values` is never attached.\n const queryText = data.query ? _INTERNAL_sanitizeSqlQuery(data.query) : undefined;\n const operation = queryText?.match(SQL_OPERATION_RE)?.[1]?.toUpperCase();\n\n return startInactiveSpan({\n name: queryText || 'mysql2.query',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MYSQL,\n [DB_QUERY_TEXT]: queryText,\n [DB_OPERATION_NAME]: operation,\n [DB_NAMESPACE]: data.database || undefined,\n [SERVER_ADDRESS]: data.serverAddress,\n [SERVER_PORT]: data.serverPort,\n },\n });\n },\n { requiresParentSpan: true },\n );\n}\n\nfunction setupConnectChannel(tracingChannel: MySQL2TracingChannelFactory, channelName: string, spanName: string): void {\n bindTracingChannelToSpan(\n tracingChannel<MySQL2ConnectData>(channelName),\n data => {\n return startInactiveSpan({\n name: spanName,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MYSQL,\n [DB_NAMESPACE]: data.database || undefined,\n [SERVER_ADDRESS]: data.serverAddress,\n [SERVER_PORT]: data.serverPort,\n },\n });\n },\n { requiresParentSpan: true },\n );\n}\n"],"names":["tracingChannel","bindTracingChannelToSpan","_INTERNAL_sanitizeSqlQuery","startInactiveSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","DB_SYSTEM_NAME","DB_QUERY_TEXT","DB_OPERATION_NAME","DB_NAMESPACE","SERVER_ADDRESS","SERVER_PORT"],"mappings":";;;;;;AAoBO,MAAM,uBAAA,GAA0B;AAChC,MAAM,yBAAA,GAA4B;AAClC,MAAM,yBAAA,GAA4B;AAClC,MAAM,8BAAA,GAAiC;AAE9C,MAAM,MAAA,GAAS,mCAAA;AACf,MAAM,0BAAA,GAA6B,OAAA;AAGnC,MAAM,gBAAA,GAAmB,WAAA;AAyDlB,SAAS,kCAAkC,cAAA,EAAmD;AACnG,EAAA,iBAAA,CAAkB,gBAAgB,uBAAuB,CAAA;AACzD,EAAA,iBAAA,CAAkB,gBAAgB,yBAAyB,CAAA;AAC3D,EAAA,mBAAA,CAAoB,cAAA,EAAgB,2BAA2B,gBAAgB,CAAA;AAC/E,EAAA,mBAAA,CAAoB,cAAA,EAAgB,gCAAgC,qBAAqB,CAAA;AAC3F;AAEA,SAAS,iBAAA,CAAkBA,kBAA6C,WAAA,EAA2B;AACjG,EAAAC,uCAAA;AAAA,IACED,iBAAgC,WAAW,CAAA;AAAA,IAC3C,CAAA,IAAA,KAAQ;AAIN,MAAA,MAAM,YAAY,IAAA,CAAK,KAAA,GAAQE,+BAAA,CAA2B,IAAA,CAAK,KAAK,CAAA,GAAI,MAAA;AACxE,MAAA,MAAM,YAAY,SAAA,EAAW,KAAA,CAAM,gBAAgB,CAAA,GAAI,CAAC,GAAG,WAAA,EAAY;AAEvE,MAAA,OAAOC,sBAAA,CAAkB;AAAA,QACvB,MAAM,SAAA,IAAa,cAAA;AAAA,QACnB,UAAA,EAAY;AAAA,UACV,CAACC,qCAAgC,GAAG,MAAA;AAAA,UACpC,CAACC,iCAA4B,GAAG,IAAA;AAAA,UAChC,CAACC,yBAAc,GAAG,0BAAA;AAAA,UAClB,CAACC,wBAAa,GAAG,SAAA;AAAA,UACjB,CAACC,4BAAiB,GAAG,SAAA;AAAA,UACrB,CAACC,uBAAY,GAAG,IAAA,CAAK,QAAA,IAAY,MAAA;AAAA,UACjC,CAACC,yBAAc,GAAG,IAAA,CAAK,aAAA;AAAA,UACvB,CAACC,sBAAW,GAAG,IAAA,CAAK;AAAA;AACtB,OACD,CAAA;AAAA,IACH,CAAA;AAAA,IACA,EAAE,oBAAoB,IAAA;AAAK,GAC7B;AACF;AAEA,SAAS,mBAAA,CAAoBX,gBAAA,EAA6C,WAAA,EAAqB,QAAA,EAAwB;AACrH,EAAAC,uCAAA;AAAA,IACED,iBAAkC,WAAW,CAAA;AAAA,IAC7C,CAAA,IAAA,KAAQ;AACN,MAAA,OAAOG,sBAAA,CAAkB;AAAA,QACvB,IAAA,EAAM,QAAA;AAAA,QACN,UAAA,EAAY;AAAA,UACV,CAACC,qCAAgC,GAAG,MAAA;AAAA,UACpC,CAACC,iCAA4B,GAAG,IAAA;AAAA,UAChC,CAACC,yBAAc,GAAG,0BAAA;AAAA,UAClB,CAACG,uBAAY,GAAG,IAAA,CAAK,QAAA,IAAY,MAAA;AAAA,UACjC,CAACC,yBAAc,GAAG,IAAA,CAAK,aAAA;AAAA,UACvB,CAACC,sBAAW,GAAG,IAAA,CAAK;AAAA;AACtB,OACD,CAAA;AAAA,IACH,CAAA;AAAA,IACA,EAAE,oBAAoB,IAAA;AAAK,GAC7B;AACF;;;;;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const module$1 = { name: "amqplib", versionRange: ">=0.5.5 <2" }; | ||
| const amqplibConfig = [ | ||
| // Producer span + trace-header injection. `sendToQueue` delegates to `publish`, so it's covered. | ||
| { | ||
| channelName: "publish", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "publish", kind: "Sync" } | ||
| }, | ||
| // Confirm-channel producer span; the trailing broker-confirm callback ends the span when the | ||
| // broker acks/nacks. It internally calls `super.publish`, so the subscriber guards against the | ||
| // base `publish` channel double-instrumenting. | ||
| { | ||
| channelName: "confirmPublish", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "ConfirmChannel", methodName: "publish", kind: "Callback" } | ||
| }, | ||
| // Records `consumerTag -> { noAck, queue }` so the per-message dispatch hook knows how to name and | ||
| // when to end the consumer span. | ||
| { | ||
| channelName: "consume", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "consume", kind: "Async" } | ||
| }, | ||
| // Per delivered message: creates the consumer span and runs the user callback under it. | ||
| { | ||
| channelName: "dispatch", | ||
| module: { ...module$1, filePath: "lib/channel.js" }, | ||
| functionQuery: { className: "BaseChannel", methodName: "dispatchMessage", kind: "Sync" } | ||
| }, | ||
| // End the consumer span when the user settles the message. | ||
| { | ||
| channelName: "ack", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "ack", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "nack", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "nack", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "reject", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "reject", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "ackAll", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "ackAll", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "nackAll", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "nackAll", kind: "Sync" } | ||
| }, | ||
| // Stashes connection attributes (url/host/port/protocol/server product) on the connection object | ||
| // for span-time reads via `channel.connection`. | ||
| { | ||
| channelName: "connect", | ||
| module: { ...module$1, filePath: "lib/connect.js" }, | ||
| functionQuery: { functionName: "connect", kind: "Callback" } | ||
| } | ||
| ]; | ||
| const amqplibChannels = { | ||
| AMQPLIB_PUBLISH: "orchestrion:amqplib:publish", | ||
| AMQPLIB_CONFIRM_PUBLISH: "orchestrion:amqplib:confirmPublish", | ||
| AMQPLIB_CONSUME: "orchestrion:amqplib:consume", | ||
| AMQPLIB_DISPATCH: "orchestrion:amqplib:dispatch", | ||
| AMQPLIB_ACK: "orchestrion:amqplib:ack", | ||
| AMQPLIB_NACK: "orchestrion:amqplib:nack", | ||
| AMQPLIB_REJECT: "orchestrion:amqplib:reject", | ||
| AMQPLIB_ACK_ALL: "orchestrion:amqplib:ackAll", | ||
| AMQPLIB_NACK_ALL: "orchestrion:amqplib:nackAll", | ||
| AMQPLIB_CONNECT: "orchestrion:amqplib:connect" | ||
| }; | ||
| exports.amqplibChannels = amqplibChannels; | ||
| exports.amqplibConfig = amqplibConfig; | ||
| //# sourceMappingURL=amqplib.js.map |
| {"version":3,"file":"amqplib.js","sources":["../../../../src/orchestrion/config/amqplib.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\n// `amqplib` splits its API across three files:\n// - `lib/channel_model.js` holds `class Channel` (publish/consume/ack/nack/reject/…) and\n// `class ConfirmChannel extends Channel` (a `publish` that takes a broker-confirm callback).\n// - `lib/channel.js` holds `class BaseChannel` whose `dispatchMessage` invokes the registered\n// consumer callback once per delivered message — the natural per-message hook for consumer spans\n// (`Channel.consume` itself only registers the callback, it isn't called per message).\n// - `lib/connect.js` holds the `connect` function whose callback receives the open connection, used\n// to capture connection attributes.\n//\n// The version range mirrors `supportedVersions` in the vendored OTel instrumentation.\nconst module = { name: 'amqplib', versionRange: '>=0.5.5 <2' } as const;\n\nexport const amqplibConfig = [\n // Producer span + trace-header injection. `sendToQueue` delegates to `publish`, so it's covered.\n {\n channelName: 'publish',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'publish', kind: 'Sync' },\n },\n // Confirm-channel producer span; the trailing broker-confirm callback ends the span when the\n // broker acks/nacks. It internally calls `super.publish`, so the subscriber guards against the\n // base `publish` channel double-instrumenting.\n {\n channelName: 'confirmPublish',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'ConfirmChannel', methodName: 'publish', kind: 'Callback' },\n },\n // Records `consumerTag -> { noAck, queue }` so the per-message dispatch hook knows how to name and\n // when to end the consumer span.\n {\n channelName: 'consume',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'consume', kind: 'Async' },\n },\n // Per delivered message: creates the consumer span and runs the user callback under it.\n {\n channelName: 'dispatch',\n module: { ...module, filePath: 'lib/channel.js' },\n functionQuery: { className: 'BaseChannel', methodName: 'dispatchMessage', kind: 'Sync' },\n },\n // End the consumer span when the user settles the message.\n {\n channelName: 'ack',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'ack', kind: 'Sync' },\n },\n {\n channelName: 'nack',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'nack', kind: 'Sync' },\n },\n {\n channelName: 'reject',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'reject', kind: 'Sync' },\n },\n {\n channelName: 'ackAll',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'ackAll', kind: 'Sync' },\n },\n {\n channelName: 'nackAll',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'nackAll', kind: 'Sync' },\n },\n // Stashes connection attributes (url/host/port/protocol/server product) on the connection object\n // for span-time reads via `channel.connection`.\n {\n channelName: 'connect',\n module: { ...module, filePath: 'lib/connect.js' },\n functionQuery: { functionName: 'connect', kind: 'Callback' },\n },\n] satisfies InstrumentationConfig[];\n\nexport const amqplibChannels = {\n AMQPLIB_PUBLISH: 'orchestrion:amqplib:publish',\n AMQPLIB_CONFIRM_PUBLISH: 'orchestrion:amqplib:confirmPublish',\n AMQPLIB_CONSUME: 'orchestrion:amqplib:consume',\n AMQPLIB_DISPATCH: 'orchestrion:amqplib:dispatch',\n AMQPLIB_ACK: 'orchestrion:amqplib:ack',\n AMQPLIB_NACK: 'orchestrion:amqplib:nack',\n AMQPLIB_REJECT: 'orchestrion:amqplib:reject',\n AMQPLIB_ACK_ALL: 'orchestrion:amqplib:ackAll',\n AMQPLIB_NACK_ALL: 'orchestrion:amqplib:nackAll',\n AMQPLIB_CONNECT: 'orchestrion:amqplib:connect',\n} as const;\n"],"names":["module"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const expressConfig = [ | ||
| // Express funnels every middleware/route handler through a single method on | ||
| // its routing `Layer`, so instrumenting that one method covers the whole | ||
| // request pipeline. The `expressChannelIntegration` opens one span per layer | ||
| // invocation. Both are `Layer.prototype.<method> = function <fn>(req, res, next)` | ||
| // prototype assignments (not `class` methods), so `expressionName` (matching | ||
| // the assignment's `left.property.name`) is used. `Callback`: the handler's | ||
| // last argument is `next`, so the transform ends the traced operation when | ||
| // `next` is invoked (and publishes `error` when it's called with an error). | ||
| // | ||
| // Express v4 ships its own router in `express/lib/router/layer.js`. | ||
| { | ||
| channelName: "handle", | ||
| module: { name: "express", versionRange: ">=4.0.0 <5", filePath: "lib/router/layer.js" }, | ||
| // v4's method is `Layer.prototype.handle_request = function handle(...)` — | ||
| // match the assigned property name, not the function name. | ||
| functionQuery: { expressionName: "handle_request", kind: "Callback" } | ||
| }, | ||
| // Express v5 delegates routing to the standalone `router` package. | ||
| { | ||
| channelName: "handle", | ||
| module: { name: "router", versionRange: ">=2.0.0 <3", filePath: "lib/layer.js" }, | ||
| functionQuery: { expressionName: "handleRequest", kind: "Callback" } | ||
| }, | ||
| // Layer *registration* methods. `Router.prototype.route`/`.use` are called | ||
| // once per registered route/middleware (including internally by `app.get`/ | ||
| // `app.use`), so subscribing here lets us record each layer's registered path | ||
| // *pattern* — which the handler path (`req.baseUrl`) can't recover for | ||
| // parameterized mounts. `Sync`: these return synchronously and, unlike a | ||
| // handler, `use`'s trailing function argument is a registration payload, not a | ||
| // callback — so `Callback` would misclassify it and never fire `end`. | ||
| // | ||
| // Express v4 ships its own router in `express/lib/router/index.js`. | ||
| { | ||
| channelName: "route", | ||
| module: { name: "express", versionRange: ">=4.0.0 <5", filePath: "lib/router/index.js" }, | ||
| functionQuery: { expressionName: "route", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "use", | ||
| module: { name: "express", versionRange: ">=4.0.0 <5", filePath: "lib/router/index.js" }, | ||
| functionQuery: { expressionName: "use", kind: "Sync" } | ||
| }, | ||
| // Express v5 delegates routing to the standalone `router` package. | ||
| { | ||
| channelName: "route", | ||
| module: { name: "router", versionRange: ">=2.0.0 <3", filePath: "index.js" }, | ||
| functionQuery: { expressionName: "route", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "use", | ||
| module: { name: "router", versionRange: ">=2.0.0 <3", filePath: "index.js" }, | ||
| functionQuery: { expressionName: "use", kind: "Sync" } | ||
| } | ||
| ]; | ||
| const expressChannels = { | ||
| // Express v4 runs each layer's handler through `Layer.prototype.handle_request` | ||
| // in the `express` module. | ||
| EXPRESS_HANDLE: "orchestrion:express:handle", | ||
| // Express v5 delegates routing to the standalone `router` package, where the | ||
| // equivalent method is `Layer.prototype.handleRequest`. | ||
| ROUTER_HANDLE: "orchestrion:router:handle", | ||
| // Layer *registration* (`Router.prototype.route`/`.use`), used to capture each | ||
| // layer's registered path pattern so the matched route can be reconstructed | ||
| // with its parameters intact (`req.baseUrl` only exposes the resolved prefix). | ||
| EXPRESS_ROUTE: "orchestrion:express:route", | ||
| EXPRESS_USE: "orchestrion:express:use", | ||
| ROUTER_ROUTE: "orchestrion:router:route", | ||
| ROUTER_USE: "orchestrion:router:use" | ||
| }; | ||
| exports.expressChannels = expressChannels; | ||
| exports.expressConfig = expressConfig; | ||
| //# sourceMappingURL=express.js.map |
| {"version":3,"file":"express.js","sources":["../../../../src/orchestrion/config/express.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\nexport const expressConfig = [\n // Express funnels every middleware/route handler through a single method on\n // its routing `Layer`, so instrumenting that one method covers the whole\n // request pipeline. The `expressChannelIntegration` opens one span per layer\n // invocation. Both are `Layer.prototype.<method> = function <fn>(req, res, next)`\n // prototype assignments (not `class` methods), so `expressionName` (matching\n // the assignment's `left.property.name`) is used. `Callback`: the handler's\n // last argument is `next`, so the transform ends the traced operation when\n // `next` is invoked (and publishes `error` when it's called with an error).\n //\n // Express v4 ships its own router in `express/lib/router/layer.js`.\n {\n channelName: 'handle',\n module: { name: 'express', versionRange: '>=4.0.0 <5', filePath: 'lib/router/layer.js' },\n // v4's method is `Layer.prototype.handle_request = function handle(...)` —\n // match the assigned property name, not the function name.\n functionQuery: { expressionName: 'handle_request', kind: 'Callback' },\n },\n // Express v5 delegates routing to the standalone `router` package.\n {\n channelName: 'handle',\n module: { name: 'router', versionRange: '>=2.0.0 <3', filePath: 'lib/layer.js' },\n functionQuery: { expressionName: 'handleRequest', kind: 'Callback' },\n },\n // Layer *registration* methods. `Router.prototype.route`/`.use` are called\n // once per registered route/middleware (including internally by `app.get`/\n // `app.use`), so subscribing here lets us record each layer's registered path\n // *pattern* — which the handler path (`req.baseUrl`) can't recover for\n // parameterized mounts. `Sync`: these return synchronously and, unlike a\n // handler, `use`'s trailing function argument is a registration payload, not a\n // callback — so `Callback` would misclassify it and never fire `end`.\n //\n // Express v4 ships its own router in `express/lib/router/index.js`.\n {\n channelName: 'route',\n module: { name: 'express', versionRange: '>=4.0.0 <5', filePath: 'lib/router/index.js' },\n functionQuery: { expressionName: 'route', kind: 'Sync' },\n },\n {\n channelName: 'use',\n module: { name: 'express', versionRange: '>=4.0.0 <5', filePath: 'lib/router/index.js' },\n functionQuery: { expressionName: 'use', kind: 'Sync' },\n },\n // Express v5 delegates routing to the standalone `router` package.\n {\n channelName: 'route',\n module: { name: 'router', versionRange: '>=2.0.0 <3', filePath: 'index.js' },\n functionQuery: { expressionName: 'route', kind: 'Sync' },\n },\n {\n channelName: 'use',\n module: { name: 'router', versionRange: '>=2.0.0 <3', filePath: 'index.js' },\n functionQuery: { expressionName: 'use', kind: 'Sync' },\n },\n] satisfies InstrumentationConfig[];\n\nexport const expressChannels = {\n // Express v4 runs each layer's handler through `Layer.prototype.handle_request`\n // in the `express` module.\n EXPRESS_HANDLE: 'orchestrion:express:handle',\n // Express v5 delegates routing to the standalone `router` package, where the\n // equivalent method is `Layer.prototype.handleRequest`.\n ROUTER_HANDLE: 'orchestrion:router:handle',\n // Layer *registration* (`Router.prototype.route`/`.use`), used to capture each\n // layer's registered path pattern so the matched route can be reconstructed\n // with its parameters intact (`req.baseUrl` only exposes the resolved prefix).\n EXPRESS_ROUTE: 'orchestrion:express:route',\n EXPRESS_USE: 'orchestrion:express:use',\n ROUTER_ROUTE: 'orchestrion:router:route',\n ROUTER_USE: 'orchestrion:router:use',\n} as const;\n"],"names":[],"mappings":";;AAEO,MAAM,aAAA,GAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW3B;AAAA,IACE,WAAA,EAAa,QAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,YAAA,EAAc,UAAU,qBAAA,EAAsB;AAAA;AAAA;AAAA,IAGvF,aAAA,EAAe,EAAE,cAAA,EAAgB,gBAAA,EAAkB,MAAM,UAAA;AAAW,GACtE;AAAA;AAAA,EAEA;AAAA,IACE,WAAA,EAAa,QAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,UAAU,YAAA,EAAc,YAAA,EAAc,UAAU,cAAA,EAAe;AAAA,IAC/E,aAAA,EAAe,EAAE,cAAA,EAAgB,eAAA,EAAiB,MAAM,UAAA;AAAW,GACrE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA;AAAA,IACE,WAAA,EAAa,OAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,YAAA,EAAc,UAAU,qBAAA,EAAsB;AAAA,IACvF,aAAA,EAAe,EAAE,cAAA,EAAgB,OAAA,EAAS,MAAM,MAAA;AAAO,GACzD;AAAA,EACA;AAAA,IACE,WAAA,EAAa,KAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,YAAA,EAAc,UAAU,qBAAA,EAAsB;AAAA,IACvF,aAAA,EAAe,EAAE,cAAA,EAAgB,KAAA,EAAO,MAAM,MAAA;AAAO,GACvD;AAAA;AAAA,EAEA;AAAA,IACE,WAAA,EAAa,OAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,UAAU,YAAA,EAAc,YAAA,EAAc,UAAU,UAAA,EAAW;AAAA,IAC3E,aAAA,EAAe,EAAE,cAAA,EAAgB,OAAA,EAAS,MAAM,MAAA;AAAO,GACzD;AAAA,EACA;AAAA,IACE,WAAA,EAAa,KAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,UAAU,YAAA,EAAc,YAAA,EAAc,UAAU,UAAA,EAAW;AAAA,IAC3E,aAAA,EAAe,EAAE,cAAA,EAAgB,KAAA,EAAO,MAAM,MAAA;AAAO;AAEzD;AAEO,MAAM,eAAA,GAAkB;AAAA;AAAA;AAAA,EAG7B,cAAA,EAAgB,4BAAA;AAAA;AAAA;AAAA,EAGhB,aAAA,EAAe,2BAAA;AAAA;AAAA;AAAA;AAAA,EAIf,aAAA,EAAe,2BAAA;AAAA,EACf,WAAA,EAAa,yBAAA;AAAA,EACb,YAAA,EAAc,0BAAA;AAAA,EACd,UAAA,EAAY;AACd;;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const NODE_DIST_FILES = ["dist/node/index.js", "dist/node/index.mjs", "dist/node/index.cjs"]; | ||
| const googleGenAiConfig = [ | ||
| // `generateContent`/`generateContentStream` are arrow properties assigned in the constructor, not class | ||
| // methods, so they need `expressionName` rather than `className`/`methodName`. | ||
| ...NODE_DIST_FILES.flatMap( | ||
| (filePath) => ["generateContent", "generateContentStream"].map((expressionName) => ({ | ||
| channelName: "generate-content", | ||
| module: { name: "@google/genai", versionRange: ">=0.10.0 <2", filePath }, | ||
| functionQuery: { expressionName, kind: "Auto" } | ||
| })) | ||
| ), | ||
| // `embedContent` and the `Chat` methods are real class methods. | ||
| ...NODE_DIST_FILES.map((filePath) => ({ | ||
| channelName: "embed-content", | ||
| module: { name: "@google/genai", versionRange: ">=0.10.0 <2", filePath }, | ||
| functionQuery: { className: "Models", methodName: "embedContent", kind: "Auto" } | ||
| })), | ||
| // `sendMessage`/`sendMessageStream` internally delegate to `Models.generateContent(Stream)`; the | ||
| // subscriber suppresses that nested `generate-content` event so a chat call yields a single span. | ||
| ...NODE_DIST_FILES.flatMap( | ||
| (filePath) => ["sendMessage", "sendMessageStream"].map((methodName) => ({ | ||
| channelName: "chat", | ||
| module: { name: "@google/genai", versionRange: ">=0.10.0 <2", filePath }, | ||
| functionQuery: { className: "Chat", methodName, kind: "Auto" } | ||
| })) | ||
| ) | ||
| ]; | ||
| const googleGenAiChannels = { | ||
| GOOGLE_GENAI_GENERATE_CONTENT: "orchestrion:@google/genai:generate-content", | ||
| GOOGLE_GENAI_EMBED_CONTENT: "orchestrion:@google/genai:embed-content", | ||
| GOOGLE_GENAI_CHAT: "orchestrion:@google/genai:chat" | ||
| }; | ||
| exports.googleGenAiChannels = googleGenAiChannels; | ||
| exports.googleGenAiConfig = googleGenAiConfig; | ||
| //# sourceMappingURL=google-genai.js.map |
| {"version":3,"file":"google-genai.js","sources":["../../../../src/orchestrion/config/google-genai.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\n// `@google/genai` ships one bundled file per module format and the matcher compares `filePath` exactly,\n// so we list every file the `node` export condition resolves to across the supported range: `index.js`\n// (ESM+CJS for <0.15.0, CJS for <1.1.0), `index.mjs` (ESM for >=0.15.0), and `index.cjs` (CJS for >=1.1.0).\n// A file that doesn't exist in a given version simply never matches, so listing all three is safe.\nconst NODE_DIST_FILES = ['dist/node/index.js', 'dist/node/index.mjs', 'dist/node/index.cjs'];\n\nexport const googleGenAiConfig = [\n // `generateContent`/`generateContentStream` are arrow properties assigned in the constructor, not class\n // methods, so they need `expressionName` rather than `className`/`methodName`.\n ...NODE_DIST_FILES.flatMap(filePath =>\n (['generateContent', 'generateContentStream'] as const).map(expressionName => ({\n channelName: 'generate-content',\n module: { name: '@google/genai', versionRange: '>=0.10.0 <2', filePath },\n functionQuery: { expressionName, kind: 'Auto' as const },\n })),\n ),\n // `embedContent` and the `Chat` methods are real class methods.\n ...NODE_DIST_FILES.map(filePath => ({\n channelName: 'embed-content',\n module: { name: '@google/genai', versionRange: '>=0.10.0 <2', filePath },\n functionQuery: { className: 'Models', methodName: 'embedContent', kind: 'Auto' as const },\n })),\n // `sendMessage`/`sendMessageStream` internally delegate to `Models.generateContent(Stream)`; the\n // subscriber suppresses that nested `generate-content` event so a chat call yields a single span.\n ...NODE_DIST_FILES.flatMap(filePath =>\n (['sendMessage', 'sendMessageStream'] as const).map(methodName => ({\n channelName: 'chat',\n module: { name: '@google/genai', versionRange: '>=0.10.0 <2', filePath },\n functionQuery: { className: 'Chat', methodName, kind: 'Auto' as const },\n })),\n ),\n] satisfies InstrumentationConfig[];\n\nexport const googleGenAiChannels = {\n GOOGLE_GENAI_GENERATE_CONTENT: 'orchestrion:@google/genai:generate-content',\n GOOGLE_GENAI_EMBED_CONTENT: 'orchestrion:@google/genai:embed-content',\n GOOGLE_GENAI_CHAT: 'orchestrion:@google/genai:chat',\n} as const;\n"],"names":[],"mappings":";;AAMA,MAAM,eAAA,GAAkB,CAAC,oBAAA,EAAsB,qBAAA,EAAuB,qBAAqB,CAAA;AAEpF,MAAM,iBAAA,GAAoB;AAAA;AAAA;AAAA,EAG/B,GAAG,eAAA,CAAgB,OAAA;AAAA,IAAQ,cACxB,CAAC,iBAAA,EAAmB,uBAAuB,CAAA,CAAY,IAAI,CAAA,cAAA,MAAmB;AAAA,MAC7E,WAAA,EAAa,kBAAA;AAAA,MACb,QAAQ,EAAE,IAAA,EAAM,eAAA,EAAiB,YAAA,EAAc,eAAe,QAAA,EAAS;AAAA,MACvE,aAAA,EAAe,EAAE,cAAA,EAAgB,IAAA,EAAM,MAAA;AAAgB,KACzD,CAAE;AAAA,GACJ;AAAA;AAAA,EAEA,GAAG,eAAA,CAAgB,GAAA,CAAI,CAAA,QAAA,MAAa;AAAA,IAClC,WAAA,EAAa,eAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,eAAA,EAAiB,YAAA,EAAc,eAAe,QAAA,EAAS;AAAA,IACvE,eAAe,EAAE,SAAA,EAAW,UAAU,UAAA,EAAY,cAAA,EAAgB,MAAM,MAAA;AAAgB,GAC1F,CAAE,CAAA;AAAA;AAAA;AAAA,EAGF,GAAG,eAAA,CAAgB,OAAA;AAAA,IAAQ,cACxB,CAAC,aAAA,EAAe,mBAAmB,CAAA,CAAY,IAAI,CAAA,UAAA,MAAe;AAAA,MACjE,WAAA,EAAa,MAAA;AAAA,MACb,QAAQ,EAAE,IAAA,EAAM,eAAA,EAAiB,YAAA,EAAc,eAAe,QAAA,EAAS;AAAA,MACvE,eAAe,EAAE,SAAA,EAAW,MAAA,EAAQ,UAAA,EAAY,MAAM,MAAA;AAAgB,KACxE,CAAE;AAAA;AAEN;AAEO,MAAM,mBAAA,GAAsB;AAAA,EACjC,6BAAA,EAA+B,4CAAA;AAAA,EAC/B,0BAAA,EAA4B,yCAAA;AAAA,EAC5B,iBAAA,EAAmB;AACrB;;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const graphqlConfig = [ | ||
| { | ||
| channelName: "parse", | ||
| module: { name: "graphql", versionRange: ">=14.0.0 <17", filePath: "language/parser.js" }, | ||
| functionQuery: { functionName: "parse", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "validate", | ||
| module: { name: "graphql", versionRange: ">=14.0.0 <17", filePath: "validation/validate.js" }, | ||
| functionQuery: { functionName: "validate", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "execute", | ||
| module: { name: "graphql", versionRange: ">=14.0.0 <17", filePath: "execution/execute.js" }, | ||
| functionQuery: { functionName: "execute", kind: "Auto" } | ||
| } | ||
| ]; | ||
| const graphqlChannels = { | ||
| GRAPHQL_PARSE: "orchestrion:graphql:parse", | ||
| GRAPHQL_VALIDATE: "orchestrion:graphql:validate", | ||
| GRAPHQL_EXECUTE: "orchestrion:graphql:execute" | ||
| }; | ||
| exports.graphqlChannels = graphqlChannels; | ||
| exports.graphqlConfig = graphqlConfig; | ||
| //# sourceMappingURL=graphql.js.map |
| {"version":3,"file":"graphql.js","sources":["../../../../src/orchestrion/config/graphql.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\n// `parse`/`validate`/`execute` are top-level named `function` declarations in graphql's compiled\n// files, stable across the supported majors, so `functionName` matches. `execute` returns\n// `PromiseOrValue`, so `Auto` covers both async (settles on `asyncEnd`) and sync (`end`) schemas.\nexport const graphqlConfig = [\n {\n channelName: 'parse',\n module: { name: 'graphql', versionRange: '>=14.0.0 <17', filePath: 'language/parser.js' },\n functionQuery: { functionName: 'parse', kind: 'Sync' },\n },\n {\n channelName: 'validate',\n module: { name: 'graphql', versionRange: '>=14.0.0 <17', filePath: 'validation/validate.js' },\n functionQuery: { functionName: 'validate', kind: 'Sync' },\n },\n {\n channelName: 'execute',\n module: { name: 'graphql', versionRange: '>=14.0.0 <17', filePath: 'execution/execute.js' },\n functionQuery: { functionName: 'execute', kind: 'Auto' },\n },\n] satisfies InstrumentationConfig[];\n\nexport const graphqlChannels = {\n GRAPHQL_PARSE: 'orchestrion:graphql:parse',\n GRAPHQL_VALIDATE: 'orchestrion:graphql:validate',\n GRAPHQL_EXECUTE: 'orchestrion:graphql:execute',\n} as const;\n"],"names":[],"mappings":";;AAKO,MAAM,aAAA,GAAgB;AAAA,EAC3B;AAAA,IACE,WAAA,EAAa,OAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,cAAA,EAAgB,UAAU,oBAAA,EAAqB;AAAA,IACxF,aAAA,EAAe,EAAE,YAAA,EAAc,OAAA,EAAS,MAAM,MAAA;AAAO,GACvD;AAAA,EACA;AAAA,IACE,WAAA,EAAa,UAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,cAAA,EAAgB,UAAU,wBAAA,EAAyB;AAAA,IAC5F,aAAA,EAAe,EAAE,YAAA,EAAc,UAAA,EAAY,MAAM,MAAA;AAAO,GAC1D;AAAA,EACA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,cAAA,EAAgB,UAAU,sBAAA,EAAuB;AAAA,IAC1F,aAAA,EAAe,EAAE,YAAA,EAAc,SAAA,EAAW,MAAM,MAAA;AAAO;AAE3D;AAEO,MAAM,eAAA,GAAkB;AAAA,EAC7B,aAAA,EAAe,2BAAA;AAAA,EACf,gBAAA,EAAkB,8BAAA;AAAA,EAClB,eAAA,EAAiB;AACnB;;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const postgresJsInstrumentationConfig = (dir) => [ | ||
| // `Query.prototype.handle` (`class Query extends Promise`) is the single | ||
| // funnel every query passes through (`then`/`catch`/`finally`/`.execute()`/ | ||
| // `.forEach()`/cursor all call it), guarded by `this.executed`. `Async` | ||
| // because `handle` is `async`. | ||
| { | ||
| channelName: "handle", | ||
| module: { name: "postgres", versionRange: ">=3.0.0 <4", filePath: `${dir}/query.js` }, | ||
| functionQuery: { className: "Query", methodName: "handle", kind: "Async" } | ||
| }, | ||
| // `function Connection(options, ...)` (default export of `connection.js`) | ||
| // returns the connection object; used to build the endpoint registry that | ||
| // resolves `server.address`/`server.port`/`db.namespace`. | ||
| { | ||
| channelName: "connection", | ||
| module: { name: "postgres", versionRange: ">=3.0.0 <4", filePath: `${dir}/connection.js` }, | ||
| functionQuery: { functionName: "Connection", kind: "Sync" } | ||
| }, | ||
| // The nested `function execute(q)` inside `Connection`; the per-connection | ||
| // hook that attaches connection attributes to the query's span. | ||
| { | ||
| channelName: "execute", | ||
| module: { name: "postgres", versionRange: ">=3.0.0 <4", filePath: `${dir}/connection.js` }, | ||
| functionQuery: { functionName: "execute", kind: "Sync" } | ||
| }, | ||
| // The connection object's `connect(query)` method. Matched by `methodName` | ||
| // (an object-literal method): `functionName` would hit the unrelated | ||
| // socket-level `async function connect()` in the same file. `self` is the | ||
| // connection object and `arguments[0]` the query, so the first query that | ||
| // opens a connection (dispatched via a bare `execute` with no `self`) still | ||
| // gets connection attributes in multi-endpoint apps. | ||
| { | ||
| channelName: "connect", | ||
| module: { name: "postgres", versionRange: ">=3.0.0 <4", filePath: `${dir}/connection.js` }, | ||
| functionQuery: { methodName: "connect", kind: "Sync" } | ||
| } | ||
| ]; | ||
| const postgresJsConfig = ["src", "cjs/src"].flatMap(postgresJsInstrumentationConfig); | ||
| const postgresJsChannels = { | ||
| POSTGRESJS_HANDLE: "orchestrion:postgres:handle", | ||
| POSTGRESJS_CONNECTION: "orchestrion:postgres:connection", | ||
| POSTGRESJS_EXECUTE: "orchestrion:postgres:execute", | ||
| POSTGRESJS_CONNECT: "orchestrion:postgres:connect" | ||
| }; | ||
| exports.postgresJsChannels = postgresJsChannels; | ||
| exports.postgresJsConfig = postgresJsConfig; | ||
| //# sourceMappingURL=postgres.js.map |
| {"version":3,"file":"postgres.js","sources":["../../../../src/orchestrion/config/postgres.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\n// postgres.js (`postgres` npm package, v3.x). Named after the npm package;\n// `postgres` doesn't collide with `pg.ts` (that file instruments `pg`/`pg-pool`).\n//\n// The ESM build lives under `src/*`, the CJS build under `cjs/src/*` (the\n// `cf/*` workerd build has no channel subscribers, see the integration).\n// Both builds share the same class/function shapes, so a single `flatMap`\n// over the two dirs emits one entry per (dir, target).\nconst postgresJsInstrumentationConfig = (dir: string): InstrumentationConfig[] => [\n // `Query.prototype.handle` (`class Query extends Promise`) is the single\n // funnel every query passes through (`then`/`catch`/`finally`/`.execute()`/\n // `.forEach()`/cursor all call it), guarded by `this.executed`. `Async`\n // because `handle` is `async`.\n {\n channelName: 'handle',\n module: { name: 'postgres', versionRange: '>=3.0.0 <4', filePath: `${dir}/query.js` },\n functionQuery: { className: 'Query', methodName: 'handle', kind: 'Async' },\n },\n // `function Connection(options, ...)` (default export of `connection.js`)\n // returns the connection object; used to build the endpoint registry that\n // resolves `server.address`/`server.port`/`db.namespace`.\n {\n channelName: 'connection',\n module: { name: 'postgres', versionRange: '>=3.0.0 <4', filePath: `${dir}/connection.js` },\n functionQuery: { functionName: 'Connection', kind: 'Sync' },\n },\n // The nested `function execute(q)` inside `Connection`; the per-connection\n // hook that attaches connection attributes to the query's span.\n {\n channelName: 'execute',\n module: { name: 'postgres', versionRange: '>=3.0.0 <4', filePath: `${dir}/connection.js` },\n functionQuery: { functionName: 'execute', kind: 'Sync' },\n },\n // The connection object's `connect(query)` method. Matched by `methodName`\n // (an object-literal method): `functionName` would hit the unrelated\n // socket-level `async function connect()` in the same file. `self` is the\n // connection object and `arguments[0]` the query, so the first query that\n // opens a connection (dispatched via a bare `execute` with no `self`) still\n // gets connection attributes in multi-endpoint apps.\n {\n channelName: 'connect',\n module: { name: 'postgres', versionRange: '>=3.0.0 <4', filePath: `${dir}/connection.js` },\n functionQuery: { methodName: 'connect', kind: 'Sync' },\n },\n];\n\nexport const postgresJsConfig = ['src', 'cjs/src'].flatMap(postgresJsInstrumentationConfig);\n\nexport const postgresJsChannels = {\n POSTGRESJS_HANDLE: 'orchestrion:postgres:handle',\n POSTGRESJS_CONNECTION: 'orchestrion:postgres:connection',\n POSTGRESJS_EXECUTE: 'orchestrion:postgres:execute',\n POSTGRESJS_CONNECT: 'orchestrion:postgres:connect',\n} as const;\n"],"names":[],"mappings":";;AASA,MAAM,+BAAA,GAAkC,CAAC,GAAA,KAAyC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhF;AAAA,IACE,WAAA,EAAa,QAAA;AAAA,IACb,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAA,EAAY,cAAc,YAAA,EAAc,QAAA,EAAU,CAAA,EAAG,GAAG,CAAA,SAAA,CAAA,EAAY;AAAA,IACpF,eAAe,EAAE,SAAA,EAAW,SAAS,UAAA,EAAY,QAAA,EAAU,MAAM,OAAA;AAAQ,GAC3E;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,WAAA,EAAa,YAAA;AAAA,IACb,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAA,EAAY,cAAc,YAAA,EAAc,QAAA,EAAU,CAAA,EAAG,GAAG,CAAA,cAAA,CAAA,EAAiB;AAAA,IACzF,aAAA,EAAe,EAAE,YAAA,EAAc,YAAA,EAAc,MAAM,MAAA;AAAO,GAC5D;AAAA;AAAA;AAAA,EAGA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAA,EAAY,cAAc,YAAA,EAAc,QAAA,EAAU,CAAA,EAAG,GAAG,CAAA,cAAA,CAAA,EAAiB;AAAA,IACzF,aAAA,EAAe,EAAE,YAAA,EAAc,SAAA,EAAW,MAAM,MAAA;AAAO,GACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAA,EAAY,cAAc,YAAA,EAAc,QAAA,EAAU,CAAA,EAAG,GAAG,CAAA,cAAA,CAAA,EAAiB;AAAA,IACzF,aAAA,EAAe,EAAE,UAAA,EAAY,SAAA,EAAW,MAAM,MAAA;AAAO;AAEzD,CAAA;AAEO,MAAM,mBAAmB,CAAC,KAAA,EAAO,SAAS,CAAA,CAAE,QAAQ,+BAA+B;AAEnF,MAAM,kBAAA,GAAqB;AAAA,EAChC,iBAAA,EAAmB,6BAAA;AAAA,EACnB,qBAAA,EAAuB,iCAAA;AAAA,EACvB,kBAAA,EAAoB,8BAAA;AAAA,EACpB,kBAAA,EAAoB;AACtB;;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const redisConfig = [ | ||
| // redis `>=2.6.0 <4` (standalone `redis`). `internal_send_command` is an | ||
| // anonymous prototype assignment (`expressionName`); it settles via the nested | ||
| // `command_obj.callback`, so `kind: 'Sync'` and the subscriber wraps that callback. | ||
| { | ||
| channelName: "command", | ||
| module: { name: "redis", versionRange: ">=2.6.0 <4", filePath: "index.js" }, | ||
| functionQuery: { expressionName: "internal_send_command", kind: "Sync" } | ||
| }, | ||
| // node-redis v4 (`@redis/client` v1). The real chokepoint (private `#sendCommand`) | ||
| // isn't matchable, so wrap both public entry points: `commandsExecutor` (friendly | ||
| // commands) and `sendCommand` (direct calls). They never overlap, so no double span. | ||
| { | ||
| channelName: "executor", | ||
| module: { name: "@redis/client", versionRange: "^1.0.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "commandsExecutor", kind: "Async" } | ||
| }, | ||
| { | ||
| channelName: "command", | ||
| module: { name: "@redis/client", versionRange: "^1.0.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "sendCommand", kind: "Async" } | ||
| }, | ||
| { | ||
| channelName: "connect", | ||
| module: { name: "@redis/client", versionRange: "^1.0.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "connect", kind: "Async" } | ||
| }, | ||
| // node-redis `>=5.0.0 <5.12.0` (`@redis/client` v5; >=5.12.0 has its own | ||
| // `node-redis:*` diagnostics_channel, see `redis-dc-subscriber.ts`). Friendly | ||
| // commands route through the public `sendCommand`, so it covers them all — no | ||
| // `executor` entry (would double-count). | ||
| { | ||
| channelName: "command", | ||
| module: { name: "@redis/client", versionRange: ">=5.0.0 <5.12.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "sendCommand", kind: "Async" } | ||
| }, | ||
| { | ||
| channelName: "connect", | ||
| module: { name: "@redis/client", versionRange: ">=5.0.0 <5.12.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "connect", kind: "Async" } | ||
| }, | ||
| // Batch (multi/pipeline) — one span per `exec`. Batched commands bypass `sendCommand`, | ||
| // so they go through the client's batch executors, which receive the queued commands | ||
| // array (→ batch size). v5 splits MULTI/PIPELINE into two methods; v4's single | ||
| // `multiExecutor` is MULTI when a `chainId` arg is present, PIPELINE otherwise. | ||
| { | ||
| channelName: "multi", | ||
| module: { name: "@redis/client", versionRange: ">=5.0.0 <5.12.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "_executeMulti", kind: "Async" } | ||
| }, | ||
| { | ||
| channelName: "pipeline", | ||
| module: { name: "@redis/client", versionRange: ">=5.0.0 <5.12.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "_executePipeline", kind: "Async" } | ||
| }, | ||
| { | ||
| channelName: "batch", | ||
| module: { name: "@redis/client", versionRange: "^1.0.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "multiExecutor", kind: "Async" } | ||
| } | ||
| ]; | ||
| const redisChannels = { | ||
| REDIS_COMMAND: "orchestrion:redis:command", | ||
| NODE_REDIS_COMMAND: "orchestrion:@redis/client:command", | ||
| NODE_REDIS_EXECUTOR: "orchestrion:@redis/client:executor", | ||
| NODE_REDIS_CONNECT: "orchestrion:@redis/client:connect", | ||
| NODE_REDIS_MULTI: "orchestrion:@redis/client:multi", | ||
| NODE_REDIS_PIPELINE: "orchestrion:@redis/client:pipeline", | ||
| NODE_REDIS_BATCH: "orchestrion:@redis/client:batch" | ||
| }; | ||
| exports.redisChannels = redisChannels; | ||
| exports.redisConfig = redisConfig; | ||
| //# sourceMappingURL=redis.js.map |
| {"version":3,"file":"redis.js","sources":["../../../../src/orchestrion/config/redis.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\nexport const redisConfig = [\n // redis `>=2.6.0 <4` (standalone `redis`). `internal_send_command` is an\n // anonymous prototype assignment (`expressionName`); it settles via the nested\n // `command_obj.callback`, so `kind: 'Sync'` and the subscriber wraps that callback.\n {\n channelName: 'command',\n module: { name: 'redis', versionRange: '>=2.6.0 <4', filePath: 'index.js' },\n functionQuery: { expressionName: 'internal_send_command', kind: 'Sync' },\n },\n // node-redis v4 (`@redis/client` v1). The real chokepoint (private `#sendCommand`)\n // isn't matchable, so wrap both public entry points: `commandsExecutor` (friendly\n // commands) and `sendCommand` (direct calls). They never overlap, so no double span.\n {\n channelName: 'executor',\n module: { name: '@redis/client', versionRange: '^1.0.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'commandsExecutor', kind: 'Async' },\n },\n {\n channelName: 'command',\n module: { name: '@redis/client', versionRange: '^1.0.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'sendCommand', kind: 'Async' },\n },\n {\n channelName: 'connect',\n module: { name: '@redis/client', versionRange: '^1.0.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'connect', kind: 'Async' },\n },\n // node-redis `>=5.0.0 <5.12.0` (`@redis/client` v5; >=5.12.0 has its own\n // `node-redis:*` diagnostics_channel, see `redis-dc-subscriber.ts`). Friendly\n // commands route through the public `sendCommand`, so it covers them all — no\n // `executor` entry (would double-count).\n {\n channelName: 'command',\n module: { name: '@redis/client', versionRange: '>=5.0.0 <5.12.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'sendCommand', kind: 'Async' },\n },\n {\n channelName: 'connect',\n module: { name: '@redis/client', versionRange: '>=5.0.0 <5.12.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'connect', kind: 'Async' },\n },\n // Batch (multi/pipeline) — one span per `exec`. Batched commands bypass `sendCommand`,\n // so they go through the client's batch executors, which receive the queued commands\n // array (→ batch size). v5 splits MULTI/PIPELINE into two methods; v4's single\n // `multiExecutor` is MULTI when a `chainId` arg is present, PIPELINE otherwise.\n {\n channelName: 'multi',\n module: { name: '@redis/client', versionRange: '>=5.0.0 <5.12.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: '_executeMulti', kind: 'Async' },\n },\n {\n channelName: 'pipeline',\n module: { name: '@redis/client', versionRange: '>=5.0.0 <5.12.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: '_executePipeline', kind: 'Async' },\n },\n {\n channelName: 'batch',\n module: { name: '@redis/client', versionRange: '^1.0.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'multiExecutor', kind: 'Async' },\n },\n] satisfies InstrumentationConfig[];\n\nexport const redisChannels = {\n REDIS_COMMAND: 'orchestrion:redis:command',\n NODE_REDIS_COMMAND: 'orchestrion:@redis/client:command',\n NODE_REDIS_EXECUTOR: 'orchestrion:@redis/client:executor',\n NODE_REDIS_CONNECT: 'orchestrion:@redis/client:connect',\n NODE_REDIS_MULTI: 'orchestrion:@redis/client:multi',\n NODE_REDIS_PIPELINE: 'orchestrion:@redis/client:pipeline',\n NODE_REDIS_BATCH: 'orchestrion:@redis/client:batch',\n} as const;\n"],"names":[],"mappings":";;AAEO,MAAM,WAAA,GAAc;AAAA;AAAA;AAAA;AAAA,EAIzB;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,SAAS,YAAA,EAAc,YAAA,EAAc,UAAU,UAAA,EAAW;AAAA,IAC1E,aAAA,EAAe,EAAE,cAAA,EAAgB,uBAAA,EAAyB,MAAM,MAAA;AAAO,GACzE;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,WAAA,EAAa,UAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,QAAA,EAAU,UAAU,0BAAA,EAA2B;AAAA,IAC9F,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,kBAAA,EAAoB,MAAM,OAAA;AAAQ,GAC3F;AAAA,EACA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,QAAA,EAAU,UAAU,0BAAA,EAA2B;AAAA,IAC9F,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,aAAA,EAAe,MAAM,OAAA;AAAQ,GACtF;AAAA,EACA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,QAAA,EAAU,UAAU,0BAAA,EAA2B;AAAA,IAC9F,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,SAAA,EAAW,MAAM,OAAA;AAAQ,GAClF;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,iBAAA,EAAmB,UAAU,0BAAA,EAA2B;AAAA,IACvG,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,aAAA,EAAe,MAAM,OAAA;AAAQ,GACtF;AAAA,EACA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,iBAAA,EAAmB,UAAU,0BAAA,EAA2B;AAAA,IACvG,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,SAAA,EAAW,MAAM,OAAA;AAAQ,GAClF;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,IACE,WAAA,EAAa,OAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,iBAAA,EAAmB,UAAU,0BAAA,EAA2B;AAAA,IACvG,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,eAAA,EAAiB,MAAM,OAAA;AAAQ,GACxF;AAAA,EACA;AAAA,IACE,WAAA,EAAa,UAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,iBAAA,EAAmB,UAAU,0BAAA,EAA2B;AAAA,IACvG,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,kBAAA,EAAoB,MAAM,OAAA;AAAQ,GAC3F;AAAA,EACA;AAAA,IACE,WAAA,EAAa,OAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,QAAA,EAAU,UAAU,0BAAA,EAA2B;AAAA,IAC9F,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,eAAA,EAAiB,MAAM,OAAA;AAAQ;AAE1F;AAEO,MAAM,aAAA,GAAgB;AAAA,EAC3B,aAAA,EAAe,2BAAA;AAAA,EACf,kBAAA,EAAoB,mCAAA;AAAA,EACpB,mBAAA,EAAqB,oCAAA;AAAA,EACrB,kBAAA,EAAoB,mCAAA;AAAA,EACpB,gBAAA,EAAkB,iCAAA;AAAA,EAClB,mBAAA,EAAqB,oCAAA;AAAA,EACrB,gBAAA,EAAkB;AACpB;;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const redisDcSubscriber = require('./redis-dc-subscriber.js'); | ||
| const _redisIntegration = ((options = {}) => { | ||
| return { | ||
| name: "Redis", | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| core.waitForTracingChannelBinding(() => { | ||
| redisDcSubscriber.subscribeRedisDiagnosticChannels(diagnosticsChannel.tracingChannel, options.responseHook); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const redisIntegration = core.defineIntegration(_redisIntegration); | ||
| exports.redisIntegration = redisIntegration; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../src/redis/index.ts"],"sourcesContent":["import { defineIntegration, type IntegrationFn, waitForTracingChannelBinding } from '@sentry/core';\nimport * as dc from 'node:diagnostics_channel';\nimport { type RedisDiagnosticChannelResponseHook, subscribeRedisDiagnosticChannels } from './redis-dc-subscriber';\n\n/** Options controlling the redis diagnostics-channel subscription. */\nexport interface RedisDiagnosticChannelsOptions {\n /**\n * Optional hook invoked once the redis command response arrives. Useful for attaching\n * response-derived attributes (e.g. cache hit/miss, payload size).\n */\n responseHook?: RedisDiagnosticChannelResponseHook;\n}\n\nconst _redisIntegration = ((options: RedisDiagnosticChannelsOptions = {}) => {\n return {\n name: 'Redis',\n setupOnce() {\n // Bail on runtimes without `tracingChannel` (Node <= 18.18.0).\n if (!dc.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n subscribeRedisDiagnosticChannels(dc.tracingChannel, options.responseHook);\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the [redis](https://www.npmjs.com/package/redis) and\n * [ioredis](https://www.npmjs.com/package/ioredis) libraries via their native\n * `node:diagnostics_channel` tracing channels (node-redis >= 5.12.0, ioredis >= 5.11.0).\n */\nexport const redisIntegration = defineIntegration(_redisIntegration);\n"],"names":["dc","waitForTracingChannelBinding","subscribeRedisDiagnosticChannels","defineIntegration"],"mappings":";;;;;;AAaA,MAAM,iBAAA,IAAqB,CAAC,OAAA,GAA0C,EAAC,KAAM;AAC3E,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,OAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAACA,mBAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAEA,MAAAC,iCAAA,CAA6B,MAAM;AACjC,QAAAC,kDAAA,CAAiCF,kBAAA,CAAG,cAAA,EAAgB,OAAA,CAAQ,YAAY,CAAA;AAAA,MAC1E,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAOO,MAAM,gBAAA,GAAmBG,uBAAkB,iBAAiB;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| function asString(value) { | ||
| return typeof value === "string" ? value : void 0; | ||
| } | ||
| function asNumber(value) { | ||
| return typeof value === "number" && !isNaN(value) ? value : void 0; | ||
| } | ||
| function sum(a, b) { | ||
| return a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0); | ||
| } | ||
| function safeStringify(value) { | ||
| if (typeof value === "string") { | ||
| return value; | ||
| } | ||
| try { | ||
| return JSON.stringify(value); | ||
| } catch { | ||
| return "[unserializable]"; | ||
| } | ||
| } | ||
| function isReadableStream(value) { | ||
| return core.isObjectLike(value) && typeof value.pipeThrough === "function" && typeof value.getReader === "function"; | ||
| } | ||
| function tapModelCallStream(stream, onFinal, onError) { | ||
| const reader = stream.getReader(); | ||
| const state = { toolCalls: [] }; | ||
| let text = ""; | ||
| let settled = false; | ||
| const finalize = () => { | ||
| if (settled) { | ||
| return; | ||
| } | ||
| settled = true; | ||
| if (text) { | ||
| state.text = text; | ||
| } | ||
| onFinal(state); | ||
| }; | ||
| const fail = (error) => { | ||
| if (settled) { | ||
| return; | ||
| } | ||
| settled = true; | ||
| onError(error); | ||
| }; | ||
| return new ReadableStream({ | ||
| async pull(controller) { | ||
| try { | ||
| const { done, value } = await reader.read(); | ||
| if (done) { | ||
| finalize(); | ||
| controller.close(); | ||
| return; | ||
| } | ||
| text += accumulateChunk(state, value) ?? ""; | ||
| controller.enqueue(value); | ||
| } catch (error) { | ||
| fail(error); | ||
| controller.error(error); | ||
| } | ||
| }, | ||
| cancel(reason) { | ||
| finalize(); | ||
| return reader.cancel(reason); | ||
| } | ||
| }); | ||
| } | ||
| function accumulateChunk(state, chunk) { | ||
| if (!core.isObjectLike(chunk)) { | ||
| return void 0; | ||
| } | ||
| const { | ||
| type, | ||
| delta, | ||
| textDelta, | ||
| id, | ||
| modelId, | ||
| toolCallId, | ||
| toolName, | ||
| input, | ||
| args, | ||
| finishReason, | ||
| usage, | ||
| providerMetadata | ||
| } = chunk; | ||
| switch (type) { | ||
| case "text-delta": { | ||
| const textChunk = delta ?? textDelta; | ||
| return typeof textChunk === "string" ? textChunk : void 0; | ||
| } | ||
| case "tool-call": | ||
| state.toolCalls.push({ toolCallId, toolName, input: input ?? args }); | ||
| return void 0; | ||
| case "response-metadata": | ||
| if (typeof id === "string") { | ||
| state.responseId = id; | ||
| } | ||
| if (typeof modelId === "string") { | ||
| state.responseModel = modelId; | ||
| } | ||
| return void 0; | ||
| case "finish": | ||
| state.finishReason = finishReason; | ||
| state.usage = usage; | ||
| if (providerMetadata !== void 0) { | ||
| state.providerMetadata = providerMetadata; | ||
| } | ||
| return void 0; | ||
| default: | ||
| return void 0; | ||
| } | ||
| } | ||
| exports.asNumber = asNumber; | ||
| exports.asString = asString; | ||
| exports.isReadableStream = isReadableStream; | ||
| exports.safeStringify = safeStringify; | ||
| exports.sum = sum; | ||
| exports.tapModelCallStream = tapModelCallStream; | ||
| //# sourceMappingURL=util.js.map |
| {"version":3,"file":"util.js","sources":["../../../src/vercel-ai/util.ts"],"sourcesContent":["/** Shared, state-free helpers for the Vercel AI (`ai`) channel subscribers, plus the streamed model-call tap. */\n\nimport { isObjectLike } from '@sentry/core';\n\n/** Narrow to a string, or `undefined` for anything else. */\nexport function asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\n/** Narrow to a finite number, or `undefined` for anything else (including `NaN`). */\nexport function asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && !isNaN(value) ? value : undefined;\n}\n\n/** Add two optional numbers, treating a missing operand as `0` but returning `undefined` when both are absent. */\nexport function sum(a: number | undefined, b: number | undefined): number | undefined {\n return a === undefined && b === undefined ? undefined : (a ?? 0) + (b ?? 0);\n}\n\n/** Stringify a value, passing strings through and falling back to a placeholder on circular/unserializable input. */\nexport function safeStringify(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '[unserializable]';\n }\n}\n\n/*\n * Streaming support for the `ai:telemetry` tracing channel.\n *\n * For a streamed model call (`doStream`) the SDK resolves the `languageModelCall` channel promise as\n * soon as it hands back the *unconsumed* stream — so `result.stream` is a `ReadableStream` and the\n * final usage / finish reason / output only arrive later, as the stream is drained (they ride the\n * stream's own `finish`/`text-delta`/`tool-call` chunks, never the channel context). The SDK\n * deliberately leaves `result` undefined on the `streamText`/`step` contexts and exposes the stream\n * only on the model call, expecting consumers to tap it themselves.\n * @see https://github.com/vercel/ai/pull/15660 (discussion: \"tee off and aggregate ourselves\")\n *\n * We replace `result.stream` with a passthrough that forwards every chunk untouched (so the SDK's own\n * consumption is unaffected) while accumulating the data we need, then hand the aggregate back once the\n * stream settles so the model-call span can be enriched and ended out-of-band.\n */\n\n/** The subset of a streamed provider chunk we read. Unknown chunk types are forwarded and ignored. */\ninterface StreamChunk {\n type?: unknown;\n delta?: unknown;\n // v4 text-delta chunks carry the text on `textDelta`; v5+ uses `delta`.\n textDelta?: unknown;\n id?: unknown;\n modelId?: unknown;\n toolCallId?: unknown;\n toolName?: unknown;\n input?: unknown;\n args?: unknown;\n finishReason?: unknown;\n usage?: unknown;\n providerMetadata?: unknown;\n error?: unknown;\n}\n\n/** The aggregate handed back once a streamed model call finishes, in the shape `enrichSpanOnEnd` expects. */\nexport interface StreamedModelCallResult {\n text?: string;\n toolCalls: Array<Record<string, unknown>>;\n usage?: unknown;\n finishReason?: unknown;\n responseId?: string;\n responseModel?: string;\n providerMetadata?: unknown;\n}\n\n/** A minimal structural check — the streamed model call exposes a web `ReadableStream` on `result.stream`. */\nexport function isReadableStream(value: unknown): value is ReadableStream<unknown> {\n return (\n isObjectLike(value) &&\n typeof (value as { pipeThrough?: unknown }).pipeThrough === 'function' &&\n typeof (value as { getReader?: unknown }).getReader === 'function'\n );\n}\n\n/**\n * Wrap a streamed model call's `ReadableStream` so its chunks are observed as the SDK consumes them,\n * without altering what the SDK sees. Returns a replacement stream to swap onto `result.stream`.\n *\n * `onFinal` runs exactly once when the stream drains cleanly; `onError` runs exactly once if it errors\n * or is cancelled. Reading one source chunk per `pull` preserves the SDK's backpressure. The\n * `try/catch` around every read guarantees the owning span is always ended — a leaked open span on a\n * mid-stream failure would be worse than a slightly-less-enriched one.\n */\nexport function tapModelCallStream(\n stream: ReadableStream<unknown>,\n onFinal: (result: StreamedModelCallResult) => void,\n onError: (error: unknown) => void,\n): ReadableStream<unknown> {\n const reader = stream.getReader();\n const state: StreamedModelCallResult = { toolCalls: [] };\n let text = '';\n let settled = false;\n\n const finalize = (): void => {\n if (settled) {\n return;\n }\n settled = true;\n if (text) {\n state.text = text;\n }\n onFinal(state);\n };\n\n const fail = (error: unknown): void => {\n if (settled) {\n return;\n }\n settled = true;\n onError(error);\n };\n\n return new ReadableStream<unknown>({\n async pull(controller) {\n try {\n const { done, value } = await reader.read();\n if (done) {\n finalize();\n controller.close();\n\n return;\n }\n text += accumulateChunk(state, value) ?? '';\n controller.enqueue(value);\n } catch (error) {\n fail(error);\n controller.error(error);\n }\n },\n cancel(reason) {\n // Consumer stopped reading early (e.g. a `break` out of `for await`); close out the span with\n // whatever we have rather than leave it open. A non-error reason is a deliberate stop, not a failure.\n finalize();\n\n return reader.cancel(reason);\n },\n });\n}\n\n/**\n * Fold a single streamed chunk into the running aggregate. Returns any text delta so the caller can\n * accumulate it (kept out of `state` until the end to avoid re-joining on every chunk).\n */\nfunction accumulateChunk(state: StreamedModelCallResult, chunk: unknown): string | undefined {\n if (!isObjectLike(chunk)) {\n return undefined;\n }\n const {\n type,\n delta,\n textDelta,\n id,\n modelId,\n toolCallId,\n toolName,\n input,\n args,\n finishReason,\n usage,\n providerMetadata,\n } = chunk as StreamChunk;\n\n switch (type) {\n case 'text-delta': {\n const textChunk = delta ?? textDelta;\n return typeof textChunk === 'string' ? textChunk : undefined;\n }\n case 'tool-call':\n state.toolCalls.push({ toolCallId, toolName, input: input ?? args });\n\n return undefined;\n case 'response-metadata':\n if (typeof id === 'string') {\n state.responseId = id;\n }\n if (typeof modelId === 'string') {\n state.responseModel = modelId;\n }\n\n return undefined;\n case 'finish':\n state.finishReason = finishReason;\n state.usage = usage;\n if (providerMetadata !== undefined) {\n state.providerMetadata = providerMetadata;\n }\n\n return undefined;\n default:\n return undefined;\n }\n}\n"],"names":["isObjectLike"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| const debugBuild = require('../debug-build.js'); | ||
| const channels = require('../orchestrion/channels.js'); | ||
| const tracingChannel = require('../tracing-channel.js'); | ||
| const vercelAiDcSubscriber = require('./vercel-ai-dc-subscriber.js'); | ||
| const util = require('./util.js'); | ||
| const PATCHED = /* @__PURE__ */ Symbol("SentryVercelAiModelPatched"); | ||
| const TOOL_PATCHED = /* @__PURE__ */ Symbol("SentryVercelAiToolPatched"); | ||
| let callIdCounter = 0; | ||
| function nextCallId() { | ||
| return `v6-${++callIdCounter}`; | ||
| } | ||
| const messages = /* @__PURE__ */ new WeakMap(); | ||
| const operationSpans = /* @__PURE__ */ new WeakSet(); | ||
| const toolCallSpans = /* @__PURE__ */ new WeakSet(); | ||
| const callIdBySpan = /* @__PURE__ */ new WeakMap(); | ||
| const recordingBySpan = /* @__PURE__ */ new WeakMap(); | ||
| const suppressedTelemetry = /* @__PURE__ */ new WeakSet(); | ||
| let subscribed = false; | ||
| function subscribeVercelAiOrchestrionChannels(tracingChannel, options = {}) { | ||
| if (subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| try { | ||
| bindOperation(tracingChannel, channels.CHANNELS.VERCEL_AI_GENERATE_TEXT, buildTextMessage("generateText"), options); | ||
| bindOperation(tracingChannel, channels.CHANNELS.VERCEL_AI_STREAM_TEXT, buildTextMessage("streamText"), options); | ||
| bindOperation(tracingChannel, channels.CHANNELS.VERCEL_AI_GENERATE_OBJECT, buildTextMessage("generateObject"), options); | ||
| bindOperation( | ||
| tracingChannel, | ||
| channels.CHANNELS.VERCEL_AI_EMBED, | ||
| (callOptions, telemetry) => ({ | ||
| type: "embed", | ||
| event: { | ||
| callId: nextCallId(), | ||
| ...modelFields(callOptions.model), | ||
| maxRetries: callOptions.maxRetries, | ||
| value: callOptions.value, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| bindOperation( | ||
| tracingChannel, | ||
| channels.CHANNELS.VERCEL_AI_EMBED_MANY, | ||
| // `embedMany` takes a `values` array (vs `embed`'s single `value`); the shared core reads it as the | ||
| // embeddings input, matching the OTel path's batch `ai.embedMany` span. | ||
| (callOptions, telemetry) => ({ | ||
| type: "embedMany", | ||
| event: { | ||
| callId: nextCallId(), | ||
| ...modelFields(callOptions.model), | ||
| maxRetries: callOptions.maxRetries, | ||
| values: callOptions.values, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| bindOperation( | ||
| tracingChannel, | ||
| channels.CHANNELS.VERCEL_AI_EXECUTE_TOOL_CALL, | ||
| (callOptions, telemetry) => ({ | ||
| type: "executeTool", | ||
| // v6 carries the tool definitions on the executeToolCall args (a record keyed by name); | ||
| // the shared core reads the matching tool's `description` for the span. | ||
| event: { | ||
| callId: nextCallId(), | ||
| toolCall: callOptions.toolCall, | ||
| tools: callOptions.tools, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| subscribeResolveLanguageModel(tracingChannel, channels.CHANNELS.VERCEL_AI_RESOLVE_LANGUAGE_MODEL, options); | ||
| } catch { | ||
| debugBuild.DEBUG_BUILD && core.debug.log("Vercel AI orchestrion channel subscription failed."); | ||
| } | ||
| } | ||
| function bindOperation(tracingChannel$1, channelName, build, options) { | ||
| const channel = tracingChannel$1(channelName); | ||
| const buildOperationSpan = (data) => { | ||
| const callOptions = core.isObjectLike(data.arguments[0]) ? data.arguments[0] : {}; | ||
| const telemetry = core.isObjectLike(callOptions.experimental_telemetry) ? callOptions.experimental_telemetry : {}; | ||
| if (telemetry.isEnabled === false && !suppressedTelemetry.has(telemetry)) { | ||
| return void 0; | ||
| } | ||
| const message = build(callOptions, telemetry); | ||
| suppressNativeTelemetry(callOptions, telemetry); | ||
| const span = vercelAiDcSubscriber.createSpanFromMessage(message, options); | ||
| if (span) { | ||
| messages.set(data, message); | ||
| operationSpans.add(span); | ||
| if (message.type === "executeTool") { | ||
| toolCallSpans.add(span); | ||
| } | ||
| const callId = util.asString(message.event.callId); | ||
| if (callId) { | ||
| callIdBySpan.set(span, callId); | ||
| } | ||
| recordingBySpan.set(span, recording(telemetry)); | ||
| if (core.isObjectLike(callOptions.tools)) { | ||
| patchOperationTools(callOptions.tools, options); | ||
| } | ||
| if (core.isObjectLike(callOptions.model) && callOptions.model.specificationVersion === "v1") { | ||
| patchModelMethods(callOptions.model, options); | ||
| } | ||
| } | ||
| return span; | ||
| }; | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| channel, | ||
| (data) => buildOperationSpan(data), | ||
| { | ||
| beforeSpanEnd: (span, data) => { | ||
| const message = messages.get(data); | ||
| if (!message) { | ||
| return; | ||
| } | ||
| if (!("error" in data)) { | ||
| message.result = message.type === "executeTool" ? { output: data.result } : data.result; | ||
| vercelAiDcSubscriber.enrichSpanOnEnd(span, message, options); | ||
| } | ||
| if (message.type !== "streamText") { | ||
| vercelAiDcSubscriber.clearOperationId(message); | ||
| } | ||
| messages.delete(data); | ||
| }, | ||
| // `streamText` returns synchronously, so its operation span would otherwise end before the stream | ||
| // drains — losing the aggregate usage/output. Defer the end and await the result's completion | ||
| // promises (`totalUsage`/`text`/…, which resolve on drain), mirroring how v7's channel defers the | ||
| // operation span on the SDK's total-usage promise. | ||
| deferSpanEnd: ({ data, end }) => deferStreamTextOperationEnd(data, end) | ||
| } | ||
| ); | ||
| } | ||
| function deferStreamTextOperationEnd(data, end) { | ||
| if (messages.get(data)?.type !== "streamText" || "error" in data || !isStreamingResult(data.result)) { | ||
| return false; | ||
| } | ||
| const streamResult = data.result; | ||
| void (async () => { | ||
| try { | ||
| const [usage, text, toolCalls, finishReason, response] = await Promise.all([ | ||
| streamResult.totalUsage ?? streamResult.usage, | ||
| streamResult.text, | ||
| streamResult.toolCalls, | ||
| streamResult.finishReason, | ||
| streamResult.response | ||
| ]); | ||
| data.result = { usage, text, toolCalls, finishReason, response }; | ||
| end(); | ||
| } catch (error) { | ||
| end(error); | ||
| } | ||
| })(); | ||
| return true; | ||
| } | ||
| function isStreamingResult(result) { | ||
| return core.isObjectLike(result) && (isThenable(result.totalUsage) || isThenable(result.usage)); | ||
| } | ||
| function isThenable(value) { | ||
| return core.isObjectLike(value) && typeof value.then === "function"; | ||
| } | ||
| function suppressNativeTelemetry(callOptions, telemetry) { | ||
| if (telemetry.isEnabled !== true) { | ||
| return; | ||
| } | ||
| const suppressed = { ...telemetry, isEnabled: false }; | ||
| suppressedTelemetry.add(suppressed); | ||
| callOptions.experimental_telemetry = suppressed; | ||
| } | ||
| function subscribeResolveLanguageModel(tracingChannel, channelName, options) { | ||
| tracingChannel(channelName).subscribe({ | ||
| end(rawCtx) { | ||
| const ctx = rawCtx; | ||
| if (!core.isObjectLike(ctx.result)) { | ||
| return; | ||
| } | ||
| patchModelMethods(ctx.result, options); | ||
| }, | ||
| start() { | ||
| }, | ||
| asyncStart() { | ||
| }, | ||
| asyncEnd() { | ||
| }, | ||
| error() { | ||
| } | ||
| }); | ||
| } | ||
| function resolveModelCallParent() { | ||
| const active = core.getActiveSpan(); | ||
| return active && operationSpans.has(active) ? active : void 0; | ||
| } | ||
| function patchModelMethods(model, options) { | ||
| if (model[PATCHED]) { | ||
| return; | ||
| } | ||
| model[PATCHED] = true; | ||
| patchModelMethod(model, "doGenerate", options); | ||
| patchModelMethod(model, "doStream", options); | ||
| } | ||
| function patchModelMethod(model, method, options) { | ||
| const original = model[method]; | ||
| if (typeof original !== "function") { | ||
| return; | ||
| } | ||
| model[method] = function(...args) { | ||
| const parent = resolveModelCallParent(); | ||
| if (!parent) { | ||
| return Promise.resolve(original.apply(this, args)); | ||
| } | ||
| const callArgs = core.isObjectLike(args[0]) ? args[0] : {}; | ||
| const callId = callIdBySpan.get(parent); | ||
| const message = { | ||
| type: "languageModelCall", | ||
| event: { | ||
| callId, | ||
| provider: model.provider, | ||
| modelId: model.modelId, | ||
| // v4 nests the tool list under `mode.tools` (the `LanguageModelV1` call shape); v5+ passes a | ||
| // top-level `tools` array. Reading both keeps `available_tools` populated on the model-call span. | ||
| tools: callArgs.tools ?? (core.isObjectLike(callArgs.mode) ? callArgs.mode.tools : void 0), | ||
| messages: callArgs.prompt, | ||
| // Inherit the enclosing operation's per-call recording flags so inputs/tools/outputs are recorded on | ||
| // the model-call span whenever they are on the parent `invoke_agent` span. | ||
| ...recordingBySpan.get(parent) | ||
| } | ||
| }; | ||
| const span = core.withActiveSpan(parent, () => vercelAiDcSubscriber.createSpanFromMessage(message, options)); | ||
| if (!span) { | ||
| return Promise.resolve(original.apply(this, args)); | ||
| } | ||
| const clearStreamCallId = () => { | ||
| if (method === "doStream" && callId) { | ||
| vercelAiDcSubscriber.clearOperationCallId(callId); | ||
| } | ||
| }; | ||
| const failSpan = (error) => { | ||
| span.setStatus({ code: core.SPAN_STATUS_ERROR, message: error instanceof Error ? error.message : "unknown_error" }); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| throw error; | ||
| }; | ||
| try { | ||
| const result = Promise.resolve(original.apply(this, args)); | ||
| return result.then((value) => { | ||
| if (method === "doStream" && core.isObjectLike(value) && util.isReadableStream(value.stream)) { | ||
| value.stream = util.tapModelCallStream( | ||
| value.stream, | ||
| (final) => { | ||
| message.result = { ...value, ...vercelAiDcSubscriber.streamedResultToChannelResult(final) }; | ||
| vercelAiDcSubscriber.enrichSpanOnEnd(span, message, options); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| }, | ||
| (error) => { | ||
| span.setStatus({ | ||
| code: core.SPAN_STATUS_ERROR, | ||
| message: error instanceof Error ? error.message : "unknown_error" | ||
| }); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| } | ||
| ); | ||
| return value; | ||
| } | ||
| message.result = value; | ||
| vercelAiDcSubscriber.enrichSpanOnEnd(span, message, options); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| return value; | ||
| }, failSpan); | ||
| } catch (error) { | ||
| return failSpan(error); | ||
| } | ||
| }; | ||
| } | ||
| function patchOperationTools(tools, options) { | ||
| try { | ||
| for (const [toolName, tool] of Object.entries(tools)) { | ||
| if (core.isObjectLike(tool)) { | ||
| patchToolExecute(toolName, tool, tools, options); | ||
| } | ||
| } | ||
| } catch { | ||
| debugBuild.DEBUG_BUILD && core.debug.log("Vercel AI orchestrion tool patching failed."); | ||
| } | ||
| } | ||
| function patchToolExecute(toolName, tool, tools, options) { | ||
| const original = tool.execute; | ||
| if (typeof original !== "function" || tool[TOOL_PATCHED]) { | ||
| return; | ||
| } | ||
| tool[TOOL_PATCHED] = true; | ||
| tool.execute = function(input, ...rest) { | ||
| const parent = resolveModelCallParent(); | ||
| if (!parent || toolCallSpans.has(parent)) { | ||
| return original.apply(this, [input, ...rest]); | ||
| } | ||
| const callOptions = core.isObjectLike(rest[0]) ? rest[0] : {}; | ||
| const message = { | ||
| type: "executeTool", | ||
| event: { | ||
| callId: callIdBySpan.get(parent), | ||
| toolCall: { toolName, toolCallId: util.asString(callOptions.toolCallId), input }, | ||
| // The `tools` record (keyed by name) lets the shared core backfill the tool's `description`. | ||
| tools, | ||
| // Inherit the enclosing operation's per-call recording flags so tool inputs/outputs are recorded | ||
| // whenever they are on the parent `invoke_agent` span. | ||
| ...recordingBySpan.get(parent) | ||
| } | ||
| }; | ||
| const span = core.withActiveSpan(parent, () => vercelAiDcSubscriber.createSpanFromMessage(message, options)); | ||
| if (!span) { | ||
| return original.apply(this, [input, ...rest]); | ||
| } | ||
| const failSpan = (error) => { | ||
| vercelAiDcSubscriber.captureToolError(span, message, error); | ||
| span.end(); | ||
| throw error; | ||
| }; | ||
| try { | ||
| const result = Promise.resolve(original.apply(this, [input, ...rest])); | ||
| return result.then((value) => { | ||
| message.result = { output: value }; | ||
| vercelAiDcSubscriber.enrichSpanOnEnd(span, message, options); | ||
| span.end(); | ||
| return value; | ||
| }, failSpan); | ||
| } catch (error) { | ||
| return failSpan(error); | ||
| } | ||
| }; | ||
| } | ||
| function buildTextMessage(type) { | ||
| return (options, telemetry) => ({ | ||
| type, | ||
| event: { | ||
| callId: nextCallId(), | ||
| operationId: `ai.${type}`, | ||
| functionId: util.asString(telemetry.functionId), | ||
| ...modelFields(options.model), | ||
| maxRetries: options.maxRetries, | ||
| // The `ai` SDK takes the system prompt as a top-level `system` option (all of v4/v5/v6); the | ||
| // shared core lifts `event.instructions` into the system-instructions attribute, matching v7's | ||
| // native channel (which carries it as a distinct field rather than inside the messages array). | ||
| instructions: util.asString(options.system), | ||
| // Normalize to the message-array shape the shared core (and v7's channel) expects: a bare string | ||
| // `prompt` becomes a single user message, matching the SDK's own normalization. | ||
| messages: normalizePromptMessages(options), | ||
| ...recording(telemetry) | ||
| } | ||
| }); | ||
| } | ||
| function normalizePromptMessages(options) { | ||
| if (Array.isArray(options.messages)) { | ||
| return options.messages; | ||
| } | ||
| if (typeof options.prompt === "string") { | ||
| return [{ role: "user", content: options.prompt }]; | ||
| } | ||
| return options.messages ?? options.prompt; | ||
| } | ||
| function recording(telemetry) { | ||
| const enabledDefault = telemetry.isEnabled === true ? true : void 0; | ||
| return { | ||
| recordInputs: telemetry.recordInputs ?? enabledDefault, | ||
| recordOutputs: telemetry.recordOutputs ?? enabledDefault | ||
| }; | ||
| } | ||
| function modelFields(model) { | ||
| return { provider: modelField(model, "provider"), modelId: modelField(model, "modelId") }; | ||
| } | ||
| function modelField(model, field) { | ||
| return core.isObjectLike(model) ? util.asString(model[field]) : void 0; | ||
| } | ||
| exports.subscribeVercelAiOrchestrionChannels = subscribeVercelAiOrchestrionChannels; | ||
| //# sourceMappingURL=vercel-ai-orchestrion-subscriber.js.map |
| {"version":3,"file":"vercel-ai-orchestrion-subscriber.js","sources":["../../../src/vercel-ai/vercel-ai-orchestrion-subscriber.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Span } from '@sentry/core';\nimport { debug, getActiveSpan, isObjectLike, SPAN_STATUS_ERROR, withActiveSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { CHANNELS } from '../orchestrion/channels';\nimport { bindTracingChannelToSpan, type TracingChannelPayloadWithSpan } from '../tracing-channel';\nimport {\n captureToolError,\n clearOperationCallId,\n clearOperationId,\n createSpanFromMessage,\n enrichSpanOnEnd,\n streamedResultToChannelResult,\n type VercelAiChannelMessage,\n type VercelAiChannelOptions,\n type VercelAiTracingChannelFactory,\n} from './vercel-ai-dc-subscriber';\nimport { asString, isReadableStream, tapModelCallStream } from './util';\n\n/**\n * v4, v5 & v6 channel adapter for the Vercel AI (`ai`) SDK.\n *\n * `ai` >= 7 publishes a normalized `ai:telemetry` tracing channel natively\n * (consumed by `subscribeVercelAiTracingChannel`). v4/v5/v6 have no such channel, so\n * orchestrion injects `orchestrion:ai:*` channels around the top-level\n * functions (see `orchestrion/config/index.ts`). The injected channels carry only the\n * wrapped call's `{ arguments, result, error }` — NOT v7's normalized `event`\n * object — so this adapter reconstructs an equivalent {@link VercelAiChannelMessage}\n * from each version's argument/result shapes and delegates to the SAME span-building core\n * (`createSpanFromMessage` / `enrichSpanOnEnd`) the v7 subscriber uses, so the\n * emitted spans are identical between v4, v5, v6 and v7. The shared core reads both v4-style\n * (`promptTokens`/`completionTokens`) and v5+-style (`inputTokens`/`outputTokens`) token names.\n *\n * The model call (`languageModelCall` / `generate_content` span) has no\n * injectable definition in `ai`. On v5/v6 we wrap `resolveLanguageModel` (the\n * single chokepoint every model call flows through) and monkey-patch\n * `doGenerate`/`doStream` on the returned model. v4 has no `resolveLanguageModel`\n * — the passed `LanguageModelV1` (`specificationVersion: 'v1'`) is called directly —\n * so we patch its `doGenerate`/`doStream` at the operation start instead (see\n * `buildOperationSpan`), gated on `'v1'` so it never touches v5/v6 models.\n *\n * Tool-call spans differ by version: v6 exposes a per-call `executeToolCall`\n * function orchestrion wraps into its own channel. v4/v5 have no such export (only a\n * batch `executeTools`), so instead we monkey-patch each tool's `execute` from\n * the operation's `tools`. On v6 that patch is inert: `executeToolCall` runs\n * `execute` inside its own tool-call span (the active async context), so the\n * patch sees that span as its parent and skips — only v4/v5 (where the parent is\n * the enclosing `invoke_agent` span) emit the patched span, so v6 never\n * double-counts tool spans.\n */\n\n/** Shape orchestrion's transform attaches to the tracing-channel context. */\ninterface OrchestrionContext {\n arguments: unknown[];\n result?: unknown;\n error?: unknown;\n}\n\n/** Builds the normalized message for a channel from the wrapped call's first-arg options. */\ntype MessageBuilder = (options: Record<string, unknown>, telemetry: Record<string, unknown>) => VercelAiChannelMessage;\n\n/** A resolved `ai` language model — has `doGenerate`/`doStream` and identity fields. */\ninterface ResolvedModel {\n modelId?: string;\n provider?: string;\n doGenerate?: (...args: unknown[]) => Promise<unknown>;\n doStream?: (...args: unknown[]) => Promise<unknown>;\n}\n\nconst PATCHED = Symbol('SentryVercelAiModelPatched');\nconst TOOL_PATCHED = Symbol('SentryVercelAiToolPatched');\n\n/** A resolved model with our patch bookkeeping (idempotency flag). */\ntype PatchableModel = ResolvedModel & { [PATCHED]?: boolean };\n\n/** A tool definition off an operation's `tools`, with our patch bookkeeping. */\ntype PatchableTool = { execute?: (...args: unknown[]) => unknown; [TOOL_PATCHED]?: boolean };\n\n// Per-operation correlation id. No Date/random (unavailable / non-deterministic) — a counter is enough.\nlet callIdCounter = 0;\nfunction nextCallId(): string {\n return `v6-${++callIdCounter}`;\n}\n\n// The message built on `start` for each operation, keyed by the (stable-identity) channel context, so\n// the `beforeSpanEnd` handler can enrich the span from the settled result and clear the `callId` maps.\nconst messages = new WeakMap<object, VercelAiChannelMessage>();\n// The spans we opened for top-level operations, and each one's `callId`. A model call resolves its\n// parent against this set (so it never mis-attributes to the enclosing `main`/user span) and reads the\n// parent's `callId` so its span can be named after the operation (e.g. `ai.streamText.doStream`).\nconst operationSpans = new WeakSet<Span>();\n// Tool-call spans — only v6's `executeToolCall` channel opens these. The v5 tool-`execute` patch\n// consults this to avoid double-counting: `executeToolCall` runs `execute` inside its span (which is\n// therefore the active parent), so a resolved parent in this set means v6 already spanned the call.\nconst toolCallSpans = new WeakSet<Span>();\nconst callIdBySpan = new WeakMap<Span, string>();\n// The operation's per-call recording flags (`experimental_telemetry.recordInputs/recordOutputs`), keyed by\n// its span. A model call carries no telemetry of its own, so it inherits the enclosing operation's flags —\n// otherwise a per-call `recordInputs: true` would record inputs on the `invoke_agent` span but not on the\n// child `generate_content` span (whose event would fall back to the global default). v7's channel forwards\n// these flags on every event, so this keeps v6 identical.\nconst recordingBySpan = new WeakMap<Span, ReturnType<typeof recording>>();\n\n// The `experimental_telemetry` objects we swapped in to suppress `ai`'s native OTel spans (see\n// `suppressNativeTelemetry`). Our skip logic treats `isEnabled === false` as \"user disabled telemetry,\n// emit no span\"; without this set, a call whose options object we already neutralized — or a user-shared\n// telemetry object we replaced on a prior call — would be misread as user-disabled and lose its span.\nconst suppressedTelemetry = new WeakSet<object>();\n\nlet subscribed = false;\n\n/**\n * Subscribe the v6 orchestrion channel adapter. Safe to always call: inert on\n * `ai` >= 7 (those channels are never published) and when orchestrion injection\n * isn't active. Idempotent.\n *\n * `tracingChannel` is the platform-provided factory (the same one passed to\n * `subscribeVercelAiTracingChannel`); `options` pins the recording settings at\n * subscribe time so we never look the integration up per event.\n */\nexport function subscribeVercelAiOrchestrionChannels(\n tracingChannel: VercelAiTracingChannelFactory,\n options: VercelAiChannelOptions = {},\n): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n try {\n bindOperation(tracingChannel, CHANNELS.VERCEL_AI_GENERATE_TEXT, buildTextMessage('generateText'), options);\n bindOperation(tracingChannel, CHANNELS.VERCEL_AI_STREAM_TEXT, buildTextMessage('streamText'), options);\n bindOperation(tracingChannel, CHANNELS.VERCEL_AI_GENERATE_OBJECT, buildTextMessage('generateObject'), options);\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EMBED,\n (callOptions, telemetry) => ({\n type: 'embed',\n event: {\n callId: nextCallId(),\n ...modelFields(callOptions.model),\n maxRetries: callOptions.maxRetries,\n value: callOptions.value,\n ...recording(telemetry),\n },\n }),\n options,\n );\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EMBED_MANY,\n // `embedMany` takes a `values` array (vs `embed`'s single `value`); the shared core reads it as the\n // embeddings input, matching the OTel path's batch `ai.embedMany` span.\n (callOptions, telemetry) => ({\n type: 'embedMany',\n event: {\n callId: nextCallId(),\n ...modelFields(callOptions.model),\n maxRetries: callOptions.maxRetries,\n values: callOptions.values,\n ...recording(telemetry),\n },\n }),\n options,\n );\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EXECUTE_TOOL_CALL,\n (callOptions, telemetry) => ({\n type: 'executeTool',\n // v6 carries the tool definitions on the executeToolCall args (a record keyed by name);\n // the shared core reads the matching tool's `description` for the span.\n event: {\n callId: nextCallId(),\n toolCall: callOptions.toolCall,\n tools: callOptions.tools,\n ...recording(telemetry),\n },\n }),\n options,\n );\n subscribeResolveLanguageModel(tracingChannel, CHANNELS.VERCEL_AI_RESOLVE_LANGUAGE_MODEL, options);\n } catch {\n DEBUG_BUILD && debug.log('Vercel AI orchestrion channel subscription failed.');\n }\n}\n\n/**\n * Bind one operation channel: `getSpan` opens a span from the message reconstructed out of the wrapped\n * call's first argument; `beforeSpanEnd` enriches it from the settled result (tokens, output messages,\n * finish reasons, …) before the helper ends the span.\n *\n * An operation whose `experimental_telemetry.isEnabled` is explicitly `false` is skipped entirely (no\n * span): the orchestrion channel fires regardless of that flag, whereas v7's native `ai:telemetry`\n * channel is simply not published in that case — so we reproduce v7's \"no telemetry → no span\".\n */\nfunction bindOperation(\n tracingChannel: VercelAiTracingChannelFactory,\n channelName: string,\n build: MessageBuilder,\n options: VercelAiChannelOptions,\n): void {\n const channel = tracingChannel<OrchestrionContext>(channelName);\n\n // Build the operation span from the wrapped call's first argument and track it (so a model call can\n // resolve it as its parent). `bindTracingChannelToSpan` calls this once at channel `start` and makes\n // the returned span the active async context for the operation's duration — that active span is what\n // `resolveModelCallParent` reads. It also sets `data._sentrySpan`, so we don't here.\n const buildOperationSpan = (data: TracingChannelPayloadWithSpan<OrchestrionContext>): Span | undefined => {\n const callOptions = isObjectLike(data.arguments[0]) ? data.arguments[0] : {};\n const telemetry = isObjectLike(callOptions.experimental_telemetry) ? callOptions.experimental_telemetry : {};\n // `isEnabled === false` means the user opted out — emit no span. But `isEnabled` is also `false` on\n // the telemetry object we swap in to suppress native spans, so don't mistake our own object for a\n // user opt-out (which would drop the span on a call whose options we already neutralized).\n if (telemetry.isEnabled === false && !suppressedTelemetry.has(telemetry)) {\n return undefined;\n }\n const message = build(callOptions, telemetry);\n // Stop `ai` from emitting its own native OTel spans for this call — we build the equivalent spans\n // from the channels, so the SDK's would be duplicates. Reads above have already captured everything\n // we need off `telemetry`.\n suppressNativeTelemetry(callOptions, telemetry);\n const span = createSpanFromMessage(message, options);\n if (span) {\n messages.set(data, message);\n operationSpans.add(span);\n // v6's `executeToolCall` span: tracked so the v5 tool-`execute` patch can recognize (and skip)\n // tool calls it runs, since that patch sees this span as its active parent (see `patchToolExecute`).\n if (message.type === 'executeTool') {\n toolCallSpans.add(span);\n }\n const callId = asString(message.event.callId);\n if (callId) {\n callIdBySpan.set(span, callId);\n }\n recordingBySpan.set(span, recording(telemetry));\n // v5 has no `executeToolCall` channel, so patch each tool's `execute` to emit the tool-call span.\n // Inert on v6 (guarded inside `patchToolExecute` when the parent is `executeToolCall`'s own span).\n if (isObjectLike(callOptions.tools)) {\n patchOperationTools(callOptions.tools, options);\n }\n // v4 has no `resolveLanguageModel` chokepoint — the passed `LanguageModelV1`\n // (`specificationVersion: 'v1'`) is called directly — so patch its `doGenerate`/`doStream`\n // here, at the operation start, instead. v5/v6 models are `'v2'` and are patched via\n // `resolveLanguageModel`, so restricting to `'v1'` keeps this strictly additive and avoids\n // double-patching. Embedding models are also `'v1'` but expose no `doGenerate`/`doStream`, so\n // the patch is a no-op for `embed`/`embedMany`.\n if (isObjectLike(callOptions.model) && callOptions.model.specificationVersion === 'v1') {\n patchModelMethods(callOptions.model as PatchableModel, options);\n }\n }\n return span;\n };\n\n bindTracingChannelToSpan(\n channel,\n (data: TracingChannelPayloadWithSpan<OrchestrionContext>) => buildOperationSpan(data),\n {\n beforeSpanEnd: (span, data) => {\n const message = messages.get(data);\n if (!message) {\n return;\n }\n // The helper's `error` handler already set the span status; only enrich from a successful result.\n if (!('error' in data)) {\n // v6's `executeToolCall` returns the tool result/error object directly, whereas the shared core\n // (matching v7) expects it nested under `output`; wrap it so tool-error detection works.\n message.result = message.type === 'executeTool' ? { output: data.result } : data.result;\n enrichSpanOnEnd(span, message, options);\n }\n // A `streamText` model call runs after this (synchronously-returning) operation's span has\n // already ended, so its `callId` entry must outlive the operation — it's cleared once the model\n // call settles (see `patchModelMethod`). Every other operation can clear here.\n if (message.type !== 'streamText') {\n clearOperationId(message);\n }\n messages.delete(data);\n },\n // `streamText` returns synchronously, so its operation span would otherwise end before the stream\n // drains — losing the aggregate usage/output. Defer the end and await the result's completion\n // promises (`totalUsage`/`text`/…, which resolve on drain), mirroring how v7's channel defers the\n // operation span on the SDK's total-usage promise.\n deferSpanEnd: ({ data, end }) => deferStreamTextOperationEnd(data, end),\n },\n );\n}\n\n/**\n * Keep a streamed `streamText` operation span open until the stream drains, then enrich it from the\n * `StreamTextResult`'s completion promises (usage/output/finish reason) and end it. Returns `false` for\n * anything that isn't a streamed `streamText` result, so the helper ends the span as usual.\n */\nfunction deferStreamTextOperationEnd(\n data: TracingChannelPayloadWithSpan<OrchestrionContext>,\n end: (error?: unknown) => void,\n): boolean {\n if (messages.get(data)?.type !== 'streamText' || 'error' in data || !isStreamingResult(data.result)) {\n return false;\n }\n\n const streamResult = data.result;\n void (async () => {\n try {\n const [usage, text, toolCalls, finishReason, response] = await Promise.all([\n streamResult.totalUsage ?? streamResult.usage,\n streamResult.text,\n streamResult.toolCalls,\n streamResult.finishReason,\n streamResult.response,\n ]);\n // Feed the resolved values back through the shared enrichment: `beforeSpanEnd` reads `data.result`.\n data.result = { usage, text, toolCalls, finishReason, response };\n end();\n } catch (error) {\n end(error);\n }\n })();\n\n return true;\n}\n\n/** A `StreamTextResult` exposes its aggregates as promises (`totalUsage`/`usage`); a settled result does not. */\nfunction isStreamingResult(result: unknown): result is Record<string, PromiseLike<unknown> | undefined> {\n return isObjectLike(result) && (isThenable(result.totalUsage) || isThenable(result.usage));\n}\n\nfunction isThenable(value: unknown): boolean {\n return isObjectLike(value) && typeof value.then === 'function';\n}\n\n/**\n * Neutralize `ai`'s native OpenTelemetry instrumentation for this call by pointing\n * `experimental_telemetry` at a copy with `isEnabled: false`. `ai`'s `getTracer` then returns its\n * internal no-op tracer, so it never creates (nor sets active) the duplicate `ai.*` spans we'd\n * otherwise have to drop.\n *\n * Only a call that explicitly enabled telemetry emits native spans — otherwise `ai` already uses its\n * no-op tracer, so there's nothing to suppress and we leave the user's options untouched. When\n * `isEnabled === true`, `telemetry` is `callOptions.experimental_telemetry` and `callOptions` is the\n * real first argument the SDK will read, so the reassignment takes effect for the wrapped call. We\n * replace rather than mutate in place, so a telemetry object the user shares across calls keeps its own\n * `isEnabled: true`; the replacement is tracked in `suppressedTelemetry` so our skip logic doesn't\n * later read it back as a user opt-out.\n */\nfunction suppressNativeTelemetry(callOptions: Record<string, unknown>, telemetry: Record<string, unknown>): void {\n if (telemetry.isEnabled !== true) {\n return;\n }\n const suppressed = { ...telemetry, isEnabled: false };\n suppressedTelemetry.add(suppressed);\n callOptions.experimental_telemetry = suppressed;\n}\n\n/**\n * `resolveLanguageModel` returns the model every call flows through. We don't span it — on `end` we\n * monkey-patch `doGenerate`/`doStream` on the returned model so each invocation produces a\n * `languageModelCall` span parented to the enclosing invoke_agent span.\n */\nfunction subscribeResolveLanguageModel(\n tracingChannel: VercelAiTracingChannelFactory,\n channelName: string,\n options: VercelAiChannelOptions,\n): void {\n tracingChannel<OrchestrionContext>(channelName).subscribe({\n end(rawCtx) {\n const ctx = rawCtx as OrchestrionContext;\n if (!isObjectLike(ctx.result)) {\n return;\n }\n // Patch the model's `doGenerate`/`doStream` once. The model call recovers its parent from the\n // active async context at call time (the operation span `bindTracingChannelToSpan` bound), which\n // propagates into the model call for `streamText` too, so there is nothing to capture on the model here.\n patchModelMethods(ctx.result as PatchableModel, options);\n },\n start() {\n /* no-op */\n },\n asyncStart() {\n /* no-op */\n },\n asyncEnd() {\n /* no-op */\n },\n error() {\n /* no-op */\n },\n });\n}\n\n/**\n * Pick the invoke_agent span a model call should hang under: the operation span that\n * `bindTracingChannelToSpan` planted as the active async context for the enclosing operation.\n *\n * Because we suppress `ai`'s native telemetry (so it never installs its own span as active), the active\n * span inside `doGenerate`/`doStream` is our operation span. The `operationSpans` gate makes this return\n * `undefined` when the active span isn't one of ours — e.g. telemetry was disabled for the call so we\n * opened no operation span and the active span is the user's enclosing span — so the model call is\n * skipped rather than mis-parented.\n *\n * This covers `generateText`/`embed` (whose model call is awaited inside the operation body) and\n * `streamText` alike — `ai` initiates the stream synchronously within the operation's bound context, so\n * the later `doStream` continuation restores the same active span even though the operation's span has\n * already ended. The per-operation binding also disambiguates concurrent calls that share one model\n * instance (a single mutable slot on the shared model could not — it would hold whichever operation\n * resolved the model last).\n */\nfunction resolveModelCallParent(): Span | undefined {\n const active = getActiveSpan();\n return active && operationSpans.has(active) ? active : undefined;\n}\n\n/**\n * Idempotently patch a resolved model's `doGenerate`/`doStream` so each invocation emits a\n * `languageModelCall` span. Shared by the v5/v6 `resolveLanguageModel` path and the v4 path (which\n * patches the passed model at the operation start, as v4 has no `resolveLanguageModel`).\n */\nfunction patchModelMethods(model: PatchableModel, options: VercelAiChannelOptions): void {\n if (model[PATCHED]) {\n return;\n }\n model[PATCHED] = true;\n patchModelMethod(model, 'doGenerate', options);\n patchModelMethod(model, 'doStream', options);\n}\n\nfunction patchModelMethod(\n model: PatchableModel,\n method: 'doGenerate' | 'doStream',\n options: VercelAiChannelOptions,\n): void {\n const original = model[method];\n if (typeof original !== 'function') {\n return;\n }\n model[method] = function (this: unknown, ...args: unknown[]): Promise<unknown> {\n const parent = resolveModelCallParent();\n // No enclosing operation span (e.g. telemetry disabled for the call) → don't open a model-call span.\n if (!parent) {\n return Promise.resolve(original.apply(this, args));\n }\n\n const callArgs = isObjectLike(args[0]) ? args[0] : {};\n // Carry the operation's `callId` so the shared core can name the span after it\n // (`ai.generateText.doGenerate` / `ai.streamText.doStream`).\n const callId = callIdBySpan.get(parent);\n const message: VercelAiChannelMessage = {\n type: 'languageModelCall',\n event: {\n callId,\n provider: model.provider,\n modelId: model.modelId,\n // v4 nests the tool list under `mode.tools` (the `LanguageModelV1` call shape); v5+ passes a\n // top-level `tools` array. Reading both keeps `available_tools` populated on the model-call span.\n tools: callArgs.tools ?? (isObjectLike(callArgs.mode) ? callArgs.mode.tools : undefined),\n messages: callArgs.prompt,\n // Inherit the enclosing operation's per-call recording flags so inputs/tools/outputs are recorded on\n // the model-call span whenever they are on the parent `invoke_agent` span.\n ...recordingBySpan.get(parent),\n },\n };\n const span = withActiveSpan(parent, () => createSpanFromMessage(message, options));\n // `languageModelCall` always opens a span; the guard just keeps the wrapper safe if that changes.\n if (!span) {\n return Promise.resolve(original.apply(this, args));\n }\n\n // `streamText` ends its operation span synchronously, so its `callId` entry was deliberately left in\n // place for this later model call; drop it now that we've used it.\n const clearStreamCallId = (): void => {\n if (method === 'doStream' && callId) {\n clearOperationCallId(callId);\n }\n };\n\n // Both the synchronous throw and the async rejection of the model call must end the span with an\n // error status (an `async` `doGenerate`/`doStream` that throws rejects rather than throwing here).\n const failSpan = (error: unknown): never => {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: error instanceof Error ? error.message : 'unknown_error' });\n span.end();\n clearStreamCallId();\n throw error;\n };\n\n try {\n const result = Promise.resolve(original.apply(this, args));\n return result.then(value => {\n // A streamed model call resolves to `{ stream, ... }` before the stream is consumed, so its\n // usage/finish/output only arrive as the stream drains. Tap it (same helper as the v7 path) and\n // defer ending this model-call span until then. The parent `invoke_agent` span is enriched\n // separately by `deferStreamTextOperationEnd`, which awaits the operation's own result promises.\n if (method === 'doStream' && isObjectLike(value) && isReadableStream(value.stream)) {\n value.stream = tapModelCallStream(\n value.stream,\n final => {\n message.result = { ...value, ...streamedResultToChannelResult(final) };\n enrichSpanOnEnd(span, message, options);\n span.end();\n clearStreamCallId();\n },\n error => {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: error instanceof Error ? error.message : 'unknown_error',\n });\n span.end();\n clearStreamCallId();\n },\n );\n\n return value;\n }\n // `doGenerate` (and any non-stream result) settles with the full result; end here, start/end\n // bracket the call to match the channel timing.\n message.result = value;\n enrichSpanOnEnd(span, message, options);\n span.end();\n clearStreamCallId();\n\n return value;\n }, failSpan);\n } catch (error) {\n return failSpan(error);\n }\n };\n}\n\n/**\n * Patch every executable tool on an operation's `tools` (a record keyed by tool name) so each\n * invocation emits a `gen_ai.execute_tool` span. v5 routes tool execution through the batch\n * `executeTools`, which binds `tool.execute` at call time — so replacing the `execute` property here\n * (before the call runs) is picked up. Tools without an `execute` (client-side tools) are skipped,\n * matching `executeTools`.\n */\nfunction patchOperationTools(tools: Record<string, unknown>, options: VercelAiChannelOptions): void {\n // This runs inside the channel `start` transform (no upstream try/catch), so a throw here would break\n // the user's `ai` call. Instrumentation must never do that — degrade to no tool span instead.\n try {\n for (const [toolName, tool] of Object.entries(tools)) {\n if (isObjectLike(tool)) {\n patchToolExecute(toolName, tool as PatchableTool, tools, options);\n }\n }\n } catch {\n DEBUG_BUILD && debug.log('Vercel AI orchestrion tool patching failed.');\n }\n}\n\nfunction patchToolExecute(\n toolName: string,\n tool: PatchableTool,\n tools: Record<string, unknown>,\n options: VercelAiChannelOptions,\n): void {\n const original = tool.execute;\n if (typeof original !== 'function' || tool[TOOL_PATCHED]) {\n return;\n }\n tool[TOOL_PATCHED] = true;\n tool.execute = function (this: unknown, input: unknown, ...rest: unknown[]): unknown {\n // Skip if there's no enclosing operation span (telemetry disabled for the call), or if that parent\n // is itself a tool-call span — the latter means v6's `executeToolCall` already opened a span for\n // this call and is running `execute` inside it, so spanning again here would double-count. On v5\n // the parent is the enclosing `invoke_agent` operation span, so we proceed.\n const parent = resolveModelCallParent();\n if (!parent || toolCallSpans.has(parent)) {\n return original.apply(this, [input, ...rest]);\n }\n\n // v5 passes `{ toolCallId, messages, abortSignal, ... }` as the second argument to `execute`.\n const callOptions = isObjectLike(rest[0]) ? rest[0] : {};\n const message: VercelAiChannelMessage = {\n type: 'executeTool',\n event: {\n callId: callIdBySpan.get(parent),\n toolCall: { toolName, toolCallId: asString(callOptions.toolCallId), input },\n // The `tools` record (keyed by name) lets the shared core backfill the tool's `description`.\n tools,\n // Inherit the enclosing operation's per-call recording flags so tool inputs/outputs are recorded\n // whenever they are on the parent `invoke_agent` span.\n ...recordingBySpan.get(parent),\n },\n };\n const span = withActiveSpan(parent, () => createSpanFromMessage(message, options));\n // `executeTool` always opens a span; the guard just keeps the wrapper safe if that changes.\n if (!span) {\n return original.apply(this, [input, ...rest]);\n }\n\n // v5's `executeTools` catches a thrown tool error and turns it into `tool-error` content rather\n // than rejecting, so the user never sees a rejection — we must capture it here. Rethrow so\n // `executeTools` still produces its `tool-error` result and the operation continues normally.\n const failSpan = (error: unknown): never => {\n captureToolError(span, message, error);\n span.end();\n throw error;\n };\n\n try {\n const result = Promise.resolve(original.apply(this, [input, ...rest]));\n return result.then(value => {\n // The shared core (matching v6/v7) expects the tool result nested under `output`.\n message.result = { output: value };\n enrichSpanOnEnd(span, message, options);\n span.end();\n return value;\n }, failSpan);\n } catch (error) {\n return failSpan(error);\n }\n };\n}\n\nfunction buildTextMessage(type: 'generateText' | 'streamText' | 'generateObject'): MessageBuilder {\n return (options, telemetry) => ({\n type,\n event: {\n callId: nextCallId(),\n operationId: `ai.${type}`,\n functionId: asString(telemetry.functionId),\n ...modelFields(options.model),\n maxRetries: options.maxRetries,\n // The `ai` SDK takes the system prompt as a top-level `system` option (all of v4/v5/v6); the\n // shared core lifts `event.instructions` into the system-instructions attribute, matching v7's\n // native channel (which carries it as a distinct field rather than inside the messages array).\n instructions: asString(options.system),\n // Normalize to the message-array shape the shared core (and v7's channel) expects: a bare string\n // `prompt` becomes a single user message, matching the SDK's own normalization.\n messages: normalizePromptMessages(options),\n ...recording(telemetry),\n },\n });\n}\n\nfunction normalizePromptMessages(options: Record<string, unknown>): unknown {\n if (Array.isArray(options.messages)) {\n return options.messages;\n }\n if (typeof options.prompt === 'string') {\n return [{ role: 'user', content: options.prompt }];\n }\n return options.messages ?? options.prompt;\n}\n\nfunction recording(telemetry: Record<string, unknown>): { recordInputs: unknown; recordOutputs: unknown } {\n // Match the OTel integration's per-call default: an explicit `recordInputs`/`recordOutputs` wins, but a\n // call that merely enables telemetry (`isEnabled: true`, no explicit flags) defaults both to `true`.\n // Unlike v7's native `ai:telemetry` channel, the orchestrion channels expose `isEnabled`, so — as noted\n // in `resolveRecording` — we CAN reproduce that default here (the native-channel subscriber cannot).\n const enabledDefault = telemetry.isEnabled === true ? true : undefined;\n return {\n recordInputs: telemetry.recordInputs ?? enabledDefault,\n recordOutputs: telemetry.recordOutputs ?? enabledDefault,\n };\n}\n\nfunction modelFields(model: unknown): { provider?: string; modelId?: string } {\n return { provider: modelField(model, 'provider'), modelId: modelField(model, 'modelId') };\n}\n\nfunction modelField(model: unknown, field: 'modelId' | 'provider'): string | undefined {\n return isObjectLike(model) ? asString(model[field]) : undefined;\n}\n"],"names":["CHANNELS","DEBUG_BUILD","debug","tracingChannel","isObjectLike","createSpanFromMessage","asString","bindTracingChannelToSpan","enrichSpanOnEnd","clearOperationId","getActiveSpan","withActiveSpan","clearOperationCallId","SPAN_STATUS_ERROR","isReadableStream","tapModelCallStream","streamedResultToChannelResult","captureToolError"],"mappings":"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"} |
| import { GRAPHQL_DOCUMENT, GRAPHQL_OPERATION_NAME, GRAPHQL_OPERATION_TYPE } from '@sentry/conventions/attributes'; | ||
| import { WEB_SERVER_GRAPHQL_SPAN_OP } from '@sentry/conventions/op'; | ||
| import { startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_ERROR } from '@sentry/core'; | ||
| import { bindTracingChannelToSpan } from '../tracing-channel.js'; | ||
| import { redactGraphqlDocument, hasResultErrors, getOperationSpanName, renameRootSpanWithOperation } from './utils.js'; | ||
| const GRAPHQL_DC_CHANNEL_PARSE = "graphql:parse"; | ||
| const GRAPHQL_DC_CHANNEL_VALIDATE = "graphql:validate"; | ||
| const GRAPHQL_DC_CHANNEL_EXECUTE = "graphql:execute"; | ||
| const GRAPHQL_DC_CHANNEL_SUBSCRIBE = "graphql:subscribe"; | ||
| const GRAPHQL_DC_CHANNEL_RESOLVE = "graphql:resolve"; | ||
| const ORIGIN = "auto.graphql.diagnostic_channel"; | ||
| const SPAN_NAME_PARSE = "graphql.parse"; | ||
| const SPAN_NAME_VALIDATE = "graphql.validate"; | ||
| const SPAN_NAME_EXECUTE = "graphql.execute"; | ||
| const SPAN_NAME_SUBSCRIBE = "graphql.subscribe"; | ||
| const SPAN_NAME_RESOLVE = "graphql.resolve"; | ||
| const GRAPHQL_FIELD_NAME = "graphql.field.name"; | ||
| const GRAPHQL_FIELD_PATH = "graphql.field.path"; | ||
| const GRAPHQL_FIELD_TYPE = "graphql.field.type"; | ||
| const GRAPHQL_PARENT_NAME = "graphql.parent.name"; | ||
| function subscribeGraphqlDiagnosticChannels(tracingChannel, options = {}) { | ||
| const ignoreResolveSpans = options.ignoreResolveSpans !== false; | ||
| const ignoreTrivialResolveSpans = options.ignoreTrivialResolveSpans !== false; | ||
| const useOperationNameForRootSpan = options.useOperationNameForRootSpan !== false; | ||
| setupParseChannel(tracingChannel); | ||
| setupValidateChannel(tracingChannel); | ||
| setupOperationChannel(tracingChannel, GRAPHQL_DC_CHANNEL_EXECUTE, SPAN_NAME_EXECUTE, useOperationNameForRootSpan); | ||
| setupOperationChannel(tracingChannel, GRAPHQL_DC_CHANNEL_SUBSCRIBE, SPAN_NAME_SUBSCRIBE, useOperationNameForRootSpan); | ||
| if (!ignoreResolveSpans) { | ||
| setupResolveChannel(tracingChannel, ignoreTrivialResolveSpans); | ||
| } | ||
| } | ||
| function setupParseChannel(tracingChannel) { | ||
| bindTracingChannelToSpan( | ||
| tracingChannel(GRAPHQL_DC_CHANNEL_PARSE), | ||
| () => startInactiveSpan({ | ||
| name: SPAN_NAME_PARSE, | ||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP | ||
| } | ||
| }) | ||
| ); | ||
| } | ||
| function setupValidateChannel(tracingChannel) { | ||
| bindTracingChannelToSpan( | ||
| tracingChannel(GRAPHQL_DC_CHANNEL_VALIDATE), | ||
| (data) => { | ||
| const document = redactGraphqlDocument(data.document); | ||
| return startInactiveSpan({ | ||
| name: SPAN_NAME_VALIDATE, | ||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP, | ||
| [GRAPHQL_DOCUMENT]: document | ||
| } | ||
| }); | ||
| }, | ||
| { | ||
| beforeSpanEnd: (span, data) => { | ||
| if (Array.isArray(data.result) && data.result.length > 0) { | ||
| span.setStatus({ code: SPAN_STATUS_ERROR, message: "invalid_argument" }); | ||
| } | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function setupOperationChannel(tracingChannel, channelName, fallbackName, useOperationNameForRootSpan) { | ||
| bindTracingChannelToSpan( | ||
| tracingChannel(channelName), | ||
| (data) => { | ||
| const document = redactGraphqlDocument(data.document); | ||
| const span = startInactiveSpan({ | ||
| name: getOperationSpanName(data.operationType, data.operationName, fallbackName), | ||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP, | ||
| [GRAPHQL_OPERATION_TYPE]: data.operationType, | ||
| [GRAPHQL_OPERATION_NAME]: data.operationName || void 0, | ||
| [GRAPHQL_DOCUMENT]: document | ||
| } | ||
| }); | ||
| if (useOperationNameForRootSpan && data.operationType) { | ||
| renameRootSpanWithOperation(span, data.operationType, data.operationName); | ||
| } | ||
| return span; | ||
| }, | ||
| { | ||
| beforeSpanEnd: (span, data) => { | ||
| if (hasResultErrors(data.result)) { | ||
| span.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" }); | ||
| } | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function setupResolveChannel(tracingChannel, ignoreTrivialResolveSpans) { | ||
| bindTracingChannelToSpan(tracingChannel(GRAPHQL_DC_CHANNEL_RESOLVE), (data) => { | ||
| if (ignoreTrivialResolveSpans && data.isDefaultResolver) { | ||
| return void 0; | ||
| } | ||
| return startInactiveSpan({ | ||
| name: `${SPAN_NAME_RESOLVE} ${data.fieldPath}`, | ||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP, | ||
| [GRAPHQL_FIELD_NAME]: data.fieldName, | ||
| [GRAPHQL_FIELD_PATH]: data.fieldPath, | ||
| [GRAPHQL_FIELD_TYPE]: data.fieldType, | ||
| [GRAPHQL_PARENT_NAME]: data.parentType | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| export { GRAPHQL_DC_CHANNEL_EXECUTE, GRAPHQL_DC_CHANNEL_PARSE, GRAPHQL_DC_CHANNEL_RESOLVE, GRAPHQL_DC_CHANNEL_SUBSCRIBE, GRAPHQL_DC_CHANNEL_VALIDATE, subscribeGraphqlDiagnosticChannels }; | ||
| //# sourceMappingURL=graphql-dc-subscriber.js.map |
| {"version":3,"file":"graphql-dc-subscriber.js","sources":["../../../src/graphql/graphql-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport { GRAPHQL_DOCUMENT, GRAPHQL_OPERATION_NAME, GRAPHQL_OPERATION_TYPE } from '@sentry/conventions/attributes';\nimport { WEB_SERVER_GRAPHQL_SPAN_OP } from '@sentry/conventions/op';\nimport {\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n} from '@sentry/core';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\nimport type { GraphqlDocumentNode } from './utils';\nimport { getOperationSpanName, hasResultErrors, redactGraphqlDocument, renameRootSpanWithOperation } from './utils';\n\n// Channel names published by graphql >= 17.0.0 (see graphql-js `src/diagnostics.ts`).\n// Hardcoded so the subscriber does not have to import graphql — the channels just\n// have to be subscribed to before the user's graphql code publishes.\nexport const GRAPHQL_DC_CHANNEL_PARSE = 'graphql:parse';\nexport const GRAPHQL_DC_CHANNEL_VALIDATE = 'graphql:validate';\nexport const GRAPHQL_DC_CHANNEL_EXECUTE = 'graphql:execute';\nexport const GRAPHQL_DC_CHANNEL_SUBSCRIBE = 'graphql:subscribe';\nexport const GRAPHQL_DC_CHANNEL_RESOLVE = 'graphql:resolve';\n\nconst ORIGIN = 'auto.graphql.diagnostic_channel';\n\nconst SPAN_NAME_PARSE = 'graphql.parse';\nconst SPAN_NAME_VALIDATE = 'graphql.validate';\nconst SPAN_NAME_EXECUTE = 'graphql.execute';\nconst SPAN_NAME_SUBSCRIBE = 'graphql.subscribe';\nconst SPAN_NAME_RESOLVE = 'graphql.resolve';\n\n// Field-level attributes for resolver spans. Not in `@sentry/conventions`; these match the keys the\n// vendored OTel instrumentation emits so there is no drift between the two paths.\nconst GRAPHQL_FIELD_NAME = 'graphql.field.name';\nconst GRAPHQL_FIELD_PATH = 'graphql.field.path';\nconst GRAPHQL_FIELD_TYPE = 'graphql.field.type';\nconst GRAPHQL_PARENT_NAME = 'graphql.parent.name';\n\n/** Context published on the sync-only `graphql:parse` channel. */\nexport interface GraphqlParseData {\n source: string | { body?: string };\n result?: GraphqlDocumentNode;\n error?: unknown;\n}\n\n/** Context published on the sync-only `graphql:validate` channel. */\nexport interface GraphqlValidateData {\n document: GraphqlDocumentNode;\n /** Validation errors returned by validation; an empty array means the document is valid. */\n result?: ReadonlyArray<unknown>;\n error?: unknown;\n}\n\n/**\n * Context published on the `graphql:execute` and `graphql:subscribe` channels.\n *\n * `result` carries an `ExecutionResult` (or, for subscriptions, an async generator); GraphQL errors\n * collected during execution surface on `result.errors` rather than as the channel's `error`\n * lifecycle event, which only fires on an abrupt throw.\n */\nexport interface GraphqlOperationData {\n document: GraphqlDocumentNode;\n operationName?: string;\n operationType?: string;\n result?: unknown;\n error?: unknown;\n}\n\n/**\n * Context published on the per-field `graphql:resolve` channel.\n *\n * A resolver throw or rejection publishes the `error` lifecycle event here; the same failure also\n * surfaces in the enclosing execution result.\n */\nexport interface GraphqlResolveData {\n fieldName: string;\n parentType: string;\n fieldType: string;\n fieldPath: string;\n /** Whether the field is handled by graphql's default property resolver (vs. a user resolver). */\n isDefaultResolver: boolean;\n alias?: string;\n args?: unknown;\n result?: unknown;\n error?: unknown;\n}\n\n/** Options controlling which graphql channels the subscriber emits spans for. */\nexport interface GraphqlDiagnosticChannelsOptions {\n /**\n * Do not create spans for resolvers. Resolver spans are per-field and can be very high volume.\n * Defaults to `true`.\n */\n ignoreResolveSpans?: boolean;\n\n /**\n * When resolver spans are enabled, do not create them for graphql's default property resolver\n * (fields without a user-defined resolver), which are rarely interesting. Defaults to `true`.\n */\n ignoreTrivialResolveSpans?: boolean;\n\n /**\n * Rename the enclosing root span to include the operation name(s), e.g.\n * `GET /graphql` -> `GET /graphql (query GetUser)`. Defaults to `true`.\n */\n useOperationNameForRootSpan?: boolean;\n}\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Node passes `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type GraphqlTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/**\n * Subscribe Sentry span handlers to graphql's diagnostics-channel events\n * (`graphql:parse`, `:validate`, `:execute`, `:subscribe`), published by graphql >= 17.0.0.\n *\n * On older graphql versions the channels are never published to, so the subscribers are inert —\n * there is no double-instrumentation against the vendored OTel patcher, which is gated to `< 17`.\n *\n * The per-field `graphql:resolve` channel is only subscribed when `ignoreResolveSpans` is `false`:\n * resolver spans are per-field and can be extremely high-volume, so they are off by default (matching\n * the legacy OTel path). When enabled, `ignoreTrivialResolveSpans` (default `true`) additionally skips\n * graphql's default property resolver.\n */\nexport function subscribeGraphqlDiagnosticChannels(\n tracingChannel: GraphqlTracingChannelFactory,\n options: GraphqlDiagnosticChannelsOptions = {},\n): void {\n const ignoreResolveSpans = options.ignoreResolveSpans !== false;\n const ignoreTrivialResolveSpans = options.ignoreTrivialResolveSpans !== false;\n const useOperationNameForRootSpan = options.useOperationNameForRootSpan !== false;\n\n setupParseChannel(tracingChannel);\n setupValidateChannel(tracingChannel);\n setupOperationChannel(tracingChannel, GRAPHQL_DC_CHANNEL_EXECUTE, SPAN_NAME_EXECUTE, useOperationNameForRootSpan);\n setupOperationChannel(tracingChannel, GRAPHQL_DC_CHANNEL_SUBSCRIBE, SPAN_NAME_SUBSCRIBE, useOperationNameForRootSpan);\n\n if (!ignoreResolveSpans) {\n setupResolveChannel(tracingChannel, ignoreTrivialResolveSpans);\n }\n}\n\nfunction setupParseChannel(tracingChannel: GraphqlTracingChannelFactory): void {\n bindTracingChannelToSpan(tracingChannel<GraphqlParseData>(GRAPHQL_DC_CHANNEL_PARSE), () =>\n startInactiveSpan({\n name: SPAN_NAME_PARSE,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n },\n }),\n );\n}\n\nfunction setupValidateChannel(tracingChannel: GraphqlTracingChannelFactory): void {\n bindTracingChannelToSpan(\n tracingChannel<GraphqlValidateData>(GRAPHQL_DC_CHANNEL_VALIDATE),\n data => {\n const document = redactGraphqlDocument(data.document);\n\n return startInactiveSpan({\n name: SPAN_NAME_VALIDATE,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n [GRAPHQL_DOCUMENT]: document,\n },\n });\n },\n {\n beforeSpanEnd: (span, data) => {\n // Validation completes normally even when it returns errors, so flag the span here.\n if (Array.isArray(data.result) && data.result.length > 0) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'invalid_argument' });\n }\n },\n },\n );\n}\n\nfunction setupOperationChannel(\n tracingChannel: GraphqlTracingChannelFactory,\n channelName: string,\n fallbackName: string,\n useOperationNameForRootSpan: boolean,\n): void {\n bindTracingChannelToSpan(\n tracingChannel<GraphqlOperationData>(channelName),\n data => {\n const document = redactGraphqlDocument(data.document);\n\n const span = startInactiveSpan({\n name: getOperationSpanName(data.operationType, data.operationName, fallbackName),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n [GRAPHQL_OPERATION_TYPE]: data.operationType,\n [GRAPHQL_OPERATION_NAME]: data.operationName || undefined,\n [GRAPHQL_DOCUMENT]: document,\n },\n });\n\n if (useOperationNameForRootSpan && data.operationType) {\n renameRootSpanWithOperation(span, data.operationType, data.operationName);\n }\n\n return span;\n },\n {\n beforeSpanEnd: (span, data) => {\n // GraphQL errors are returned on `result.errors`, not as a thrown error, so flag the span here.\n if (hasResultErrors(data.result)) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n }\n },\n },\n );\n}\n\nfunction setupResolveChannel(tracingChannel: GraphqlTracingChannelFactory, ignoreTrivialResolveSpans: boolean): void {\n bindTracingChannelToSpan(tracingChannel<GraphqlResolveData>(GRAPHQL_DC_CHANNEL_RESOLVE), data => {\n // Returning `undefined` opts this field out: no span is created and the active context is left\n // untouched, so the field still resolves under its parent span.\n if (ignoreTrivialResolveSpans && data.isDefaultResolver) {\n return undefined;\n }\n\n return startInactiveSpan({\n name: `${SPAN_NAME_RESOLVE} ${data.fieldPath}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n [GRAPHQL_FIELD_NAME]: data.fieldName,\n [GRAPHQL_FIELD_PATH]: data.fieldPath,\n [GRAPHQL_FIELD_TYPE]: data.fieldType,\n [GRAPHQL_PARENT_NAME]: data.parentType,\n },\n });\n });\n}\n"],"names":[],"mappings":"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"} |
| import { defineIntegration, waitForTracingChannelBinding } from '@sentry/core'; | ||
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { subscribeGraphqlDiagnosticChannels } from './graphql-dc-subscriber.js'; | ||
| const _graphqlIntegration = ((options = {}) => { | ||
| return { | ||
| name: "Graphql", | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| waitForTracingChannelBinding(() => { | ||
| subscribeGraphqlDiagnosticChannels(diagnosticsChannel.tracingChannel, options); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const graphqlIntegration = defineIntegration(_graphqlIntegration); | ||
| export { graphqlIntegration }; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../src/graphql/index.ts"],"sourcesContent":["import { defineIntegration, type IntegrationFn, waitForTracingChannelBinding } from '@sentry/core';\nimport * as dc from 'node:diagnostics_channel';\nimport { type GraphqlDiagnosticChannelsOptions, subscribeGraphqlDiagnosticChannels } from './graphql-dc-subscriber';\n\nconst _graphqlIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {\n return {\n name: 'Graphql' as const,\n setupOnce() {\n // Bail on Node <= 18.18.0, where `tracingChannel` does not exist.\n if (!dc.tracingChannel) {\n return;\n }\n\n // Subscribe to graphql's native tracing channels (graphql >= 17).\n // This is a no-op on versions that don't publish to the channels, so it is always safe to call.\n waitForTracingChannelBinding(() => {\n subscribeGraphqlDiagnosticChannels(dc.tracingChannel, options);\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the [graphql](https://www.npmjs.com/package/graphql) library via its native\n * `node:diagnostics_channel` tracing channels (graphql >= 17).\n *\n * On older graphql versions the channels are never published to, so this integration is inert and\n * the vendored OTel instrumentation (gated to `< 17`) handles instrumentation instead.\n */\nexport const graphqlIntegration = defineIntegration(_graphqlIntegration);\n"],"names":["dc"],"mappings":";;;;AAIA,MAAM,mBAAA,IAAuB,CAAC,OAAA,GAA4C,EAAC,KAAM;AAC/E,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,SAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAACA,mBAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAIA,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,kCAAA,CAAmCA,kBAAA,CAAG,gBAAgB,OAAO,CAAA;AAAA,MAC/D,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AASO,MAAM,kBAAA,GAAqB,kBAAkB,mBAAmB;;;;"} |
| import { SENTRY_GRAPHQL_OPERATION } from '@sentry/conventions/attributes'; | ||
| import { isObjectLike, getRootSpan, spanToJSON } from '@sentry/core'; | ||
| const ORIGINAL_DESCRIPTION_ATTRIBUTE = "original-description"; | ||
| const REDACTED_LITERAL_KINDS = /* @__PURE__ */ new Set(["Int", "Float", "String", "BlockString"]); | ||
| function renameRootSpanWithOperation(span, operationType, operationName) { | ||
| const rootSpan = getRootSpan(span); | ||
| if (rootSpan === span) { | ||
| return; | ||
| } | ||
| const rootSpanJson = spanToJSON(rootSpan); | ||
| const newOperation = operationName ? `${operationType} ${operationName}` : operationType; | ||
| const existingOperations = rootSpanJson.data[SENTRY_GRAPHQL_OPERATION]; | ||
| let operations; | ||
| if (Array.isArray(existingOperations)) { | ||
| operations = [...existingOperations, newOperation]; | ||
| } else if (typeof existingOperations === "string") { | ||
| operations = [existingOperations, newOperation]; | ||
| } else { | ||
| operations = newOperation; | ||
| } | ||
| rootSpan.setAttribute(SENTRY_GRAPHQL_OPERATION, operations); | ||
| const originalDescription = rootSpanJson.data[ORIGINAL_DESCRIPTION_ATTRIBUTE] ?? rootSpanJson.description; | ||
| if (!rootSpanJson.data[ORIGINAL_DESCRIPTION_ATTRIBUTE]) { | ||
| rootSpan.setAttribute(ORIGINAL_DESCRIPTION_ATTRIBUTE, originalDescription); | ||
| } | ||
| rootSpan.updateName(`${originalDescription} (${getGraphqlOperationNamesFromAttribute(operations)})`); | ||
| } | ||
| function getGraphqlOperationNamesFromAttribute(attr) { | ||
| if (Array.isArray(attr)) { | ||
| const sorted = attr.slice().sort(); | ||
| if (sorted.length <= 5) { | ||
| return sorted.join(", "); | ||
| } | ||
| return `${sorted.slice(0, 5).join(", ")}, +${sorted.length - 5}`; | ||
| } | ||
| return attr; | ||
| } | ||
| function getOperationSpanName(operationType, operationName, fallbackName) { | ||
| if (operationType && operationName) { | ||
| return `${operationType} ${operationName}`; | ||
| } | ||
| if (operationType) { | ||
| return operationType; | ||
| } | ||
| return fallbackName; | ||
| } | ||
| function hasResultErrors(result) { | ||
| if (isObjectLike(result) && "errors" in result) { | ||
| const errors = result.errors; | ||
| return Array.isArray(errors) && errors.length > 0; | ||
| } | ||
| return false; | ||
| } | ||
| function redactGraphqlDocument(document) { | ||
| const loc = document?.loc; | ||
| const body = loc?.source?.body; | ||
| if (typeof body !== "string" || !loc?.startToken) { | ||
| return void 0; | ||
| } | ||
| try { | ||
| const ranges = []; | ||
| for (let token = loc.startToken; token; token = token.next) { | ||
| if (REDACTED_LITERAL_KINDS.has(token.kind)) { | ||
| ranges.push({ start: token.start, end: token.end, kind: token.kind }); | ||
| } | ||
| } | ||
| let out = body; | ||
| for (let i = ranges.length - 1; i >= 0; i--) { | ||
| const { start, end, kind } = ranges[i]; | ||
| const replacement = kind === "String" || kind === "BlockString" ? '"*"' : "*"; | ||
| out = out.slice(0, start) + replacement + out.slice(end); | ||
| } | ||
| return out; | ||
| } catch { | ||
| return void 0; | ||
| } | ||
| } | ||
| export { getOperationSpanName, hasResultErrors, redactGraphqlDocument, renameRootSpanWithOperation }; | ||
| //# sourceMappingURL=utils.js.map |
| {"version":3,"file":"utils.js","sources":["../../../src/graphql/utils.ts"],"sourcesContent":["import { SENTRY_GRAPHQL_OPERATION } from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport { isObjectLike, getRootSpan, spanToJSON } from '@sentry/core';\n\n// Same key the OTel path uses, so renames stay consistent across both.\nconst ORIGINAL_DESCRIPTION_ATTRIBUTE = 'original-description';\n\n// graphql-js token kinds whose values may carry user data (literal arguments). We\n// replace them in the serialized document so raw inline values can never reach\n// `graphql.document`. Mirrors the legacy OTel instrumentation's redaction set.\nconst REDACTED_LITERAL_KINDS = new Set(['Int', 'Float', 'String', 'BlockString']);\n\n/** Minimal shape of a graphql-js lexer token, enough to locate literal spans for redaction. */\ninterface GraphqlToken {\n kind: string;\n start: number;\n end: number;\n next?: GraphqlToken | null;\n}\n\n/** Minimal shape of a parsed graphql-js `DocumentNode`, enough to read its source and tokens. */\nexport interface GraphqlDocumentNode {\n loc?: {\n startToken?: GraphqlToken;\n source?: { body?: string };\n };\n}\n\n/**\n * Rename the enclosing root span to include the operation name(s), e.g. `GET /graphql (query GetUser)`.\n * Mirrors the legacy OTel `useOperationNameForRootSpan` behavior; `parseSpanDescription` reads the same\n * `sentry.graphql.operation` attribute on the OTel export path.\n */\nexport function renameRootSpanWithOperation(span: Span, operationType: string, operationName?: string): void {\n const rootSpan = getRootSpan(span);\n // Nothing to rename if the operation span is itself the root (graphql ran outside any span).\n if (rootSpan === span) {\n return;\n }\n\n const rootSpanJson = spanToJSON(rootSpan);\n\n const newOperation = operationName ? `${operationType} ${operationName}` : operationType;\n\n // A single operation is stored as a string, multiple as an array.\n const existingOperations = rootSpanJson.data[SENTRY_GRAPHQL_OPERATION];\n let operations: string | string[];\n if (Array.isArray(existingOperations)) {\n operations = [...(existingOperations as string[]), newOperation];\n } else if (typeof existingOperations === 'string') {\n operations = [existingOperations, newOperation];\n } else {\n operations = newOperation;\n }\n rootSpan.setAttribute(SENTRY_GRAPHQL_OPERATION, operations);\n\n // Keep the pre-rename name so repeated renames don't compound.\n const originalDescription =\n (rootSpanJson.data[ORIGINAL_DESCRIPTION_ATTRIBUTE] as string | undefined) ?? rootSpanJson.description;\n if (!rootSpanJson.data[ORIGINAL_DESCRIPTION_ATTRIBUTE]) {\n rootSpan.setAttribute(ORIGINAL_DESCRIPTION_ATTRIBUTE, originalDescription);\n }\n\n rootSpan.updateName(`${originalDescription} (${getGraphqlOperationNamesFromAttribute(operations)})`);\n}\n\n/** Format the accumulated operations for the root span name: up to 5 sorted names, then `+N`. */\nfunction getGraphqlOperationNamesFromAttribute(attr: string | string[]): string {\n if (Array.isArray(attr)) {\n // oxlint-disable-next-line typescript/require-array-sort-compare\n const sorted = attr.slice().sort();\n if (sorted.length <= 5) {\n return sorted.join(', ');\n }\n\n return `${sorted.slice(0, 5).join(', ')}, +${sorted.length - 5}`;\n }\n\n return attr;\n}\n\n/**\n * Span name follows the GraphQL semantic conventions: `<operation.type> <operation.name>` when both\n * are available, `<operation.type>` when only the type is, otherwise a static fallback.\n */\nexport function getOperationSpanName(\n operationType: string | undefined,\n operationName: string | undefined,\n fallbackName: string,\n): string {\n if (operationType && operationName) {\n return `${operationType} ${operationName}`;\n }\n if (operationType) {\n return operationType;\n }\n\n return fallbackName;\n}\n\n/** Whether a graphql execution result carries GraphQL errors (returned on `result.errors`). */\nexport function hasResultErrors(result: unknown): boolean {\n if (isObjectLike(result) && 'errors' in result) {\n const errors = (result as { errors?: unknown }).errors;\n\n return Array.isArray(errors) && errors.length > 0;\n }\n\n return false;\n}\n\n/**\n * Serialize a parsed document into `graphql.document` while redacting every literal argument value:\n * the original source text is preserved verbatim except that string/number literal spans are\n * replaced (`\"foo\"` -> `\"*\"`, `42` -> `*`). graphql does not sanitize its channel payload, so this\n * prevents raw inline values (potential PII) from leaving the process. Variable values are never\n * included. Returns `undefined` (rather than throwing) on anything it cannot serialize.\n */\nexport function redactGraphqlDocument(document: GraphqlDocumentNode | undefined): string | undefined {\n const loc = document?.loc;\n const body = loc?.source?.body;\n if (typeof body !== 'string' || !loc?.startToken) {\n return undefined;\n }\n\n try {\n // Collect literal token spans, then splice them out back-to-front so earlier offsets stay valid.\n const ranges: Array<{ start: number; end: number; kind: string }> = [];\n for (let token: GraphqlToken | null | undefined = loc.startToken; token; token = token.next) {\n if (REDACTED_LITERAL_KINDS.has(token.kind)) {\n ranges.push({ start: token.start, end: token.end, kind: token.kind });\n }\n }\n\n let out = body;\n // Reverse index loop (not `for...of`) so we splice back-to-front; `i` is bounded by the loop, so\n // `ranges[i]` is always present and the `!` just satisfies `noUncheckedIndexedAccess`.\n for (let i = ranges.length - 1; i >= 0; i--) {\n const { start, end, kind } = ranges[i]!;\n const replacement = kind === 'String' || kind === 'BlockString' ? '\"*\"' : '*';\n out = out.slice(0, start) + replacement + out.slice(end);\n }\n\n return out;\n } catch {\n return undefined;\n }\n}\n"],"names":[],"mappings":"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"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { defineIntegration, debug, waitForTracingChannelBinding, continueTrace, timestampInSeconds, startInactiveSpan, SPAN_KIND, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getTraceData, SPAN_STATUS_ERROR } from '@sentry/core'; | ||
| import { MESSAGING_SYSTEM, NETWORK_PROTOCOL_NAME, SERVER_ADDRESS, SERVER_PORT, NET_PEER_NAME, NET_PEER_PORT, MESSAGING_MESSAGE_ID, MESSAGING_OPERATION_TYPE, MESSAGING_DESTINATION_NAME, NETWORK_PROTOCOL_VERSION, URL_FULL } from '@sentry/conventions/attributes'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
| import { CHANNELS } from '../../orchestrion/channels.js'; | ||
| import { bindTracingChannelToSpan } from '../../tracing-channel.js'; | ||
| const INTEGRATION_NAME = "Amqplib"; | ||
| const PUBLISHER_ORIGIN = "auto.amqplib.orchestrion.publisher"; | ||
| const CONSUMER_ORIGIN = "auto.amqplib.orchestrion.consumer"; | ||
| const ATTR_MESSAGING_OPERATION = "messaging.operation"; | ||
| const ATTR_MESSAGING_DESTINATION = "messaging.destination"; | ||
| const ATTR_MESSAGING_DESTINATION_KIND = "messaging.destination_kind"; | ||
| const ATTR_MESSAGING_RABBITMQ_ROUTING_KEY = "messaging.rabbitmq.routing_key"; | ||
| const ATTR_MESSAGING_PROTOCOL = "messaging.protocol"; | ||
| const ATTR_MESSAGING_PROTOCOL_VERSION_LEGACY = "messaging.protocol_version"; | ||
| const ATTR_MESSAGING_URL = "messaging.url"; | ||
| const ATTR_MESSAGING_MESSAGE_ID = "messaging.message_id"; | ||
| const ATTR_MESSAGING_CONVERSATION_ID_LEGACY = "messaging.conversation_id"; | ||
| const ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY = "messaging.rabbitmq.destination.routing_key"; | ||
| const ATTR_MESSAGING_CONVERSATION_ID = "messaging.message.conversation_id"; | ||
| const MESSAGING_DESTINATION_KIND_VALUE_TOPIC = "topic"; | ||
| const MESSAGING_OPERATION_VALUE_PROCESS = "process"; | ||
| const MESSAGING_OPERATION_VALUE_SEND = "send"; | ||
| const CONSUME_TIMEOUT_MS = 1e3 * 60; | ||
| const END_OP = { | ||
| Ack: "ack", | ||
| AckAll: "ackAll", | ||
| Reject: "reject", | ||
| Nack: "nack", | ||
| NackAll: "nackAll", | ||
| ChannelClosed: "channel closed", | ||
| ChannelError: "channel error", | ||
| InstrumentationTimeout: "instrumentation timeout" | ||
| }; | ||
| const MESSAGE_STORED_SPAN = /* @__PURE__ */ Symbol("sentry.amqplib.message.stored-span"); | ||
| const CHANNEL_SPANS_NOT_ENDED = /* @__PURE__ */ Symbol("sentry.amqplib.channel.spans-not-ended"); | ||
| const CHANNEL_CONSUME_TIMEOUT_TIMER = /* @__PURE__ */ Symbol("sentry.amqplib.channel.consume-timeout-timer"); | ||
| const CHANNEL_CONSUMER_INFO = /* @__PURE__ */ Symbol("sentry.amqplib.channel.consumer-info"); | ||
| const CHANNEL_IS_CONFIRM_PUBLISHING = /* @__PURE__ */ Symbol("sentry.amqplib.channel.is-confirm-publishing"); | ||
| const CONNECTION_ATTRIBUTES = /* @__PURE__ */ Symbol("sentry.amqplib.connection.attributes"); | ||
| const NOOP = () => { | ||
| }; | ||
| let subscribed = false; | ||
| const _amqplibChannelIntegration = (() => { | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel || subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| DEBUG_BUILD && debug.log("[orchestrion:amqplib] subscribing to amqplib tracing channels"); | ||
| waitForTracingChannelBinding(() => { | ||
| subscribeConnect(); | ||
| subscribePublish(); | ||
| subscribeConfirmPublish(); | ||
| subscribeConsume(); | ||
| subscribeDispatch(); | ||
| subscribeSettle(); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| function subscribePublish() { | ||
| bindTracingChannelToSpan(diagnosticsChannel.tracingChannel(CHANNELS.AMQPLIB_PUBLISH), (data) => { | ||
| if (data.self?.[CHANNEL_IS_CONFIRM_PUBLISHING]) { | ||
| return void 0; | ||
| } | ||
| return startPublishSpan(data); | ||
| }); | ||
| } | ||
| function subscribeConfirmPublish() { | ||
| const channel = diagnosticsChannel.tracingChannel(CHANNELS.AMQPLIB_CONFIRM_PUBLISH); | ||
| bindTracingChannelToSpan(channel, (data) => { | ||
| if (data.self) { | ||
| data.self[CHANNEL_IS_CONFIRM_PUBLISHING] = true; | ||
| } | ||
| return startPublishSpan(data); | ||
| }); | ||
| channel.end.subscribe((message) => { | ||
| const self = message.self; | ||
| if (self) { | ||
| self[CHANNEL_IS_CONFIRM_PUBLISHING] = false; | ||
| } | ||
| }); | ||
| } | ||
| function subscribeConsume() { | ||
| const channel = diagnosticsChannel.tracingChannel(CHANNELS.AMQPLIB_CONSUME); | ||
| channel.start.subscribe(NOOP); | ||
| channel.asyncEnd.subscribe((message) => { | ||
| const data = message; | ||
| const consumerChannel = data.self; | ||
| const result = data.result; | ||
| const consumerTag = result?.consumerTag; | ||
| if (!consumerChannel || !consumerTag) { | ||
| return; | ||
| } | ||
| ensureChannelState(consumerChannel); | ||
| const queueArg = data.arguments[0]; | ||
| const queue = typeof queueArg === "string" ? queueArg : "<unknown>"; | ||
| const options = data.arguments[2]; | ||
| consumerChannel[CHANNEL_CONSUMER_INFO]?.set(consumerTag, { noAck: !!options?.noAck, queue }); | ||
| }); | ||
| } | ||
| function subscribeDispatch() { | ||
| bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(CHANNELS.AMQPLIB_DISPATCH), | ||
| (data) => { | ||
| const channel = data.self; | ||
| const fields = data.arguments[0]; | ||
| const msg = data.arguments[1]; | ||
| if (!channel || !msg) { | ||
| return void 0; | ||
| } | ||
| ensureChannelState(channel); | ||
| const info = fields?.consumerTag ? channel[CHANNEL_CONSUMER_INFO]?.get(fields.consumerTag) : void 0; | ||
| const queue = info?.queue ?? msg.fields?.routingKey ?? "<unknown>"; | ||
| const noAck = info?.noAck ?? false; | ||
| const headers = msg.properties?.headers; | ||
| const sentryTrace = getHeaderAsString(headers, "sentry-trace"); | ||
| const baggage = getHeaderAsString(headers, "baggage"); | ||
| const span = continueTrace({ sentryTrace, baggage }, () => startConsumeSpan(queue, msg, channel)); | ||
| if (!noAck) { | ||
| channel[CHANNEL_SPANS_NOT_ENDED]?.push({ msg, timeOfConsume: timestampInSeconds() }); | ||
| msg[MESSAGE_STORED_SPAN] = span; | ||
| } | ||
| data._sentryNoAck = noAck; | ||
| return span; | ||
| }, | ||
| { | ||
| // Manual-ack consumers: the span outlives the dispatch call and is ended by ack/nack/reject | ||
| // (or timeout/close), so take ownership and don't let the helper end it here. noAck consumers | ||
| // have no settle call, so let the helper end the span when dispatch returns. | ||
| deferSpanEnd({ data }) { | ||
| return !data._sentryNoAck; | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function subscribeSettle() { | ||
| diagnosticsChannel.tracingChannel(CHANNELS.AMQPLIB_ACK).start.subscribe((message) => handleAck(message, false, END_OP.Ack)); | ||
| diagnosticsChannel.tracingChannel(CHANNELS.AMQPLIB_NACK).start.subscribe((message) => handleAck(message, true, END_OP.Nack)); | ||
| diagnosticsChannel.tracingChannel(CHANNELS.AMQPLIB_REJECT).start.subscribe((message) => handleAck(message, true, END_OP.Reject)); | ||
| diagnosticsChannel.tracingChannel(CHANNELS.AMQPLIB_ACK_ALL).start.subscribe((message) => { | ||
| const data = message; | ||
| if (data.self) { | ||
| endAllSpansOnChannel(data.self, false, END_OP.AckAll, void 0); | ||
| } | ||
| }); | ||
| diagnosticsChannel.tracingChannel(CHANNELS.AMQPLIB_NACK_ALL).start.subscribe((message) => { | ||
| const data = message; | ||
| if (data.self) { | ||
| endAllSpansOnChannel(data.self, true, END_OP.NackAll, data.arguments[0]); | ||
| } | ||
| }); | ||
| } | ||
| function subscribeConnect() { | ||
| const channel = diagnosticsChannel.tracingChannel(CHANNELS.AMQPLIB_CONNECT); | ||
| channel.start.subscribe(NOOP); | ||
| channel.asyncEnd.subscribe((message) => { | ||
| const data = message; | ||
| const conn = data.result; | ||
| if (!conn || typeof conn !== "object") { | ||
| return; | ||
| } | ||
| conn[CONNECTION_ATTRIBUTES] = { | ||
| ...getConnectionAttributesFromUrl(data.arguments?.[0]), | ||
| ...getConnectionAttributesFromServer(conn) | ||
| }; | ||
| }); | ||
| } | ||
| function handleAck(data, isRejected, endOperation) { | ||
| const channel = data.self; | ||
| if (!channel) { | ||
| return; | ||
| } | ||
| const message = data.arguments[0]; | ||
| if (!message) { | ||
| return; | ||
| } | ||
| const allUpToOrRequeue = data.arguments[1]; | ||
| const requeue = data.arguments[2]; | ||
| const requeueResolved = endOperation === END_OP.Reject ? allUpToOrRequeue : requeue; | ||
| const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? []; | ||
| const msgIndex = spansNotEnded.findIndex((msgDetails) => msgDetails.msg === message); | ||
| if (msgIndex < 0) { | ||
| endConsumerSpan(message, isRejected, endOperation, requeueResolved); | ||
| } else if (endOperation !== END_OP.Reject && allUpToOrRequeue) { | ||
| for (let i = 0; i <= msgIndex; i++) { | ||
| endConsumerSpan(spansNotEnded[i].msg, isRejected, endOperation, requeueResolved); | ||
| } | ||
| spansNotEnded.splice(0, msgIndex + 1); | ||
| } else { | ||
| endConsumerSpan(message, isRejected, endOperation, requeueResolved); | ||
| spansNotEnded.splice(msgIndex, 1); | ||
| } | ||
| } | ||
| function ensureChannelState(channel) { | ||
| if (Object.prototype.hasOwnProperty.call(channel, CHANNEL_SPANS_NOT_ENDED)) { | ||
| return; | ||
| } | ||
| channel[CHANNEL_SPANS_NOT_ENDED] = []; | ||
| channel[CHANNEL_CONSUMER_INFO] = /* @__PURE__ */ new Map(); | ||
| const timer = setInterval(() => checkConsumeTimeoutOnChannel(channel), CONSUME_TIMEOUT_MS); | ||
| timer.unref?.(); | ||
| channel[CHANNEL_CONSUME_TIMEOUT_TIMER] = timer; | ||
| if (typeof channel.on === "function") { | ||
| channel.on("close", () => { | ||
| endAllSpansOnChannel(channel, true, END_OP.ChannelClosed, void 0); | ||
| clearConsumeTimeoutTimer(channel); | ||
| }); | ||
| channel.on("error", () => { | ||
| endAllSpansOnChannel(channel, true, END_OP.ChannelError, void 0); | ||
| clearConsumeTimeoutTimer(channel); | ||
| }); | ||
| } | ||
| } | ||
| function clearConsumeTimeoutTimer(channel) { | ||
| const activeTimer = channel[CHANNEL_CONSUME_TIMEOUT_TIMER]; | ||
| if (activeTimer) { | ||
| clearInterval(activeTimer); | ||
| channel[CHANNEL_CONSUME_TIMEOUT_TIMER] = void 0; | ||
| } | ||
| } | ||
| function checkConsumeTimeoutOnChannel(channel) { | ||
| const currentTime = timestampInSeconds(); | ||
| const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? []; | ||
| let i; | ||
| for (i = 0; i < spansNotEnded.length; i++) { | ||
| const currMessage = spansNotEnded[i]; | ||
| const timeFromConsumeMs = (currentTime - currMessage.timeOfConsume) * 1e3; | ||
| if (timeFromConsumeMs < CONSUME_TIMEOUT_MS) { | ||
| break; | ||
| } | ||
| endConsumerSpan(currMessage.msg, null, END_OP.InstrumentationTimeout, true); | ||
| } | ||
| spansNotEnded.splice(0, i); | ||
| } | ||
| function endAllSpansOnChannel(channel, isRejected, operation, requeue) { | ||
| const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? []; | ||
| spansNotEnded.forEach((msgDetails) => { | ||
| endConsumerSpan(msgDetails.msg, isRejected, operation, requeue); | ||
| }); | ||
| channel[CHANNEL_SPANS_NOT_ENDED] = []; | ||
| } | ||
| function endConsumerSpan(message, isRejected, operation, requeue) { | ||
| const storedSpan = message[MESSAGE_STORED_SPAN]; | ||
| if (!storedSpan) { | ||
| return; | ||
| } | ||
| if (isRejected !== false) { | ||
| storedSpan.setStatus({ | ||
| code: SPAN_STATUS_ERROR, | ||
| message: operation !== END_OP.ChannelClosed && operation !== END_OP.ChannelError ? `${operation} called on message${requeue === true ? " with requeue" : requeue === false ? " without requeue" : ""}` : operation | ||
| }); | ||
| } | ||
| storedSpan.end(); | ||
| message[MESSAGE_STORED_SPAN] = void 0; | ||
| } | ||
| function startPublishSpan(data) { | ||
| const exchangeArg = data.arguments[0]; | ||
| const routingKeyArg = data.arguments[1]; | ||
| const exchange = typeof exchangeArg === "string" ? exchangeArg : ""; | ||
| const routingKey = typeof routingKeyArg === "string" ? routingKeyArg : ""; | ||
| let options = data.arguments[3]; | ||
| const span = startInactiveSpan({ | ||
| name: `publish ${normalizeExchange(exchange)}`, | ||
| op: "message", | ||
| kind: SPAN_KIND.PRODUCER, | ||
| attributes: { | ||
| ...getStoredConnectionAttributes(data.self), | ||
| [ATTR_MESSAGING_DESTINATION]: exchange, | ||
| // TODO(v11) remove this attribute | ||
| [MESSAGING_DESTINATION_NAME]: exchange, | ||
| [ATTR_MESSAGING_DESTINATION_KIND]: MESSAGING_DESTINATION_KIND_VALUE_TOPIC, | ||
| // TODO(v11) remove this attribute | ||
| [ATTR_MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey, | ||
| // TODO(v11) remove this attribute | ||
| [ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY]: routingKey, | ||
| [MESSAGING_OPERATION_TYPE]: MESSAGING_OPERATION_VALUE_SEND, | ||
| [ATTR_MESSAGING_MESSAGE_ID]: options?.messageId, | ||
| // todo(v11) remove this attribute | ||
| [MESSAGING_MESSAGE_ID]: options?.messageId, | ||
| [ATTR_MESSAGING_CONVERSATION_ID_LEGACY]: options?.correlationId, | ||
| // todo(v11) remove this attribute | ||
| [ATTR_MESSAGING_CONVERSATION_ID]: options?.correlationId, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: PUBLISHER_ORIGIN | ||
| } | ||
| }); | ||
| if (!options || typeof options !== "object") { | ||
| options = {}; | ||
| data.arguments[3] = options; | ||
| } | ||
| const headers = options.headers && typeof options.headers === "object" ? options.headers : options.headers = {}; | ||
| const traceData = getTraceData({ span }); | ||
| if (traceData["sentry-trace"]) { | ||
| headers["sentry-trace"] = traceData["sentry-trace"]; | ||
| } | ||
| if (traceData.baggage) { | ||
| headers["baggage"] = traceData.baggage; | ||
| } | ||
| return span; | ||
| } | ||
| function startConsumeSpan(queue, msg, channel) { | ||
| return startInactiveSpan({ | ||
| name: `${queue} process`, | ||
| op: "message", | ||
| kind: SPAN_KIND.CONSUMER, | ||
| attributes: { | ||
| ...getStoredConnectionAttributes(channel), | ||
| [ATTR_MESSAGING_DESTINATION]: msg.fields?.exchange, | ||
| // TODO(v11) remove this attribute | ||
| [MESSAGING_DESTINATION_NAME]: msg.fields?.exchange, | ||
| [ATTR_MESSAGING_DESTINATION_KIND]: MESSAGING_DESTINATION_KIND_VALUE_TOPIC, | ||
| // TODO(v11) remove this attribute | ||
| [ATTR_MESSAGING_RABBITMQ_ROUTING_KEY]: msg.fields?.routingKey, | ||
| // TODO(v11) remove this attribute | ||
| [ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY]: msg.fields?.routingKey, | ||
| [ATTR_MESSAGING_OPERATION]: MESSAGING_OPERATION_VALUE_PROCESS, | ||
| // TODO(v11) remove this attribute | ||
| [MESSAGING_OPERATION_TYPE]: MESSAGING_OPERATION_VALUE_PROCESS, | ||
| [ATTR_MESSAGING_MESSAGE_ID]: msg.properties?.messageId, | ||
| // todo(v11) remove this attribute | ||
| [MESSAGING_MESSAGE_ID]: msg.properties?.messageId, | ||
| [ATTR_MESSAGING_CONVERSATION_ID_LEGACY]: msg.properties?.correlationId, | ||
| // todo(v11) remove this attribute | ||
| [ATTR_MESSAGING_CONVERSATION_ID]: msg.properties?.correlationId, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: CONSUMER_ORIGIN | ||
| } | ||
| }); | ||
| } | ||
| function getStoredConnectionAttributes(channel) { | ||
| const connection = channel?.connection; | ||
| const stored = connection?.[CONNECTION_ATTRIBUTES]; | ||
| if (stored) { | ||
| return stored; | ||
| } | ||
| const product = connection?.serverProperties?.product ?? connection?.connection?.serverProperties?.product; | ||
| if (typeof product === "string" && product) { | ||
| return { [MESSAGING_SYSTEM]: product.toLowerCase() }; | ||
| } | ||
| return {}; | ||
| } | ||
| function getConnectionAttributesFromServer(conn) { | ||
| const product = conn.serverProperties?.product ?? conn.connection?.serverProperties?.product; | ||
| if (typeof product === "string" && product) { | ||
| return { [MESSAGING_SYSTEM]: product.toLowerCase() }; | ||
| } | ||
| return {}; | ||
| } | ||
| function getConnectionAttributesFromUrl(url) { | ||
| const attributes = { | ||
| // The only protocol supported by the instrumented library. | ||
| [ATTR_MESSAGING_PROTOCOL_VERSION_LEGACY]: "0.9.1", | ||
| // TODO(v11): remove this attribute | ||
| [NETWORK_PROTOCOL_VERSION]: "0.9.1" | ||
| }; | ||
| const resolvedUrl = url || "amqp://localhost"; | ||
| if (typeof resolvedUrl === "object") { | ||
| const connectOptions = resolvedUrl; | ||
| const protocol = getProtocol(connectOptions.protocol); | ||
| const hostname = getHostname(connectOptions.hostname); | ||
| const port = getPort(connectOptions.port, protocol); | ||
| attributes[ATTR_MESSAGING_PROTOCOL] = protocol; | ||
| attributes[NETWORK_PROTOCOL_NAME] = protocol; | ||
| attributes[SERVER_ADDRESS] = hostname; | ||
| attributes[SERVER_PORT] = port; | ||
| attributes[NET_PEER_NAME] = hostname; | ||
| attributes[NET_PEER_PORT] = port; | ||
| } else if (typeof resolvedUrl === "string") { | ||
| const censoredUrl = censorPassword(resolvedUrl); | ||
| attributes[ATTR_MESSAGING_URL] = censoredUrl; | ||
| attributes[URL_FULL] = censoredUrl; | ||
| try { | ||
| const urlParts = new URL(censoredUrl); | ||
| const protocol = getProtocol(urlParts.protocol); | ||
| const hostname = getHostname(urlParts.hostname); | ||
| const port = getPort(urlParts.port ? parseInt(urlParts.port, 10) : void 0, protocol); | ||
| attributes[ATTR_MESSAGING_PROTOCOL] = protocol; | ||
| attributes[NETWORK_PROTOCOL_NAME] = protocol; | ||
| attributes[SERVER_ADDRESS] = hostname; | ||
| attributes[SERVER_PORT] = port; | ||
| attributes[NET_PEER_NAME] = hostname; | ||
| attributes[NET_PEER_PORT] = port; | ||
| } catch { | ||
| } | ||
| } | ||
| return attributes; | ||
| } | ||
| function normalizeExchange(exchangeName) { | ||
| return exchangeName !== "" ? exchangeName : "<default>"; | ||
| } | ||
| function censorPassword(url) { | ||
| return url.replace(/:[^:@/]*@/, ":***@"); | ||
| } | ||
| function getPort(portFromUrl, resolvedProtocol) { | ||
| return portFromUrl || (resolvedProtocol === "AMQP" ? 5672 : 5671); | ||
| } | ||
| function getProtocol(protocolFromUrl) { | ||
| const resolvedProtocol = protocolFromUrl || "amqp"; | ||
| const noEndingColon = resolvedProtocol.endsWith(":") ? resolvedProtocol.substring(0, resolvedProtocol.length - 1) : resolvedProtocol; | ||
| return noEndingColon.toUpperCase(); | ||
| } | ||
| function getHostname(hostnameFromUrl) { | ||
| return hostnameFromUrl || "localhost"; | ||
| } | ||
| function getHeaderAsString(headers, key) { | ||
| const value = headers?.[key]; | ||
| if (value == null) { | ||
| return void 0; | ||
| } | ||
| return Array.isArray(value) ? String(value[0]) : String(value); | ||
| } | ||
| const amqplibChannelIntegration = defineIntegration(_amqplibChannelIntegration); | ||
| export { amqplibChannelIntegration }; | ||
| //# sourceMappingURL=amqplib.js.map |
| {"version":3,"file":"amqplib.js","sources":["../../../../src/integrations/tracing-channel/amqplib.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Span, SpanAttributes } from '@sentry/core';\nimport {\n continueTrace,\n debug,\n defineIntegration,\n getTraceData,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n timestampInSeconds,\n waitForTracingChannelBinding,\n} from '@sentry/core';\n// eslint-disable-next-line typescript/no-deprecated -- NET_PEER_* emitted alongside SERVER_* for backwards compatibility (TODO(v11): remove)\nimport {\n MESSAGING_SYSTEM,\n MESSAGING_MESSAGE_ID,\n MESSAGING_OPERATION_TYPE,\n MESSAGING_DESTINATION_NAME,\n NET_PEER_NAME,\n NET_PEER_PORT,\n NETWORK_PROTOCOL_NAME,\n NETWORK_PROTOCOL_VERSION,\n SERVER_ADDRESS,\n SERVER_PORT,\n URL_FULL,\n} from '@sentry/conventions/attributes';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, the OTel 'Amqplib' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Amqplib' as const;\n\nconst PUBLISHER_ORIGIN = 'auto.amqplib.orchestrion.publisher';\nconst CONSUMER_ORIGIN = 'auto.amqplib.orchestrion.consumer';\n\n// Legacy messaging semantic-conventions, inlined to keep this integration free of `@opentelemetry/*`\n// deps. These mirror what the vendored OTel amqplib instrumentation has always emitted. We keep\n// emitting them alongside the current `@sentry/conventions` attributes for backwards compatibility.\n// TODO(v11): remove these legacy attributes.\nconst ATTR_MESSAGING_OPERATION = 'messaging.operation';\nconst ATTR_MESSAGING_DESTINATION = 'messaging.destination';\nconst ATTR_MESSAGING_DESTINATION_KIND = 'messaging.destination_kind';\nconst ATTR_MESSAGING_RABBITMQ_ROUTING_KEY = 'messaging.rabbitmq.routing_key';\nconst ATTR_MESSAGING_PROTOCOL = 'messaging.protocol';\nconst ATTR_MESSAGING_PROTOCOL_VERSION_LEGACY = 'messaging.protocol_version';\nconst ATTR_MESSAGING_URL = 'messaging.url';\nconst ATTR_MESSAGING_MESSAGE_ID = 'messaging.message_id';\nconst ATTR_MESSAGING_CONVERSATION_ID_LEGACY = 'messaging.conversation_id';\n\n// TODO(v11): replace with the corresponding attribute from `@sentry/conventions` once it is added there.\nconst ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY = 'messaging.rabbitmq.destination.routing_key';\nconst ATTR_MESSAGING_CONVERSATION_ID = 'messaging.message.conversation_id';\n\nconst MESSAGING_DESTINATION_KIND_VALUE_TOPIC = 'topic';\nconst MESSAGING_OPERATION_VALUE_PROCESS = 'process';\nconst MESSAGING_OPERATION_VALUE_SEND = 'send';\n\n// To prevent reference leaks from un-acked messages, their spans are closed after this timeout. The\n// upstream instrumentation exposed this as the `consumeTimeoutMs` option; the SDK always used the default.\nconst CONSUME_TIMEOUT_MS = 1000 * 60; // 1 minute\n\n// The end-operation labels used in the consumer span's error status message.\nconst END_OP = {\n Ack: 'ack',\n AckAll: 'ackAll',\n Reject: 'reject',\n Nack: 'nack',\n NackAll: 'nackAll',\n ChannelClosed: 'channel closed',\n ChannelError: 'channel error',\n InstrumentationTimeout: 'instrumentation timeout',\n} as const;\ntype EndOp = (typeof END_OP)[keyof typeof END_OP];\n\n// State stashed on the live amqplib objects (mirrors the vendored OTel instrumentation's symbols).\nconst MESSAGE_STORED_SPAN: unique symbol = Symbol('sentry.amqplib.message.stored-span');\nconst CHANNEL_SPANS_NOT_ENDED: unique symbol = Symbol('sentry.amqplib.channel.spans-not-ended');\nconst CHANNEL_CONSUME_TIMEOUT_TIMER: unique symbol = Symbol('sentry.amqplib.channel.consume-timeout-timer');\nconst CHANNEL_CONSUMER_INFO: unique symbol = Symbol('sentry.amqplib.channel.consumer-info');\nconst CHANNEL_IS_CONFIRM_PUBLISHING: unique symbol = Symbol('sentry.amqplib.channel.is-confirm-publishing');\nconst CONNECTION_ATTRIBUTES: unique symbol = Symbol('sentry.amqplib.connection.attributes');\n\ninterface MessageFields {\n deliveryTag?: number;\n exchange?: string;\n routingKey?: string;\n consumerTag?: string;\n}\n\ninterface MessageProperties {\n headers?: Record<string, unknown>;\n messageId?: unknown;\n correlationId?: unknown;\n}\n\ninterface ConsumeMessage {\n fields?: MessageFields;\n properties?: MessageProperties;\n [MESSAGE_STORED_SPAN]?: Span;\n}\n\ninterface PublishOptions {\n headers?: Record<string, unknown>;\n messageId?: unknown;\n correlationId?: unknown;\n}\n\ninterface ConnectionLike {\n serverProperties?: { product?: string };\n // Some amqplib versions nest the low-level connection one level deeper.\n connection?: { serverProperties?: { product?: string } };\n [CONNECTION_ATTRIBUTES]?: SpanAttributes;\n}\n\ninterface ConsumerInfo {\n noAck: boolean;\n queue: string;\n}\n\ninterface ChannelLike {\n connection?: ConnectionLike;\n on?: (event: string, listener: (...args: unknown[]) => void) => unknown;\n [CHANNEL_SPANS_NOT_ENDED]?: { msg: ConsumeMessage; timeOfConsume: number }[];\n [CHANNEL_CONSUME_TIMEOUT_TIMER]?: ReturnType<typeof setInterval>;\n [CHANNEL_CONSUMER_INFO]?: Map<string, ConsumerInfo>;\n [CHANNEL_IS_CONFIRM_PUBLISHING]?: boolean;\n}\n\n/**\n * The shape orchestrion's transform attaches to the tracing-channel `context`. Documented here rather\n * than imported because orchestrion's runtime doesn't export it.\n */\ninterface AmqpChannelContext {\n // The live args array passed to the wrapped call.\n arguments: unknown[];\n self?: ChannelLike;\n result?: unknown;\n error?: unknown;\n}\n\ninterface AmqpDispatchContext extends AmqpChannelContext {\n // Whether the delivering consumer registered with `noAck`; set at span creation so `deferSpanEnd`\n // knows whether the helper should end the span (noAck) or leave it open until ack/nack (manual ack).\n _sentryNoAck?: boolean;\n}\n\ninterface AmqpConnectContext {\n arguments?: unknown[];\n result?: unknown;\n}\n\nconst NOOP = (): void => {};\n\n// Guards against subscribing to the amqplib channels more than once in a process. Core dedupes\n// `setupOnce` by integration *name*, which is not enough here: the Deno SDK wraps this integration\n// under a different name (`DenoAmqplib`) via `extendIntegration`, so adding both would otherwise run\n// the subscribe logic twice and emit duplicate spans for every operation.\nlet subscribed = false;\n\nconst _amqplibChannelIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel || subscribed) {\n return;\n }\n subscribed = true;\n\n DEBUG_BUILD && debug.log('[orchestrion:amqplib] subscribing to amqplib tracing channels');\n\n waitForTracingChannelBinding(() => {\n subscribeConnect();\n subscribePublish();\n subscribeConfirmPublish();\n subscribeConsume();\n subscribeDispatch();\n subscribeSettle();\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Producer span for `Channel.prototype.publish`. Creates a PRODUCER span, injects the trace headers\n * into the publish options, and ends when the (synchronous) publish call returns. Skips the confirm\n * channel's internal `super.publish` call, which is already handled by {@link subscribeConfirmPublish}.\n */\nfunction subscribePublish(): void {\n bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_PUBLISH), data => {\n if (data.self?.[CHANNEL_IS_CONFIRM_PUBLISHING]) {\n return undefined;\n }\n return startPublishSpan(data);\n });\n}\n\n/**\n * Producer span for `ConfirmChannel.prototype.publish`. The span ends on the broker-confirm callback\n * (the channel's trailing `cb` arg, wrapped by orchestrion) rather than synchronously. A synchronous\n * flag on the channel suppresses the base `publish` channel that `super.publish` triggers.\n */\nfunction subscribeConfirmPublish(): void {\n const channel = diagnosticsChannel.tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_CONFIRM_PUBLISH);\n\n bindTracingChannelToSpan(channel, data => {\n if (data.self) {\n data.self[CHANNEL_IS_CONFIRM_PUBLISHING] = true;\n }\n return startPublishSpan(data);\n });\n\n // `super.publish` runs synchronously inside the confirm publish body, so clearing the flag on the\n // synchronous `end` (which fires after the body returns) is enough to guard the base publish.\n channel.end.subscribe(message => {\n const self = (message as AmqpChannelContext).self;\n if (self) {\n self[CHANNEL_IS_CONFIRM_PUBLISHING] = false;\n }\n });\n}\n\n/**\n * Records `consumerTag -> { noAck, queue }` when a consumer is registered, so the per-message\n * dispatch hook can name the span after the queue and know when to end it.\n */\nfunction subscribeConsume(): void {\n const channel = diagnosticsChannel.tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_CONSUME);\n\n // A `start` subscriber is required for orchestrion to wrap `consume` at all.\n channel.start.subscribe(NOOP);\n channel.asyncEnd.subscribe(message => {\n const data = message as AmqpChannelContext;\n const consumerChannel = data.self;\n const result = data.result as { consumerTag?: string } | undefined;\n const consumerTag = result?.consumerTag;\n if (!consumerChannel || !consumerTag) {\n return;\n }\n\n ensureChannelState(consumerChannel);\n const queueArg = data.arguments[0];\n const queue = typeof queueArg === 'string' ? queueArg : '<unknown>';\n const options = data.arguments[2] as { noAck?: boolean } | undefined;\n consumerChannel[CHANNEL_CONSUMER_INFO]?.set(consumerTag, { noAck: !!options?.noAck, queue });\n });\n}\n\n/**\n * Per delivered message (`BaseChannel.prototype.dispatchMessage`): continues the producer's trace,\n * opens a CONSUMER span, and runs the user callback under it. Manual-ack consumers keep the span open\n * until `ack`/`nack`/`reject`/timeout/close; `noAck` consumers end it when dispatch returns.\n */\nfunction subscribeDispatch(): void {\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<AmqpDispatchContext>(CHANNELS.AMQPLIB_DISPATCH),\n data => {\n const channel = data.self;\n const fields = data.arguments[0] as MessageFields | undefined;\n const msg = data.arguments[1] as ConsumeMessage | null | undefined;\n // `message` is null for a consumer-cancel notification: not a real message, so no span.\n if (!channel || !msg) {\n return undefined;\n }\n\n ensureChannelState(channel);\n const info = fields?.consumerTag ? channel[CHANNEL_CONSUMER_INFO]?.get(fields.consumerTag) : undefined;\n const queue = info?.queue ?? msg.fields?.routingKey ?? '<unknown>';\n const noAck = info?.noAck ?? false;\n\n const headers = msg.properties?.headers;\n const sentryTrace = getHeaderAsString(headers, 'sentry-trace');\n const baggage = getHeaderAsString(headers, 'baggage');\n\n // Continue the producer's trace so the consumer span links back to the publishing service.\n const span = continueTrace({ sentryTrace, baggage }, () => startConsumeSpan(queue, msg, channel));\n\n if (!noAck) {\n // Track the message so its span can be ended when the user calls ack/nack/reject (or on\n // timeout/channel close).\n channel[CHANNEL_SPANS_NOT_ENDED]?.push({ msg, timeOfConsume: timestampInSeconds() });\n msg[MESSAGE_STORED_SPAN] = span;\n }\n\n data._sentryNoAck = noAck;\n return span;\n },\n {\n // Manual-ack consumers: the span outlives the dispatch call and is ended by ack/nack/reject\n // (or timeout/close), so take ownership and don't let the helper end it here. noAck consumers\n // have no settle call, so let the helper end the span when dispatch returns.\n deferSpanEnd({ data }) {\n return !data._sentryNoAck;\n },\n },\n );\n}\n\n/** Ends consumer spans when the user acks/nacks/rejects a message or the channel closes/errors. */\nfunction subscribeSettle(): void {\n diagnosticsChannel\n .tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_ACK)\n .start.subscribe(message => handleAck(message as AmqpChannelContext, false, END_OP.Ack));\n diagnosticsChannel\n .tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_NACK)\n .start.subscribe(message => handleAck(message as AmqpChannelContext, true, END_OP.Nack));\n diagnosticsChannel\n .tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_REJECT)\n .start.subscribe(message => handleAck(message as AmqpChannelContext, true, END_OP.Reject));\n diagnosticsChannel.tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_ACK_ALL).start.subscribe(message => {\n const data = message as AmqpChannelContext;\n if (data.self) {\n endAllSpansOnChannel(data.self, false, END_OP.AckAll, undefined);\n }\n });\n diagnosticsChannel.tracingChannel<AmqpChannelContext>(CHANNELS.AMQPLIB_NACK_ALL).start.subscribe(message => {\n const data = message as AmqpChannelContext;\n if (data.self) {\n endAllSpansOnChannel(data.self, true, END_OP.NackAll, data.arguments[0] as boolean | undefined);\n }\n });\n}\n\n/** Captures connection attributes on the connection object for span-time reads via `channel.connection`. */\nfunction subscribeConnect(): void {\n const channel = diagnosticsChannel.tracingChannel<AmqpConnectContext>(CHANNELS.AMQPLIB_CONNECT);\n // A `start` subscriber is required for orchestrion to wrap the callback-style `connect` at all.\n channel.start.subscribe(NOOP);\n channel.asyncEnd.subscribe(message => {\n const data = message as AmqpConnectContext;\n const conn = data.result as ConnectionLike | undefined;\n if (!conn || typeof conn !== 'object') {\n return;\n }\n conn[CONNECTION_ATTRIBUTES] = {\n ...getConnectionAttributesFromUrl(data.arguments?.[0]),\n ...getConnectionAttributesFromServer(conn),\n };\n });\n}\n\nfunction handleAck(data: AmqpChannelContext, isRejected: boolean, endOperation: EndOp): void {\n const channel = data.self;\n if (!channel) {\n return;\n }\n const message = data.arguments[0] as ConsumeMessage | undefined;\n if (!message) {\n return;\n }\n\n // `reject(message, requeue)` carries requeue in arg 1; `ack`/`nack` carry `allUpTo` in arg 1.\n const allUpToOrRequeue = data.arguments[1] as boolean | undefined;\n const requeue = data.arguments[2] as boolean | undefined;\n const requeueResolved = endOperation === END_OP.Reject ? allUpToOrRequeue : requeue;\n\n const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? [];\n const msgIndex = spansNotEnded.findIndex(msgDetails => msgDetails.msg === message);\n if (msgIndex < 0) {\n // Not tracked (e.g. the user acked the same message twice) — end the stored span directly.\n endConsumerSpan(message, isRejected, endOperation, requeueResolved);\n } else if (endOperation !== END_OP.Reject && allUpToOrRequeue) {\n for (let i = 0; i <= msgIndex; i++) {\n endConsumerSpan(spansNotEnded[i]!.msg, isRejected, endOperation, requeueResolved);\n }\n spansNotEnded.splice(0, msgIndex + 1);\n } else {\n endConsumerSpan(message, isRejected, endOperation, requeueResolved);\n spansNotEnded.splice(msgIndex, 1);\n }\n}\n\nfunction ensureChannelState(channel: ChannelLike): void {\n if (Object.prototype.hasOwnProperty.call(channel, CHANNEL_SPANS_NOT_ENDED)) {\n return;\n }\n\n channel[CHANNEL_SPANS_NOT_ENDED] = [];\n channel[CHANNEL_CONSUMER_INFO] = new Map();\n\n const timer = setInterval(() => checkConsumeTimeoutOnChannel(channel), CONSUME_TIMEOUT_MS);\n timer.unref?.();\n channel[CHANNEL_CONSUME_TIMEOUT_TIMER] = timer;\n\n // End outstanding spans and stop the timer when the channel goes away (replaces patching `emit`).\n // amqplib emits 'close' after 'error', but we clear in both to avoid leaking the interval (which\n // pins the channel via its closure) should a version or edge case ever skip the trailing 'close'.\n if (typeof channel.on === 'function') {\n channel.on('close', () => {\n endAllSpansOnChannel(channel, true, END_OP.ChannelClosed, undefined);\n clearConsumeTimeoutTimer(channel);\n });\n channel.on('error', () => {\n endAllSpansOnChannel(channel, true, END_OP.ChannelError, undefined);\n clearConsumeTimeoutTimer(channel);\n });\n }\n}\n\n/** Stops and clears the per-channel consume-timeout interval. Idempotent. */\nfunction clearConsumeTimeoutTimer(channel: ChannelLike): void {\n const activeTimer = channel[CHANNEL_CONSUME_TIMEOUT_TIMER];\n if (activeTimer) {\n clearInterval(activeTimer);\n channel[CHANNEL_CONSUME_TIMEOUT_TIMER] = undefined;\n }\n}\n\nfunction checkConsumeTimeoutOnChannel(channel: ChannelLike): void {\n const currentTime = timestampInSeconds();\n const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? [];\n let i: number;\n for (i = 0; i < spansNotEnded.length; i++) {\n const currMessage = spansNotEnded[i]!;\n const timeFromConsumeMs = (currentTime - currMessage.timeOfConsume) * 1000;\n if (timeFromConsumeMs < CONSUME_TIMEOUT_MS) {\n break;\n }\n endConsumerSpan(currMessage.msg, null, END_OP.InstrumentationTimeout, true);\n }\n spansNotEnded.splice(0, i);\n}\n\nfunction endAllSpansOnChannel(\n channel: ChannelLike,\n isRejected: boolean,\n operation: EndOp,\n requeue: boolean | undefined,\n): void {\n const spansNotEnded = channel[CHANNEL_SPANS_NOT_ENDED] ?? [];\n spansNotEnded.forEach(msgDetails => {\n endConsumerSpan(msgDetails.msg, isRejected, operation, requeue);\n });\n channel[CHANNEL_SPANS_NOT_ENDED] = [];\n}\n\nfunction endConsumerSpan(\n message: ConsumeMessage,\n isRejected: boolean | null,\n operation: EndOp,\n requeue: boolean | undefined,\n): void {\n const storedSpan = message[MESSAGE_STORED_SPAN];\n if (!storedSpan) {\n return;\n }\n if (isRejected !== false) {\n storedSpan.setStatus({\n code: SPAN_STATUS_ERROR,\n message:\n operation !== END_OP.ChannelClosed && operation !== END_OP.ChannelError\n ? `${operation} called on message${\n requeue === true ? ' with requeue' : requeue === false ? ' without requeue' : ''\n }`\n : operation,\n });\n }\n storedSpan.end();\n message[MESSAGE_STORED_SPAN] = undefined;\n}\n\n/** Starts an inactive PRODUCER span and propagates its trace into the publish `options.headers`. */\nfunction startPublishSpan(data: AmqpChannelContext): Span {\n const exchangeArg = data.arguments[0];\n const routingKeyArg = data.arguments[1];\n const exchange = typeof exchangeArg === 'string' ? exchangeArg : '';\n const routingKey = typeof routingKeyArg === 'string' ? routingKeyArg : '';\n let options = data.arguments[3] as PublishOptions | undefined;\n\n const span = startInactiveSpan({\n name: `publish ${normalizeExchange(exchange)}`,\n op: 'message',\n kind: SPAN_KIND.PRODUCER,\n attributes: {\n ...getStoredConnectionAttributes(data.self),\n [ATTR_MESSAGING_DESTINATION]: exchange, // TODO(v11) remove this attribute\n [MESSAGING_DESTINATION_NAME]: exchange,\n [ATTR_MESSAGING_DESTINATION_KIND]: MESSAGING_DESTINATION_KIND_VALUE_TOPIC, // TODO(v11) remove this attribute\n [ATTR_MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey, // TODO(v11) remove this attribute\n [ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY]: routingKey,\n [MESSAGING_OPERATION_TYPE]: MESSAGING_OPERATION_VALUE_SEND,\n [ATTR_MESSAGING_MESSAGE_ID]: options?.messageId as string | undefined, // todo(v11) remove this attribute\n [MESSAGING_MESSAGE_ID]: options?.messageId as string | undefined,\n [ATTR_MESSAGING_CONVERSATION_ID_LEGACY]: options?.correlationId as string | undefined, // todo(v11) remove this attribute\n [ATTR_MESSAGING_CONVERSATION_ID]: options?.correlationId as string | undefined,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: PUBLISHER_ORIGIN,\n },\n });\n\n if (!options || typeof options !== 'object') {\n options = {};\n data.arguments[3] = options;\n }\n const headers = options.headers && typeof options.headers === 'object' ? options.headers : (options.headers = {});\n const traceData = getTraceData({ span });\n if (traceData['sentry-trace']) {\n headers['sentry-trace'] = traceData['sentry-trace'];\n }\n if (traceData.baggage) {\n headers['baggage'] = traceData.baggage;\n }\n\n return span;\n}\n\n/** Starts an inactive CONSUMER (process) span carrying the amqplib messaging attributes. */\nfunction startConsumeSpan(queue: string, msg: ConsumeMessage, channel: ChannelLike): Span {\n return startInactiveSpan({\n name: `${queue} process`,\n op: 'message',\n kind: SPAN_KIND.CONSUMER,\n attributes: {\n ...getStoredConnectionAttributes(channel),\n [ATTR_MESSAGING_DESTINATION]: msg.fields?.exchange, // TODO(v11) remove this attribute\n [MESSAGING_DESTINATION_NAME]: msg.fields?.exchange,\n [ATTR_MESSAGING_DESTINATION_KIND]: MESSAGING_DESTINATION_KIND_VALUE_TOPIC, // TODO(v11) remove this attribute\n [ATTR_MESSAGING_RABBITMQ_ROUTING_KEY]: msg.fields?.routingKey, // TODO(v11) remove this attribute\n [ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY]: msg.fields?.routingKey,\n [ATTR_MESSAGING_OPERATION]: MESSAGING_OPERATION_VALUE_PROCESS, // TODO(v11) remove this attribute\n [MESSAGING_OPERATION_TYPE]: MESSAGING_OPERATION_VALUE_PROCESS,\n [ATTR_MESSAGING_MESSAGE_ID]: msg.properties?.messageId as string | undefined, // todo(v11) remove this attribute\n [MESSAGING_MESSAGE_ID]: msg.properties?.messageId as string | undefined,\n [ATTR_MESSAGING_CONVERSATION_ID_LEGACY]: msg.properties?.correlationId as string | undefined, // todo(v11) remove this attribute\n [ATTR_MESSAGING_CONVERSATION_ID]: msg.properties?.correlationId as string | undefined,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: CONSUMER_ORIGIN,\n },\n });\n}\n\n/**\n * Reads the connection attributes stashed by the `connect` channel, falling back to the live\n * connection's server product so `messaging.system` is populated even when the connection was\n * established before the integration ran.\n */\nfunction getStoredConnectionAttributes(channel: ChannelLike | undefined): SpanAttributes {\n const connection = channel?.connection;\n const stored = connection?.[CONNECTION_ATTRIBUTES];\n if (stored) {\n return stored;\n }\n const product = connection?.serverProperties?.product ?? connection?.connection?.serverProperties?.product;\n if (typeof product === 'string' && product) {\n return { [MESSAGING_SYSTEM]: product.toLowerCase() };\n }\n return {};\n}\n\nfunction getConnectionAttributesFromServer(conn: ConnectionLike): SpanAttributes {\n const product = conn.serverProperties?.product ?? conn.connection?.serverProperties?.product;\n if (typeof product === 'string' && product) {\n return { [MESSAGING_SYSTEM]: product.toLowerCase() };\n }\n return {};\n}\n\nfunction getConnectionAttributesFromUrl(url: unknown): SpanAttributes {\n const attributes: SpanAttributes = {\n // The only protocol supported by the instrumented library.\n [ATTR_MESSAGING_PROTOCOL_VERSION_LEGACY]: '0.9.1', // TODO(v11): remove this attribute\n [NETWORK_PROTOCOL_VERSION]: '0.9.1',\n };\n\n const resolvedUrl = url || 'amqp://localhost';\n if (typeof resolvedUrl === 'object') {\n const connectOptions = resolvedUrl as { protocol?: string; hostname?: string; port?: number };\n const protocol = getProtocol(connectOptions.protocol);\n const hostname = getHostname(connectOptions.hostname);\n const port = getPort(connectOptions.port, protocol);\n\n attributes[ATTR_MESSAGING_PROTOCOL] = protocol; // TODO(v11) remove this attribute\n attributes[NETWORK_PROTOCOL_NAME] = protocol;\n\n attributes[SERVER_ADDRESS] = hostname;\n attributes[SERVER_PORT] = port;\n // TODO(v11): remove deprecated options\n // eslint-disable-next-line typescript/no-deprecated -- emitted alongside SERVER_ADDRESS/SERVER_PORT for backwards compatibility\n attributes[NET_PEER_NAME] = hostname;\n // eslint-disable-next-line typescript/no-deprecated -- emitted alongside SERVER_ADDRESS/SERVER_PORT for backwards compatibility\n attributes[NET_PEER_PORT] = port;\n } else if (typeof resolvedUrl === 'string') {\n const censoredUrl = censorPassword(resolvedUrl);\n attributes[ATTR_MESSAGING_URL] = censoredUrl; // todo(v11) remove this attribute\n attributes[URL_FULL] = censoredUrl;\n\n try {\n const urlParts = new URL(censoredUrl);\n const protocol = getProtocol(urlParts.protocol);\n const hostname = getHostname(urlParts.hostname);\n const port = getPort(urlParts.port ? parseInt(urlParts.port, 10) : undefined, protocol);\n\n attributes[ATTR_MESSAGING_PROTOCOL] = protocol; // TODO(v11) remove this attribute\n attributes[NETWORK_PROTOCOL_NAME] = protocol;\n\n attributes[SERVER_ADDRESS] = hostname;\n attributes[SERVER_PORT] = port;\n // eslint-disable-next-line typescript/no-deprecated -- emitted alongside SERVER_ADDRESS/SERVER_PORT for backwards compatibility\n attributes[NET_PEER_NAME] = hostname;\n // eslint-disable-next-line typescript/no-deprecated -- emitted alongside SERVER_ADDRESS/SERVER_PORT for backwards compatibility\n attributes[NET_PEER_PORT] = port;\n } catch {\n // best-effort: a malformed url simply yields fewer connection attributes\n }\n }\n return attributes;\n}\n\nfunction normalizeExchange(exchangeName: string): string {\n return exchangeName !== '' ? exchangeName : '<default>';\n}\n\nfunction censorPassword(url: string): string {\n return url.replace(/:[^:@/]*@/, ':***@');\n}\n\nfunction getPort(portFromUrl: number | undefined, resolvedProtocol: string): number {\n // Mimics amqplib's own defaulting; the resolved protocol is upper-cased.\n return portFromUrl || (resolvedProtocol === 'AMQP' ? 5672 : 5671);\n}\n\nfunction getProtocol(protocolFromUrl: string | undefined): string {\n const resolvedProtocol = protocolFromUrl || 'amqp';\n const noEndingColon = resolvedProtocol.endsWith(':')\n ? resolvedProtocol.substring(0, resolvedProtocol.length - 1)\n : resolvedProtocol;\n return noEndingColon.toUpperCase();\n}\n\nfunction getHostname(hostnameFromUrl: string | undefined): string {\n // An empty hostname is forwarded to `net`, which defaults it to localhost.\n return hostnameFromUrl || 'localhost';\n}\n\nfunction getHeaderAsString(headers: Record<string, unknown> | undefined, key: string): string | undefined {\n const value = headers?.[key];\n if (value == null) {\n return undefined;\n }\n return Array.isArray(value) ? String(value[0]) : String(value);\n}\n\n/**\n * EXPERIMENTAL: orchestrion-driven `amqplib` integration.\n *\n * Subscribes to the `orchestrion:amqplib:*` diagnostics_channels that the orchestrion code transform\n * injects into `amqplib`'s channel/connection methods. Requires the orchestrion runtime hook or\n * bundler plugin to be active.\n */\nexport const amqplibChannelIntegration = defineIntegration(_amqplibChannelIntegration);\n"],"names":[],"mappings":"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"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { defineIntegration, waitForTracingChannelBinding } from '@sentry/core'; | ||
| import { instrumentExpress } from './instrumentation.js'; | ||
| const INTEGRATION_NAME = "Express"; | ||
| const _expressChannelIntegration = ((options = {}) => { | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| waitForTracingChannelBinding(() => { | ||
| instrumentExpress(options, diagnosticsChannel.tracingChannel); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const expressChannelIntegration = defineIntegration(_expressChannelIntegration); | ||
| export { expressChannelIntegration }; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing-channel/express/index.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, waitForTracingChannelBinding } from '@sentry/core';\nimport type { ExpressIntegrationOptions } from './types';\nimport { instrumentExpress } from './instrumentation';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, the OTel 'Express' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Express' as const;\n\nconst _expressChannelIntegration = ((options: ExpressIntegrationOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n instrumentExpress(options, diagnosticsChannel.tracingChannel);\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * EXPERIMENTAL — orchestrion-driven Express integration.\n *\n * Subscribes to the `orchestrion:express:handle` (Express v4) and\n * `orchestrion:router:handle` (Express v5, via the `router` package)\n * diagnostics_channels that the orchestrion code transform injects into the\n * routing layer's request handler (`Layer.prototype.handle_request` /\n * `handleRequest`). One span is opened per layer invocation — producing the\n * same spans as the OTel Express instrumentation.\n *\n * Requires the orchestrion runtime hook or bundler plugin to be active — wire\n * that up via `experimentalUseDiagnosticsChannelInjection()`.\n */\nexport const expressChannelIntegration = defineIntegration(_expressChannelIntegration);\n"],"names":[],"mappings":";;;;AAQA,MAAM,gBAAA,GAAmB,SAAA;AAEzB,MAAM,0BAAA,IAA8B,CAAC,OAAA,GAAqC,EAAC,KAAM;AAC/E,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,mBAAmB,cAAA,EAAgB;AACtC,QAAA;AAAA,MACF;AAEA,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,iBAAA,CAAkB,OAAA,EAAS,mBAAmB,cAAc,CAAA;AAAA,MAC9D,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAeO,MAAM,yBAAA,GAA4B,kBAAkB,0BAA0B;;;;"} |
| import { HTTP_ROUTE } from '@sentry/conventions/attributes'; | ||
| import { debug, getActiveSpan, getIsolationScope, getDefaultIsolationScope, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getRootSpan, spanToJSON, stringMatchesSomePattern } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../../debug-build.js'; | ||
| import { CHANNELS } from '../../../orchestrion/channels.js'; | ||
| import { bindTracingChannelToSpan } from '../../../tracing-channel.js'; | ||
| import { setLayerRegisteredPath, getLayerPath, getLayerRegisteredPath, pushLayerPath, getConstructedRoute, getActualMatchedRoute, popLayerPath } from './route.js'; | ||
| const ORIGIN = "auto.http.express"; | ||
| const ATTR_EXPRESS_NAME = "express.name"; | ||
| const ATTR_EXPRESS_TYPE = "express.type"; | ||
| const NOOP = () => { | ||
| }; | ||
| let _isInstrumented = false; | ||
| function instrumentExpress(options, tracingChannel) { | ||
| if (_isInstrumented) { | ||
| return; | ||
| } | ||
| _isInstrumented = true; | ||
| for (const channelName of [ | ||
| CHANNELS.EXPRESS_ROUTE, | ||
| CHANNELS.EXPRESS_USE, | ||
| CHANNELS.ROUTER_ROUTE, | ||
| CHANNELS.ROUTER_USE | ||
| ]) { | ||
| tracingChannel(channelName).subscribe({ | ||
| start: NOOP, | ||
| asyncStart: NOOP, | ||
| asyncEnd: NOOP, | ||
| error: NOOP, | ||
| end: captureRegisteredLayerPath | ||
| }); | ||
| } | ||
| for (const channelName of [CHANNELS.EXPRESS_HANDLE, CHANNELS.ROUTER_HANDLE]) { | ||
| DEBUG_BUILD && debug.log(`[orchestrion:express] subscribing to channel "${channelName}"`); | ||
| const channel = tracingChannel(channelName); | ||
| bindTracingChannelToSpan(channel, (data) => getSpanForLayer(data, options), { | ||
| beforeSpanEnd(_span, data) { | ||
| data._sentryCleanup?.(); | ||
| } | ||
| }); | ||
| channel.subscribe({ | ||
| start: NOOP, | ||
| asyncEnd: NOOP, | ||
| end: NOOP, | ||
| error: NOOP, | ||
| asyncStart: popLayerPathForLayer | ||
| }); | ||
| } | ||
| } | ||
| function captureRegisteredLayerPath(data) { | ||
| const stack = data.self?.stack; | ||
| if (!Array.isArray(stack)) { | ||
| return; | ||
| } | ||
| const layer = stack[stack.length - 1]; | ||
| if (layer) { | ||
| setLayerRegisteredPath(layer, getLayerPath(data.arguments ?? [])); | ||
| } | ||
| } | ||
| function popLayerPathForLayer(data) { | ||
| if (!data._sentryStoredLayer) { | ||
| return; | ||
| } | ||
| data._sentryStoredLayer = false; | ||
| const req = data.arguments?.[0]; | ||
| if (req) { | ||
| popLayerPath(req); | ||
| } | ||
| } | ||
| function getSpanForLayer(data, options) { | ||
| const layer = data.self; | ||
| const args = data.arguments; | ||
| if (!layer || !Array.isArray(args)) { | ||
| return void 0; | ||
| } | ||
| if (layer.handle?.length === 4) { | ||
| return void 0; | ||
| } | ||
| if (layer.method && !layer.route) { | ||
| return void 0; | ||
| } | ||
| const req = args[0]; | ||
| const res = args[1]; | ||
| if (!req) { | ||
| return void 0; | ||
| } | ||
| if (!getActiveSpan()) { | ||
| return void 0; | ||
| } | ||
| const type = getLayerType(layer); | ||
| const registeredPath = getLayerRegisteredPath(layer); | ||
| if (registeredPath != null) { | ||
| pushLayerPath(req, registeredPath); | ||
| data._sentryStoredLayer = true; | ||
| } | ||
| const constructedRoute = type === "request_handler" ? getConstructedRoute(req) : void 0; | ||
| const matchedRoute = type === "request_handler" && constructedRoute != null ? getActualMatchedRoute(req, constructedRoute) : void 0; | ||
| const name = type === "request_handler" ? constructedRoute || "request handler" : type === "router" ? layer.path ?? "/" : layer.name ?? "<anonymous>"; | ||
| if (matchedRoute) { | ||
| setHttpServerSpanRoute(matchedRoute); | ||
| } | ||
| if (type === "request_handler" && constructedRoute) { | ||
| const isolationScope = getIsolationScope(); | ||
| if (isolationScope !== getDefaultIsolationScope()) { | ||
| const method = typeof req.method === "string" ? req.method.toUpperCase() : "GET"; | ||
| isolationScope.setTransactionName(`${method} ${constructedRoute}`); | ||
| } else { | ||
| DEBUG_BUILD && debug.warn( | ||
| "[orchestrion:express] Isolation scope is still default isolation scope - skipping transaction name" | ||
| ); | ||
| } | ||
| } | ||
| if (isLayerIgnored(name, type, options)) { | ||
| return void 0; | ||
| } | ||
| const span = startInactiveSpan({ | ||
| name, | ||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.express`, | ||
| [ATTR_EXPRESS_NAME]: name, | ||
| [ATTR_EXPRESS_TYPE]: type, | ||
| ...matchedRoute ? { [HTTP_ROUTE]: matchedRoute } : {} | ||
| } | ||
| }); | ||
| if (res && typeof res.once === "function") { | ||
| const onFinish = () => { | ||
| span.end(); | ||
| }; | ||
| res.once("finish", onFinish); | ||
| data._sentryCleanup = () => res.removeListener("finish", onFinish); | ||
| } | ||
| return span; | ||
| } | ||
| function getLayerType(layer) { | ||
| if (layer.name === "router") { | ||
| return "router"; | ||
| } | ||
| if (layer.name === "bound dispatch" || layer.name === "handle") { | ||
| return "request_handler"; | ||
| } | ||
| return "middleware"; | ||
| } | ||
| function setHttpServerSpanRoute(route) { | ||
| const activeSpan = getActiveSpan(); | ||
| const rootSpan = activeSpan && getRootSpan(activeSpan); | ||
| if (!rootSpan) { | ||
| return; | ||
| } | ||
| if (spanToJSON(rootSpan).data[SEMANTIC_ATTRIBUTE_SENTRY_OP] !== "http.server") { | ||
| return; | ||
| } | ||
| rootSpan.setAttribute(HTTP_ROUTE, route); | ||
| } | ||
| function isLayerIgnored(name, type, options) { | ||
| const { ignoreLayers, ignoreLayersType } = options; | ||
| if (Array.isArray(ignoreLayersType) && ignoreLayersType.includes(type)) { | ||
| return true; | ||
| } | ||
| if (!Array.isArray(ignoreLayers)) { | ||
| return false; | ||
| } | ||
| try { | ||
| return stringMatchesSomePattern(name, ignoreLayers, true); | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
| export { instrumentExpress }; | ||
| //# sourceMappingURL=instrumentation.js.map |
| {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing-channel/express/instrumentation.ts"],"sourcesContent":["import type * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { HTTP_ROUTE } from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport {\n debug,\n getActiveSpan,\n getDefaultIsolationScope,\n getIsolationScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n spanToJSON,\n startInactiveSpan,\n stringMatchesSomePattern,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../../debug-build';\nimport { CHANNELS } from '../../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../../tracing-channel';\nimport {\n getActualMatchedRoute,\n getConstructedRoute,\n getLayerPath,\n getLayerRegisteredPath,\n popLayerPath,\n pushLayerPath,\n setLayerRegisteredPath,\n} from './route';\nimport type {\n ExpressIntegrationOptions,\n ExpressLayer,\n ExpressLayerType,\n ExpressRequest,\n ExpressResponse,\n HandleChannelContext,\n RegistrationChannelContext,\n} from './types';\n\nconst ORIGIN = 'auto.http.express';\n\n// `express.name`/`express.type` are Sentry-internal Express attributes (not part\n// of `@sentry/conventions`); kept in sync with `@sentry/core`'s OTel-derived\n// Express integration so the emitted spans are identical across both code paths.\nconst ATTR_EXPRESS_NAME = 'express.name';\nconst ATTR_EXPRESS_TYPE = 'express.type';\n\nconst NOOP = (): void => {};\n\nlet _isInstrumented = false;\n\nexport function instrumentExpress(\n options: ExpressIntegrationOptions,\n tracingChannel: typeof diagnosticsChannel.tracingChannel,\n): void {\n if (_isInstrumented) {\n return;\n }\n _isInstrumented = true;\n\n // Record each layer's registered path *pattern* as it is registered, so the\n // matched route can be reconstructed with its parameters intact at request\n // time. Only the `end` event matters (the layer is on the router's stack by\n // then); the others are required by the subscriber type, so no-op them.\n for (const channelName of [\n CHANNELS.EXPRESS_ROUTE,\n CHANNELS.EXPRESS_USE,\n CHANNELS.ROUTER_ROUTE,\n CHANNELS.ROUTER_USE,\n ]) {\n tracingChannel<RegistrationChannelContext>(channelName).subscribe({\n start: NOOP,\n asyncStart: NOOP,\n asyncEnd: NOOP,\n error: NOOP,\n end: captureRegisteredLayerPath,\n });\n }\n\n for (const channelName of [CHANNELS.EXPRESS_HANDLE, CHANNELS.ROUTER_HANDLE]) {\n DEBUG_BUILD && debug.log(`[orchestrion:express] subscribing to channel \"${channelName}\"`);\n\n const channel = tracingChannel<HandleChannelContext>(channelName);\n\n bindTracingChannelToSpan(channel, data => getSpanForLayer(data, options), {\n beforeSpanEnd(_span, data) {\n data._sentryCleanup?.();\n },\n });\n\n // Pop the layer path when the layer hands off via `next`. `asyncStart` fires\n // when `next` is called and *before* the downstream layer runs, so the\n // per-request path chain reflects only the current chain when each layer\n // reconstructs its route. Only `asyncStart` is relevant here.\n channel.subscribe({\n start: NOOP,\n asyncEnd: NOOP,\n end: NOOP,\n error: NOOP,\n asyncStart: popLayerPathForLayer,\n });\n }\n}\n\n/** Record the freshly-registered layer's path pattern from a `route`/`use` call. */\nfunction captureRegisteredLayerPath(data: RegistrationChannelContext): void {\n const stack = data.self?.stack;\n if (!Array.isArray(stack)) {\n return;\n }\n const layer = stack[stack.length - 1];\n if (layer) {\n setLayerRegisteredPath(layer, getLayerPath(data.arguments ?? []));\n }\n}\n\n/** Pop the path a layer pushed once it hands control onward via `next`. */\nfunction popLayerPathForLayer(data: HandleChannelContext): void {\n if (!data._sentryStoredLayer) {\n return;\n }\n // Clear the marker first so a layer that (incorrectly) calls `next` more than\n // once can't pop again and take a parent's entry off the stack with it.\n data._sentryStoredLayer = false;\n const req = data.arguments?.[0] as ExpressRequest | undefined;\n if (req) {\n popLayerPath(req);\n }\n}\n\n/**\n * Open a span for one layer invocation. Returns `undefined` to opt the layer\n * out (error handlers, or a layer with no active parent trace) — the helper\n * then leaves the active context untouched.\n */\nfunction getSpanForLayer(data: HandleChannelContext, options: ExpressIntegrationOptions): Span | undefined {\n const layer = data.self;\n const args = data.arguments;\n if (!layer || !Array.isArray(args)) {\n return undefined;\n }\n\n // Express only treats a 4-arg handler as an error handler and skips it in\n // the normal request pipeline; match the OTel integration and don't trace it.\n if (layer.handle?.length === 4) {\n return undefined;\n }\n\n // A Route dispatches to its handlers via the same `handle_request` method,\n // but those inner layers already run inside the route-dispatch layer's\n // `request_handler` span. They carry `.method` (and no `.route`); skip them\n // so we emit one span per route, matching the OTel Express integration.\n if (layer.method && !layer.route) {\n return undefined;\n }\n\n const req = args[0] as ExpressRequest | undefined;\n const res = args[1] as ExpressResponse | undefined;\n if (!req) {\n return undefined;\n }\n\n // No active parent span means this request is being ignored (unsampled /\n // filtered), so don't open a span\n if (!getActiveSpan()) {\n return undefined;\n }\n\n const type = getLayerType(layer);\n\n // Push this layer's registered path onto the request's chain so a\n // `request_handler` can reconstruct the full route with parameters intact\n // (`req.baseUrl` only exposes the *resolved* mount prefix). The matching pop\n // happens on `asyncStart` when the layer hands off via `next`.\n const registeredPath = getLayerRegisteredPath(layer);\n if (registeredPath != null) {\n pushLayerPath(req, registeredPath);\n data._sentryStoredLayer = true;\n }\n\n // `constructedRoute` (the full registered pattern) names the span/transaction;\n // `matchedRoute` (validated against the request URL) is the `http.route`.\n const constructedRoute = type === 'request_handler' ? getConstructedRoute(req) : undefined;\n const matchedRoute =\n type === 'request_handler' && constructedRoute != null ? getActualMatchedRoute(req, constructedRoute) : undefined;\n\n const name =\n type === 'request_handler'\n ? constructedRoute || 'request handler'\n : type === 'router'\n ? (layer.path ?? '/')\n : (layer.name ?? '<anonymous>');\n\n // Propagate the route to the root `http.server` span *before* the ignore\n // check, so the transaction is still named even when the layer's own span is\n // ignored — matches the OTel Express integration's `onRouteResolved` timing.\n if (matchedRoute) {\n setHttpServerSpanRoute(matchedRoute);\n }\n\n if (type === 'request_handler' && constructedRoute) {\n const isolationScope = getIsolationScope();\n if (isolationScope !== getDefaultIsolationScope()) {\n const method = typeof req.method === 'string' ? req.method.toUpperCase() : 'GET';\n isolationScope.setTransactionName(`${method} ${constructedRoute}`);\n } else {\n DEBUG_BUILD &&\n debug.warn(\n '[orchestrion:express] Isolation scope is still default isolation scope - skipping transaction name',\n );\n }\n }\n\n // Honor `ignoreLayers`/`ignoreLayersType`: skip the span for matching layers.\n // We intentionally do NOT pop the pushed path here (unlike OTel Express, which\n // pops on ignore): the path is still popped on `asyncStart` when the layer\n // calls `next`, so a following sibling isn't polluted, while an ignored\n // *router* keeps its mount prefix on the stack for the sub-stack it dispatches\n // — so routes under an ignored router stay correct. The only entries that\n // never pop come from layers that end the response without `next()`, and those\n // sit on a per-request store that is discarded when the request ends.\n if (isLayerIgnored(name, type, options)) {\n return undefined;\n }\n\n const span = startInactiveSpan({\n name,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.express`,\n [ATTR_EXPRESS_NAME]: name,\n [ATTR_EXPRESS_TYPE]: type,\n ...(matchedRoute ? { [HTTP_ROUTE]: matchedRoute } : {}),\n },\n });\n\n // A layer that sends the response (route handlers, typically) never calls\n // `next`, so the channel's `asyncEnd` never fires. End on the response's\n // `finish` in that case. When `next` *is* called, the helper ends the span\n // (via `asyncEnd`) and `beforeSpanEnd` removes this now-redundant listener.\n if (res && typeof res.once === 'function') {\n const onFinish = (): void => {\n span.end();\n };\n res.once('finish', onFinish);\n data._sentryCleanup = () => res.removeListener('finish', onFinish);\n }\n\n return span;\n}\n\nfunction getLayerType(layer: ExpressLayer): ExpressLayerType {\n if (layer.name === 'router') {\n return 'router';\n }\n // `bound dispatch` (v4) / `handle` — the route-dispatch layer created by `router.route()`.\n if (layer.name === 'bound dispatch' || layer.name === 'handle') {\n return 'request_handler';\n }\n return 'middleware';\n}\n\n/**\n * Propagate the resolved route to the root `http.server` span so the\n * transaction gets a parameterized `http.route`. Mirrors `@sentry/node`'s\n * `setHttpServerSpanRouteAttribute`; inlined to keep this package free of\n * `@sentry/node` deps. No-op unless the root span is an `http.server` span.\n */\nfunction setHttpServerSpanRoute(route: string): void {\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n if (!rootSpan) {\n return;\n }\n if (spanToJSON(rootSpan).data[SEMANTIC_ATTRIBUTE_SENTRY_OP] !== 'http.server') {\n return;\n }\n rootSpan.setAttribute(HTTP_ROUTE, route);\n}\n\n/**\n * Whether a layer should be skipped per the `ignoreLayers`/`ignoreLayersType`\n * options. Matches `@sentry/core`'s Express `isLayerIgnored`: `ignoreLayersType`\n * filters by layer type, `ignoreLayers` matches the layer's name against\n * string/RegExp/predicate patterns (exact string match).\n */\nfunction isLayerIgnored(name: string, type: ExpressLayerType, options: ExpressIntegrationOptions): boolean {\n const { ignoreLayers, ignoreLayersType } = options;\n\n if (Array.isArray(ignoreLayersType) && ignoreLayersType.includes(type)) {\n return true;\n }\n\n if (!Array.isArray(ignoreLayers)) {\n return false;\n }\n\n try {\n return stringMatchesSomePattern(name, ignoreLayers, true);\n } catch {\n return false;\n }\n}\n"],"names":[],"mappings":"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"} |
| const layerRegisteredPaths = /* @__PURE__ */ new WeakMap(); | ||
| function setLayerRegisteredPath(layer, path) { | ||
| layerRegisteredPaths.set(layer, path); | ||
| } | ||
| function getLayerRegisteredPath(layer) { | ||
| return layerRegisteredPaths.get(layer); | ||
| } | ||
| const requestLayerPaths = /* @__PURE__ */ new WeakMap(); | ||
| function getStore(req) { | ||
| let store = requestLayerPaths.get(req); | ||
| if (!store) { | ||
| store = []; | ||
| requestLayerPaths.set(req, store); | ||
| } | ||
| return store; | ||
| } | ||
| function pushLayerPath(req, path) { | ||
| getStore(req).push(path); | ||
| } | ||
| function popLayerPath(req) { | ||
| getStore(req).pop(); | ||
| } | ||
| function getLayerPath(args) { | ||
| const firstArg = args[0]; | ||
| if (Array.isArray(firstArg)) { | ||
| return firstArg.map((segment) => extractLayerPathSegment(segment) ?? "").join(","); | ||
| } | ||
| return extractLayerPathSegment(firstArg); | ||
| } | ||
| function extractLayerPathSegment(segment) { | ||
| return typeof segment === "string" ? segment : segment instanceof RegExp || typeof segment === "number" ? String(segment) : void 0; | ||
| } | ||
| function getConstructedRoute(req) { | ||
| const layersStore = getStore(req); | ||
| let constructedRoute = ""; | ||
| for (const path of layersStore) { | ||
| if (path === "/" || path === "/*") { | ||
| continue; | ||
| } | ||
| constructedRoute += !constructedRoute || constructedRoute.endsWith("/") ? path : `/${path}`; | ||
| } | ||
| return constructedRoute.replace(/\/{2,}/g, "/"); | ||
| } | ||
| function getActualMatchedRoute(req, constructedRoute) { | ||
| const layersStore = getStore(req); | ||
| if (layersStore.length === 0) { | ||
| return void 0; | ||
| } | ||
| const originalUrl = typeof req.originalUrl === "string" ? req.originalUrl : ""; | ||
| if (layersStore.every((path) => path === "/")) { | ||
| return originalUrl === "/" ? "/" : void 0; | ||
| } | ||
| if (constructedRoute === "*") { | ||
| return constructedRoute; | ||
| } | ||
| if (constructedRoute.includes("/") && (constructedRoute.includes(",") || constructedRoute.includes("\\") || constructedRoute.includes("*") || constructedRoute.includes("["))) { | ||
| return constructedRoute; | ||
| } | ||
| const normalizedRoute = constructedRoute.startsWith("/") ? constructedRoute : `/${constructedRoute}`; | ||
| const isValidRoute = normalizedRoute.length > 0 && (originalUrl === normalizedRoute || originalUrl.startsWith(normalizedRoute) || isRoutePattern(normalizedRoute)); | ||
| return isValidRoute ? normalizedRoute : void 0; | ||
| } | ||
| function isRoutePattern(route) { | ||
| return route.includes(":") || route.includes("*"); | ||
| } | ||
| export { getActualMatchedRoute, getConstructedRoute, getLayerPath, getLayerRegisteredPath, popLayerPath, pushLayerPath, setLayerRegisteredPath }; | ||
| //# sourceMappingURL=route.js.map |
| {"version":3,"file":"route.js","sources":["../../../../../src/integrations/tracing-channel/express/route.ts"],"sourcesContent":["import type { ExpressLayer, ExpressRequest } from './types';\n\n/**\n * Registered path *pattern* per routing `Layer`, captured when the layer is\n * registered via `Router.prototype.route`/`.use`. `undefined` means the layer\n * was registered without an explicit path (e.g. `app.use(mw)`), so it does not\n * contribute to the reconstructed route.\n *\n * This is the piece `req.baseUrl` can't provide: at request time `req.baseUrl`\n * holds the *resolved* mount prefix (`/api/v1`), whereas we want the registered\n * pattern (`/api/:version`).\n */\nconst layerRegisteredPaths = new WeakMap<ExpressLayer, string | undefined>();\n\n/** Record the path pattern a layer was registered with. */\nexport function setLayerRegisteredPath(layer: ExpressLayer, path: string | undefined): void {\n layerRegisteredPaths.set(layer, path);\n}\n\n/** Read the path pattern a layer was registered with, if any. */\nexport function getLayerRegisteredPath(layer: ExpressLayer): string | undefined {\n return layerRegisteredPaths.get(layer);\n}\n\n/**\n * Per-request ordered stack of the registered path patterns of the layers\n * currently on the matched chain. Layers push on entry and pop when they hand\n * off via `next`, so at any point it reflects the path from the app root down\n * to the currently-executing layer. `WeakMap` so entries are released with the\n * request.\n */\nconst requestLayerPaths = new WeakMap<ExpressRequest, string[]>();\n\nfunction getStore(req: ExpressRequest): string[] {\n let store = requestLayerPaths.get(req);\n if (!store) {\n store = [];\n requestLayerPaths.set(req, store);\n }\n return store;\n}\n\n/** Push a layer's registered path onto the request's chain. */\nexport function pushLayerPath(req: ExpressRequest, path: string): void {\n getStore(req).push(path);\n}\n\n/** Pop the most recently pushed layer path off the request's chain. */\nexport function popLayerPath(req: ExpressRequest): void {\n getStore(req).pop();\n}\n\n/**\n * The path pattern a `route`/`use` call registered, derived from its arguments.\n * A leading string/RegExp/number path becomes the pattern (arrays are joined\n * with `,`); a bare handler function yields `undefined`. Kept in sync with\n * `@sentry/core`'s Express `getLayerPath`.\n */\nexport function getLayerPath(args: unknown[]): string | undefined {\n const firstArg = args[0];\n if (Array.isArray(firstArg)) {\n return firstArg.map(segment => extractLayerPathSegment(segment) ?? '').join(',');\n }\n return extractLayerPathSegment(firstArg);\n}\n\nfunction extractLayerPathSegment(segment: unknown): string | undefined {\n return typeof segment === 'string'\n ? segment\n : segment instanceof RegExp || typeof segment === 'number'\n ? String(segment)\n : undefined;\n}\n\n/**\n * Concatenate the stored layer paths into the full route pattern (parameters\n * preserved), e.g. `/api/:version/user`. Mirrors `@sentry/core`.\n */\nexport function getConstructedRoute(req: ExpressRequest): string {\n const layersStore = getStore(req);\n\n let constructedRoute = '';\n for (const path of layersStore) {\n if (path === '/' || path === '/*') {\n continue;\n }\n constructedRoute += !constructedRoute || constructedRoute.endsWith('/') ? path : `/${path}`;\n }\n\n return constructedRoute.replace(/\\/{2,}/g, '/');\n}\n\n/**\n * Validate the constructed route against the request URL, returning it only\n * when it plausibly corresponds to a real match (otherwise `undefined`). Mirrors\n * `@sentry/core`'s `getActualMatchedRoute` — used for the `http.route` attribute.\n */\nexport function getActualMatchedRoute(req: ExpressRequest, constructedRoute: string): string | undefined {\n const layersStore = getStore(req);\n\n if (layersStore.length === 0) {\n return undefined;\n }\n\n const originalUrl = typeof req.originalUrl === 'string' ? req.originalUrl : '';\n\n // The layer store also includes root paths in case a non-existing url was requested.\n if (layersStore.every(path => path === '/')) {\n return originalUrl === '/' ? '/' : undefined;\n }\n\n if (constructedRoute === '*') {\n return constructedRoute;\n }\n\n // For RegExp routes or route arrays, return the constructed route as-is.\n if (\n constructedRoute.includes('/') &&\n (constructedRoute.includes(',') ||\n constructedRoute.includes('\\\\') ||\n constructedRoute.includes('*') ||\n constructedRoute.includes('['))\n ) {\n return constructedRoute;\n }\n\n const normalizedRoute = constructedRoute.startsWith('/') ? constructedRoute : `/${constructedRoute}`;\n\n const isValidRoute =\n normalizedRoute.length > 0 &&\n (originalUrl === normalizedRoute || originalUrl.startsWith(normalizedRoute) || isRoutePattern(normalizedRoute));\n\n return isValidRoute ? normalizedRoute : undefined;\n}\n\n/** Whether a route contains parameter/wildcard patterns (`:id`, `*`). */\nfunction isRoutePattern(route: string): boolean {\n return route.includes(':') || route.includes('*');\n}\n"],"names":[],"mappings":"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"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { defineIntegration, waitForTracingChannelBinding, debug, addGoogleGenAIResponseAttributes, resolveAIRecordingOptions, instrumentGoogleGenAIStream, _INTERNAL_shouldSkipAiProviderWrapping, getActiveSpan, spanToJSON, shouldEnableTruncation, extractGoogleGenAIRequestAttributes, GEN_AI_REQUEST_MODEL_ATTRIBUTE, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan, addGoogleGenAIRequestAttributes } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
| import { CHANNELS } from '../../orchestrion/channels.js'; | ||
| import { bindTracingChannelToSpan } from '../../tracing-channel.js'; | ||
| const INTEGRATION_NAME = "Google_GenAI"; | ||
| const ORIGIN = "auto.ai.orchestrion.google_genai"; | ||
| const INSTRUMENTED_CHANNELS = [ | ||
| { channel: CHANNELS.GOOGLE_GENAI_GENERATE_CONTENT, operation: "generate_content" }, | ||
| { channel: CHANNELS.GOOGLE_GENAI_EMBED_CONTENT, operation: "embeddings" }, | ||
| { channel: CHANNELS.GOOGLE_GENAI_CHAT, operation: "chat" } | ||
| ]; | ||
| let subscribed = false; | ||
| const _googleGenAIChannelIntegration = ((options = {}) => { | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel || subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| waitForTracingChannelBinding(() => { | ||
| for (const { channel, operation } of INSTRUMENTED_CHANNELS) { | ||
| DEBUG_BUILD && debug.log(`[orchestrion:google-genai] subscribing to channel "${channel}"`); | ||
| bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(channel), | ||
| (data) => createGenAiSpan(data, operation, options), | ||
| { | ||
| beforeSpanEnd: (span, data) => { | ||
| if (operation !== "embeddings") { | ||
| addGoogleGenAIResponseAttributes( | ||
| span, | ||
| data.result, | ||
| resolveAIRecordingOptions(options).recordOutputs | ||
| ); | ||
| } | ||
| }, | ||
| deferSpanEnd: ({ span, data }) => wrapStreamResult(span, data, options) | ||
| } | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| function createGenAiSpan(data, operation, options) { | ||
| if (_INTERNAL_shouldSkipAiProviderWrapping(INTEGRATION_NAME)) { | ||
| return void 0; | ||
| } | ||
| if (operation !== "chat") { | ||
| const activeSpan = getActiveSpan(); | ||
| if (activeSpan) { | ||
| const { op, origin } = spanToJSON(activeSpan); | ||
| if (origin === ORIGIN && op === "gen_ai.chat") { | ||
| return void 0; | ||
| } | ||
| } | ||
| } | ||
| const args = data.arguments ?? []; | ||
| const params = args[0]; | ||
| const { recordInputs } = resolveAIRecordingOptions(options); | ||
| const enableTruncation = shouldEnableTruncation(options.enableTruncation); | ||
| const attributes = extractGoogleGenAIRequestAttributes(operation, params, data.self); | ||
| const model = attributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] || "unknown"; | ||
| attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN; | ||
| const span = startInactiveSpan({ | ||
| name: `${operation} ${model}`, | ||
| op: `gen_ai.${operation}`, | ||
| attributes | ||
| }); | ||
| if (recordInputs && params) { | ||
| addGoogleGenAIRequestAttributes(span, params, operation, enableTruncation); | ||
| } | ||
| return span; | ||
| } | ||
| function isAsyncIterable(value) { | ||
| return !!value && typeof value[Symbol.asyncIterator] === "function"; | ||
| } | ||
| function wrapStreamResult(span, data, options) { | ||
| const result = data.result; | ||
| if (!isAsyncIterable(result)) { | ||
| return false; | ||
| } | ||
| const { recordOutputs } = resolveAIRecordingOptions(options); | ||
| const iterate = result[Symbol.asyncIterator].bind(result); | ||
| const instrumented = instrumentGoogleGenAIStream({ [Symbol.asyncIterator]: iterate }, span, recordOutputs ?? false); | ||
| result[Symbol.asyncIterator] = () => instrumented; | ||
| return true; | ||
| } | ||
| const googleGenAIChannelIntegration = defineIntegration(_googleGenAIChannelIntegration); | ||
| export { googleGenAIChannelIntegration }; | ||
| //# sourceMappingURL=google-genai.js.map |
| {"version":3,"file":"google-genai.js","sources":["../../../../src/integrations/tracing-channel/google-genai.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { GoogleGenAIOptions, GoogleGenAIResponse, IntegrationFn, Span } from '@sentry/core';\nimport {\n _INTERNAL_shouldSkipAiProviderWrapping,\n addGoogleGenAIRequestAttributes,\n addGoogleGenAIResponseAttributes,\n debug,\n defineIntegration,\n extractGoogleGenAIRequestAttributes,\n GEN_AI_REQUEST_MODEL_ATTRIBUTE,\n getActiveSpan,\n instrumentGoogleGenAIStream,\n resolveAIRecordingOptions,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n spanToJSON,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Same name as the OTel integration by design: when enabled, the OTel 'Google_GenAI'\n// integration is dropped from the default set (see the Node opt-in loader).\nconst INTEGRATION_NAME = 'Google_GenAI' as const;\n\n// Distinct from the proxy's `auto.ai.google_genai` so spans from the orchestrion path\n// are attributable separately from the OTel/proxy one.\nconst ORIGIN = 'auto.ai.orchestrion.google_genai';\n\n// Each instrumented method maps to the gen_ai operation its span reports.\nconst INSTRUMENTED_CHANNELS = [\n { channel: CHANNELS.GOOGLE_GENAI_GENERATE_CONTENT, operation: 'generate_content' },\n { channel: CHANNELS.GOOGLE_GENAI_EMBED_CONTENT, operation: 'embeddings' },\n { channel: CHANNELS.GOOGLE_GENAI_CHAT, operation: 'chat' },\n] as const;\n\ninterface GoogleGenAIChannelContext {\n arguments: unknown[];\n // The transform stashes the call's `this` here, which chat methods need since the model lives on\n // the `Chat` instance (`this.model`/`this.modelVersion`), not in the call arguments.\n self?: unknown;\n result?: unknown;\n}\n\nlet subscribed = false;\n\nconst _googleGenAIChannelIntegration = ((options: GoogleGenAIOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19, and a second `init()` would double-subscribe.\n if (!diagnosticsChannel.tracingChannel || subscribed) {\n return;\n }\n subscribed = true;\n\n // `bindTracingChannelToSpan` needs the async-context binding that `initOpenTelemetry()` registers\n // after `setupOnce` runs, so wait for it before subscribing.\n waitForTracingChannelBinding(() => {\n for (const { channel, operation } of INSTRUMENTED_CHANNELS) {\n DEBUG_BUILD && debug.log(`[orchestrion:google-genai] subscribing to channel \"${channel}\"`);\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<GoogleGenAIChannelContext>(channel),\n data => createGenAiSpan(data, operation, options),\n {\n beforeSpanEnd: (span, data) => {\n // Embeddings responses carry no content attributes.\n if (operation !== 'embeddings') {\n addGoogleGenAIResponseAttributes(\n span,\n data.result as GoogleGenAIResponse,\n resolveAIRecordingOptions(options).recordOutputs,\n );\n }\n },\n deferSpanEnd: ({ span, data }) => wrapStreamResult(span, data, options),\n },\n );\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Build the span for an instrumented call.\n * Returning `undefined` opts the payload out so no span is opened.\n */\nfunction createGenAiSpan(\n data: GoogleGenAIChannelContext,\n operation: string,\n options: GoogleGenAIOptions,\n): Span | undefined {\n // When another provider (e.g. LangChain) is driving the SDK, it records the spans itself and marks this\n // provider as skipped; skip here to avoid double spans.\n if (_INTERNAL_shouldSkipAiProviderWrapping(INTEGRATION_NAME)) {\n return undefined;\n }\n\n // `chat.sendMessage()`/`sendMessageStream()` internally call `Models.generateContent(Stream)`, which\n // publishes the `generate-content` channel while the chat span is active. Skip that nested event so a\n // chat call yields a single `gen_ai.chat` span instead of a chat span wrapping a generate_content one.\n if (operation !== 'chat') {\n const activeSpan = getActiveSpan();\n if (activeSpan) {\n const { op, origin } = spanToJSON(activeSpan);\n if (origin === ORIGIN && op === 'gen_ai.chat') {\n return undefined;\n }\n }\n }\n\n const args = data.arguments ?? [];\n const params = args[0] as Record<string, unknown> | undefined;\n\n const { recordInputs } = resolveAIRecordingOptions(options);\n const enableTruncation = shouldEnableTruncation(options.enableTruncation);\n\n const attributes = extractGoogleGenAIRequestAttributes(operation, params, data.self);\n const model = (attributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] as string) || 'unknown';\n attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN;\n\n const span = startInactiveSpan({\n name: `${operation} ${model}`,\n op: `gen_ai.${operation}`,\n attributes,\n });\n\n if (recordInputs && params) {\n addGoogleGenAIRequestAttributes(span, params, operation, enableTruncation);\n }\n\n return span;\n}\n\ntype AsyncIterableStream = { [Symbol.asyncIterator]: () => AsyncIterator<unknown> };\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterableStream {\n return !!value && typeof (value as AsyncIterableStream)[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * Only the streaming methods (`generateContentStream`/`sendMessageStream`) resolve to an async iterable.\n * For a stream we patch `result[Symbol.asyncIterator]` in place so `instrumentGoogleGenAIStream` ends the\n * span when iteration finishes.\n */\nfunction wrapStreamResult(span: Span, data: GoogleGenAIChannelContext, options: GoogleGenAIOptions): boolean {\n const result = data.result;\n if (!isAsyncIterable(result)) {\n return false;\n }\n\n const { recordOutputs } = resolveAIRecordingOptions(options);\n const iterate = result[Symbol.asyncIterator].bind(result);\n const instrumented = instrumentGoogleGenAIStream({ [Symbol.asyncIterator]: iterate }, span, recordOutputs ?? false);\n result[Symbol.asyncIterator] = () => instrumented;\n\n return true;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven Google GenAI integration. Subscribes to the\n * `orchestrion:@google/genai:*` diagnostics_channels injected into the SDK's `Models`\n * (`generateContent`/`generateContentStream`/`embedContent`) and `Chat`\n * (`sendMessage`/`sendMessageStream`) methods, so it requires the orchestrion runtime hook or\n * bundler plugin.\n */\nexport const googleGenAIChannelIntegration = defineIntegration(_googleGenAIChannelIntegration);\n"],"names":[],"mappings":"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"} |
| const ORIGIN = "auto.graphql.diagnostic_channel"; | ||
| const SPAN_NAME_PARSE = "graphql.parse"; | ||
| const SPAN_NAME_VALIDATE = "graphql.validate"; | ||
| const SPAN_NAME_EXECUTE = "graphql.execute"; | ||
| const SPAN_NAME_RESOLVE = "graphql.resolve"; | ||
| const GRAPHQL_FIELD_NAME = "graphql.field.name"; | ||
| const GRAPHQL_FIELD_PATH = "graphql.field.path"; | ||
| const GRAPHQL_FIELD_TYPE = "graphql.field.type"; | ||
| const GRAPHQL_PARENT_NAME = "graphql.parent.name"; | ||
| const GRAPHQL_DATA_SYMBOL = /* @__PURE__ */ Symbol.for("opentelemetry.graphql_data"); | ||
| const GRAPHQL_PATCHED_SYMBOL = /* @__PURE__ */ Symbol.for("opentelemetry.patched"); | ||
| export { GRAPHQL_DATA_SYMBOL, GRAPHQL_FIELD_NAME, GRAPHQL_FIELD_PATH, GRAPHQL_FIELD_TYPE, GRAPHQL_PARENT_NAME, GRAPHQL_PATCHED_SYMBOL, ORIGIN, SPAN_NAME_EXECUTE, SPAN_NAME_PARSE, SPAN_NAME_RESOLVE, SPAN_NAME_VALIDATE }; | ||
| //# sourceMappingURL=constants.js.map |
| {"version":3,"file":"constants.js","sources":["../../../../../src/integrations/tracing-channel/graphql/constants.ts"],"sourcesContent":["/*\n * These mirror the constants in `@sentry/server-utils`'s native graphql subscriber\n * (`src/graphql/graphql-dc-subscriber.ts`) so the orchestrion path (graphql v14–16) and the native\n * `diagnostics_channel` path (graphql >= 17) emit identical spans — same origin, span names and\n * field-attribute keys. `graphql.document`/`graphql.operation.*` and the span `op` come from\n * `@sentry/conventions` directly and are imported where used.\n */\n\nexport const ORIGIN = 'auto.graphql.diagnostic_channel';\n\nexport const SPAN_NAME_PARSE = 'graphql.parse';\nexport const SPAN_NAME_VALIDATE = 'graphql.validate';\nexport const SPAN_NAME_EXECUTE = 'graphql.execute';\nexport const SPAN_NAME_RESOLVE = 'graphql.resolve';\n\n// Field-level resolver-span attributes; not in `@sentry/conventions`.\nexport const GRAPHQL_FIELD_NAME = 'graphql.field.name';\nexport const GRAPHQL_FIELD_PATH = 'graphql.field.path';\nexport const GRAPHQL_FIELD_TYPE = 'graphql.field.type';\nexport const GRAPHQL_PARENT_NAME = 'graphql.parent.name';\n\n// `Symbol.for` keys shared with any co-resident OTel graphql instrumentation on purpose: the paths are\n// mutually exclusive at runtime, and reusing the key keeps nested-execute detection and resolver\n// parenting consistent if both ever load.\nexport const GRAPHQL_DATA_SYMBOL = Symbol.for('opentelemetry.graphql_data');\nexport const GRAPHQL_PATCHED_SYMBOL = Symbol.for('opentelemetry.patched');\n"],"names":[],"mappings":"AAQO,MAAM,MAAA,GAAS;AAEf,MAAM,eAAA,GAAkB;AACxB,MAAM,kBAAA,GAAqB;AAC3B,MAAM,iBAAA,GAAoB;AAC1B,MAAM,iBAAA,GAAoB;AAG1B,MAAM,kBAAA,GAAqB;AAC3B,MAAM,kBAAA,GAAqB;AAC3B,MAAM,kBAAA,GAAqB;AAC3B,MAAM,mBAAA,GAAsB;AAK5B,MAAM,mBAAA,mBAAsB,MAAA,CAAO,GAAA,CAAI,4BAA4B;AACnE,MAAM,sBAAA,mBAAyB,MAAA,CAAO,GAAA,CAAI,uBAAuB;;;;"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { defineIntegration, waitForTracingChannelBinding, debug, extendIntegration } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../../debug-build.js'; | ||
| import { graphqlIntegration } from '../../../graphql/index.js'; | ||
| import { CHANNELS } from '../../../orchestrion/channels.js'; | ||
| import { bindTracingChannelToSpan } from '../../../tracing-channel.js'; | ||
| import { startParseSpan, finalizeValidateSpan, startValidateSpan, finalizeExecuteSpan, startExecuteSpan } from './spans.js'; | ||
| const INTEGRATION_NAME = "Graphql"; | ||
| function getOptionsWithDefaults(options) { | ||
| return { | ||
| ignoreResolveSpans: options.ignoreResolveSpans !== false, | ||
| ignoreTrivialResolveSpans: options.ignoreTrivialResolveSpans !== false, | ||
| useOperationNameForRootSpan: options.useOperationNameForRootSpan !== false | ||
| }; | ||
| } | ||
| function safe(fn) { | ||
| try { | ||
| return fn(); | ||
| } catch (error) { | ||
| DEBUG_BUILD && debug.warn("[orchestrion:graphql] error building span", error); | ||
| return void 0; | ||
| } | ||
| } | ||
| const _graphqlChannelIntegration = ((options = {}) => { | ||
| const config = getOptionsWithDefaults(options); | ||
| const getConfig = () => config; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| waitForTracingChannelBinding(() => { | ||
| bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(CHANNELS.GRAPHQL_PARSE), | ||
| () => safe(() => startParseSpan()) | ||
| ); | ||
| bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(CHANNELS.GRAPHQL_VALIDATE), | ||
| (data) => safe(() => startValidateSpan(data.arguments[1])), | ||
| { beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) } | ||
| ); | ||
| bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(CHANNELS.GRAPHQL_EXECUTE), | ||
| (data) => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)), | ||
| { beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) } | ||
| ); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const graphqlChannelIntegration = defineIntegration(_graphqlChannelIntegration); | ||
| const graphqlDiagnosticsChannelIntegration = (options) => { | ||
| const orchestrion = graphqlChannelIntegration(options); | ||
| return extendIntegration(graphqlIntegration(options), { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce: () => orchestrion.setupOnce?.() | ||
| }); | ||
| }; | ||
| export { graphqlChannelIntegration, graphqlDiagnosticsChannelIntegration }; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing-channel/graphql/index.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';\nimport { DEBUG_BUILD } from '../../../debug-build';\nimport { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql';\nimport type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber';\nimport { CHANNELS } from '../../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../../tracing-channel';\nimport {\n finalizeExecuteSpan,\n finalizeValidateSpan,\n startExecuteSpan,\n startParseSpan,\n startValidateSpan,\n} from './spans';\nimport type { GraphqlResolvedConfig } from './types';\n\n// Same name as the OTel/native integration by design, so enabling injection swaps this in for it.\nconst INTEGRATION_NAME = 'Graphql' as const;\n\n// The context orchestrion's transform attaches to each channel: `arguments` is the live args of the\n// wrapped call, `result` the settled return value.\ninterface GraphqlChannelContext {\n arguments: unknown[];\n self?: unknown;\n result?: unknown;\n error?: unknown;\n}\n\nfunction getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): GraphqlResolvedConfig {\n return {\n ignoreResolveSpans: options.ignoreResolveSpans !== false,\n ignoreTrivialResolveSpans: options.ignoreTrivialResolveSpans !== false,\n useOperationNameForRootSpan: options.useOperationNameForRootSpan !== false,\n };\n}\n\n/**\n * Runs a span-building callback so a throw inside it can never break the user's graphql call: these\n * run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan`\n * producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call.\n */\nfunction safe<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch (error) {\n DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error);\n return undefined;\n }\n}\n\nconst _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {\n const config = getOptionsWithDefaults(options);\n const getConfig = (): GraphqlResolvedConfig => config;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_PARSE), () =>\n safe(() => startParseSpan()),\n );\n\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_VALIDATE),\n data => safe(() => startValidateSpan(data.arguments[1])),\n { beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) },\n );\n\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_EXECUTE),\n data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),\n { beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) },\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * EXPERIMENTAL — orchestrion-driven graphql integration for graphql v14–16 (v17 publishes native\n * `diagnostics_channel` events handled by `@sentry/server-utils`'s graphql integration instead).\n *\n * Subscribes to the `orchestrion:graphql:{parse,validate,execute}` channels the orchestrion code\n * transform injects into `graphql`'s `language/parser.js`, `validation/validate.js` and\n * `execution/execute.js`, emitting spans identical to the native path. Requires the orchestrion\n * runtime hook or bundler plugin — wire it up via `experimentalUseDiagnosticsChannelInjection()`.\n *\n * @experimental\n */\nexport const graphqlChannelIntegration = defineIntegration(_graphqlChannelIntegration);\n\n/**\n * The complete graphql diagnostics-channel integration: the native subscriber (graphql v17) composed\n * with the orchestrion subscriber (v14–16), so opting into injection instruments every supported\n * version via diagnostics channels without the OTel patcher. Reuses the OTel `Graphql` name so\n * enabling injection swaps this in for it.\n */\nexport const graphqlDiagnosticsChannelIntegration = (options?: GraphqlDiagnosticChannelsOptions) => {\n const orchestrion = graphqlChannelIntegration(options);\n return extendIntegration(graphqlNativeIntegration(options), {\n name: INTEGRATION_NAME,\n setupOnce: () => orchestrion.setupOnce?.(),\n });\n};\n"],"names":["graphqlNativeIntegration"],"mappings":";;;;;;;;AAkBA,MAAM,gBAAA,GAAmB,SAAA;AAWzB,SAAS,uBAAuB,OAAA,EAAkE;AAChG,EAAA,OAAO;AAAA,IACL,kBAAA,EAAoB,QAAQ,kBAAA,KAAuB,KAAA;AAAA,IACnD,yBAAA,EAA2B,QAAQ,yBAAA,KAA8B,KAAA;AAAA,IACjE,2BAAA,EAA6B,QAAQ,2BAAA,KAAgC;AAAA,GACvE;AACF;AAOA,SAAS,KAAQ,EAAA,EAA4B;AAC3C,EAAA,IAAI;AACF,IAAA,OAAO,EAAA,EAAG;AAAA,EACZ,SAAS,KAAA,EAAO;AACd,IAAA,WAAA,IAAe,KAAA,CAAM,IAAA,CAAK,2CAAA,EAA6C,KAAK,CAAA;AAC5E,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAEA,MAAM,0BAAA,IAA8B,CAAC,OAAA,GAA4C,EAAC,KAAM;AACtF,EAAA,MAAM,MAAA,GAAS,uBAAuB,OAAO,CAAA;AAC7C,EAAA,MAAM,YAAY,MAA6B,MAAA;AAE/C,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,IAAI,CAAC,mBAAmB,cAAA,EAAgB;AACtC,QAAA;AAAA,MACF;AAEA,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,wBAAA;AAAA,UAAyB,kBAAA,CAAmB,cAAA,CAAsC,QAAA,CAAS,aAAa,CAAA;AAAA,UAAG,MACzG,IAAA,CAAK,MAAM,cAAA,EAAgB;AAAA,SAC7B;AAEA,QAAA,wBAAA;AAAA,UACE,kBAAA,CAAmB,cAAA,CAAsC,QAAA,CAAS,gBAAgB,CAAA;AAAA,UAClF,CAAA,IAAA,KAAQ,KAAK,MAAM,iBAAA,CAAkB,KAAK,SAAA,CAAU,CAAC,CAAC,CAAC,CAAA;AAAA,UACvD,EAAE,aAAA,EAAe,CAAC,IAAA,EAAM,IAAA,KAAS,KAAK,IAAA,CAAK,MAAM,oBAAA,CAAqB,IAAA,EAAM,IAAA,CAAK,MAAM,CAAC,CAAA;AAAE,SAC5F;AAEA,QAAA,wBAAA;AAAA,UACE,kBAAA,CAAmB,cAAA,CAAsC,QAAA,CAAS,eAAe,CAAA;AAAA,UACjF,CAAA,IAAA,KAAQ,IAAA,CAAK,MAAM,gBAAA,CAAiB,IAAA,CAAK,WAAW,IAAA,CAAK,IAAA,EAAM,MAAA,EAAQ,SAAS,CAAC,CAAA;AAAA,UACjF,EAAE,aAAA,EAAe,CAAC,IAAA,EAAM,IAAA,KAAS,KAAK,IAAA,CAAK,MAAM,mBAAA,CAAoB,IAAA,EAAM,IAAA,CAAK,MAAM,CAAC,CAAA;AAAE,SAC3F;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAaO,MAAM,yBAAA,GAA4B,kBAAkB,0BAA0B;AAQ9E,MAAM,oCAAA,GAAuC,CAAC,OAAA,KAA+C;AAClG,EAAA,MAAM,WAAA,GAAc,0BAA0B,OAAO,CAAA;AACrD,EAAA,OAAO,iBAAA,CAAkBA,kBAAA,CAAyB,OAAO,CAAA,EAAG;AAAA,IAC1D,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,EAAW,MAAM,WAAA,CAAY,SAAA;AAAY,GAC1C,CAAA;AACH;;;;"} |
| import { WEB_SERVER_GRAPHQL_SPAN_OP } from '@sentry/conventions/op'; | ||
| import { isObjectLike, withActiveSpan, SPAN_STATUS_ERROR, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
| import { GRAPHQL_PATCHED_SYMBOL, GRAPHQL_DATA_SYMBOL, GRAPHQL_PARENT_NAME, GRAPHQL_FIELD_TYPE, GRAPHQL_FIELD_PATH, GRAPHQL_FIELD_NAME, ORIGIN, SPAN_NAME_RESOLVE } from './constants.js'; | ||
| function isPromise(value) { | ||
| return typeof value?.then === "function"; | ||
| } | ||
| function wrapFields(type, getConfig) { | ||
| if (!type || type[GRAPHQL_PATCHED_SYMBOL]) { | ||
| return; | ||
| } | ||
| type[GRAPHQL_PATCHED_SYMBOL] = true; | ||
| const fields = type.getFields(); | ||
| Object.keys(fields).forEach((key) => { | ||
| const field = fields[key]; | ||
| if (!field) { | ||
| return; | ||
| } | ||
| if (field.resolve) { | ||
| field.resolve = wrapFieldResolver(getConfig, field.resolve); | ||
| } | ||
| if (field.type) { | ||
| for (const unwrappedType of unwrapType(field.type)) { | ||
| wrapFields(unwrappedType, getConfig); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| function wrapFieldResolver(getConfig, fieldResolver, isDefaultResolver = false) { | ||
| if (typeof fieldResolver !== "function" || fieldResolver[GRAPHQL_PATCHED_SYMBOL]) { | ||
| return fieldResolver; | ||
| } | ||
| function wrappedFieldResolver(source, args, rawContextValue, info) { | ||
| if (!fieldResolver) { | ||
| return void 0; | ||
| } | ||
| const contextValue = rawContextValue ?? {}; | ||
| const config = getConfig(); | ||
| if (config.ignoreTrivialResolveSpans && isDefaultResolver && (isObjectLike(source) || typeof source === "function")) { | ||
| const property = source[info.fieldName]; | ||
| if (typeof property !== "function") { | ||
| return fieldResolver.call(this, source, args, contextValue, info); | ||
| } | ||
| } | ||
| if (!contextValue[GRAPHQL_DATA_SYMBOL]) { | ||
| return fieldResolver.call(this, source, args, contextValue, info); | ||
| } | ||
| const path = pathToArray(info.path); | ||
| const { field, spanAdded } = createFieldIfNotExists(contextValue, info, path); | ||
| const span = field.span; | ||
| return withActiveSpan(span, () => { | ||
| try { | ||
| const res = fieldResolver.call(this, source, args, contextValue, info); | ||
| if (isPromise(res)) { | ||
| return res.then( | ||
| (r) => { | ||
| endResolveSpan(span, spanAdded); | ||
| return r; | ||
| }, | ||
| (err) => { | ||
| endResolveSpan(span, spanAdded, err); | ||
| throw err; | ||
| } | ||
| ); | ||
| } | ||
| endResolveSpan(span, spanAdded); | ||
| return res; | ||
| } catch (err) { | ||
| endResolveSpan(span, spanAdded, err); | ||
| throw err; | ||
| } | ||
| }); | ||
| } | ||
| wrappedFieldResolver[GRAPHQL_PATCHED_SYMBOL] = true; | ||
| return wrappedFieldResolver; | ||
| } | ||
| function endResolveSpan(span, shouldEndSpan, error) { | ||
| if (!shouldEndSpan) { | ||
| return; | ||
| } | ||
| if (error) { | ||
| span.setStatus({ code: SPAN_STATUS_ERROR, message: error.message }); | ||
| } | ||
| span.end(); | ||
| } | ||
| function createFieldIfNotExists(contextValue, info, path) { | ||
| const existing = getField(contextValue, path); | ||
| if (existing) { | ||
| return { field: existing, spanAdded: false }; | ||
| } | ||
| const field = { span: createResolverSpan(info, path, getParentFieldSpan(contextValue, path)) }; | ||
| addField(contextValue, path, field); | ||
| return { field, spanAdded: true }; | ||
| } | ||
| function createResolverSpan(info, path, parentSpan) { | ||
| const attributes = { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP, | ||
| [GRAPHQL_FIELD_NAME]: info.fieldName, | ||
| [GRAPHQL_FIELD_PATH]: path.join("."), | ||
| [GRAPHQL_FIELD_TYPE]: info.returnType.toString(), | ||
| [GRAPHQL_PARENT_NAME]: info.parentType.name | ||
| }; | ||
| return startInactiveSpan({ name: `${SPAN_NAME_RESOLVE} ${path.join(".")}`, attributes, parentSpan }); | ||
| } | ||
| function addField(contextValue, path, field) { | ||
| const data = contextValue[GRAPHQL_DATA_SYMBOL]; | ||
| if (data) { | ||
| data.fields[path.join(".")] = field; | ||
| } | ||
| } | ||
| function getField(contextValue, path) { | ||
| return contextValue[GRAPHQL_DATA_SYMBOL]?.fields[path.join(".")]; | ||
| } | ||
| function getParentFieldSpan(contextValue, path) { | ||
| for (let i = path.length - 1; i > 0; i--) { | ||
| const field = getField(contextValue, path.slice(0, i)); | ||
| if (field) { | ||
| return field.span; | ||
| } | ||
| } | ||
| return contextValue[GRAPHQL_DATA_SYMBOL]?.span; | ||
| } | ||
| function pathToArray(path) { | ||
| const flattened = []; | ||
| let curr = path; | ||
| while (curr) { | ||
| flattened.push(String(curr.key)); | ||
| curr = curr.prev; | ||
| } | ||
| return flattened.reverse(); | ||
| } | ||
| function unwrapType(type) { | ||
| if ("ofType" in type && type.ofType) { | ||
| return unwrapType(type.ofType); | ||
| } | ||
| if (isGraphQLUnionType(type)) { | ||
| return type.getTypes(); | ||
| } | ||
| if (isGraphQLObjectType(type)) { | ||
| return [type]; | ||
| } | ||
| return []; | ||
| } | ||
| function isGraphQLUnionType(type) { | ||
| return "getTypes" in type && typeof type.getTypes === "function"; | ||
| } | ||
| function isGraphQLObjectType(type) { | ||
| return "getFields" in type && typeof type.getFields === "function"; | ||
| } | ||
| function getOperation(document, operationName) { | ||
| const definitions = document?.definitions; | ||
| if (!definitions || !Array.isArray(definitions)) { | ||
| return void 0; | ||
| } | ||
| const isOperation = (def) => !!def?.operation && ["query", "mutation", "subscription"].indexOf(def.operation) !== -1; | ||
| if (operationName) { | ||
| return definitions.filter(isOperation).find((def) => operationName === def?.name?.value); | ||
| } | ||
| return definitions.find(isOperation); | ||
| } | ||
| export { getOperation, wrapFieldResolver, wrapFields }; | ||
| //# sourceMappingURL=resolvers.js.map |
| {"version":3,"file":"resolvers.js","sources":["../../../../../src/integrations/tracing-channel/graphql/resolvers.ts"],"sourcesContent":["/*\n * Resolver-span wrapping for the orchestrion graphql path. graphql v14–16 publishes no per-field\n * `resolve` channel (unlike v17's native one), so the schema's field resolvers are swapped for\n * span-creating proxies under the execute channel's `start` (the \"consumer trick\" — the transform\n * can't target user resolvers, but `execute` receives the schema, so we mutate it before the wrapped\n * call runs). Resolver spans use the same origin/op/field attributes as the native subscriber.\n */\n\nimport { WEB_SERVER_GRAPHQL_SPAN_OP } from '@sentry/conventions/op';\nimport type { Span, SpanAttributes } from '@sentry/core';\nimport {\n isObjectLike,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n withActiveSpan,\n} from '@sentry/core';\nimport {\n GRAPHQL_DATA_SYMBOL,\n GRAPHQL_FIELD_NAME,\n GRAPHQL_FIELD_PATH,\n GRAPHQL_FIELD_TYPE,\n GRAPHQL_PARENT_NAME,\n GRAPHQL_PATCHED_SYMBOL,\n ORIGIN,\n SPAN_NAME_RESOLVE,\n} from './constants';\nimport type {\n DefinitionNode,\n DocumentNode,\n GraphQLFieldResolver,\n GraphQLObjectType,\n GraphQLOutputType,\n GraphQLPath,\n GraphQLResolveInfo,\n GraphQLType,\n GraphQLUnionType,\n GraphqlResolvedConfig,\n Maybe,\n ObjectWithGraphQLData,\n Patched,\n} from './types';\n\nfunction isPromise(value: unknown): value is Promise<unknown> {\n return typeof (value as { then?: unknown } | undefined)?.then === 'function';\n}\n\n/**\n * Walks the query/mutation type tree and swaps each field's `resolve` for a span-creating proxy.\n * Idempotent per type via {@link GRAPHQL_PATCHED_SYMBOL}.\n */\nexport function wrapFields(type: Maybe<GraphQLObjectType & Patched>, getConfig: () => GraphqlResolvedConfig): void {\n if (!type || type[GRAPHQL_PATCHED_SYMBOL]) {\n return;\n }\n\n type[GRAPHQL_PATCHED_SYMBOL] = true;\n const fields = type.getFields();\n\n Object.keys(fields).forEach(key => {\n const field = fields[key];\n if (!field) {\n return;\n }\n\n if (field.resolve) {\n field.resolve = wrapFieldResolver(getConfig, field.resolve);\n }\n\n if (field.type) {\n for (const unwrappedType of unwrapType(field.type)) {\n wrapFields(unwrappedType, getConfig);\n }\n }\n });\n}\n\nexport function wrapFieldResolver(\n getConfig: () => GraphqlResolvedConfig,\n fieldResolver: Maybe<GraphQLFieldResolver & Patched>,\n isDefaultResolver = false,\n): GraphQLFieldResolver & Patched {\n // Return the resolver untouched if it isn't a function or is already a wrapped one — the wrapper we\n // return is marked with `GRAPHQL_PATCHED_SYMBOL` below precisely so it can be detected here. (The\n // guard must test the incoming `fieldResolver`, not the freshly-hoisted `wrappedFieldResolver`.)\n if (typeof fieldResolver !== 'function' || fieldResolver[GRAPHQL_PATCHED_SYMBOL]) {\n return fieldResolver as GraphQLFieldResolver;\n }\n\n function wrappedFieldResolver(\n this: unknown,\n source: unknown,\n args: unknown,\n rawContextValue: unknown,\n info: GraphQLResolveInfo,\n ): unknown {\n if (!fieldResolver) {\n return undefined;\n }\n\n const contextValue = (rawContextValue ?? {}) as ObjectWithGraphQLData;\n const config = getConfig();\n\n // Mirror graphql's own \"trivial resolver\" check: a default resolver that just reads a\n // non-function property is not worth a span.\n if (\n config.ignoreTrivialResolveSpans &&\n isDefaultResolver &&\n (isObjectLike(source) || typeof source === 'function')\n ) {\n const property = (source as Record<string, unknown>)[info.fieldName];\n if (typeof property !== 'function') {\n return fieldResolver.call(this, source, args, contextValue, info);\n }\n }\n\n if (!contextValue[GRAPHQL_DATA_SYMBOL]) {\n return fieldResolver.call(this, source, args, contextValue, info);\n }\n\n const path = pathToArray(info.path);\n const { field, spanAdded } = createFieldIfNotExists(contextValue, info, path);\n const span = field.span;\n\n return withActiveSpan(span, () => {\n try {\n const res = fieldResolver.call(this, source, args, contextValue, info);\n if (isPromise(res)) {\n return res.then(\n r => {\n endResolveSpan(span, spanAdded);\n return r;\n },\n (err: Error) => {\n endResolveSpan(span, spanAdded, err);\n throw err;\n },\n );\n }\n endResolveSpan(span, spanAdded);\n return res;\n } catch (err) {\n endResolveSpan(span, spanAdded, err as Error);\n throw err;\n }\n });\n }\n\n (wrappedFieldResolver as Patched)[GRAPHQL_PATCHED_SYMBOL] = true;\n return wrappedFieldResolver;\n}\n\nfunction endResolveSpan(span: Span, shouldEndSpan: boolean, error?: Error): void {\n if (!shouldEndSpan) {\n return;\n }\n if (error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: error.message });\n }\n span.end();\n}\n\nfunction createFieldIfNotExists(\n contextValue: ObjectWithGraphQLData,\n info: GraphQLResolveInfo,\n path: string[],\n): { field: { span: Span }; spanAdded: boolean } {\n const existing = getField(contextValue, path);\n if (existing) {\n return { field: existing, spanAdded: false };\n }\n\n const field = { span: createResolverSpan(info, path, getParentFieldSpan(contextValue, path)) };\n addField(contextValue, path, field);\n return { field, spanAdded: true };\n}\n\nfunction createResolverSpan(info: GraphQLResolveInfo, path: string[], parentSpan?: Span): Span {\n const attributes: SpanAttributes = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n [GRAPHQL_FIELD_NAME]: info.fieldName,\n [GRAPHQL_FIELD_PATH]: path.join('.'),\n [GRAPHQL_FIELD_TYPE]: info.returnType.toString(),\n [GRAPHQL_PARENT_NAME]: info.parentType.name,\n };\n\n return startInactiveSpan({ name: `${SPAN_NAME_RESOLVE} ${path.join('.')}`, attributes, parentSpan });\n}\n\nfunction addField(contextValue: ObjectWithGraphQLData, path: string[], field: { span: Span }): void {\n const data = contextValue[GRAPHQL_DATA_SYMBOL];\n if (data) {\n data.fields[path.join('.')] = field;\n }\n}\n\nfunction getField(contextValue: ObjectWithGraphQLData, path: string[]): { span: Span } | undefined {\n return contextValue[GRAPHQL_DATA_SYMBOL]?.fields[path.join('.')];\n}\n\nfunction getParentFieldSpan(contextValue: ObjectWithGraphQLData, path: string[]): Span | undefined {\n for (let i = path.length - 1; i > 0; i--) {\n const field = getField(contextValue, path.slice(0, i));\n if (field) {\n return field.span;\n }\n }\n return contextValue[GRAPHQL_DATA_SYMBOL]?.span;\n}\n\nfunction pathToArray(path: GraphQLPath): string[] {\n const flattened: string[] = [];\n let curr: GraphQLPath | undefined = path;\n while (curr) {\n flattened.push(String(curr.key));\n curr = curr.prev;\n }\n return flattened.reverse();\n}\n\nfunction unwrapType(type: GraphQLOutputType): readonly (GraphQLObjectType & Patched)[] {\n // The structural index signature widens `ofType` to `unknown`, so narrow it back explicitly.\n if ('ofType' in type && type.ofType) {\n return unwrapType(type.ofType as GraphQLOutputType);\n }\n if (isGraphQLUnionType(type)) {\n return type.getTypes();\n }\n if (isGraphQLObjectType(type)) {\n return [type];\n }\n return [];\n}\n\nfunction isGraphQLUnionType(type: GraphQLType): type is GraphQLUnionType {\n return 'getTypes' in type && typeof type.getTypes === 'function';\n}\n\nfunction isGraphQLObjectType(type: GraphQLType): type is GraphQLObjectType {\n return 'getFields' in type && typeof (type as GraphQLObjectType).getFields === 'function';\n}\n\n/**\n * Returns the operation definition for `operationName` (or the first operation) from a parsed\n * document, or `undefined` for schema documents / when no operation is present.\n */\nexport function getOperation(document: DocumentNode, operationName?: Maybe<string>): DefinitionNode | undefined {\n const definitions: readonly DefinitionNode[] | undefined = document?.definitions;\n if (!definitions || !Array.isArray(definitions)) {\n return undefined;\n }\n\n const isOperation = (def: DefinitionNode): boolean =>\n !!def?.operation && ['query', 'mutation', 'subscription'].indexOf(def.operation) !== -1;\n\n if (operationName) {\n return definitions.filter(isOperation).find((def: DefinitionNode) => operationName === def?.name?.value);\n }\n return definitions.find(isOperation);\n}\n"],"names":[],"mappings":"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"} |
| import { GRAPHQL_DOCUMENT, GRAPHQL_OPERATION_NAME, GRAPHQL_OPERATION_TYPE } from '@sentry/conventions/attributes'; | ||
| import { WEB_SERVER_GRAPHQL_SPAN_OP } from '@sentry/conventions/op'; | ||
| import { startInactiveSpan, SPAN_STATUS_ERROR, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
| import { redactGraphqlDocument, hasResultErrors, getOperationSpanName, renameRootSpanWithOperation } from '../../../graphql/utils.js'; | ||
| import { ORIGIN, SPAN_NAME_PARSE, SPAN_NAME_VALIDATE, GRAPHQL_DATA_SYMBOL, SPAN_NAME_EXECUTE } from './constants.js'; | ||
| import { wrapFieldResolver, wrapFields, getOperation } from './resolvers.js'; | ||
| const BASE_ATTRIBUTES = { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP | ||
| }; | ||
| function startParseSpan() { | ||
| return startInactiveSpan({ name: SPAN_NAME_PARSE, attributes: { ...BASE_ATTRIBUTES } }); | ||
| } | ||
| function startValidateSpan(documentAST) { | ||
| return startInactiveSpan({ | ||
| name: SPAN_NAME_VALIDATE, | ||
| attributes: { ...BASE_ATTRIBUTES, [GRAPHQL_DOCUMENT]: redactGraphqlDocument(documentAST) } | ||
| }); | ||
| } | ||
| function finalizeValidateSpan(span, result) { | ||
| if (Array.isArray(result) && result.length > 0) { | ||
| span.setStatus({ code: SPAN_STATUS_ERROR, message: "invalid_argument" }); | ||
| } | ||
| } | ||
| function normalizeExecuteArgs(argsArray) { | ||
| if (argsArray.length >= 2) { | ||
| return { | ||
| schema: argsArray[0 /* SCHEMA */], | ||
| document: argsArray[1 /* DOCUMENT */], | ||
| contextValue: argsArray[3 /* CONTEXT_VALUE */] ?? {}, | ||
| operationName: argsArray[5 /* OPERATION_NAME */], | ||
| fieldResolver: argsArray[6 /* FIELD_RESOLVER */], | ||
| writeBack: (contextValue, fieldResolver) => { | ||
| argsArray[3 /* CONTEXT_VALUE */] = contextValue; | ||
| argsArray[6 /* FIELD_RESOLVER */] = fieldResolver; | ||
| } | ||
| }; | ||
| } | ||
| const obj = argsArray[0] ?? {}; | ||
| return { | ||
| schema: obj.schema, | ||
| document: obj.document, | ||
| contextValue: obj.contextValue ?? {}, | ||
| operationName: obj.operationName, | ||
| fieldResolver: obj.fieldResolver, | ||
| writeBack: (contextValue, fieldResolver) => { | ||
| obj.contextValue = contextValue; | ||
| obj.fieldResolver = fieldResolver; | ||
| } | ||
| }; | ||
| } | ||
| function startExecuteSpan(argsArray, self, config, getConfig) { | ||
| const args = normalizeExecuteArgs(argsArray); | ||
| const { schema, document } = args; | ||
| let { contextValue, fieldResolver } = args; | ||
| const alreadyInstrumented = !!contextValue[GRAPHQL_DATA_SYMBOL]; | ||
| if (!config.ignoreResolveSpans && !alreadyInstrumented) { | ||
| const isUsingDefaultResolver = fieldResolver == null; | ||
| const defaultFieldResolver = self?.defaultFieldResolver; | ||
| const fieldResolverForExecute = fieldResolver ?? defaultFieldResolver; | ||
| if (fieldResolverForExecute) { | ||
| fieldResolver = wrapFieldResolver(getConfig, fieldResolverForExecute, isUsingDefaultResolver); | ||
| } | ||
| if (schema) { | ||
| wrapFields(schema.getQueryType(), getConfig); | ||
| wrapFields(schema.getMutationType(), getConfig); | ||
| } | ||
| } | ||
| const operation = getOperation(document, args.operationName); | ||
| const operationType = operation?.operation; | ||
| const operationName = operation?.name?.value ?? args.operationName ?? void 0; | ||
| const span = startInactiveSpan({ | ||
| name: getOperationSpanName(operationType, operationName || void 0, SPAN_NAME_EXECUTE), | ||
| attributes: { | ||
| ...BASE_ATTRIBUTES, | ||
| [GRAPHQL_OPERATION_TYPE]: operationType, | ||
| [GRAPHQL_OPERATION_NAME]: operationName || void 0, | ||
| [GRAPHQL_DOCUMENT]: redactGraphqlDocument(document) | ||
| } | ||
| }); | ||
| if (config.useOperationNameForRootSpan && operationType) { | ||
| renameRootSpanWithOperation(span, operationType, operationName || void 0); | ||
| } | ||
| contextValue[GRAPHQL_DATA_SYMBOL] = { source: document, span, fields: {} }; | ||
| args.writeBack(contextValue, fieldResolver); | ||
| return span; | ||
| } | ||
| function finalizeExecuteSpan(span, result) { | ||
| if (hasResultErrors(result)) { | ||
| span.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" }); | ||
| } | ||
| } | ||
| export { finalizeExecuteSpan, finalizeValidateSpan, startExecuteSpan, startParseSpan, startValidateSpan }; | ||
| //# sourceMappingURL=spans.js.map |
| {"version":3,"file":"spans.js","sources":["../../../../../src/integrations/tracing-channel/graphql/spans.ts"],"sourcesContent":["/*\n * Span builders for the orchestrion graphql channels (v14–16). They emit the same spans as the native\n * `diagnostics_channel` subscriber (`../../../graphql/graphql-dc-subscriber.ts`, graphql >= 17) by\n * reusing its `utils` and conventions — only the data source differs: here the payloads are the raw\n * arguments of the injected `parse`/`validate`/`execute` calls rather than graphql's native events.\n */\n\nimport { GRAPHQL_DOCUMENT, GRAPHQL_OPERATION_NAME, GRAPHQL_OPERATION_TYPE } from '@sentry/conventions/attributes';\nimport { WEB_SERVER_GRAPHQL_SPAN_OP } from '@sentry/conventions/op';\nimport type { Span } from '@sentry/core';\nimport {\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n} from '@sentry/core';\nimport type { GraphqlDocumentNode } from '../../../graphql/utils';\nimport {\n getOperationSpanName,\n hasResultErrors,\n redactGraphqlDocument,\n renameRootSpanWithOperation,\n} from '../../../graphql/utils';\nimport { GRAPHQL_DATA_SYMBOL, ORIGIN, SPAN_NAME_EXECUTE, SPAN_NAME_PARSE, SPAN_NAME_VALIDATE } from './constants';\nimport { getOperation, wrapFields, wrapFieldResolver } from './resolvers';\nimport type {\n DocumentNode,\n GraphQLFieldResolver,\n GraphQLSchema,\n GraphqlResolvedConfig,\n Maybe,\n ObjectWithGraphQLData,\n} from './types';\n\nconst BASE_ATTRIBUTES = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: WEB_SERVER_GRAPHQL_SPAN_OP,\n} as const;\n\nexport function startParseSpan(): Span {\n return startInactiveSpan({ name: SPAN_NAME_PARSE, attributes: { ...BASE_ATTRIBUTES } });\n}\n\n/** `documentAST` is the 2nd argument to `validate(schema, documentAST, …)`. */\nexport function startValidateSpan(documentAST: unknown): Span {\n return startInactiveSpan({\n name: SPAN_NAME_VALIDATE,\n attributes: { ...BASE_ATTRIBUTES, [GRAPHQL_DOCUMENT]: redactGraphqlDocument(documentAST as GraphqlDocumentNode) },\n });\n}\n\n/** `result` is validation's return value: a (possibly empty) array of errors. */\nexport function finalizeValidateSpan(span: Span, result: unknown): void {\n if (Array.isArray(result) && result.length > 0) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'invalid_argument' });\n }\n}\n\n/** Positional slots of a `graphql.execute(schema, document, …)` call (v14/v15 legacy signature). */\nconst enum ExecuteArg {\n SCHEMA = 0,\n DOCUMENT = 1,\n CONTEXT_VALUE = 3,\n OPERATION_NAME = 5,\n FIELD_RESOLVER = 6,\n}\n\ninterface NormalizedExecuteArgs {\n schema?: GraphQLSchema;\n document?: DocumentNode;\n contextValue: ObjectWithGraphQLData;\n operationName?: Maybe<string>;\n fieldResolver?: Maybe<GraphQLFieldResolver>;\n /** Writes `contextValue`/`fieldResolver` mutations back to the live channel `arguments`. */\n writeBack: (contextValue: ObjectWithGraphQLData, fieldResolver: Maybe<GraphQLFieldResolver>) => void;\n}\n\n/**\n * `execute` accepts either a single `ExecutionArgs` object (modern callers, always in v16) or\n * positional args (v14/v15). Both are normalized here; `writeBack` puts mutations onto the correct\n * slot so they reach the real call.\n */\nfunction normalizeExecuteArgs(argsArray: unknown[]): NormalizedExecuteArgs {\n if (argsArray.length >= 2) {\n return {\n schema: argsArray[ExecuteArg.SCHEMA] as GraphQLSchema | undefined,\n document: argsArray[ExecuteArg.DOCUMENT] as DocumentNode | undefined,\n contextValue: (argsArray[ExecuteArg.CONTEXT_VALUE] ?? {}) as ObjectWithGraphQLData,\n operationName: argsArray[ExecuteArg.OPERATION_NAME] as Maybe<string>,\n fieldResolver: argsArray[ExecuteArg.FIELD_RESOLVER] as Maybe<GraphQLFieldResolver>,\n writeBack: (contextValue, fieldResolver) => {\n argsArray[ExecuteArg.CONTEXT_VALUE] = contextValue;\n argsArray[ExecuteArg.FIELD_RESOLVER] = fieldResolver;\n },\n };\n }\n\n const obj = (argsArray[0] ?? {}) as {\n schema?: GraphQLSchema;\n document?: DocumentNode;\n contextValue?: unknown;\n operationName?: Maybe<string>;\n fieldResolver?: Maybe<GraphQLFieldResolver>;\n };\n return {\n schema: obj.schema,\n document: obj.document,\n contextValue: (obj.contextValue ?? {}) as ObjectWithGraphQLData,\n operationName: obj.operationName,\n fieldResolver: obj.fieldResolver,\n writeBack: (contextValue, fieldResolver) => {\n obj.contextValue = contextValue;\n obj.fieldResolver = fieldResolver;\n },\n };\n}\n\n/**\n * Opens the execute span and, unless resolver spans are disabled, swaps the schema's field resolvers\n * (and the default field resolver) for span-creating proxies — mutating the live `arguments` in place\n * so the wrapped `execute` call runs with them. Always returns a span; the caller guards against\n * throws (see `safe` in `index.ts`).\n */\nexport function startExecuteSpan(\n argsArray: unknown[],\n self: unknown,\n config: GraphqlResolvedConfig,\n getConfig: () => GraphqlResolvedConfig,\n): Span {\n const args = normalizeExecuteArgs(argsArray);\n const { schema, document } = args;\n let { contextValue, fieldResolver } = args;\n\n // Skip resolver wrapping when disabled or when a parent execute already set up this context\n // (nested execute reusing the same contextValue).\n const alreadyInstrumented = !!contextValue[GRAPHQL_DATA_SYMBOL];\n if (!config.ignoreResolveSpans && !alreadyInstrumented) {\n const isUsingDefaultResolver = fieldResolver == null;\n const defaultFieldResolver = (self as { defaultFieldResolver?: GraphQLFieldResolver } | undefined)\n ?.defaultFieldResolver;\n const fieldResolverForExecute = fieldResolver ?? defaultFieldResolver;\n if (fieldResolverForExecute) {\n fieldResolver = wrapFieldResolver(getConfig, fieldResolverForExecute, isUsingDefaultResolver);\n }\n\n if (schema) {\n wrapFields(schema.getQueryType(), getConfig);\n wrapFields(schema.getMutationType(), getConfig);\n }\n }\n\n // v14–16 channels carry only the raw args, so derive the operation from the document (native v17\n // provides `operationType`/`operationName` on the event directly).\n const operation = getOperation(document as DocumentNode, args.operationName);\n const operationType = operation?.operation;\n const operationName = operation?.name?.value ?? args.operationName ?? undefined;\n\n const span = startInactiveSpan({\n name: getOperationSpanName(operationType, operationName || undefined, SPAN_NAME_EXECUTE),\n attributes: {\n ...BASE_ATTRIBUTES,\n [GRAPHQL_OPERATION_TYPE]: operationType,\n [GRAPHQL_OPERATION_NAME]: operationName || undefined,\n [GRAPHQL_DOCUMENT]: redactGraphqlDocument(document as GraphqlDocumentNode | undefined),\n },\n });\n\n if (config.useOperationNameForRootSpan && operationType) {\n renameRootSpanWithOperation(span, operationType, operationName || undefined);\n }\n\n // The resolver proxies read the execute span (and their own bookkeeping) off this symbol.\n contextValue[GRAPHQL_DATA_SYMBOL] = { source: document, span, fields: {} };\n args.writeBack(contextValue, fieldResolver);\n\n return span;\n}\n\n/** `result` is the settled `ExecutionResult`; GraphQL errors surface on `result.errors`, not a throw. */\nexport function finalizeExecuteSpan(span: Span, result: unknown): void {\n if (hasResultErrors(result)) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n }\n}\n"],"names":[],"mappings":"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"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { DB_QUERY_TEXT, DB_SYSTEM_NAME, ERROR_TYPE } from '@sentry/conventions/attributes'; | ||
| import { defineIntegration, debug, waitForTracingChannelBinding, _INTERNAL_reconstructPostgresQuery, _INTERNAL_sanitizeSqlQuery, startInactiveSpan, SPAN_KIND, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, _INTERNAL_buildPostgresConnectionContext, _INTERNAL_setPostgresOperationName, SPAN_STATUS_ERROR, _INTERNAL_setPostgresConnectionAttributes } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
| import { CHANNELS } from '../../orchestrion/channels.js'; | ||
| import { bindTracingChannelToSpan } from '../../tracing-channel.js'; | ||
| const INTEGRATION_NAME = "PostgresJs"; | ||
| const ORIGIN = "auto.db.orchestrion.postgresjs"; | ||
| const DB_RESPONSE_STATUS_CODE = "db.response.status_code"; | ||
| const NOOP = () => { | ||
| }; | ||
| const QUERY_FROM_INSTRUMENTED_SQL = /* @__PURE__ */ Symbol.for("sentry.query.from.instrumented.sql"); | ||
| const QUERY_SPAN = /* @__PURE__ */ Symbol("sentryPostgresJsSpan"); | ||
| const CONNECTION_ATTRS_SET = /* @__PURE__ */ Symbol("sentryPostgresJsConnectionAttrsSet"); | ||
| const SPAN_ENDED = /* @__PURE__ */ Symbol("sentryPostgresJsSpanEnded"); | ||
| const connectionContexts = /* @__PURE__ */ new WeakMap(); | ||
| const endpointRegistry = []; | ||
| function registerEndpoint(context) { | ||
| const alreadyKnown = endpointRegistry.some( | ||
| (e) => e.ATTR_SERVER_ADDRESS === context.ATTR_SERVER_ADDRESS && e.ATTR_SERVER_PORT === context.ATTR_SERVER_PORT && e.ATTR_DB_NAMESPACE === context.ATTR_DB_NAMESPACE | ||
| ); | ||
| if (!alreadyKnown) { | ||
| endpointRegistry.push(context); | ||
| } | ||
| } | ||
| function resolveSingleEndpoint() { | ||
| return endpointRegistry.length === 1 ? endpointRegistry[0] : void 0; | ||
| } | ||
| function recordConnectionFromChannel(message) { | ||
| const connection = message.result; | ||
| const options = message.arguments?.[0]; | ||
| if (!connection || typeof connection !== "object" || !options) { | ||
| return; | ||
| } | ||
| const context = _INTERNAL_buildPostgresConnectionContext(options); | ||
| connectionContexts.set(connection, context); | ||
| registerEndpoint(context); | ||
| } | ||
| function setConnectionAttributes(span, query, context) { | ||
| const queryRecord = query; | ||
| if (queryRecord[CONNECTION_ATTRS_SET]) { | ||
| return; | ||
| } | ||
| queryRecord[CONNECTION_ATTRS_SET] = true; | ||
| _INTERNAL_setPostgresConnectionAttributes(span, context); | ||
| } | ||
| function attachConnectionAttributesFromChannel(message) { | ||
| const connection = message.self; | ||
| const query = message.arguments?.[0]; | ||
| if (!connection || !query) { | ||
| return; | ||
| } | ||
| const span = query[QUERY_SPAN]; | ||
| const context = connectionContexts.get(connection); | ||
| if (span && context) { | ||
| setConnectionAttributes(span, query, context); | ||
| } | ||
| } | ||
| function wrapQuerySettlement(data, span, sanitizedSqlQuery) { | ||
| const query = data.self; | ||
| if (!query) { | ||
| return; | ||
| } | ||
| const markEnded = () => { | ||
| data[SPAN_ENDED] = true; | ||
| }; | ||
| const originalResolve = query.resolve; | ||
| if (typeof originalResolve === "function") { | ||
| query.resolve = function(...resolveArgs) { | ||
| markEnded(); | ||
| try { | ||
| const command = resolveArgs[0]?.command; | ||
| _INTERNAL_setPostgresOperationName(span, sanitizedSqlQuery, command); | ||
| span.end(); | ||
| } catch (e) { | ||
| DEBUG_BUILD && debug.error("[orchestrion:postgresjs] error ending span in resolve:", e); | ||
| } | ||
| return originalResolve.apply(this, resolveArgs); | ||
| }; | ||
| } | ||
| const originalReject = query.reject; | ||
| if (typeof originalReject === "function") { | ||
| query.reject = function(...rejectArgs) { | ||
| markEnded(); | ||
| try { | ||
| const err = rejectArgs[0]; | ||
| span.setStatus({ code: SPAN_STATUS_ERROR, message: err?.message || "unknown_error" }); | ||
| span.setAttribute(DB_RESPONSE_STATUS_CODE, err?.code || "unknown"); | ||
| span.setAttribute(ERROR_TYPE, err?.name || "unknown"); | ||
| _INTERNAL_setPostgresOperationName(span, sanitizedSqlQuery); | ||
| span.end(); | ||
| } catch (e) { | ||
| DEBUG_BUILD && debug.error("[orchestrion:postgresjs] error ending span in reject:", e); | ||
| } | ||
| return originalReject.apply(this, rejectArgs); | ||
| }; | ||
| } | ||
| } | ||
| const _postgresJsChannelIntegration = ((options = {}) => { | ||
| const { requireParentSpan, requestHook } = options; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| DEBUG_BUILD && debug.log(`[orchestrion:postgresjs] subscribing to "${CHANNELS.POSTGRESJS_HANDLE}"`); | ||
| diagnosticsChannel.tracingChannel(CHANNELS.POSTGRESJS_CONNECTION).subscribe({ | ||
| start: NOOP, | ||
| asyncStart: NOOP, | ||
| asyncEnd: NOOP, | ||
| error: NOOP, | ||
| end: recordConnectionFromChannel | ||
| }); | ||
| diagnosticsChannel.tracingChannel(CHANNELS.POSTGRESJS_EXECUTE).subscribe({ | ||
| end: NOOP, | ||
| asyncStart: NOOP, | ||
| asyncEnd: NOOP, | ||
| error: NOOP, | ||
| start: attachConnectionAttributesFromChannel | ||
| }); | ||
| diagnosticsChannel.tracingChannel(CHANNELS.POSTGRESJS_CONNECT).subscribe({ | ||
| end: NOOP, | ||
| asyncStart: NOOP, | ||
| asyncEnd: NOOP, | ||
| error: NOOP, | ||
| start: attachConnectionAttributesFromChannel | ||
| }); | ||
| waitForTracingChannelBinding(() => { | ||
| bindTracingChannelToSpan( | ||
| diagnosticsChannel.tracingChannel(CHANNELS.POSTGRESJS_HANDLE), | ||
| (data) => { | ||
| const query = data.self; | ||
| if (!query) { | ||
| return void 0; | ||
| } | ||
| if (query.executed === true || query[QUERY_FROM_INSTRUMENTED_SQL]) { | ||
| return void 0; | ||
| } | ||
| const fullQuery = _INTERNAL_reconstructPostgresQuery(query.strings); | ||
| const sanitizedSqlQuery = _INTERNAL_sanitizeSqlQuery(fullQuery); | ||
| const span = startInactiveSpan({ | ||
| name: sanitizedSqlQuery || "postgresjs.query", | ||
| op: "db", | ||
| kind: SPAN_KIND.CLIENT, | ||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [DB_SYSTEM_NAME]: "postgres", | ||
| [DB_QUERY_TEXT]: sanitizedSqlQuery | ||
| } | ||
| }); | ||
| query[QUERY_SPAN] = span; | ||
| const context = resolveSingleEndpoint(); | ||
| if (context) { | ||
| setConnectionAttributes(span, query, context); | ||
| } | ||
| if (requestHook) { | ||
| try { | ||
| requestHook(span, sanitizedSqlQuery, context); | ||
| } catch (e) { | ||
| span.setAttribute("sentry.hook.error", "requestHook failed"); | ||
| DEBUG_BUILD && debug.error("[orchestrion:postgresjs] error in requestHook:", e); | ||
| } | ||
| } | ||
| wrapQuerySettlement(data, span, sanitizedSqlQuery); | ||
| return span; | ||
| }, | ||
| { | ||
| requiresParentSpan: requireParentSpan !== false, | ||
| deferSpanEnd({ data }) { | ||
| if (data[SPAN_ENDED]) { | ||
| return true; | ||
| } | ||
| if ("error" in data) { | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| } | ||
| ); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const postgresJsChannelIntegration = defineIntegration(_postgresJsChannelIntegration); | ||
| export { postgresJsChannelIntegration }; | ||
| //# sourceMappingURL=postgres-js.js.map |
| {"version":3,"file":"postgres-js.js","sources":["../../../../src/integrations/tracing-channel/postgres-js.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { DB_QUERY_TEXT, DB_SYSTEM_NAME, ERROR_TYPE } from '@sentry/conventions/attributes';\nimport type { IntegrationFn, PostgresConnectionContext, Span } from '@sentry/core';\nimport {\n _INTERNAL_buildPostgresConnectionContext,\n _INTERNAL_reconstructPostgresQuery,\n _INTERNAL_sanitizeSqlQuery,\n _INTERNAL_setPostgresConnectionAttributes,\n _INTERNAL_setPostgresOperationName,\n debug,\n defineIntegration,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Same name as the OTel `PostgresJs` integration by design: when this is\n// enabled, the OTel integration of the same name is dropped from the default\n// set (see `experimentalUseDiagnosticsChannelInjection`).\nconst INTEGRATION_NAME = 'PostgresJs' as const;\n\nconst ORIGIN = 'auto.db.orchestrion.postgresjs';\n\n// Not part of `@sentry/conventions`, so we keep it inline (matches the OTel `PostgresJsInstrumentation`).\nconst DB_RESPONSE_STATUS_CODE = 'db.response.status_code';\n\nconst NOOP = (): void => {};\n\n// Same `Symbol.for()` marker the core `instrumentPostgresJsSql` wrapper sets on\n// queries it manually instruments, so we skip them there and never double-span.\nconst QUERY_FROM_INSTRUMENTED_SQL = Symbol.for('sentry.query.from.instrumented.sql');\n// The query span, stashed on the `Query` so the `execute`/`connect` channels can\n// attach connection attributes to it.\nconst QUERY_SPAN = Symbol('sentryPostgresJsSpan');\n// Set once connection attributes are on the span, so the fallback and the\n// `execute`/`connect` channels don't both write them.\nconst CONNECTION_ATTRS_SET = Symbol('sentryPostgresJsConnectionAttrsSet');\n// Set on the channel context once the resolve/reject wrappers have ended the\n// span, so `deferSpanEnd` knows the wrappers own the lifecycle.\nconst SPAN_ENDED = Symbol('sentryPostgresJsSpanEnded');\n\nexport interface PostgresJsChannelIntegrationOptions {\n /**\n * Only create spans when there's already an active parent span. Defaults to\n * `true`, matching the OTel `postgresJsIntegration`.\n */\n requireParentSpan?: boolean;\n /**\n * Hook to modify the query span before the query runs. Receives the span, the\n * sanitized SQL, and (when resolvable) the connection context.\n */\n requestHook?: (span: Span, sanitizedSqlQuery: string, postgresConnectionContext?: PostgresConnectionContext) => void;\n}\n\n/** The `Query` instance postgres.js passes as `self` to `Query.prototype.handle`. */\ninterface PostgresQuery {\n strings?: string[];\n executed?: boolean;\n resolve?: (...args: unknown[]) => unknown;\n reject?: (...args: unknown[]) => unknown;\n}\n\ninterface PostgresJsQueryContext {\n arguments?: unknown[];\n self?: PostgresQuery;\n result?: unknown;\n error?: unknown;\n}\n\n// A connection object -> its resolved context, populated on the `connection`\n// channel and read on the `execute`/`connect` channels (keyed by the same object).\nconst connectionContexts = new WeakMap<object, PostgresConnectionContext>();\n// Distinct endpoints seen so far (value-compared, so N connections to one DB\n// count once). When exactly one endpoint exists — the common case, and the only\n// one the tests exercise — every query resolves to it at handle-start.\nconst endpointRegistry: PostgresConnectionContext[] = [];\n\nfunction registerEndpoint(context: PostgresConnectionContext): void {\n const alreadyKnown = endpointRegistry.some(\n e =>\n e.ATTR_SERVER_ADDRESS === context.ATTR_SERVER_ADDRESS &&\n e.ATTR_SERVER_PORT === context.ATTR_SERVER_PORT &&\n e.ATTR_DB_NAMESPACE === context.ATTR_DB_NAMESPACE,\n );\n if (!alreadyKnown) {\n endpointRegistry.push(context);\n }\n}\n\n/** The single known endpoint, or `undefined` when zero or multiple are known. */\nfunction resolveSingleEndpoint(): PostgresConnectionContext | undefined {\n return endpointRegistry.length === 1 ? endpointRegistry[0] : undefined;\n}\n\n/**\n * Record a connection from the `connection` channel `end` (`result` is the\n * connection object, `arguments[0]` the parsed options), keying its resolved\n * context by the connection object and tracking its endpoint.\n */\nfunction recordConnectionFromChannel(message: PostgresJsQueryContext): void {\n const connection = message.result;\n const options = message.arguments?.[0] as { host?: string[]; port?: number[]; database?: string } | undefined;\n if (!connection || typeof connection !== 'object' || !options) {\n return;\n }\n const context = _INTERNAL_buildPostgresConnectionContext(options);\n connectionContexts.set(connection, context);\n registerEndpoint(context);\n}\n\nfunction setConnectionAttributes(span: Span, query: PostgresQuery, context: PostgresConnectionContext): void {\n const queryRecord = query as Record<symbol, unknown>;\n if (queryRecord[CONNECTION_ATTRS_SET]) {\n return;\n }\n queryRecord[CONNECTION_ATTRS_SET] = true;\n _INTERNAL_setPostgresConnectionAttributes(span, context);\n}\n\n/**\n * Backfill connection attributes onto a query's span from a channel whose `self`\n * is the connection object and `arguments[0]` the query. Shared by the `execute`\n * and `connect` channels; both carry that shape and both resolve the context via\n * the `connectionContexts` WeakMap. Idempotent (guarded inside `setConnectionAttributes`).\n */\nfunction attachConnectionAttributesFromChannel(message: PostgresJsQueryContext): void {\n const connection = message.self as object | undefined;\n const query = message.arguments?.[0] as PostgresQuery | undefined;\n if (!connection || !query) {\n return;\n }\n const span = (query as Record<symbol, unknown>)[QUERY_SPAN] as Span | undefined;\n const context = connectionContexts.get(connection);\n if (span && context) {\n setConnectionAttributes(span, query, context);\n }\n}\n\n/**\n * Wrap `query.resolve`/`query.reject` so the span ends when the query settles.\n *\n * `Query extends Promise` and `async handle()` only dispatches — its promise\n * resolves immediately, long before the query completes. postgres.js signals\n * completion by calling `this.resolve`/`this.reject`, so we own the span end\n * there. Wrapping happens at handle-start because `reject` can fire\n * synchronously during dispatch and `cursor()` reassigns both before executing.\n */\nfunction wrapQuerySettlement(data: PostgresJsQueryContext, span: Span, sanitizedSqlQuery: string): void {\n const query = data.self;\n if (!query) {\n return;\n }\n\n // Claim ownership of ending the span up front, so `deferSpanEnd` defers to the\n // wrapper even if `span.end()` below throws.\n const markEnded = (): void => {\n (data as Record<symbol, unknown>)[SPAN_ENDED] = true;\n };\n\n const originalResolve = query.resolve;\n if (typeof originalResolve === 'function') {\n query.resolve = function (this: unknown, ...resolveArgs: unknown[]): unknown {\n markEnded();\n try {\n const command = (resolveArgs[0] as { command?: string } | undefined)?.command;\n _INTERNAL_setPostgresOperationName(span, sanitizedSqlQuery, command);\n span.end();\n } catch (e) {\n DEBUG_BUILD && debug.error('[orchestrion:postgresjs] error ending span in resolve:', e);\n }\n return originalResolve.apply(this, resolveArgs);\n };\n }\n\n const originalReject = query.reject;\n if (typeof originalReject === 'function') {\n query.reject = function (this: unknown, ...rejectArgs: unknown[]): unknown {\n markEnded();\n try {\n const err = rejectArgs[0] as { message?: string; code?: string; name?: string } | undefined;\n span.setStatus({ code: SPAN_STATUS_ERROR, message: err?.message || 'unknown_error' });\n span.setAttribute(DB_RESPONSE_STATUS_CODE, err?.code || 'unknown');\n span.setAttribute(ERROR_TYPE, err?.name || 'unknown');\n _INTERNAL_setPostgresOperationName(span, sanitizedSqlQuery);\n span.end();\n } catch (e) {\n DEBUG_BUILD && debug.error('[orchestrion:postgresjs] error ending span in reject:', e);\n }\n return originalReject.apply(this, rejectArgs);\n };\n }\n}\n\nconst _postgresJsChannelIntegration = ((options: PostgresJsChannelIntegrationOptions = {}) => {\n const { requireParentSpan, requestHook } = options;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD && debug.log(`[orchestrion:postgresjs] subscribing to \"${CHANNELS.POSTGRESJS_HANDLE}\"`);\n\n // Connection + execute are pure observers (no span, no async binding), so\n // subscribe immediately — factory-time `Connection()` calls happen before\n // `waitForTracingChannelBinding` resolves and must still be recorded.\n diagnosticsChannel.tracingChannel<PostgresJsQueryContext>(CHANNELS.POSTGRESJS_CONNECTION).subscribe({\n start: NOOP,\n asyncStart: NOOP,\n asyncEnd: NOOP,\n error: NOOP,\n end: recordConnectionFromChannel,\n });\n\n // Per-connection attributes for queries reusing an already-open connection\n // (`c.execute(q)`, `self === c`). `execute` is also called bare\n // (`self === undefined`) for the first query on each connection, `fetchState`\n // and `retry`; those miss here (the `connect` channel below covers the first\n // user query, and the single-endpoint fallback covers the common case).\n diagnosticsChannel.tracingChannel<PostgresJsQueryContext>(CHANNELS.POSTGRESJS_EXECUTE).subscribe({\n end: NOOP,\n asyncStart: NOOP,\n asyncEnd: NOOP,\n error: NOOP,\n start: attachConnectionAttributesFromChannel,\n });\n\n // The connection's `connect(query)` method (`self === c`, `arguments[0]` the\n // query) fires when a fresh connection is opened for a query. That first query\n // is later dispatched via a bare `execute` (no `self`), so this is where it\n // gets its connection attributes in multi-endpoint apps.\n diagnosticsChannel.tracingChannel<PostgresJsQueryContext>(CHANNELS.POSTGRESJS_CONNECT).subscribe({\n end: NOOP,\n asyncStart: NOOP,\n asyncEnd: NOOP,\n error: NOOP,\n start: attachConnectionAttributesFromChannel,\n });\n\n // The span-creating `handle` subscription needs the async-context binding\n // that `initOpenTelemetry()` registers after integration setup.\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan<PostgresJsQueryContext>(\n diagnosticsChannel.tracingChannel<PostgresJsQueryContext>(CHANNELS.POSTGRESJS_HANDLE),\n data => {\n const query = data.self;\n if (!query) {\n return undefined;\n }\n\n // Opt out of re-entrant `handle()` calls (then/catch/finally re-invoke it, guarded by\n // `executed`) and queries already wrapped by the portable `instrumentPostgresJsSql`. The\n // parent-span requirement is applied via `requiresParentSpan` below.\n if (query.executed === true || (query as Record<symbol, unknown>)[QUERY_FROM_INSTRUMENTED_SQL]) {\n return undefined;\n }\n\n const fullQuery = _INTERNAL_reconstructPostgresQuery(query.strings);\n const sanitizedSqlQuery = _INTERNAL_sanitizeSqlQuery(fullQuery);\n\n // `kind: CLIENT` matches the mysql/pg channel subscribers.\n const span = startInactiveSpan({\n name: sanitizedSqlQuery || 'postgresjs.query',\n op: 'db',\n kind: SPAN_KIND.CLIENT,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [DB_SYSTEM_NAME]: 'postgres',\n [DB_QUERY_TEXT]: sanitizedSqlQuery,\n },\n });\n\n // Stash for the `execute`/`connect` channels to attach per-connection attributes.\n (query as Record<symbol, unknown>)[QUERY_SPAN] = span;\n\n // Single-endpoint fallback: resolve context now so `requestHook` has it\n // and the first-query-per-connection (bare `execute`) path still gets attrs.\n const context = resolveSingleEndpoint();\n if (context) {\n setConnectionAttributes(span, query, context);\n }\n\n if (requestHook) {\n try {\n requestHook(span, sanitizedSqlQuery, context);\n } catch (e) {\n span.setAttribute('sentry.hook.error', 'requestHook failed');\n DEBUG_BUILD && debug.error('[orchestrion:postgresjs] error in requestHook:', e);\n }\n }\n\n wrapQuerySettlement(data, span, sanitizedSqlQuery);\n\n return span;\n },\n {\n requiresParentSpan: requireParentSpan !== false,\n deferSpanEnd({ data }) {\n // `handle` is async: its promise settles on dispatch (asyncEnd), long\n // before the query does. The resolve/reject wrappers own the ending.\n if ((data as Record<symbol, unknown>)[SPAN_ENDED]) {\n return true; // wrappers already ended it\n }\n if ('error' in data) {\n return false; // `handle()` itself threw; the error subscriber annotated the span, let the helper end it\n }\n // NOTE: for a cursor consumed as an async iterator, only the first batch\n // reaches `handle` (the `executed` guard blocks the rest), so the span\n // ends on the first batch — a pre-existing flaw kept for parity.\n return true; // query in flight; the wrappers will end the span when it settles\n },\n },\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * EXPERIMENTAL — orchestrion-driven postgres.js (`postgres` v3.x) integration.\n *\n * Subscribes to the `orchestrion:postgres:handle` / `:connection` / `:execute` /\n * `:connect` diagnostics channels injected into postgres.js' `Query.prototype.handle`\n * and `Connection`/`execute`/`connect` (in `src/*` and `cjs/src/*`) and creates db\n * spans matching the OTel `postgresJsIntegration`. Requires the orchestrion runtime\n * hook or bundler plugin.\n */\nexport const postgresJsChannelIntegration = defineIntegration(_postgresJsChannelIntegration);\n"],"names":[],"mappings":";;;;;;;AAwBA,MAAM,gBAAA,GAAmB,YAAA;AAEzB,MAAM,MAAA,GAAS,gCAAA;AAGf,MAAM,uBAAA,GAA0B,yBAAA;AAEhC,MAAM,OAAO,MAAY;AAAC,CAAA;AAI1B,MAAM,2BAAA,mBAA8B,MAAA,CAAO,GAAA,CAAI,oCAAoC,CAAA;AAGnF,MAAM,UAAA,0BAAoB,sBAAsB,CAAA;AAGhD,MAAM,oBAAA,0BAA8B,oCAAoC,CAAA;AAGxE,MAAM,UAAA,0BAAoB,2BAA2B,CAAA;AAgCrD,MAAM,kBAAA,uBAAyB,OAAA,EAA2C;AAI1E,MAAM,mBAAgD,EAAC;AAEvD,SAAS,iBAAiB,OAAA,EAA0C;AAClE,EAAA,MAAM,eAAe,gBAAA,CAAiB,IAAA;AAAA,IACpC,CAAA,CAAA,KACE,CAAA,CAAE,mBAAA,KAAwB,OAAA,CAAQ,mBAAA,IAClC,CAAA,CAAE,gBAAA,KAAqB,OAAA,CAAQ,gBAAA,IAC/B,CAAA,CAAE,iBAAA,KAAsB,OAAA,CAAQ;AAAA,GACpC;AACA,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,gBAAA,CAAiB,KAAK,OAAO,CAAA;AAAA,EAC/B;AACF;AAGA,SAAS,qBAAA,GAA+D;AACtE,EAAA,OAAO,gBAAA,CAAiB,MAAA,KAAW,CAAA,GAAI,gBAAA,CAAiB,CAAC,CAAA,GAAI,MAAA;AAC/D;AAOA,SAAS,4BAA4B,OAAA,EAAuC;AAC1E,EAAA,MAAM,aAAa,OAAA,CAAQ,MAAA;AAC3B,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,SAAA,GAAY,CAAC,CAAA;AACrC,EAAA,IAAI,CAAC,UAAA,IAAc,OAAO,UAAA,KAAe,QAAA,IAAY,CAAC,OAAA,EAAS;AAC7D,IAAA;AAAA,EACF;AACA,EAAA,MAAM,OAAA,GAAU,yCAAyC,OAAO,CAAA;AAChE,EAAA,kBAAA,CAAmB,GAAA,CAAI,YAAY,OAAO,CAAA;AAC1C,EAAA,gBAAA,CAAiB,OAAO,CAAA;AAC1B;AAEA,SAAS,uBAAA,CAAwB,IAAA,EAAY,KAAA,EAAsB,OAAA,EAA0C;AAC3G,EAAA,MAAM,WAAA,GAAc,KAAA;AACpB,EAAA,IAAI,WAAA,CAAY,oBAAoB,CAAA,EAAG;AACrC,IAAA;AAAA,EACF;AACA,EAAA,WAAA,CAAY,oBAAoB,CAAA,GAAI,IAAA;AACpC,EAAA,yCAAA,CAA0C,MAAM,OAAO,CAAA;AACzD;AAQA,SAAS,sCAAsC,OAAA,EAAuC;AACpF,EAAA,MAAM,aAAa,OAAA,CAAQ,IAAA;AAC3B,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,SAAA,GAAY,CAAC,CAAA;AACnC,EAAA,IAAI,CAAC,UAAA,IAAc,CAAC,KAAA,EAAO;AACzB,IAAA;AAAA,EACF;AACA,EAAA,MAAM,IAAA,GAAQ,MAAkC,UAAU,CAAA;AAC1D,EAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,GAAA,CAAI,UAAU,CAAA;AACjD,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,uBAAA,CAAwB,IAAA,EAAM,OAAO,OAAO,CAAA;AAAA,EAC9C;AACF;AAWA,SAAS,mBAAA,CAAoB,IAAA,EAA8B,IAAA,EAAY,iBAAA,EAAiC;AACtG,EAAA,MAAM,QAAQ,IAAA,CAAK,IAAA;AACnB,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA;AAAA,EACF;AAIA,EAAA,MAAM,YAAY,MAAY;AAC5B,IAAC,IAAA,CAAiC,UAAU,CAAA,GAAI,IAAA;AAAA,EAClD,CAAA;AAEA,EAAA,MAAM,kBAAkB,KAAA,CAAM,OAAA;AAC9B,EAAA,IAAI,OAAO,oBAAoB,UAAA,EAAY;AACzC,IAAA,KAAA,CAAM,OAAA,GAAU,YAA4B,WAAA,EAAiC;AAC3E,MAAA,SAAA,EAAU;AACV,MAAA,IAAI;AACF,QAAA,MAAM,OAAA,GAAW,WAAA,CAAY,CAAC,CAAA,EAAwC,OAAA;AACtE,QAAA,kCAAA,CAAmC,IAAA,EAAM,mBAAmB,OAAO,CAAA;AACnE,QAAA,IAAA,CAAK,GAAA,EAAI;AAAA,MACX,SAAS,CAAA,EAAG;AACV,QAAA,WAAA,IAAe,KAAA,CAAM,KAAA,CAAM,wDAAA,EAA0D,CAAC,CAAA;AAAA,MACxF;AACA,MAAA,OAAO,eAAA,CAAgB,KAAA,CAAM,IAAA,EAAM,WAAW,CAAA;AAAA,IAChD,CAAA;AAAA,EACF;AAEA,EAAA,MAAM,iBAAiB,KAAA,CAAM,MAAA;AAC7B,EAAA,IAAI,OAAO,mBAAmB,UAAA,EAAY;AACxC,IAAA,KAAA,CAAM,MAAA,GAAS,YAA4B,UAAA,EAAgC;AACzE,MAAA,SAAA,EAAU;AACV,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,WAAW,CAAC,CAAA;AACxB,QAAA,IAAA,CAAK,SAAA,CAAU,EAAE,IAAA,EAAM,iBAAA,EAAmB,SAAS,GAAA,EAAK,OAAA,IAAW,iBAAiB,CAAA;AACpF,QAAA,IAAA,CAAK,YAAA,CAAa,uBAAA,EAAyB,GAAA,EAAK,IAAA,IAAQ,SAAS,CAAA;AACjE,QAAA,IAAA,CAAK,YAAA,CAAa,UAAA,EAAY,GAAA,EAAK,IAAA,IAAQ,SAAS,CAAA;AACpD,QAAA,kCAAA,CAAmC,MAAM,iBAAiB,CAAA;AAC1D,QAAA,IAAA,CAAK,GAAA,EAAI;AAAA,MACX,SAAS,CAAA,EAAG;AACV,QAAA,WAAA,IAAe,KAAA,CAAM,KAAA,CAAM,uDAAA,EAAyD,CAAC,CAAA;AAAA,MACvF;AACA,MAAA,OAAO,cAAA,CAAe,KAAA,CAAM,IAAA,EAAM,UAAU,CAAA;AAAA,IAC9C,CAAA;AAAA,EACF;AACF;AAEA,MAAM,6BAAA,IAAiC,CAAC,OAAA,GAA+C,EAAC,KAAM;AAC5F,EAAA,MAAM,EAAE,iBAAA,EAAmB,WAAA,EAAY,GAAI,OAAA;AAE3C,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,mBAAmB,cAAA,EAAgB;AACtC,QAAA;AAAA,MACF;AAEA,MAAA,WAAA,IAAe,KAAA,CAAM,GAAA,CAAI,CAAA,yCAAA,EAA4C,QAAA,CAAS,iBAAiB,CAAA,CAAA,CAAG,CAAA;AAKlG,MAAA,kBAAA,CAAmB,cAAA,CAAuC,QAAA,CAAS,qBAAqB,CAAA,CAAE,SAAA,CAAU;AAAA,QAClG,KAAA,EAAO,IAAA;AAAA,QACP,UAAA,EAAY,IAAA;AAAA,QACZ,QAAA,EAAU,IAAA;AAAA,QACV,KAAA,EAAO,IAAA;AAAA,QACP,GAAA,EAAK;AAAA,OACN,CAAA;AAOD,MAAA,kBAAA,CAAmB,cAAA,CAAuC,QAAA,CAAS,kBAAkB,CAAA,CAAE,SAAA,CAAU;AAAA,QAC/F,GAAA,EAAK,IAAA;AAAA,QACL,UAAA,EAAY,IAAA;AAAA,QACZ,QAAA,EAAU,IAAA;AAAA,QACV,KAAA,EAAO,IAAA;AAAA,QACP,KAAA,EAAO;AAAA,OACR,CAAA;AAMD,MAAA,kBAAA,CAAmB,cAAA,CAAuC,QAAA,CAAS,kBAAkB,CAAA,CAAE,SAAA,CAAU;AAAA,QAC/F,GAAA,EAAK,IAAA;AAAA,QACL,UAAA,EAAY,IAAA;AAAA,QACZ,QAAA,EAAU,IAAA;AAAA,QACV,KAAA,EAAO,IAAA;AAAA,QACP,KAAA,EAAO;AAAA,OACR,CAAA;AAID,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,wBAAA;AAAA,UACE,kBAAA,CAAmB,cAAA,CAAuC,QAAA,CAAS,iBAAiB,CAAA;AAAA,UACpF,CAAA,IAAA,KAAQ;AACN,YAAA,MAAM,QAAQ,IAAA,CAAK,IAAA;AACnB,YAAA,IAAI,CAAC,KAAA,EAAO;AACV,cAAA,OAAO,MAAA;AAAA,YACT;AAKA,YAAA,IAAI,KAAA,CAAM,QAAA,KAAa,IAAA,IAAS,KAAA,CAAkC,2BAA2B,CAAA,EAAG;AAC9F,cAAA,OAAO,MAAA;AAAA,YACT;AAEA,YAAA,MAAM,SAAA,GAAY,kCAAA,CAAmC,KAAA,CAAM,OAAO,CAAA;AAClE,YAAA,MAAM,iBAAA,GAAoB,2BAA2B,SAAS,CAAA;AAG9D,YAAA,MAAM,OAAO,iBAAA,CAAkB;AAAA,cAC7B,MAAM,iBAAA,IAAqB,kBAAA;AAAA,cAC3B,EAAA,EAAI,IAAA;AAAA,cACJ,MAAM,SAAA,CAAU,MAAA;AAAA,cAChB,UAAA,EAAY;AAAA,gBACV,CAAC,gCAAgC,GAAG,MAAA;AAAA,gBACpC,CAAC,cAAc,GAAG,UAAA;AAAA,gBAClB,CAAC,aAAa,GAAG;AAAA;AACnB,aACD,CAAA;AAGD,YAAC,KAAA,CAAkC,UAAU,CAAA,GAAI,IAAA;AAIjD,YAAA,MAAM,UAAU,qBAAA,EAAsB;AACtC,YAAA,IAAI,OAAA,EAAS;AACX,cAAA,uBAAA,CAAwB,IAAA,EAAM,OAAO,OAAO,CAAA;AAAA,YAC9C;AAEA,YAAA,IAAI,WAAA,EAAa;AACf,cAAA,IAAI;AACF,gBAAA,WAAA,CAAY,IAAA,EAAM,mBAAmB,OAAO,CAAA;AAAA,cAC9C,SAAS,CAAA,EAAG;AACV,gBAAA,IAAA,CAAK,YAAA,CAAa,qBAAqB,oBAAoB,CAAA;AAC3D,gBAAA,WAAA,IAAe,KAAA,CAAM,KAAA,CAAM,gDAAA,EAAkD,CAAC,CAAA;AAAA,cAChF;AAAA,YACF;AAEA,YAAA,mBAAA,CAAoB,IAAA,EAAM,MAAM,iBAAiB,CAAA;AAEjD,YAAA,OAAO,IAAA;AAAA,UACT,CAAA;AAAA,UACA;AAAA,YACE,oBAAoB,iBAAA,KAAsB,KAAA;AAAA,YAC1C,YAAA,CAAa,EAAE,IAAA,EAAK,EAAG;AAGrB,cAAA,IAAK,IAAA,CAAiC,UAAU,CAAA,EAAG;AACjD,gBAAA,OAAO,IAAA;AAAA,cACT;AACA,cAAA,IAAI,WAAW,IAAA,EAAM;AACnB,gBAAA,OAAO,KAAA;AAAA,cACT;AAIA,cAAA,OAAO,IAAA;AAAA,YACT;AAAA;AACF,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAWO,MAAM,4BAAA,GAA+B,kBAAkB,6BAA6B;;;;"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { SERVER_PORT, SERVER_ADDRESS, DB_OPERATION_BATCH_SIZE, DB_SYSTEM_NAME, DB_STATEMENT, NET_PEER_PORT, NET_PEER_NAME, DB_SYSTEM } from '@sentry/conventions/attributes'; | ||
| import { defineIntegration, debug, waitForTracingChannelBinding, getActiveSpan, withActiveSpan, startInactiveSpan, SPAN_KIND, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_ERROR, isObjectLike } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
| import { CHANNELS } from '../../orchestrion/channels.js'; | ||
| import { defaultDbStatementSerializer } from '../../redis/redis-statement-serializer.js'; | ||
| import { bindTracingChannelToSpan } from '../../tracing-channel.js'; | ||
| const INTEGRATION_NAME = "RedisChannel"; | ||
| const ORIGIN = "auto.db.orchestrion.redis"; | ||
| const ATTR_DB_CONNECTION_STRING = "db.connection_string"; | ||
| const DB_SYSTEM_VALUE_REDIS = "redis"; | ||
| function endSpan(span, err) { | ||
| if (err) { | ||
| span.setStatus({ code: SPAN_STATUS_ERROR, message: err instanceof Error ? err.message : String(err) }); | ||
| } | ||
| span.end(); | ||
| } | ||
| function runResponseHook(hook, span, command, args, result) { | ||
| if (!hook) { | ||
| return; | ||
| } | ||
| try { | ||
| hook(span, command, args, result); | ||
| } catch { | ||
| } | ||
| } | ||
| function stripCommandOptions(args) { | ||
| const first = args[0]; | ||
| if (isObjectLike(first) && Object.getOwnPropertySymbols(first).length > 0) { | ||
| return args.slice(1); | ||
| } | ||
| return args; | ||
| } | ||
| function removeCredentialsFromConnectionString(url) { | ||
| if (typeof url !== "string" || !url) { | ||
| return void 0; | ||
| } | ||
| try { | ||
| const parsed = new URL(url); | ||
| parsed.searchParams.delete("user_pwd"); | ||
| parsed.username = ""; | ||
| parsed.password = ""; | ||
| return parsed.href; | ||
| } catch { | ||
| return void 0; | ||
| } | ||
| } | ||
| function nodeRedisAttributes(options) { | ||
| return { | ||
| [DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS, | ||
| [NET_PEER_NAME]: options?.socket?.host, | ||
| [NET_PEER_PORT]: options?.socket?.port, | ||
| [ATTR_DB_CONNECTION_STRING]: removeCredentialsFromConnectionString(options?.url), | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN | ||
| }; | ||
| } | ||
| function startCommandSpan(commandName, commandArgs, attributes) { | ||
| return startInactiveSpan({ | ||
| name: `redis-${commandName}`, | ||
| kind: SPAN_KIND.CLIENT, | ||
| attributes: { | ||
| ...attributes, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db", | ||
| [DB_STATEMENT]: defaultDbStatementSerializer(commandName, commandArgs) | ||
| } | ||
| }); | ||
| } | ||
| function subscribeLegacyRedisCommand(responseHook) { | ||
| const channel = diagnosticsChannel.tracingChannel(CHANNELS.REDIS_COMMAND); | ||
| const noop = () => { | ||
| }; | ||
| channel.subscribe({ | ||
| end: noop, | ||
| asyncStart: noop, | ||
| asyncEnd: noop, | ||
| start(data) { | ||
| const command = data.arguments?.[0]; | ||
| if (!command || typeof command !== "object") { | ||
| return; | ||
| } | ||
| const originalCallback = command.callback; | ||
| if (typeof originalCallback !== "function") { | ||
| return; | ||
| } | ||
| const client = data.self; | ||
| const attributes = { | ||
| [DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN | ||
| }; | ||
| attributes[NET_PEER_NAME] = client?.connection_options?.host; | ||
| attributes[NET_PEER_PORT] = client?.connection_options?.port; | ||
| if (client?.address) { | ||
| attributes[ATTR_DB_CONNECTION_STRING] = `redis://${client.address}`; | ||
| } | ||
| const span = startCommandSpan(command.command, command.args ?? [], attributes); | ||
| data._sentrySpan = span; | ||
| const parentSpan = getActiveSpan(); | ||
| command.callback = function(err, reply) { | ||
| if (!err) { | ||
| runResponseHook(responseHook, span, command.command, command.args ?? [], reply); | ||
| } | ||
| endSpan(span, err); | ||
| const args = arguments; | ||
| return withActiveSpan(parentSpan ?? null, () => originalCallback.apply(this, args)); | ||
| }; | ||
| }, | ||
| error(data) { | ||
| const span = data._sentrySpan; | ||
| if (span) { | ||
| endSpan(span, data.error); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| function bindNodeRedisCommandChannel(channelName, getWireArgs, responseHook) { | ||
| const channel = diagnosticsChannel.tracingChannel(channelName); | ||
| bindTracingChannelToSpan( | ||
| channel, | ||
| (data) => { | ||
| const wireArgs = getWireArgs(data); | ||
| if (!wireArgs?.length) { | ||
| return void 0; | ||
| } | ||
| const commandName = String(wireArgs[0]); | ||
| const options = data.self?.options; | ||
| return startCommandSpan(commandName, wireArgs.slice(1), nodeRedisAttributes(options)); | ||
| }, | ||
| { | ||
| captureError: false, | ||
| beforeSpanEnd(span, data) { | ||
| if ("error" in data || !responseHook) { | ||
| return; | ||
| } | ||
| const wireArgs = getWireArgs(data); | ||
| if (wireArgs?.length) { | ||
| runResponseHook(responseHook, span, String(wireArgs[0]), wireArgs.slice(1), data.result); | ||
| } | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function getSendCommandArgs(data) { | ||
| const args = data.arguments?.[0]; | ||
| return Array.isArray(args) ? args : void 0; | ||
| } | ||
| function getExecutorArgs(data) { | ||
| const command = data.arguments?.[0]; | ||
| const jsArgs = data.arguments?.[1]; | ||
| if (typeof command?.transformArguments !== "function" || !Array.isArray(jsArgs)) { | ||
| return void 0; | ||
| } | ||
| try { | ||
| return command.transformArguments(...stripCommandOptions(jsArgs)); | ||
| } catch { | ||
| return void 0; | ||
| } | ||
| } | ||
| function bindNodeRedisConnectChannel() { | ||
| const channel = diagnosticsChannel.tracingChannel(CHANNELS.NODE_REDIS_CONNECT); | ||
| bindTracingChannelToSpan( | ||
| channel, | ||
| (data) => { | ||
| const options = data.self?.options; | ||
| return startInactiveSpan({ | ||
| name: "redis-connect", | ||
| kind: SPAN_KIND.CLIENT, | ||
| attributes: { ...nodeRedisAttributes(options), [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db" } | ||
| }); | ||
| }, | ||
| { captureError: false } | ||
| ); | ||
| } | ||
| function bindNodeRedisBatchChannel(channelName, getOperation) { | ||
| const channel = diagnosticsChannel.tracingChannel(channelName); | ||
| bindTracingChannelToSpan( | ||
| channel, | ||
| (data) => { | ||
| const commands = data.arguments?.[0]; | ||
| const size = Array.isArray(commands) ? commands.length : void 0; | ||
| const socket = data.self?.options?.socket; | ||
| return startInactiveSpan({ | ||
| name: getOperation(data), | ||
| kind: SPAN_KIND.CLIENT, | ||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db.redis", | ||
| [DB_SYSTEM_NAME]: DB_SYSTEM_VALUE_REDIS, | ||
| ...size && size > 1 ? { [DB_OPERATION_BATCH_SIZE]: size } : {}, | ||
| ...socket?.host != null ? { [SERVER_ADDRESS]: socket.host } : {}, | ||
| ...socket?.port != null ? { [SERVER_PORT]: socket.port } : {} | ||
| } | ||
| }); | ||
| }, | ||
| { captureError: false } | ||
| ); | ||
| } | ||
| const _redisChannelIntegration = ((options = {}) => { | ||
| const responseHook = options.responseHook; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| DEBUG_BUILD && debug.log(`[orchestrion:redis] subscribing to "${CHANNELS.REDIS_COMMAND}" and node-redis channels`); | ||
| subscribeLegacyRedisCommand(responseHook); | ||
| waitForTracingChannelBinding(() => { | ||
| bindNodeRedisCommandChannel(CHANNELS.NODE_REDIS_COMMAND, getSendCommandArgs, responseHook); | ||
| bindNodeRedisCommandChannel(CHANNELS.NODE_REDIS_EXECUTOR, getExecutorArgs, responseHook); | ||
| bindNodeRedisConnectChannel(); | ||
| bindNodeRedisBatchChannel(CHANNELS.NODE_REDIS_MULTI, () => "MULTI"); | ||
| bindNodeRedisBatchChannel(CHANNELS.NODE_REDIS_PIPELINE, () => "PIPELINE"); | ||
| bindNodeRedisBatchChannel( | ||
| CHANNELS.NODE_REDIS_BATCH, | ||
| (data) => data.arguments?.[2] !== void 0 ? "MULTI" : "PIPELINE" | ||
| ); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const redisChannelIntegration = defineIntegration(_redisChannelIntegration); | ||
| export { redisChannelIntegration }; | ||
| //# sourceMappingURL=redis.js.map |
| {"version":3,"file":"redis.js","sources":["../../../../src/integrations/tracing-channel/redis.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-deprecated -- we intentionally emit the OLD db/net semconv\n to match `@opentelemetry/instrumentation-redis`. TODO(v11): switch to the non-deprecated\n `db.system.name`/`db.query.text`/`server.address`/`server.port` conventions and drop this disable. */\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport {\n DB_OPERATION_BATCH_SIZE,\n DB_STATEMENT,\n DB_SYSTEM,\n DB_SYSTEM_NAME,\n NET_PEER_NAME,\n NET_PEER_PORT,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport type { IntegrationFn, Span, SpanAttributes } from '@sentry/core';\nimport {\n isObjectLike,\n debug,\n defineIntegration,\n getActiveSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n waitForTracingChannelBinding,\n withActiveSpan,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { defaultDbStatementSerializer } from '../../redis/redis-statement-serializer';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// A distinct name from the composite OTel `Redis` integration — they can't share one, and\n// `Redis` stays in the set for its native diagnostics_channel subscriber (node-redis >=5.12 /\n// ioredis >=5.11). When this integration is active, the OTel `RedisInstrumentation` monkey-patch\n// is fully gated off in the node SDK.\nconst INTEGRATION_NAME = 'RedisChannel' as const;\n\nconst ORIGIN = 'auto.db.orchestrion.redis';\n\n// todo(v11): drop this — it is already covered by host and port.\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst DB_SYSTEM_VALUE_REDIS = 'redis';\n\n/** Mirrors `@opentelemetry/instrumentation-redis`' response hook. Not called for failed commands. */\nexport type RedisResponseHook = (span: Span, command: string, args: Array<string | Buffer>, result: unknown) => void;\n\nexport interface RedisChannelIntegrationOptions {\n responseHook?: RedisResponseHook;\n}\n\n/** Structural type for a node-redis (`@redis/client`) command definition. */\ninterface RedisCommandDefinition {\n transformArguments?: (...args: unknown[]) => Array<string | Buffer>;\n}\n\n/** Structural type for the `command_obj` `redis` v2-v3 passes to `internal_send_command`. */\ninterface LegacyRedisCommand {\n command: string;\n args: Array<string | Buffer>;\n callback?: (err: Error | null | undefined, reply: unknown) => unknown;\n}\n\ninterface LegacyRedisClient {\n connection_options?: { host?: string; port?: number };\n address?: string;\n}\n\ninterface NodeRedisClientOptions {\n socket?: { host?: string; port?: number };\n url?: string;\n}\n\ninterface NodeRedisClient {\n options?: NodeRedisClientOptions;\n}\n\ninterface CommandContext {\n arguments?: unknown[];\n self?: unknown;\n result?: unknown;\n error?: unknown;\n}\n\nfunction endSpan(span: Span, err: unknown): void {\n if (err) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: err instanceof Error ? err.message : String(err) });\n }\n span.end();\n}\n\nfunction runResponseHook(\n hook: RedisResponseHook | undefined,\n span: Span,\n command: string,\n args: Array<string | Buffer>,\n result: unknown,\n): void {\n if (!hook) {\n return;\n }\n try {\n hook(span, command, args, result);\n } catch {\n // never let a user-provided response hook break instrumentation\n }\n}\n\n// Strip a leading `commandOptions(...)` object (tagged with a `Symbol`) before\n// deriving the wire arguments, mirroring `@redis/client`'s `transformCommandArguments`.\nfunction stripCommandOptions(args: unknown[]): unknown[] {\n const first = args[0];\n if (isObjectLike(first) && Object.getOwnPropertySymbols(first).length > 0) {\n return args.slice(1);\n }\n return args;\n}\n\nfunction removeCredentialsFromConnectionString(url: string | undefined): string | undefined {\n if (typeof url !== 'string' || !url) {\n return undefined;\n }\n try {\n const parsed = new URL(url);\n parsed.searchParams.delete('user_pwd');\n parsed.username = '';\n parsed.password = '';\n return parsed.href;\n } catch {\n return undefined;\n }\n}\n\nfunction nodeRedisAttributes(options: NodeRedisClientOptions | undefined): SpanAttributes {\n return {\n [DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS,\n [NET_PEER_NAME]: options?.socket?.host,\n [NET_PEER_PORT]: options?.socket?.port,\n [ATTR_DB_CONNECTION_STRING]: removeCredentialsFromConnectionString(options?.url),\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n };\n}\n\nfunction startCommandSpan(commandName: string, commandArgs: Array<string | Buffer>, attributes: SpanAttributes): Span {\n return startInactiveSpan({\n name: `redis-${commandName}`,\n kind: SPAN_KIND.CLIENT,\n attributes: {\n ...attributes,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',\n [DB_STATEMENT]: defaultDbStatementSerializer(commandName, commandArgs),\n },\n });\n}\n\n// --- redis v2-v3: `RedisClient.prototype.internal_send_command(command_obj)` ---\n\n// Settles via `command_obj.callback`, not the sync return — so instead of\n// `bindTracingChannelToSpan` we open the span in `start`, wrap the callback to end it, and end on `error` for sync throws.\nfunction subscribeLegacyRedisCommand(responseHook: RedisResponseHook | undefined): void {\n const channel = diagnosticsChannel.tracingChannel<CommandContext>(CHANNELS.REDIS_COMMAND);\n const noop = (): void => {};\n channel.subscribe({\n end: noop,\n asyncStart: noop,\n asyncEnd: noop,\n start(data) {\n const command = data.arguments?.[0] as LegacyRedisCommand | undefined;\n if (!command || typeof command !== 'object') {\n return;\n }\n // The span is ended via the wrapped callback (or the sync-throw `error` path). A\n // command with no callback has no completion signal to end it on, so don't open one.\n const originalCallback = command.callback;\n if (typeof originalCallback !== 'function') {\n return;\n }\n const client = data.self as LegacyRedisClient | undefined;\n const attributes: SpanAttributes = {\n [DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n };\n\n attributes[NET_PEER_NAME] = client?.connection_options?.host;\n attributes[NET_PEER_PORT] = client?.connection_options?.port;\n\n if (client?.address) {\n attributes[ATTR_DB_CONNECTION_STRING] = `redis://${client.address}`;\n }\n const span = startCommandSpan(command.command, command.args ?? [], attributes);\n (data as CommandContext & { _sentrySpan?: Span })._sentrySpan = span;\n\n const parentSpan = getActiveSpan();\n command.callback = function (this: unknown, err: Error | null | undefined, reply: unknown) {\n if (!err) {\n runResponseHook(responseHook, span, command.command, command.args ?? [], reply);\n }\n endSpan(span, err);\n // eslint-disable-next-line prefer-rest-params\n const args = arguments as unknown as [Error | null | undefined, unknown];\n return withActiveSpan(parentSpan ?? null, () => originalCallback.apply(this, args));\n };\n },\n error(data) {\n // Synchronous throw: the wrapped callback never fires, so end here instead.\n const span = (data as CommandContext & { _sentrySpan?: Span })._sentrySpan;\n if (span) {\n endSpan(span, data.error);\n }\n },\n });\n}\n\n// --- node-redis v4/v5 (`@redis/client`) ---\n\nfunction bindNodeRedisCommandChannel(\n channelName: string,\n getWireArgs: (data: CommandContext) => Array<string | Buffer> | undefined,\n responseHook: RedisResponseHook | undefined,\n): void {\n const channel = diagnosticsChannel.tracingChannel<CommandContext, CommandContext>(channelName);\n bindTracingChannelToSpan(\n channel,\n data => {\n const wireArgs = getWireArgs(data);\n if (!wireArgs?.length) {\n return undefined;\n }\n const commandName = String(wireArgs[0]);\n const options = (data.self as NodeRedisClient | undefined)?.options;\n return startCommandSpan(commandName, wireArgs.slice(1), nodeRedisAttributes(options));\n },\n {\n captureError: false,\n beforeSpanEnd(span, data) {\n if ('error' in data || !responseHook) {\n return;\n }\n const wireArgs = getWireArgs(data);\n if (wireArgs?.length) {\n runResponseHook(responseHook, span, String(wireArgs[0]), wireArgs.slice(1), data.result);\n }\n },\n },\n );\n}\n\n// `sendCommand(args, options)` — `args` are already the wire arguments.\nfunction getSendCommandArgs(data: CommandContext): Array<string | Buffer> | undefined {\n const args = data.arguments?.[0];\n return Array.isArray(args) ? (args as Array<string | Buffer>) : undefined;\n}\n\n// `commandsExecutor(command, jsArgs)` — derive the wire arguments the same way\n// `@redis/client` does internally, via `command.transformArguments`.\nfunction getExecutorArgs(data: CommandContext): Array<string | Buffer> | undefined {\n const command = data.arguments?.[0] as RedisCommandDefinition | undefined;\n const jsArgs = data.arguments?.[1];\n if (typeof command?.transformArguments !== 'function' || !Array.isArray(jsArgs)) {\n return undefined;\n }\n try {\n return command.transformArguments(...stripCommandOptions(jsArgs));\n } catch {\n return undefined;\n }\n}\n\nfunction bindNodeRedisConnectChannel(): void {\n const channel = diagnosticsChannel.tracingChannel<CommandContext, CommandContext>(CHANNELS.NODE_REDIS_CONNECT);\n bindTracingChannelToSpan(\n channel,\n data => {\n const options = (data.self as NodeRedisClient | undefined)?.options;\n return startInactiveSpan({\n name: 'redis-connect',\n kind: SPAN_KIND.CLIENT,\n attributes: { ...nodeRedisAttributes(options), [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db' },\n });\n },\n { captureError: false },\n );\n}\n\n// Batch (multi/pipeline): one span per `exec`. Batched commands bypass `sendCommand`, so\n// the executor's `ctx.arguments[0]` (the queued commands) gives the batch size. Span shape\n// mirrors the native `node-redis:batch` span (see `redis-dc-subscriber.ts`).\nfunction bindNodeRedisBatchChannel(channelName: string, getOperation: (data: CommandContext) => string): void {\n const channel = diagnosticsChannel.tracingChannel<CommandContext, CommandContext>(channelName);\n bindTracingChannelToSpan(\n channel,\n data => {\n const commands = data.arguments?.[0];\n const size = Array.isArray(commands) ? commands.length : undefined;\n const socket = (data.self as NodeRedisClient | undefined)?.options?.socket;\n return startInactiveSpan({\n name: getOperation(data),\n kind: SPAN_KIND.CLIENT,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [DB_SYSTEM_NAME]: DB_SYSTEM_VALUE_REDIS,\n ...(size && size > 1 ? { [DB_OPERATION_BATCH_SIZE]: size } : {}),\n ...(socket?.host != null ? { [SERVER_ADDRESS]: socket.host } : {}),\n ...(socket?.port != null ? { [SERVER_PORT]: socket.port } : {}),\n },\n });\n },\n { captureError: false },\n );\n}\n\nconst _redisChannelIntegration = ((options: RedisChannelIntegrationOptions = {}) => {\n const responseHook = options.responseHook;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD &&\n debug.log(`[orchestrion:redis] subscribing to \"${CHANNELS.REDIS_COMMAND}\" and node-redis channels`);\n\n // redis v2-v3 uses a nested callback rather than `bindStore`, so it can be\n // subscribed synchronously here.\n subscribeLegacyRedisCommand(responseHook);\n\n waitForTracingChannelBinding(() => {\n bindNodeRedisCommandChannel(CHANNELS.NODE_REDIS_COMMAND, getSendCommandArgs, responseHook);\n bindNodeRedisCommandChannel(CHANNELS.NODE_REDIS_EXECUTOR, getExecutorArgs, responseHook);\n bindNodeRedisConnectChannel();\n bindNodeRedisBatchChannel(CHANNELS.NODE_REDIS_MULTI, () => 'MULTI');\n bindNodeRedisBatchChannel(CHANNELS.NODE_REDIS_PIPELINE, () => 'PIPELINE');\n bindNodeRedisBatchChannel(CHANNELS.NODE_REDIS_BATCH, data =>\n data.arguments?.[2] !== undefined ? 'MULTI' : 'PIPELINE',\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * EXPERIMENTAL — orchestrion-driven redis integration for `redis` v2-v3 and\n * node-redis v4/v5 `<5.12.0` (`@redis/client`). Covers single commands, `connect`,\n * and multi/pipeline batches, fully replacing `@opentelemetry/instrumentation-redis`.\n * Requires the orchestrion runtime hook or bundler plugin.\n */\nexport const redisChannelIntegration = defineIntegration(_redisChannelIntegration);\n"],"names":[],"mappings":"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"} |
| import { defineIntegration, waitForTracingChannelBinding } from '@sentry/core'; | ||
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { subscribeMysql2DiagnosticChannels } from './mysql2-dc-subscriber.js'; | ||
| const _mysql2Integration = (() => { | ||
| return { | ||
| name: "Mysql2", | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| waitForTracingChannelBinding(() => { | ||
| subscribeMysql2DiagnosticChannels(diagnosticsChannel.tracingChannel); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const mysql2Integration = defineIntegration(_mysql2Integration); | ||
| export { mysql2Integration }; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../src/mysql2/index.ts"],"sourcesContent":["import { defineIntegration, type IntegrationFn, waitForTracingChannelBinding } from '@sentry/core';\nimport * as dc from 'node:diagnostics_channel';\nimport { subscribeMysql2DiagnosticChannels } from './mysql2-dc-subscriber';\n\nconst _mysql2Integration = (() => {\n return {\n name: 'Mysql2',\n setupOnce() {\n // Bail on Node <= 18.18.0, where `tracingChannel` does not exist.\n if (!dc.tracingChannel) {\n return;\n }\n\n // Subscribe to mysql2's native tracing channels (mysql2 >= 3.20.0).\n // This is a no-op on versions that don't publish to the channels, so it is always safe to call.\n waitForTracingChannelBinding(() => {\n subscribeMysql2DiagnosticChannels(dc.tracingChannel);\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the [mysql2](https://www.npmjs.com/package/mysql2) library via its native\n * `node:diagnostics_channel` tracing channels (mysql2 >= 3.20.0).\n *\n * On older mysql2 versions the channels are never published to, so this integration is inert and\n * the vendored OTel instrumentation (gated to `< 3.20.0`) handles instrumentation instead.\n */\nexport const mysql2Integration = defineIntegration(_mysql2Integration);\n"],"names":["dc"],"mappings":";;;;AAIA,MAAM,sBAAsB,MAAM;AAChC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAACA,mBAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAIA,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,iCAAA,CAAkCA,mBAAG,cAAc,CAAA;AAAA,MACrD,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AASO,MAAM,iBAAA,GAAoB,kBAAkB,kBAAkB;;;;"} |
| import { SERVER_PORT, SERVER_ADDRESS, DB_NAMESPACE, DB_OPERATION_NAME, DB_QUERY_TEXT, DB_SYSTEM_NAME } from '@sentry/conventions/attributes'; | ||
| import { _INTERNAL_sanitizeSqlQuery, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
| import { bindTracingChannelToSpan } from '../tracing-channel.js'; | ||
| const MYSQL2_DC_CHANNEL_QUERY = "mysql2:query"; | ||
| const MYSQL2_DC_CHANNEL_EXECUTE = "mysql2:execute"; | ||
| const MYSQL2_DC_CHANNEL_CONNECT = "mysql2:connect"; | ||
| const MYSQL2_DC_CHANNEL_POOL_CONNECT = "mysql2:pool:connect"; | ||
| const ORIGIN = "auto.db.mysql2.diagnostic_channel"; | ||
| const DB_SYSTEM_NAME_VALUE_MYSQL = "mysql"; | ||
| const SQL_OPERATION_RE = /^\s*(\w+)/; | ||
| function subscribeMysql2DiagnosticChannels(tracingChannel) { | ||
| setupQueryChannel(tracingChannel, MYSQL2_DC_CHANNEL_QUERY); | ||
| setupQueryChannel(tracingChannel, MYSQL2_DC_CHANNEL_EXECUTE); | ||
| setupConnectChannel(tracingChannel, MYSQL2_DC_CHANNEL_CONNECT, "mysql2.connect"); | ||
| setupConnectChannel(tracingChannel, MYSQL2_DC_CHANNEL_POOL_CONNECT, "mysql2.pool.connect"); | ||
| } | ||
| function setupQueryChannel(tracingChannel, channelName) { | ||
| bindTracingChannelToSpan( | ||
| tracingChannel(channelName), | ||
| (data) => { | ||
| const queryText = data.query ? _INTERNAL_sanitizeSqlQuery(data.query) : void 0; | ||
| const operation = queryText?.match(SQL_OPERATION_RE)?.[1]?.toUpperCase(); | ||
| return startInactiveSpan({ | ||
| name: queryText || "mysql2.query", | ||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db", | ||
| [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MYSQL, | ||
| [DB_QUERY_TEXT]: queryText, | ||
| [DB_OPERATION_NAME]: operation, | ||
| [DB_NAMESPACE]: data.database || void 0, | ||
| [SERVER_ADDRESS]: data.serverAddress, | ||
| [SERVER_PORT]: data.serverPort | ||
| } | ||
| }); | ||
| }, | ||
| { requiresParentSpan: true } | ||
| ); | ||
| } | ||
| function setupConnectChannel(tracingChannel, channelName, spanName) { | ||
| bindTracingChannelToSpan( | ||
| tracingChannel(channelName), | ||
| (data) => { | ||
| return startInactiveSpan({ | ||
| name: spanName, | ||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db", | ||
| [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MYSQL, | ||
| [DB_NAMESPACE]: data.database || void 0, | ||
| [SERVER_ADDRESS]: data.serverAddress, | ||
| [SERVER_PORT]: data.serverPort | ||
| } | ||
| }); | ||
| }, | ||
| { requiresParentSpan: true } | ||
| ); | ||
| } | ||
| export { MYSQL2_DC_CHANNEL_CONNECT, MYSQL2_DC_CHANNEL_EXECUTE, MYSQL2_DC_CHANNEL_POOL_CONNECT, MYSQL2_DC_CHANNEL_QUERY, subscribeMysql2DiagnosticChannels }; | ||
| //# sourceMappingURL=mysql2-dc-subscriber.js.map |
| {"version":3,"file":"mysql2-dc-subscriber.js","sources":["../../../src/mysql2/mysql2-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport {\n DB_NAMESPACE,\n DB_OPERATION_NAME,\n DB_QUERY_TEXT,\n DB_SYSTEM_NAME,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport {\n _INTERNAL_sanitizeSqlQuery,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startInactiveSpan,\n} from '@sentry/core';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n// Channel names published by mysql2 >= 3.20.0 (see mysql2 `lib/tracing.js`).\n// Hardcoded so the subscriber does not have to import mysql2 — the channels\n// just have to be subscribed to before the user's mysql2 code publishes.\nexport const MYSQL2_DC_CHANNEL_QUERY = 'mysql2:query';\nexport const MYSQL2_DC_CHANNEL_EXECUTE = 'mysql2:execute';\nexport const MYSQL2_DC_CHANNEL_CONNECT = 'mysql2:connect';\nexport const MYSQL2_DC_CHANNEL_POOL_CONNECT = 'mysql2:pool:connect';\n\nconst ORIGIN = 'auto.db.mysql2.diagnostic_channel';\nconst DB_SYSTEM_NAME_VALUE_MYSQL = 'mysql';\n\n// Leading keyword of a SQL statement (SELECT, INSERT, …) → `db.operation.name`.\nconst SQL_OPERATION_RE = /^\\s*(\\w+)/;\n\n/**\n * Shape of the context object mysql2 >= 3.20.0 publishes on its query/execute\n * tracing channels (see mysql2 `lib/base/connection.js`).\n *\n * Node's `traceCallback`/`tracePromise` mutate this same object with\n * `result`/`error` once the operation settles, which `bindTracingChannelToSpan`\n * reads in its lifecycle handlers — hence both are declared optional here.\n *\n * `query` is the SQL statement. On the `query` channel mysql2 has already\n * inlined `values` into it (`Connection.format`), so it carries raw user data;\n * on the `execute` channel it keeps `?` placeholders. Either way we sanitize it\n * before emitting `db.query.text` and never attach `values`.\n */\nexport interface MySQL2QueryData {\n query?: string;\n values?: unknown;\n database?: string;\n serverAddress?: string;\n /** Absent for unix-socket connections, where `serverAddress` is the socket path. */\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Shape of the context object mysql2 >= 3.20.0 publishes on its\n * `connect`/`pool:connect` channels.\n */\nexport interface MySQL2ConnectData {\n database?: string;\n serverAddress?: string;\n serverPort?: number;\n user?: string;\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Node passes `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type MySQL2TracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/**\n * Subscribe Sentry span handlers to mysql2's diagnostics-channel events\n * (`mysql2:query`, `:execute`, `:connect`, `:pool:connect`), published by\n * mysql2 >= 3.20.0.\n *\n * On older mysql2 versions the channels are never published to, so the\n * subscribers are inert — there is no double-instrumentation against the\n * vendored OTel patcher, which is gated to `< 3.20.0`.\n */\nexport function subscribeMysql2DiagnosticChannels(tracingChannel: MySQL2TracingChannelFactory): void {\n setupQueryChannel(tracingChannel, MYSQL2_DC_CHANNEL_QUERY);\n setupQueryChannel(tracingChannel, MYSQL2_DC_CHANNEL_EXECUTE);\n setupConnectChannel(tracingChannel, MYSQL2_DC_CHANNEL_CONNECT, 'mysql2.connect');\n setupConnectChannel(tracingChannel, MYSQL2_DC_CHANNEL_POOL_CONNECT, 'mysql2.pool.connect');\n}\n\nfunction setupQueryChannel(tracingChannel: MySQL2TracingChannelFactory, channelName: string): void {\n bindTracingChannelToSpan(\n tracingChannel<MySQL2QueryData>(channelName),\n data => {\n // mysql2 does not sanitize its channel payload, so the statement may carry\n // raw user values (on the `query` channel they are inlined). Strip every\n // literal before it leaves the process; `values` is never attached.\n const queryText = data.query ? _INTERNAL_sanitizeSqlQuery(data.query) : undefined;\n const operation = queryText?.match(SQL_OPERATION_RE)?.[1]?.toUpperCase();\n\n return startInactiveSpan({\n name: queryText || 'mysql2.query',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MYSQL,\n [DB_QUERY_TEXT]: queryText,\n [DB_OPERATION_NAME]: operation,\n [DB_NAMESPACE]: data.database || undefined,\n [SERVER_ADDRESS]: data.serverAddress,\n [SERVER_PORT]: data.serverPort,\n },\n });\n },\n { requiresParentSpan: true },\n );\n}\n\nfunction setupConnectChannel(tracingChannel: MySQL2TracingChannelFactory, channelName: string, spanName: string): void {\n bindTracingChannelToSpan(\n tracingChannel<MySQL2ConnectData>(channelName),\n data => {\n return startInactiveSpan({\n name: spanName,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MYSQL,\n [DB_NAMESPACE]: data.database || undefined,\n [SERVER_ADDRESS]: data.serverAddress,\n [SERVER_PORT]: data.serverPort,\n },\n });\n },\n { requiresParentSpan: true },\n );\n}\n"],"names":[],"mappings":";;;;AAoBO,MAAM,uBAAA,GAA0B;AAChC,MAAM,yBAAA,GAA4B;AAClC,MAAM,yBAAA,GAA4B;AAClC,MAAM,8BAAA,GAAiC;AAE9C,MAAM,MAAA,GAAS,mCAAA;AACf,MAAM,0BAAA,GAA6B,OAAA;AAGnC,MAAM,gBAAA,GAAmB,WAAA;AAyDlB,SAAS,kCAAkC,cAAA,EAAmD;AACnG,EAAA,iBAAA,CAAkB,gBAAgB,uBAAuB,CAAA;AACzD,EAAA,iBAAA,CAAkB,gBAAgB,yBAAyB,CAAA;AAC3D,EAAA,mBAAA,CAAoB,cAAA,EAAgB,2BAA2B,gBAAgB,CAAA;AAC/E,EAAA,mBAAA,CAAoB,cAAA,EAAgB,gCAAgC,qBAAqB,CAAA;AAC3F;AAEA,SAAS,iBAAA,CAAkB,gBAA6C,WAAA,EAA2B;AACjG,EAAA,wBAAA;AAAA,IACE,eAAgC,WAAW,CAAA;AAAA,IAC3C,CAAA,IAAA,KAAQ;AAIN,MAAA,MAAM,YAAY,IAAA,CAAK,KAAA,GAAQ,0BAAA,CAA2B,IAAA,CAAK,KAAK,CAAA,GAAI,MAAA;AACxE,MAAA,MAAM,YAAY,SAAA,EAAW,KAAA,CAAM,gBAAgB,CAAA,GAAI,CAAC,GAAG,WAAA,EAAY;AAEvE,MAAA,OAAO,iBAAA,CAAkB;AAAA,QACvB,MAAM,SAAA,IAAa,cAAA;AAAA,QACnB,UAAA,EAAY;AAAA,UACV,CAAC,gCAAgC,GAAG,MAAA;AAAA,UACpC,CAAC,4BAA4B,GAAG,IAAA;AAAA,UAChC,CAAC,cAAc,GAAG,0BAAA;AAAA,UAClB,CAAC,aAAa,GAAG,SAAA;AAAA,UACjB,CAAC,iBAAiB,GAAG,SAAA;AAAA,UACrB,CAAC,YAAY,GAAG,IAAA,CAAK,QAAA,IAAY,MAAA;AAAA,UACjC,CAAC,cAAc,GAAG,IAAA,CAAK,aAAA;AAAA,UACvB,CAAC,WAAW,GAAG,IAAA,CAAK;AAAA;AACtB,OACD,CAAA;AAAA,IACH,CAAA;AAAA,IACA,EAAE,oBAAoB,IAAA;AAAK,GAC7B;AACF;AAEA,SAAS,mBAAA,CAAoB,cAAA,EAA6C,WAAA,EAAqB,QAAA,EAAwB;AACrH,EAAA,wBAAA;AAAA,IACE,eAAkC,WAAW,CAAA;AAAA,IAC7C,CAAA,IAAA,KAAQ;AACN,MAAA,OAAO,iBAAA,CAAkB;AAAA,QACvB,IAAA,EAAM,QAAA;AAAA,QACN,UAAA,EAAY;AAAA,UACV,CAAC,gCAAgC,GAAG,MAAA;AAAA,UACpC,CAAC,4BAA4B,GAAG,IAAA;AAAA,UAChC,CAAC,cAAc,GAAG,0BAAA;AAAA,UAClB,CAAC,YAAY,GAAG,IAAA,CAAK,QAAA,IAAY,MAAA;AAAA,UACjC,CAAC,cAAc,GAAG,IAAA,CAAK,aAAA;AAAA,UACvB,CAAC,WAAW,GAAG,IAAA,CAAK;AAAA;AACtB,OACD,CAAA;AAAA,IACH,CAAA;AAAA,IACA,EAAE,oBAAoB,IAAA;AAAK,GAC7B;AACF;;;;"} |
| const module$1 = { name: "amqplib", versionRange: ">=0.5.5 <2" }; | ||
| const amqplibConfig = [ | ||
| // Producer span + trace-header injection. `sendToQueue` delegates to `publish`, so it's covered. | ||
| { | ||
| channelName: "publish", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "publish", kind: "Sync" } | ||
| }, | ||
| // Confirm-channel producer span; the trailing broker-confirm callback ends the span when the | ||
| // broker acks/nacks. It internally calls `super.publish`, so the subscriber guards against the | ||
| // base `publish` channel double-instrumenting. | ||
| { | ||
| channelName: "confirmPublish", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "ConfirmChannel", methodName: "publish", kind: "Callback" } | ||
| }, | ||
| // Records `consumerTag -> { noAck, queue }` so the per-message dispatch hook knows how to name and | ||
| // when to end the consumer span. | ||
| { | ||
| channelName: "consume", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "consume", kind: "Async" } | ||
| }, | ||
| // Per delivered message: creates the consumer span and runs the user callback under it. | ||
| { | ||
| channelName: "dispatch", | ||
| module: { ...module$1, filePath: "lib/channel.js" }, | ||
| functionQuery: { className: "BaseChannel", methodName: "dispatchMessage", kind: "Sync" } | ||
| }, | ||
| // End the consumer span when the user settles the message. | ||
| { | ||
| channelName: "ack", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "ack", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "nack", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "nack", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "reject", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "reject", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "ackAll", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "ackAll", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "nackAll", | ||
| module: { ...module$1, filePath: "lib/channel_model.js" }, | ||
| functionQuery: { className: "Channel", methodName: "nackAll", kind: "Sync" } | ||
| }, | ||
| // Stashes connection attributes (url/host/port/protocol/server product) on the connection object | ||
| // for span-time reads via `channel.connection`. | ||
| { | ||
| channelName: "connect", | ||
| module: { ...module$1, filePath: "lib/connect.js" }, | ||
| functionQuery: { functionName: "connect", kind: "Callback" } | ||
| } | ||
| ]; | ||
| const amqplibChannels = { | ||
| AMQPLIB_PUBLISH: "orchestrion:amqplib:publish", | ||
| AMQPLIB_CONFIRM_PUBLISH: "orchestrion:amqplib:confirmPublish", | ||
| AMQPLIB_CONSUME: "orchestrion:amqplib:consume", | ||
| AMQPLIB_DISPATCH: "orchestrion:amqplib:dispatch", | ||
| AMQPLIB_ACK: "orchestrion:amqplib:ack", | ||
| AMQPLIB_NACK: "orchestrion:amqplib:nack", | ||
| AMQPLIB_REJECT: "orchestrion:amqplib:reject", | ||
| AMQPLIB_ACK_ALL: "orchestrion:amqplib:ackAll", | ||
| AMQPLIB_NACK_ALL: "orchestrion:amqplib:nackAll", | ||
| AMQPLIB_CONNECT: "orchestrion:amqplib:connect" | ||
| }; | ||
| export { amqplibChannels, amqplibConfig }; | ||
| //# sourceMappingURL=amqplib.js.map |
| {"version":3,"file":"amqplib.js","sources":["../../../../src/orchestrion/config/amqplib.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\n// `amqplib` splits its API across three files:\n// - `lib/channel_model.js` holds `class Channel` (publish/consume/ack/nack/reject/…) and\n// `class ConfirmChannel extends Channel` (a `publish` that takes a broker-confirm callback).\n// - `lib/channel.js` holds `class BaseChannel` whose `dispatchMessage` invokes the registered\n// consumer callback once per delivered message — the natural per-message hook for consumer spans\n// (`Channel.consume` itself only registers the callback, it isn't called per message).\n// - `lib/connect.js` holds the `connect` function whose callback receives the open connection, used\n// to capture connection attributes.\n//\n// The version range mirrors `supportedVersions` in the vendored OTel instrumentation.\nconst module = { name: 'amqplib', versionRange: '>=0.5.5 <2' } as const;\n\nexport const amqplibConfig = [\n // Producer span + trace-header injection. `sendToQueue` delegates to `publish`, so it's covered.\n {\n channelName: 'publish',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'publish', kind: 'Sync' },\n },\n // Confirm-channel producer span; the trailing broker-confirm callback ends the span when the\n // broker acks/nacks. It internally calls `super.publish`, so the subscriber guards against the\n // base `publish` channel double-instrumenting.\n {\n channelName: 'confirmPublish',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'ConfirmChannel', methodName: 'publish', kind: 'Callback' },\n },\n // Records `consumerTag -> { noAck, queue }` so the per-message dispatch hook knows how to name and\n // when to end the consumer span.\n {\n channelName: 'consume',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'consume', kind: 'Async' },\n },\n // Per delivered message: creates the consumer span and runs the user callback under it.\n {\n channelName: 'dispatch',\n module: { ...module, filePath: 'lib/channel.js' },\n functionQuery: { className: 'BaseChannel', methodName: 'dispatchMessage', kind: 'Sync' },\n },\n // End the consumer span when the user settles the message.\n {\n channelName: 'ack',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'ack', kind: 'Sync' },\n },\n {\n channelName: 'nack',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'nack', kind: 'Sync' },\n },\n {\n channelName: 'reject',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'reject', kind: 'Sync' },\n },\n {\n channelName: 'ackAll',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'ackAll', kind: 'Sync' },\n },\n {\n channelName: 'nackAll',\n module: { ...module, filePath: 'lib/channel_model.js' },\n functionQuery: { className: 'Channel', methodName: 'nackAll', kind: 'Sync' },\n },\n // Stashes connection attributes (url/host/port/protocol/server product) on the connection object\n // for span-time reads via `channel.connection`.\n {\n channelName: 'connect',\n module: { ...module, filePath: 'lib/connect.js' },\n functionQuery: { functionName: 'connect', kind: 'Callback' },\n },\n] satisfies InstrumentationConfig[];\n\nexport const amqplibChannels = {\n AMQPLIB_PUBLISH: 'orchestrion:amqplib:publish',\n AMQPLIB_CONFIRM_PUBLISH: 'orchestrion:amqplib:confirmPublish',\n AMQPLIB_CONSUME: 'orchestrion:amqplib:consume',\n AMQPLIB_DISPATCH: 'orchestrion:amqplib:dispatch',\n AMQPLIB_ACK: 'orchestrion:amqplib:ack',\n AMQPLIB_NACK: 'orchestrion:amqplib:nack',\n AMQPLIB_REJECT: 'orchestrion:amqplib:reject',\n AMQPLIB_ACK_ALL: 'orchestrion:amqplib:ackAll',\n AMQPLIB_NACK_ALL: 'orchestrion:amqplib:nackAll',\n AMQPLIB_CONNECT: 'orchestrion:amqplib:connect',\n} as const;\n"],"names":["module"],"mappings":"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"} |
| const expressConfig = [ | ||
| // Express funnels every middleware/route handler through a single method on | ||
| // its routing `Layer`, so instrumenting that one method covers the whole | ||
| // request pipeline. The `expressChannelIntegration` opens one span per layer | ||
| // invocation. Both are `Layer.prototype.<method> = function <fn>(req, res, next)` | ||
| // prototype assignments (not `class` methods), so `expressionName` (matching | ||
| // the assignment's `left.property.name`) is used. `Callback`: the handler's | ||
| // last argument is `next`, so the transform ends the traced operation when | ||
| // `next` is invoked (and publishes `error` when it's called with an error). | ||
| // | ||
| // Express v4 ships its own router in `express/lib/router/layer.js`. | ||
| { | ||
| channelName: "handle", | ||
| module: { name: "express", versionRange: ">=4.0.0 <5", filePath: "lib/router/layer.js" }, | ||
| // v4's method is `Layer.prototype.handle_request = function handle(...)` — | ||
| // match the assigned property name, not the function name. | ||
| functionQuery: { expressionName: "handle_request", kind: "Callback" } | ||
| }, | ||
| // Express v5 delegates routing to the standalone `router` package. | ||
| { | ||
| channelName: "handle", | ||
| module: { name: "router", versionRange: ">=2.0.0 <3", filePath: "lib/layer.js" }, | ||
| functionQuery: { expressionName: "handleRequest", kind: "Callback" } | ||
| }, | ||
| // Layer *registration* methods. `Router.prototype.route`/`.use` are called | ||
| // once per registered route/middleware (including internally by `app.get`/ | ||
| // `app.use`), so subscribing here lets us record each layer's registered path | ||
| // *pattern* — which the handler path (`req.baseUrl`) can't recover for | ||
| // parameterized mounts. `Sync`: these return synchronously and, unlike a | ||
| // handler, `use`'s trailing function argument is a registration payload, not a | ||
| // callback — so `Callback` would misclassify it and never fire `end`. | ||
| // | ||
| // Express v4 ships its own router in `express/lib/router/index.js`. | ||
| { | ||
| channelName: "route", | ||
| module: { name: "express", versionRange: ">=4.0.0 <5", filePath: "lib/router/index.js" }, | ||
| functionQuery: { expressionName: "route", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "use", | ||
| module: { name: "express", versionRange: ">=4.0.0 <5", filePath: "lib/router/index.js" }, | ||
| functionQuery: { expressionName: "use", kind: "Sync" } | ||
| }, | ||
| // Express v5 delegates routing to the standalone `router` package. | ||
| { | ||
| channelName: "route", | ||
| module: { name: "router", versionRange: ">=2.0.0 <3", filePath: "index.js" }, | ||
| functionQuery: { expressionName: "route", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "use", | ||
| module: { name: "router", versionRange: ">=2.0.0 <3", filePath: "index.js" }, | ||
| functionQuery: { expressionName: "use", kind: "Sync" } | ||
| } | ||
| ]; | ||
| const expressChannels = { | ||
| // Express v4 runs each layer's handler through `Layer.prototype.handle_request` | ||
| // in the `express` module. | ||
| EXPRESS_HANDLE: "orchestrion:express:handle", | ||
| // Express v5 delegates routing to the standalone `router` package, where the | ||
| // equivalent method is `Layer.prototype.handleRequest`. | ||
| ROUTER_HANDLE: "orchestrion:router:handle", | ||
| // Layer *registration* (`Router.prototype.route`/`.use`), used to capture each | ||
| // layer's registered path pattern so the matched route can be reconstructed | ||
| // with its parameters intact (`req.baseUrl` only exposes the resolved prefix). | ||
| EXPRESS_ROUTE: "orchestrion:express:route", | ||
| EXPRESS_USE: "orchestrion:express:use", | ||
| ROUTER_ROUTE: "orchestrion:router:route", | ||
| ROUTER_USE: "orchestrion:router:use" | ||
| }; | ||
| export { expressChannels, expressConfig }; | ||
| //# sourceMappingURL=express.js.map |
| {"version":3,"file":"express.js","sources":["../../../../src/orchestrion/config/express.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\nexport const expressConfig = [\n // Express funnels every middleware/route handler through a single method on\n // its routing `Layer`, so instrumenting that one method covers the whole\n // request pipeline. The `expressChannelIntegration` opens one span per layer\n // invocation. Both are `Layer.prototype.<method> = function <fn>(req, res, next)`\n // prototype assignments (not `class` methods), so `expressionName` (matching\n // the assignment's `left.property.name`) is used. `Callback`: the handler's\n // last argument is `next`, so the transform ends the traced operation when\n // `next` is invoked (and publishes `error` when it's called with an error).\n //\n // Express v4 ships its own router in `express/lib/router/layer.js`.\n {\n channelName: 'handle',\n module: { name: 'express', versionRange: '>=4.0.0 <5', filePath: 'lib/router/layer.js' },\n // v4's method is `Layer.prototype.handle_request = function handle(...)` —\n // match the assigned property name, not the function name.\n functionQuery: { expressionName: 'handle_request', kind: 'Callback' },\n },\n // Express v5 delegates routing to the standalone `router` package.\n {\n channelName: 'handle',\n module: { name: 'router', versionRange: '>=2.0.0 <3', filePath: 'lib/layer.js' },\n functionQuery: { expressionName: 'handleRequest', kind: 'Callback' },\n },\n // Layer *registration* methods. `Router.prototype.route`/`.use` are called\n // once per registered route/middleware (including internally by `app.get`/\n // `app.use`), so subscribing here lets us record each layer's registered path\n // *pattern* — which the handler path (`req.baseUrl`) can't recover for\n // parameterized mounts. `Sync`: these return synchronously and, unlike a\n // handler, `use`'s trailing function argument is a registration payload, not a\n // callback — so `Callback` would misclassify it and never fire `end`.\n //\n // Express v4 ships its own router in `express/lib/router/index.js`.\n {\n channelName: 'route',\n module: { name: 'express', versionRange: '>=4.0.0 <5', filePath: 'lib/router/index.js' },\n functionQuery: { expressionName: 'route', kind: 'Sync' },\n },\n {\n channelName: 'use',\n module: { name: 'express', versionRange: '>=4.0.0 <5', filePath: 'lib/router/index.js' },\n functionQuery: { expressionName: 'use', kind: 'Sync' },\n },\n // Express v5 delegates routing to the standalone `router` package.\n {\n channelName: 'route',\n module: { name: 'router', versionRange: '>=2.0.0 <3', filePath: 'index.js' },\n functionQuery: { expressionName: 'route', kind: 'Sync' },\n },\n {\n channelName: 'use',\n module: { name: 'router', versionRange: '>=2.0.0 <3', filePath: 'index.js' },\n functionQuery: { expressionName: 'use', kind: 'Sync' },\n },\n] satisfies InstrumentationConfig[];\n\nexport const expressChannels = {\n // Express v4 runs each layer's handler through `Layer.prototype.handle_request`\n // in the `express` module.\n EXPRESS_HANDLE: 'orchestrion:express:handle',\n // Express v5 delegates routing to the standalone `router` package, where the\n // equivalent method is `Layer.prototype.handleRequest`.\n ROUTER_HANDLE: 'orchestrion:router:handle',\n // Layer *registration* (`Router.prototype.route`/`.use`), used to capture each\n // layer's registered path pattern so the matched route can be reconstructed\n // with its parameters intact (`req.baseUrl` only exposes the resolved prefix).\n EXPRESS_ROUTE: 'orchestrion:express:route',\n EXPRESS_USE: 'orchestrion:express:use',\n ROUTER_ROUTE: 'orchestrion:router:route',\n ROUTER_USE: 'orchestrion:router:use',\n} as const;\n"],"names":[],"mappings":"AAEO,MAAM,aAAA,GAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW3B;AAAA,IACE,WAAA,EAAa,QAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,YAAA,EAAc,UAAU,qBAAA,EAAsB;AAAA;AAAA;AAAA,IAGvF,aAAA,EAAe,EAAE,cAAA,EAAgB,gBAAA,EAAkB,MAAM,UAAA;AAAW,GACtE;AAAA;AAAA,EAEA;AAAA,IACE,WAAA,EAAa,QAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,UAAU,YAAA,EAAc,YAAA,EAAc,UAAU,cAAA,EAAe;AAAA,IAC/E,aAAA,EAAe,EAAE,cAAA,EAAgB,eAAA,EAAiB,MAAM,UAAA;AAAW,GACrE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA;AAAA,IACE,WAAA,EAAa,OAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,YAAA,EAAc,UAAU,qBAAA,EAAsB;AAAA,IACvF,aAAA,EAAe,EAAE,cAAA,EAAgB,OAAA,EAAS,MAAM,MAAA;AAAO,GACzD;AAAA,EACA;AAAA,IACE,WAAA,EAAa,KAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,YAAA,EAAc,UAAU,qBAAA,EAAsB;AAAA,IACvF,aAAA,EAAe,EAAE,cAAA,EAAgB,KAAA,EAAO,MAAM,MAAA;AAAO,GACvD;AAAA;AAAA,EAEA;AAAA,IACE,WAAA,EAAa,OAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,UAAU,YAAA,EAAc,YAAA,EAAc,UAAU,UAAA,EAAW;AAAA,IAC3E,aAAA,EAAe,EAAE,cAAA,EAAgB,OAAA,EAAS,MAAM,MAAA;AAAO,GACzD;AAAA,EACA;AAAA,IACE,WAAA,EAAa,KAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,UAAU,YAAA,EAAc,YAAA,EAAc,UAAU,UAAA,EAAW;AAAA,IAC3E,aAAA,EAAe,EAAE,cAAA,EAAgB,KAAA,EAAO,MAAM,MAAA;AAAO;AAEzD;AAEO,MAAM,eAAA,GAAkB;AAAA;AAAA;AAAA,EAG7B,cAAA,EAAgB,4BAAA;AAAA;AAAA;AAAA,EAGhB,aAAA,EAAe,2BAAA;AAAA;AAAA;AAAA;AAAA,EAIf,aAAA,EAAe,2BAAA;AAAA,EACf,WAAA,EAAa,yBAAA;AAAA,EACb,YAAA,EAAc,0BAAA;AAAA,EACd,UAAA,EAAY;AACd;;;;"} |
| const NODE_DIST_FILES = ["dist/node/index.js", "dist/node/index.mjs", "dist/node/index.cjs"]; | ||
| const googleGenAiConfig = [ | ||
| // `generateContent`/`generateContentStream` are arrow properties assigned in the constructor, not class | ||
| // methods, so they need `expressionName` rather than `className`/`methodName`. | ||
| ...NODE_DIST_FILES.flatMap( | ||
| (filePath) => ["generateContent", "generateContentStream"].map((expressionName) => ({ | ||
| channelName: "generate-content", | ||
| module: { name: "@google/genai", versionRange: ">=0.10.0 <2", filePath }, | ||
| functionQuery: { expressionName, kind: "Auto" } | ||
| })) | ||
| ), | ||
| // `embedContent` and the `Chat` methods are real class methods. | ||
| ...NODE_DIST_FILES.map((filePath) => ({ | ||
| channelName: "embed-content", | ||
| module: { name: "@google/genai", versionRange: ">=0.10.0 <2", filePath }, | ||
| functionQuery: { className: "Models", methodName: "embedContent", kind: "Auto" } | ||
| })), | ||
| // `sendMessage`/`sendMessageStream` internally delegate to `Models.generateContent(Stream)`; the | ||
| // subscriber suppresses that nested `generate-content` event so a chat call yields a single span. | ||
| ...NODE_DIST_FILES.flatMap( | ||
| (filePath) => ["sendMessage", "sendMessageStream"].map((methodName) => ({ | ||
| channelName: "chat", | ||
| module: { name: "@google/genai", versionRange: ">=0.10.0 <2", filePath }, | ||
| functionQuery: { className: "Chat", methodName, kind: "Auto" } | ||
| })) | ||
| ) | ||
| ]; | ||
| const googleGenAiChannels = { | ||
| GOOGLE_GENAI_GENERATE_CONTENT: "orchestrion:@google/genai:generate-content", | ||
| GOOGLE_GENAI_EMBED_CONTENT: "orchestrion:@google/genai:embed-content", | ||
| GOOGLE_GENAI_CHAT: "orchestrion:@google/genai:chat" | ||
| }; | ||
| export { googleGenAiChannels, googleGenAiConfig }; | ||
| //# sourceMappingURL=google-genai.js.map |
| {"version":3,"file":"google-genai.js","sources":["../../../../src/orchestrion/config/google-genai.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\n// `@google/genai` ships one bundled file per module format and the matcher compares `filePath` exactly,\n// so we list every file the `node` export condition resolves to across the supported range: `index.js`\n// (ESM+CJS for <0.15.0, CJS for <1.1.0), `index.mjs` (ESM for >=0.15.0), and `index.cjs` (CJS for >=1.1.0).\n// A file that doesn't exist in a given version simply never matches, so listing all three is safe.\nconst NODE_DIST_FILES = ['dist/node/index.js', 'dist/node/index.mjs', 'dist/node/index.cjs'];\n\nexport const googleGenAiConfig = [\n // `generateContent`/`generateContentStream` are arrow properties assigned in the constructor, not class\n // methods, so they need `expressionName` rather than `className`/`methodName`.\n ...NODE_DIST_FILES.flatMap(filePath =>\n (['generateContent', 'generateContentStream'] as const).map(expressionName => ({\n channelName: 'generate-content',\n module: { name: '@google/genai', versionRange: '>=0.10.0 <2', filePath },\n functionQuery: { expressionName, kind: 'Auto' as const },\n })),\n ),\n // `embedContent` and the `Chat` methods are real class methods.\n ...NODE_DIST_FILES.map(filePath => ({\n channelName: 'embed-content',\n module: { name: '@google/genai', versionRange: '>=0.10.0 <2', filePath },\n functionQuery: { className: 'Models', methodName: 'embedContent', kind: 'Auto' as const },\n })),\n // `sendMessage`/`sendMessageStream` internally delegate to `Models.generateContent(Stream)`; the\n // subscriber suppresses that nested `generate-content` event so a chat call yields a single span.\n ...NODE_DIST_FILES.flatMap(filePath =>\n (['sendMessage', 'sendMessageStream'] as const).map(methodName => ({\n channelName: 'chat',\n module: { name: '@google/genai', versionRange: '>=0.10.0 <2', filePath },\n functionQuery: { className: 'Chat', methodName, kind: 'Auto' as const },\n })),\n ),\n] satisfies InstrumentationConfig[];\n\nexport const googleGenAiChannels = {\n GOOGLE_GENAI_GENERATE_CONTENT: 'orchestrion:@google/genai:generate-content',\n GOOGLE_GENAI_EMBED_CONTENT: 'orchestrion:@google/genai:embed-content',\n GOOGLE_GENAI_CHAT: 'orchestrion:@google/genai:chat',\n} as const;\n"],"names":[],"mappings":"AAMA,MAAM,eAAA,GAAkB,CAAC,oBAAA,EAAsB,qBAAA,EAAuB,qBAAqB,CAAA;AAEpF,MAAM,iBAAA,GAAoB;AAAA;AAAA;AAAA,EAG/B,GAAG,eAAA,CAAgB,OAAA;AAAA,IAAQ,cACxB,CAAC,iBAAA,EAAmB,uBAAuB,CAAA,CAAY,IAAI,CAAA,cAAA,MAAmB;AAAA,MAC7E,WAAA,EAAa,kBAAA;AAAA,MACb,QAAQ,EAAE,IAAA,EAAM,eAAA,EAAiB,YAAA,EAAc,eAAe,QAAA,EAAS;AAAA,MACvE,aAAA,EAAe,EAAE,cAAA,EAAgB,IAAA,EAAM,MAAA;AAAgB,KACzD,CAAE;AAAA,GACJ;AAAA;AAAA,EAEA,GAAG,eAAA,CAAgB,GAAA,CAAI,CAAA,QAAA,MAAa;AAAA,IAClC,WAAA,EAAa,eAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,eAAA,EAAiB,YAAA,EAAc,eAAe,QAAA,EAAS;AAAA,IACvE,eAAe,EAAE,SAAA,EAAW,UAAU,UAAA,EAAY,cAAA,EAAgB,MAAM,MAAA;AAAgB,GAC1F,CAAE,CAAA;AAAA;AAAA;AAAA,EAGF,GAAG,eAAA,CAAgB,OAAA;AAAA,IAAQ,cACxB,CAAC,aAAA,EAAe,mBAAmB,CAAA,CAAY,IAAI,CAAA,UAAA,MAAe;AAAA,MACjE,WAAA,EAAa,MAAA;AAAA,MACb,QAAQ,EAAE,IAAA,EAAM,eAAA,EAAiB,YAAA,EAAc,eAAe,QAAA,EAAS;AAAA,MACvE,eAAe,EAAE,SAAA,EAAW,MAAA,EAAQ,UAAA,EAAY,MAAM,MAAA;AAAgB,KACxE,CAAE;AAAA;AAEN;AAEO,MAAM,mBAAA,GAAsB;AAAA,EACjC,6BAAA,EAA+B,4CAAA;AAAA,EAC/B,0BAAA,EAA4B,yCAAA;AAAA,EAC5B,iBAAA,EAAmB;AACrB;;;;"} |
| const graphqlConfig = [ | ||
| { | ||
| channelName: "parse", | ||
| module: { name: "graphql", versionRange: ">=14.0.0 <17", filePath: "language/parser.js" }, | ||
| functionQuery: { functionName: "parse", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "validate", | ||
| module: { name: "graphql", versionRange: ">=14.0.0 <17", filePath: "validation/validate.js" }, | ||
| functionQuery: { functionName: "validate", kind: "Sync" } | ||
| }, | ||
| { | ||
| channelName: "execute", | ||
| module: { name: "graphql", versionRange: ">=14.0.0 <17", filePath: "execution/execute.js" }, | ||
| functionQuery: { functionName: "execute", kind: "Auto" } | ||
| } | ||
| ]; | ||
| const graphqlChannels = { | ||
| GRAPHQL_PARSE: "orchestrion:graphql:parse", | ||
| GRAPHQL_VALIDATE: "orchestrion:graphql:validate", | ||
| GRAPHQL_EXECUTE: "orchestrion:graphql:execute" | ||
| }; | ||
| export { graphqlChannels, graphqlConfig }; | ||
| //# sourceMappingURL=graphql.js.map |
| {"version":3,"file":"graphql.js","sources":["../../../../src/orchestrion/config/graphql.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\n// `parse`/`validate`/`execute` are top-level named `function` declarations in graphql's compiled\n// files, stable across the supported majors, so `functionName` matches. `execute` returns\n// `PromiseOrValue`, so `Auto` covers both async (settles on `asyncEnd`) and sync (`end`) schemas.\nexport const graphqlConfig = [\n {\n channelName: 'parse',\n module: { name: 'graphql', versionRange: '>=14.0.0 <17', filePath: 'language/parser.js' },\n functionQuery: { functionName: 'parse', kind: 'Sync' },\n },\n {\n channelName: 'validate',\n module: { name: 'graphql', versionRange: '>=14.0.0 <17', filePath: 'validation/validate.js' },\n functionQuery: { functionName: 'validate', kind: 'Sync' },\n },\n {\n channelName: 'execute',\n module: { name: 'graphql', versionRange: '>=14.0.0 <17', filePath: 'execution/execute.js' },\n functionQuery: { functionName: 'execute', kind: 'Auto' },\n },\n] satisfies InstrumentationConfig[];\n\nexport const graphqlChannels = {\n GRAPHQL_PARSE: 'orchestrion:graphql:parse',\n GRAPHQL_VALIDATE: 'orchestrion:graphql:validate',\n GRAPHQL_EXECUTE: 'orchestrion:graphql:execute',\n} as const;\n"],"names":[],"mappings":"AAKO,MAAM,aAAA,GAAgB;AAAA,EAC3B;AAAA,IACE,WAAA,EAAa,OAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,cAAA,EAAgB,UAAU,oBAAA,EAAqB;AAAA,IACxF,aAAA,EAAe,EAAE,YAAA,EAAc,OAAA,EAAS,MAAM,MAAA;AAAO,GACvD;AAAA,EACA;AAAA,IACE,WAAA,EAAa,UAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,cAAA,EAAgB,UAAU,wBAAA,EAAyB;AAAA,IAC5F,aAAA,EAAe,EAAE,YAAA,EAAc,UAAA,EAAY,MAAM,MAAA;AAAO,GAC1D;AAAA,EACA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,WAAW,YAAA,EAAc,cAAA,EAAgB,UAAU,sBAAA,EAAuB;AAAA,IAC1F,aAAA,EAAe,EAAE,YAAA,EAAc,SAAA,EAAW,MAAM,MAAA;AAAO;AAE3D;AAEO,MAAM,eAAA,GAAkB;AAAA,EAC7B,aAAA,EAAe,2BAAA;AAAA,EACf,gBAAA,EAAkB,8BAAA;AAAA,EAClB,eAAA,EAAiB;AACnB;;;;"} |
| const postgresJsInstrumentationConfig = (dir) => [ | ||
| // `Query.prototype.handle` (`class Query extends Promise`) is the single | ||
| // funnel every query passes through (`then`/`catch`/`finally`/`.execute()`/ | ||
| // `.forEach()`/cursor all call it), guarded by `this.executed`. `Async` | ||
| // because `handle` is `async`. | ||
| { | ||
| channelName: "handle", | ||
| module: { name: "postgres", versionRange: ">=3.0.0 <4", filePath: `${dir}/query.js` }, | ||
| functionQuery: { className: "Query", methodName: "handle", kind: "Async" } | ||
| }, | ||
| // `function Connection(options, ...)` (default export of `connection.js`) | ||
| // returns the connection object; used to build the endpoint registry that | ||
| // resolves `server.address`/`server.port`/`db.namespace`. | ||
| { | ||
| channelName: "connection", | ||
| module: { name: "postgres", versionRange: ">=3.0.0 <4", filePath: `${dir}/connection.js` }, | ||
| functionQuery: { functionName: "Connection", kind: "Sync" } | ||
| }, | ||
| // The nested `function execute(q)` inside `Connection`; the per-connection | ||
| // hook that attaches connection attributes to the query's span. | ||
| { | ||
| channelName: "execute", | ||
| module: { name: "postgres", versionRange: ">=3.0.0 <4", filePath: `${dir}/connection.js` }, | ||
| functionQuery: { functionName: "execute", kind: "Sync" } | ||
| }, | ||
| // The connection object's `connect(query)` method. Matched by `methodName` | ||
| // (an object-literal method): `functionName` would hit the unrelated | ||
| // socket-level `async function connect()` in the same file. `self` is the | ||
| // connection object and `arguments[0]` the query, so the first query that | ||
| // opens a connection (dispatched via a bare `execute` with no `self`) still | ||
| // gets connection attributes in multi-endpoint apps. | ||
| { | ||
| channelName: "connect", | ||
| module: { name: "postgres", versionRange: ">=3.0.0 <4", filePath: `${dir}/connection.js` }, | ||
| functionQuery: { methodName: "connect", kind: "Sync" } | ||
| } | ||
| ]; | ||
| const postgresJsConfig = ["src", "cjs/src"].flatMap(postgresJsInstrumentationConfig); | ||
| const postgresJsChannels = { | ||
| POSTGRESJS_HANDLE: "orchestrion:postgres:handle", | ||
| POSTGRESJS_CONNECTION: "orchestrion:postgres:connection", | ||
| POSTGRESJS_EXECUTE: "orchestrion:postgres:execute", | ||
| POSTGRESJS_CONNECT: "orchestrion:postgres:connect" | ||
| }; | ||
| export { postgresJsChannels, postgresJsConfig }; | ||
| //# sourceMappingURL=postgres.js.map |
| {"version":3,"file":"postgres.js","sources":["../../../../src/orchestrion/config/postgres.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\n// postgres.js (`postgres` npm package, v3.x). Named after the npm package;\n// `postgres` doesn't collide with `pg.ts` (that file instruments `pg`/`pg-pool`).\n//\n// The ESM build lives under `src/*`, the CJS build under `cjs/src/*` (the\n// `cf/*` workerd build has no channel subscribers, see the integration).\n// Both builds share the same class/function shapes, so a single `flatMap`\n// over the two dirs emits one entry per (dir, target).\nconst postgresJsInstrumentationConfig = (dir: string): InstrumentationConfig[] => [\n // `Query.prototype.handle` (`class Query extends Promise`) is the single\n // funnel every query passes through (`then`/`catch`/`finally`/`.execute()`/\n // `.forEach()`/cursor all call it), guarded by `this.executed`. `Async`\n // because `handle` is `async`.\n {\n channelName: 'handle',\n module: { name: 'postgres', versionRange: '>=3.0.0 <4', filePath: `${dir}/query.js` },\n functionQuery: { className: 'Query', methodName: 'handle', kind: 'Async' },\n },\n // `function Connection(options, ...)` (default export of `connection.js`)\n // returns the connection object; used to build the endpoint registry that\n // resolves `server.address`/`server.port`/`db.namespace`.\n {\n channelName: 'connection',\n module: { name: 'postgres', versionRange: '>=3.0.0 <4', filePath: `${dir}/connection.js` },\n functionQuery: { functionName: 'Connection', kind: 'Sync' },\n },\n // The nested `function execute(q)` inside `Connection`; the per-connection\n // hook that attaches connection attributes to the query's span.\n {\n channelName: 'execute',\n module: { name: 'postgres', versionRange: '>=3.0.0 <4', filePath: `${dir}/connection.js` },\n functionQuery: { functionName: 'execute', kind: 'Sync' },\n },\n // The connection object's `connect(query)` method. Matched by `methodName`\n // (an object-literal method): `functionName` would hit the unrelated\n // socket-level `async function connect()` in the same file. `self` is the\n // connection object and `arguments[0]` the query, so the first query that\n // opens a connection (dispatched via a bare `execute` with no `self`) still\n // gets connection attributes in multi-endpoint apps.\n {\n channelName: 'connect',\n module: { name: 'postgres', versionRange: '>=3.0.0 <4', filePath: `${dir}/connection.js` },\n functionQuery: { methodName: 'connect', kind: 'Sync' },\n },\n];\n\nexport const postgresJsConfig = ['src', 'cjs/src'].flatMap(postgresJsInstrumentationConfig);\n\nexport const postgresJsChannels = {\n POSTGRESJS_HANDLE: 'orchestrion:postgres:handle',\n POSTGRESJS_CONNECTION: 'orchestrion:postgres:connection',\n POSTGRESJS_EXECUTE: 'orchestrion:postgres:execute',\n POSTGRESJS_CONNECT: 'orchestrion:postgres:connect',\n} as const;\n"],"names":[],"mappings":"AASA,MAAM,+BAAA,GAAkC,CAAC,GAAA,KAAyC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhF;AAAA,IACE,WAAA,EAAa,QAAA;AAAA,IACb,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAA,EAAY,cAAc,YAAA,EAAc,QAAA,EAAU,CAAA,EAAG,GAAG,CAAA,SAAA,CAAA,EAAY;AAAA,IACpF,eAAe,EAAE,SAAA,EAAW,SAAS,UAAA,EAAY,QAAA,EAAU,MAAM,OAAA;AAAQ,GAC3E;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,WAAA,EAAa,YAAA;AAAA,IACb,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAA,EAAY,cAAc,YAAA,EAAc,QAAA,EAAU,CAAA,EAAG,GAAG,CAAA,cAAA,CAAA,EAAiB;AAAA,IACzF,aAAA,EAAe,EAAE,YAAA,EAAc,YAAA,EAAc,MAAM,MAAA;AAAO,GAC5D;AAAA;AAAA;AAAA,EAGA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAA,EAAY,cAAc,YAAA,EAAc,QAAA,EAAU,CAAA,EAAG,GAAG,CAAA,cAAA,CAAA,EAAiB;AAAA,IACzF,aAAA,EAAe,EAAE,YAAA,EAAc,SAAA,EAAW,MAAM,MAAA;AAAO,GACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAA,EAAY,cAAc,YAAA,EAAc,QAAA,EAAU,CAAA,EAAG,GAAG,CAAA,cAAA,CAAA,EAAiB;AAAA,IACzF,aAAA,EAAe,EAAE,UAAA,EAAY,SAAA,EAAW,MAAM,MAAA;AAAO;AAEzD,CAAA;AAEO,MAAM,mBAAmB,CAAC,KAAA,EAAO,SAAS,CAAA,CAAE,QAAQ,+BAA+B;AAEnF,MAAM,kBAAA,GAAqB;AAAA,EAChC,iBAAA,EAAmB,6BAAA;AAAA,EACnB,qBAAA,EAAuB,iCAAA;AAAA,EACvB,kBAAA,EAAoB,8BAAA;AAAA,EACpB,kBAAA,EAAoB;AACtB;;;;"} |
| const redisConfig = [ | ||
| // redis `>=2.6.0 <4` (standalone `redis`). `internal_send_command` is an | ||
| // anonymous prototype assignment (`expressionName`); it settles via the nested | ||
| // `command_obj.callback`, so `kind: 'Sync'` and the subscriber wraps that callback. | ||
| { | ||
| channelName: "command", | ||
| module: { name: "redis", versionRange: ">=2.6.0 <4", filePath: "index.js" }, | ||
| functionQuery: { expressionName: "internal_send_command", kind: "Sync" } | ||
| }, | ||
| // node-redis v4 (`@redis/client` v1). The real chokepoint (private `#sendCommand`) | ||
| // isn't matchable, so wrap both public entry points: `commandsExecutor` (friendly | ||
| // commands) and `sendCommand` (direct calls). They never overlap, so no double span. | ||
| { | ||
| channelName: "executor", | ||
| module: { name: "@redis/client", versionRange: "^1.0.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "commandsExecutor", kind: "Async" } | ||
| }, | ||
| { | ||
| channelName: "command", | ||
| module: { name: "@redis/client", versionRange: "^1.0.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "sendCommand", kind: "Async" } | ||
| }, | ||
| { | ||
| channelName: "connect", | ||
| module: { name: "@redis/client", versionRange: "^1.0.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "connect", kind: "Async" } | ||
| }, | ||
| // node-redis `>=5.0.0 <5.12.0` (`@redis/client` v5; >=5.12.0 has its own | ||
| // `node-redis:*` diagnostics_channel, see `redis-dc-subscriber.ts`). Friendly | ||
| // commands route through the public `sendCommand`, so it covers them all — no | ||
| // `executor` entry (would double-count). | ||
| { | ||
| channelName: "command", | ||
| module: { name: "@redis/client", versionRange: ">=5.0.0 <5.12.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "sendCommand", kind: "Async" } | ||
| }, | ||
| { | ||
| channelName: "connect", | ||
| module: { name: "@redis/client", versionRange: ">=5.0.0 <5.12.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "connect", kind: "Async" } | ||
| }, | ||
| // Batch (multi/pipeline) — one span per `exec`. Batched commands bypass `sendCommand`, | ||
| // so they go through the client's batch executors, which receive the queued commands | ||
| // array (→ batch size). v5 splits MULTI/PIPELINE into two methods; v4's single | ||
| // `multiExecutor` is MULTI when a `chainId` arg is present, PIPELINE otherwise. | ||
| { | ||
| channelName: "multi", | ||
| module: { name: "@redis/client", versionRange: ">=5.0.0 <5.12.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "_executeMulti", kind: "Async" } | ||
| }, | ||
| { | ||
| channelName: "pipeline", | ||
| module: { name: "@redis/client", versionRange: ">=5.0.0 <5.12.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "_executePipeline", kind: "Async" } | ||
| }, | ||
| { | ||
| channelName: "batch", | ||
| module: { name: "@redis/client", versionRange: "^1.0.0", filePath: "dist/lib/client/index.js" }, | ||
| functionQuery: { className: "RedisClient", methodName: "multiExecutor", kind: "Async" } | ||
| } | ||
| ]; | ||
| const redisChannels = { | ||
| REDIS_COMMAND: "orchestrion:redis:command", | ||
| NODE_REDIS_COMMAND: "orchestrion:@redis/client:command", | ||
| NODE_REDIS_EXECUTOR: "orchestrion:@redis/client:executor", | ||
| NODE_REDIS_CONNECT: "orchestrion:@redis/client:connect", | ||
| NODE_REDIS_MULTI: "orchestrion:@redis/client:multi", | ||
| NODE_REDIS_PIPELINE: "orchestrion:@redis/client:pipeline", | ||
| NODE_REDIS_BATCH: "orchestrion:@redis/client:batch" | ||
| }; | ||
| export { redisChannels, redisConfig }; | ||
| //# sourceMappingURL=redis.js.map |
| {"version":3,"file":"redis.js","sources":["../../../../src/orchestrion/config/redis.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\nexport const redisConfig = [\n // redis `>=2.6.0 <4` (standalone `redis`). `internal_send_command` is an\n // anonymous prototype assignment (`expressionName`); it settles via the nested\n // `command_obj.callback`, so `kind: 'Sync'` and the subscriber wraps that callback.\n {\n channelName: 'command',\n module: { name: 'redis', versionRange: '>=2.6.0 <4', filePath: 'index.js' },\n functionQuery: { expressionName: 'internal_send_command', kind: 'Sync' },\n },\n // node-redis v4 (`@redis/client` v1). The real chokepoint (private `#sendCommand`)\n // isn't matchable, so wrap both public entry points: `commandsExecutor` (friendly\n // commands) and `sendCommand` (direct calls). They never overlap, so no double span.\n {\n channelName: 'executor',\n module: { name: '@redis/client', versionRange: '^1.0.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'commandsExecutor', kind: 'Async' },\n },\n {\n channelName: 'command',\n module: { name: '@redis/client', versionRange: '^1.0.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'sendCommand', kind: 'Async' },\n },\n {\n channelName: 'connect',\n module: { name: '@redis/client', versionRange: '^1.0.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'connect', kind: 'Async' },\n },\n // node-redis `>=5.0.0 <5.12.0` (`@redis/client` v5; >=5.12.0 has its own\n // `node-redis:*` diagnostics_channel, see `redis-dc-subscriber.ts`). Friendly\n // commands route through the public `sendCommand`, so it covers them all — no\n // `executor` entry (would double-count).\n {\n channelName: 'command',\n module: { name: '@redis/client', versionRange: '>=5.0.0 <5.12.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'sendCommand', kind: 'Async' },\n },\n {\n channelName: 'connect',\n module: { name: '@redis/client', versionRange: '>=5.0.0 <5.12.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'connect', kind: 'Async' },\n },\n // Batch (multi/pipeline) — one span per `exec`. Batched commands bypass `sendCommand`,\n // so they go through the client's batch executors, which receive the queued commands\n // array (→ batch size). v5 splits MULTI/PIPELINE into two methods; v4's single\n // `multiExecutor` is MULTI when a `chainId` arg is present, PIPELINE otherwise.\n {\n channelName: 'multi',\n module: { name: '@redis/client', versionRange: '>=5.0.0 <5.12.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: '_executeMulti', kind: 'Async' },\n },\n {\n channelName: 'pipeline',\n module: { name: '@redis/client', versionRange: '>=5.0.0 <5.12.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: '_executePipeline', kind: 'Async' },\n },\n {\n channelName: 'batch',\n module: { name: '@redis/client', versionRange: '^1.0.0', filePath: 'dist/lib/client/index.js' },\n functionQuery: { className: 'RedisClient', methodName: 'multiExecutor', kind: 'Async' },\n },\n] satisfies InstrumentationConfig[];\n\nexport const redisChannels = {\n REDIS_COMMAND: 'orchestrion:redis:command',\n NODE_REDIS_COMMAND: 'orchestrion:@redis/client:command',\n NODE_REDIS_EXECUTOR: 'orchestrion:@redis/client:executor',\n NODE_REDIS_CONNECT: 'orchestrion:@redis/client:connect',\n NODE_REDIS_MULTI: 'orchestrion:@redis/client:multi',\n NODE_REDIS_PIPELINE: 'orchestrion:@redis/client:pipeline',\n NODE_REDIS_BATCH: 'orchestrion:@redis/client:batch',\n} as const;\n"],"names":[],"mappings":"AAEO,MAAM,WAAA,GAAc;AAAA;AAAA;AAAA;AAAA,EAIzB;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,SAAS,YAAA,EAAc,YAAA,EAAc,UAAU,UAAA,EAAW;AAAA,IAC1E,aAAA,EAAe,EAAE,cAAA,EAAgB,uBAAA,EAAyB,MAAM,MAAA;AAAO,GACzE;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,WAAA,EAAa,UAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,QAAA,EAAU,UAAU,0BAAA,EAA2B;AAAA,IAC9F,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,kBAAA,EAAoB,MAAM,OAAA;AAAQ,GAC3F;AAAA,EACA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,QAAA,EAAU,UAAU,0BAAA,EAA2B;AAAA,IAC9F,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,aAAA,EAAe,MAAM,OAAA;AAAQ,GACtF;AAAA,EACA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,QAAA,EAAU,UAAU,0BAAA,EAA2B;AAAA,IAC9F,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,SAAA,EAAW,MAAM,OAAA;AAAQ,GAClF;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,iBAAA,EAAmB,UAAU,0BAAA,EAA2B;AAAA,IACvG,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,aAAA,EAAe,MAAM,OAAA;AAAQ,GACtF;AAAA,EACA;AAAA,IACE,WAAA,EAAa,SAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,iBAAA,EAAmB,UAAU,0BAAA,EAA2B;AAAA,IACvG,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,SAAA,EAAW,MAAM,OAAA;AAAQ,GAClF;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,IACE,WAAA,EAAa,OAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,iBAAA,EAAmB,UAAU,0BAAA,EAA2B;AAAA,IACvG,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,eAAA,EAAiB,MAAM,OAAA;AAAQ,GACxF;AAAA,EACA;AAAA,IACE,WAAA,EAAa,UAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,iBAAA,EAAmB,UAAU,0BAAA,EAA2B;AAAA,IACvG,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,kBAAA,EAAoB,MAAM,OAAA;AAAQ,GAC3F;AAAA,EACA;AAAA,IACE,WAAA,EAAa,OAAA;AAAA,IACb,QAAQ,EAAE,IAAA,EAAM,iBAAiB,YAAA,EAAc,QAAA,EAAU,UAAU,0BAAA,EAA2B;AAAA,IAC9F,eAAe,EAAE,SAAA,EAAW,eAAe,UAAA,EAAY,eAAA,EAAiB,MAAM,OAAA;AAAQ;AAE1F;AAEO,MAAM,aAAA,GAAgB;AAAA,EAC3B,aAAA,EAAe,2BAAA;AAAA,EACf,kBAAA,EAAoB,mCAAA;AAAA,EACpB,mBAAA,EAAqB,oCAAA;AAAA,EACrB,kBAAA,EAAoB,mCAAA;AAAA,EACpB,gBAAA,EAAkB,iCAAA;AAAA,EAClB,mBAAA,EAAqB,oCAAA;AAAA,EACrB,gBAAA,EAAkB;AACpB;;;;"} |
| import { defineIntegration, waitForTracingChannelBinding } from '@sentry/core'; | ||
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { subscribeRedisDiagnosticChannels } from './redis-dc-subscriber.js'; | ||
| const _redisIntegration = ((options = {}) => { | ||
| return { | ||
| name: "Redis", | ||
| setupOnce() { | ||
| if (!diagnosticsChannel.tracingChannel) { | ||
| return; | ||
| } | ||
| waitForTracingChannelBinding(() => { | ||
| subscribeRedisDiagnosticChannels(diagnosticsChannel.tracingChannel, options.responseHook); | ||
| }); | ||
| } | ||
| }; | ||
| }); | ||
| const redisIntegration = defineIntegration(_redisIntegration); | ||
| export { redisIntegration }; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../src/redis/index.ts"],"sourcesContent":["import { defineIntegration, type IntegrationFn, waitForTracingChannelBinding } from '@sentry/core';\nimport * as dc from 'node:diagnostics_channel';\nimport { type RedisDiagnosticChannelResponseHook, subscribeRedisDiagnosticChannels } from './redis-dc-subscriber';\n\n/** Options controlling the redis diagnostics-channel subscription. */\nexport interface RedisDiagnosticChannelsOptions {\n /**\n * Optional hook invoked once the redis command response arrives. Useful for attaching\n * response-derived attributes (e.g. cache hit/miss, payload size).\n */\n responseHook?: RedisDiagnosticChannelResponseHook;\n}\n\nconst _redisIntegration = ((options: RedisDiagnosticChannelsOptions = {}) => {\n return {\n name: 'Redis',\n setupOnce() {\n // Bail on runtimes without `tracingChannel` (Node <= 18.18.0).\n if (!dc.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n subscribeRedisDiagnosticChannels(dc.tracingChannel, options.responseHook);\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the [redis](https://www.npmjs.com/package/redis) and\n * [ioredis](https://www.npmjs.com/package/ioredis) libraries via their native\n * `node:diagnostics_channel` tracing channels (node-redis >= 5.12.0, ioredis >= 5.11.0).\n */\nexport const redisIntegration = defineIntegration(_redisIntegration);\n"],"names":["dc"],"mappings":";;;;AAaA,MAAM,iBAAA,IAAqB,CAAC,OAAA,GAA0C,EAAC,KAAM;AAC3E,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,OAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAACA,mBAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAEA,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,gCAAA,CAAiCA,kBAAA,CAAG,cAAA,EAAgB,OAAA,CAAQ,YAAY,CAAA;AAAA,MAC1E,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAOO,MAAM,gBAAA,GAAmB,kBAAkB,iBAAiB;;;;"} |
| import { isObjectLike } from '@sentry/core'; | ||
| function asString(value) { | ||
| return typeof value === "string" ? value : void 0; | ||
| } | ||
| function asNumber(value) { | ||
| return typeof value === "number" && !isNaN(value) ? value : void 0; | ||
| } | ||
| function sum(a, b) { | ||
| return a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0); | ||
| } | ||
| function safeStringify(value) { | ||
| if (typeof value === "string") { | ||
| return value; | ||
| } | ||
| try { | ||
| return JSON.stringify(value); | ||
| } catch { | ||
| return "[unserializable]"; | ||
| } | ||
| } | ||
| function isReadableStream(value) { | ||
| return isObjectLike(value) && typeof value.pipeThrough === "function" && typeof value.getReader === "function"; | ||
| } | ||
| function tapModelCallStream(stream, onFinal, onError) { | ||
| const reader = stream.getReader(); | ||
| const state = { toolCalls: [] }; | ||
| let text = ""; | ||
| let settled = false; | ||
| const finalize = () => { | ||
| if (settled) { | ||
| return; | ||
| } | ||
| settled = true; | ||
| if (text) { | ||
| state.text = text; | ||
| } | ||
| onFinal(state); | ||
| }; | ||
| const fail = (error) => { | ||
| if (settled) { | ||
| return; | ||
| } | ||
| settled = true; | ||
| onError(error); | ||
| }; | ||
| return new ReadableStream({ | ||
| async pull(controller) { | ||
| try { | ||
| const { done, value } = await reader.read(); | ||
| if (done) { | ||
| finalize(); | ||
| controller.close(); | ||
| return; | ||
| } | ||
| text += accumulateChunk(state, value) ?? ""; | ||
| controller.enqueue(value); | ||
| } catch (error) { | ||
| fail(error); | ||
| controller.error(error); | ||
| } | ||
| }, | ||
| cancel(reason) { | ||
| finalize(); | ||
| return reader.cancel(reason); | ||
| } | ||
| }); | ||
| } | ||
| function accumulateChunk(state, chunk) { | ||
| if (!isObjectLike(chunk)) { | ||
| return void 0; | ||
| } | ||
| const { | ||
| type, | ||
| delta, | ||
| textDelta, | ||
| id, | ||
| modelId, | ||
| toolCallId, | ||
| toolName, | ||
| input, | ||
| args, | ||
| finishReason, | ||
| usage, | ||
| providerMetadata | ||
| } = chunk; | ||
| switch (type) { | ||
| case "text-delta": { | ||
| const textChunk = delta ?? textDelta; | ||
| return typeof textChunk === "string" ? textChunk : void 0; | ||
| } | ||
| case "tool-call": | ||
| state.toolCalls.push({ toolCallId, toolName, input: input ?? args }); | ||
| return void 0; | ||
| case "response-metadata": | ||
| if (typeof id === "string") { | ||
| state.responseId = id; | ||
| } | ||
| if (typeof modelId === "string") { | ||
| state.responseModel = modelId; | ||
| } | ||
| return void 0; | ||
| case "finish": | ||
| state.finishReason = finishReason; | ||
| state.usage = usage; | ||
| if (providerMetadata !== void 0) { | ||
| state.providerMetadata = providerMetadata; | ||
| } | ||
| return void 0; | ||
| default: | ||
| return void 0; | ||
| } | ||
| } | ||
| export { asNumber, asString, isReadableStream, safeStringify, sum, tapModelCallStream }; | ||
| //# sourceMappingURL=util.js.map |
| {"version":3,"file":"util.js","sources":["../../../src/vercel-ai/util.ts"],"sourcesContent":["/** Shared, state-free helpers for the Vercel AI (`ai`) channel subscribers, plus the streamed model-call tap. */\n\nimport { isObjectLike } from '@sentry/core';\n\n/** Narrow to a string, or `undefined` for anything else. */\nexport function asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\n/** Narrow to a finite number, or `undefined` for anything else (including `NaN`). */\nexport function asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && !isNaN(value) ? value : undefined;\n}\n\n/** Add two optional numbers, treating a missing operand as `0` but returning `undefined` when both are absent. */\nexport function sum(a: number | undefined, b: number | undefined): number | undefined {\n return a === undefined && b === undefined ? undefined : (a ?? 0) + (b ?? 0);\n}\n\n/** Stringify a value, passing strings through and falling back to a placeholder on circular/unserializable input. */\nexport function safeStringify(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '[unserializable]';\n }\n}\n\n/*\n * Streaming support for the `ai:telemetry` tracing channel.\n *\n * For a streamed model call (`doStream`) the SDK resolves the `languageModelCall` channel promise as\n * soon as it hands back the *unconsumed* stream — so `result.stream` is a `ReadableStream` and the\n * final usage / finish reason / output only arrive later, as the stream is drained (they ride the\n * stream's own `finish`/`text-delta`/`tool-call` chunks, never the channel context). The SDK\n * deliberately leaves `result` undefined on the `streamText`/`step` contexts and exposes the stream\n * only on the model call, expecting consumers to tap it themselves.\n * @see https://github.com/vercel/ai/pull/15660 (discussion: \"tee off and aggregate ourselves\")\n *\n * We replace `result.stream` with a passthrough that forwards every chunk untouched (so the SDK's own\n * consumption is unaffected) while accumulating the data we need, then hand the aggregate back once the\n * stream settles so the model-call span can be enriched and ended out-of-band.\n */\n\n/** The subset of a streamed provider chunk we read. Unknown chunk types are forwarded and ignored. */\ninterface StreamChunk {\n type?: unknown;\n delta?: unknown;\n // v4 text-delta chunks carry the text on `textDelta`; v5+ uses `delta`.\n textDelta?: unknown;\n id?: unknown;\n modelId?: unknown;\n toolCallId?: unknown;\n toolName?: unknown;\n input?: unknown;\n args?: unknown;\n finishReason?: unknown;\n usage?: unknown;\n providerMetadata?: unknown;\n error?: unknown;\n}\n\n/** The aggregate handed back once a streamed model call finishes, in the shape `enrichSpanOnEnd` expects. */\nexport interface StreamedModelCallResult {\n text?: string;\n toolCalls: Array<Record<string, unknown>>;\n usage?: unknown;\n finishReason?: unknown;\n responseId?: string;\n responseModel?: string;\n providerMetadata?: unknown;\n}\n\n/** A minimal structural check — the streamed model call exposes a web `ReadableStream` on `result.stream`. */\nexport function isReadableStream(value: unknown): value is ReadableStream<unknown> {\n return (\n isObjectLike(value) &&\n typeof (value as { pipeThrough?: unknown }).pipeThrough === 'function' &&\n typeof (value as { getReader?: unknown }).getReader === 'function'\n );\n}\n\n/**\n * Wrap a streamed model call's `ReadableStream` so its chunks are observed as the SDK consumes them,\n * without altering what the SDK sees. Returns a replacement stream to swap onto `result.stream`.\n *\n * `onFinal` runs exactly once when the stream drains cleanly; `onError` runs exactly once if it errors\n * or is cancelled. Reading one source chunk per `pull` preserves the SDK's backpressure. The\n * `try/catch` around every read guarantees the owning span is always ended — a leaked open span on a\n * mid-stream failure would be worse than a slightly-less-enriched one.\n */\nexport function tapModelCallStream(\n stream: ReadableStream<unknown>,\n onFinal: (result: StreamedModelCallResult) => void,\n onError: (error: unknown) => void,\n): ReadableStream<unknown> {\n const reader = stream.getReader();\n const state: StreamedModelCallResult = { toolCalls: [] };\n let text = '';\n let settled = false;\n\n const finalize = (): void => {\n if (settled) {\n return;\n }\n settled = true;\n if (text) {\n state.text = text;\n }\n onFinal(state);\n };\n\n const fail = (error: unknown): void => {\n if (settled) {\n return;\n }\n settled = true;\n onError(error);\n };\n\n return new ReadableStream<unknown>({\n async pull(controller) {\n try {\n const { done, value } = await reader.read();\n if (done) {\n finalize();\n controller.close();\n\n return;\n }\n text += accumulateChunk(state, value) ?? '';\n controller.enqueue(value);\n } catch (error) {\n fail(error);\n controller.error(error);\n }\n },\n cancel(reason) {\n // Consumer stopped reading early (e.g. a `break` out of `for await`); close out the span with\n // whatever we have rather than leave it open. A non-error reason is a deliberate stop, not a failure.\n finalize();\n\n return reader.cancel(reason);\n },\n });\n}\n\n/**\n * Fold a single streamed chunk into the running aggregate. Returns any text delta so the caller can\n * accumulate it (kept out of `state` until the end to avoid re-joining on every chunk).\n */\nfunction accumulateChunk(state: StreamedModelCallResult, chunk: unknown): string | undefined {\n if (!isObjectLike(chunk)) {\n return undefined;\n }\n const {\n type,\n delta,\n textDelta,\n id,\n modelId,\n toolCallId,\n toolName,\n input,\n args,\n finishReason,\n usage,\n providerMetadata,\n } = chunk as StreamChunk;\n\n switch (type) {\n case 'text-delta': {\n const textChunk = delta ?? textDelta;\n return typeof textChunk === 'string' ? textChunk : undefined;\n }\n case 'tool-call':\n state.toolCalls.push({ toolCallId, toolName, input: input ?? args });\n\n return undefined;\n case 'response-metadata':\n if (typeof id === 'string') {\n state.responseId = id;\n }\n if (typeof modelId === 'string') {\n state.responseModel = modelId;\n }\n\n return undefined;\n case 'finish':\n state.finishReason = finishReason;\n state.usage = usage;\n if (providerMetadata !== undefined) {\n state.providerMetadata = providerMetadata;\n }\n\n return undefined;\n default:\n return undefined;\n }\n}\n"],"names":[],"mappings":"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"} |
| import { debug, isObjectLike, withActiveSpan, SPAN_STATUS_ERROR, getActiveSpan } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../debug-build.js'; | ||
| import { CHANNELS } from '../orchestrion/channels.js'; | ||
| import { bindTracingChannelToSpan } from '../tracing-channel.js'; | ||
| import { enrichSpanOnEnd, clearOperationId, createSpanFromMessage, streamedResultToChannelResult, clearOperationCallId, captureToolError } from './vercel-ai-dc-subscriber.js'; | ||
| import { asString, isReadableStream, tapModelCallStream } from './util.js'; | ||
| const PATCHED = /* @__PURE__ */ Symbol("SentryVercelAiModelPatched"); | ||
| const TOOL_PATCHED = /* @__PURE__ */ Symbol("SentryVercelAiToolPatched"); | ||
| let callIdCounter = 0; | ||
| function nextCallId() { | ||
| return `v6-${++callIdCounter}`; | ||
| } | ||
| const messages = /* @__PURE__ */ new WeakMap(); | ||
| const operationSpans = /* @__PURE__ */ new WeakSet(); | ||
| const toolCallSpans = /* @__PURE__ */ new WeakSet(); | ||
| const callIdBySpan = /* @__PURE__ */ new WeakMap(); | ||
| const recordingBySpan = /* @__PURE__ */ new WeakMap(); | ||
| const suppressedTelemetry = /* @__PURE__ */ new WeakSet(); | ||
| let subscribed = false; | ||
| function subscribeVercelAiOrchestrionChannels(tracingChannel, options = {}) { | ||
| if (subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| try { | ||
| bindOperation(tracingChannel, CHANNELS.VERCEL_AI_GENERATE_TEXT, buildTextMessage("generateText"), options); | ||
| bindOperation(tracingChannel, CHANNELS.VERCEL_AI_STREAM_TEXT, buildTextMessage("streamText"), options); | ||
| bindOperation(tracingChannel, CHANNELS.VERCEL_AI_GENERATE_OBJECT, buildTextMessage("generateObject"), options); | ||
| bindOperation( | ||
| tracingChannel, | ||
| CHANNELS.VERCEL_AI_EMBED, | ||
| (callOptions, telemetry) => ({ | ||
| type: "embed", | ||
| event: { | ||
| callId: nextCallId(), | ||
| ...modelFields(callOptions.model), | ||
| maxRetries: callOptions.maxRetries, | ||
| value: callOptions.value, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| bindOperation( | ||
| tracingChannel, | ||
| CHANNELS.VERCEL_AI_EMBED_MANY, | ||
| // `embedMany` takes a `values` array (vs `embed`'s single `value`); the shared core reads it as the | ||
| // embeddings input, matching the OTel path's batch `ai.embedMany` span. | ||
| (callOptions, telemetry) => ({ | ||
| type: "embedMany", | ||
| event: { | ||
| callId: nextCallId(), | ||
| ...modelFields(callOptions.model), | ||
| maxRetries: callOptions.maxRetries, | ||
| values: callOptions.values, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| bindOperation( | ||
| tracingChannel, | ||
| CHANNELS.VERCEL_AI_EXECUTE_TOOL_CALL, | ||
| (callOptions, telemetry) => ({ | ||
| type: "executeTool", | ||
| // v6 carries the tool definitions on the executeToolCall args (a record keyed by name); | ||
| // the shared core reads the matching tool's `description` for the span. | ||
| event: { | ||
| callId: nextCallId(), | ||
| toolCall: callOptions.toolCall, | ||
| tools: callOptions.tools, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| subscribeResolveLanguageModel(tracingChannel, CHANNELS.VERCEL_AI_RESOLVE_LANGUAGE_MODEL, options); | ||
| } catch { | ||
| DEBUG_BUILD && debug.log("Vercel AI orchestrion channel subscription failed."); | ||
| } | ||
| } | ||
| function bindOperation(tracingChannel, channelName, build, options) { | ||
| const channel = tracingChannel(channelName); | ||
| const buildOperationSpan = (data) => { | ||
| const callOptions = isObjectLike(data.arguments[0]) ? data.arguments[0] : {}; | ||
| const telemetry = isObjectLike(callOptions.experimental_telemetry) ? callOptions.experimental_telemetry : {}; | ||
| if (telemetry.isEnabled === false && !suppressedTelemetry.has(telemetry)) { | ||
| return void 0; | ||
| } | ||
| const message = build(callOptions, telemetry); | ||
| suppressNativeTelemetry(callOptions, telemetry); | ||
| const span = createSpanFromMessage(message, options); | ||
| if (span) { | ||
| messages.set(data, message); | ||
| operationSpans.add(span); | ||
| if (message.type === "executeTool") { | ||
| toolCallSpans.add(span); | ||
| } | ||
| const callId = asString(message.event.callId); | ||
| if (callId) { | ||
| callIdBySpan.set(span, callId); | ||
| } | ||
| recordingBySpan.set(span, recording(telemetry)); | ||
| if (isObjectLike(callOptions.tools)) { | ||
| patchOperationTools(callOptions.tools, options); | ||
| } | ||
| if (isObjectLike(callOptions.model) && callOptions.model.specificationVersion === "v1") { | ||
| patchModelMethods(callOptions.model, options); | ||
| } | ||
| } | ||
| return span; | ||
| }; | ||
| bindTracingChannelToSpan( | ||
| channel, | ||
| (data) => buildOperationSpan(data), | ||
| { | ||
| beforeSpanEnd: (span, data) => { | ||
| const message = messages.get(data); | ||
| if (!message) { | ||
| return; | ||
| } | ||
| if (!("error" in data)) { | ||
| message.result = message.type === "executeTool" ? { output: data.result } : data.result; | ||
| enrichSpanOnEnd(span, message, options); | ||
| } | ||
| if (message.type !== "streamText") { | ||
| clearOperationId(message); | ||
| } | ||
| messages.delete(data); | ||
| }, | ||
| // `streamText` returns synchronously, so its operation span would otherwise end before the stream | ||
| // drains — losing the aggregate usage/output. Defer the end and await the result's completion | ||
| // promises (`totalUsage`/`text`/…, which resolve on drain), mirroring how v7's channel defers the | ||
| // operation span on the SDK's total-usage promise. | ||
| deferSpanEnd: ({ data, end }) => deferStreamTextOperationEnd(data, end) | ||
| } | ||
| ); | ||
| } | ||
| function deferStreamTextOperationEnd(data, end) { | ||
| if (messages.get(data)?.type !== "streamText" || "error" in data || !isStreamingResult(data.result)) { | ||
| return false; | ||
| } | ||
| const streamResult = data.result; | ||
| void (async () => { | ||
| try { | ||
| const [usage, text, toolCalls, finishReason, response] = await Promise.all([ | ||
| streamResult.totalUsage ?? streamResult.usage, | ||
| streamResult.text, | ||
| streamResult.toolCalls, | ||
| streamResult.finishReason, | ||
| streamResult.response | ||
| ]); | ||
| data.result = { usage, text, toolCalls, finishReason, response }; | ||
| end(); | ||
| } catch (error) { | ||
| end(error); | ||
| } | ||
| })(); | ||
| return true; | ||
| } | ||
| function isStreamingResult(result) { | ||
| return isObjectLike(result) && (isThenable(result.totalUsage) || isThenable(result.usage)); | ||
| } | ||
| function isThenable(value) { | ||
| return isObjectLike(value) && typeof value.then === "function"; | ||
| } | ||
| function suppressNativeTelemetry(callOptions, telemetry) { | ||
| if (telemetry.isEnabled !== true) { | ||
| return; | ||
| } | ||
| const suppressed = { ...telemetry, isEnabled: false }; | ||
| suppressedTelemetry.add(suppressed); | ||
| callOptions.experimental_telemetry = suppressed; | ||
| } | ||
| function subscribeResolveLanguageModel(tracingChannel, channelName, options) { | ||
| tracingChannel(channelName).subscribe({ | ||
| end(rawCtx) { | ||
| const ctx = rawCtx; | ||
| if (!isObjectLike(ctx.result)) { | ||
| return; | ||
| } | ||
| patchModelMethods(ctx.result, options); | ||
| }, | ||
| start() { | ||
| }, | ||
| asyncStart() { | ||
| }, | ||
| asyncEnd() { | ||
| }, | ||
| error() { | ||
| } | ||
| }); | ||
| } | ||
| function resolveModelCallParent() { | ||
| const active = getActiveSpan(); | ||
| return active && operationSpans.has(active) ? active : void 0; | ||
| } | ||
| function patchModelMethods(model, options) { | ||
| if (model[PATCHED]) { | ||
| return; | ||
| } | ||
| model[PATCHED] = true; | ||
| patchModelMethod(model, "doGenerate", options); | ||
| patchModelMethod(model, "doStream", options); | ||
| } | ||
| function patchModelMethod(model, method, options) { | ||
| const original = model[method]; | ||
| if (typeof original !== "function") { | ||
| return; | ||
| } | ||
| model[method] = function(...args) { | ||
| const parent = resolveModelCallParent(); | ||
| if (!parent) { | ||
| return Promise.resolve(original.apply(this, args)); | ||
| } | ||
| const callArgs = isObjectLike(args[0]) ? args[0] : {}; | ||
| const callId = callIdBySpan.get(parent); | ||
| const message = { | ||
| type: "languageModelCall", | ||
| event: { | ||
| callId, | ||
| provider: model.provider, | ||
| modelId: model.modelId, | ||
| // v4 nests the tool list under `mode.tools` (the `LanguageModelV1` call shape); v5+ passes a | ||
| // top-level `tools` array. Reading both keeps `available_tools` populated on the model-call span. | ||
| tools: callArgs.tools ?? (isObjectLike(callArgs.mode) ? callArgs.mode.tools : void 0), | ||
| messages: callArgs.prompt, | ||
| // Inherit the enclosing operation's per-call recording flags so inputs/tools/outputs are recorded on | ||
| // the model-call span whenever they are on the parent `invoke_agent` span. | ||
| ...recordingBySpan.get(parent) | ||
| } | ||
| }; | ||
| const span = withActiveSpan(parent, () => createSpanFromMessage(message, options)); | ||
| if (!span) { | ||
| return Promise.resolve(original.apply(this, args)); | ||
| } | ||
| const clearStreamCallId = () => { | ||
| if (method === "doStream" && callId) { | ||
| clearOperationCallId(callId); | ||
| } | ||
| }; | ||
| const failSpan = (error) => { | ||
| span.setStatus({ code: SPAN_STATUS_ERROR, message: error instanceof Error ? error.message : "unknown_error" }); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| throw error; | ||
| }; | ||
| try { | ||
| const result = Promise.resolve(original.apply(this, args)); | ||
| return result.then((value) => { | ||
| if (method === "doStream" && isObjectLike(value) && isReadableStream(value.stream)) { | ||
| value.stream = tapModelCallStream( | ||
| value.stream, | ||
| (final) => { | ||
| message.result = { ...value, ...streamedResultToChannelResult(final) }; | ||
| enrichSpanOnEnd(span, message, options); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| }, | ||
| (error) => { | ||
| span.setStatus({ | ||
| code: SPAN_STATUS_ERROR, | ||
| message: error instanceof Error ? error.message : "unknown_error" | ||
| }); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| } | ||
| ); | ||
| return value; | ||
| } | ||
| message.result = value; | ||
| enrichSpanOnEnd(span, message, options); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| return value; | ||
| }, failSpan); | ||
| } catch (error) { | ||
| return failSpan(error); | ||
| } | ||
| }; | ||
| } | ||
| function patchOperationTools(tools, options) { | ||
| try { | ||
| for (const [toolName, tool] of Object.entries(tools)) { | ||
| if (isObjectLike(tool)) { | ||
| patchToolExecute(toolName, tool, tools, options); | ||
| } | ||
| } | ||
| } catch { | ||
| DEBUG_BUILD && debug.log("Vercel AI orchestrion tool patching failed."); | ||
| } | ||
| } | ||
| function patchToolExecute(toolName, tool, tools, options) { | ||
| const original = tool.execute; | ||
| if (typeof original !== "function" || tool[TOOL_PATCHED]) { | ||
| return; | ||
| } | ||
| tool[TOOL_PATCHED] = true; | ||
| tool.execute = function(input, ...rest) { | ||
| const parent = resolveModelCallParent(); | ||
| if (!parent || toolCallSpans.has(parent)) { | ||
| return original.apply(this, [input, ...rest]); | ||
| } | ||
| const callOptions = isObjectLike(rest[0]) ? rest[0] : {}; | ||
| const message = { | ||
| type: "executeTool", | ||
| event: { | ||
| callId: callIdBySpan.get(parent), | ||
| toolCall: { toolName, toolCallId: asString(callOptions.toolCallId), input }, | ||
| // The `tools` record (keyed by name) lets the shared core backfill the tool's `description`. | ||
| tools, | ||
| // Inherit the enclosing operation's per-call recording flags so tool inputs/outputs are recorded | ||
| // whenever they are on the parent `invoke_agent` span. | ||
| ...recordingBySpan.get(parent) | ||
| } | ||
| }; | ||
| const span = withActiveSpan(parent, () => createSpanFromMessage(message, options)); | ||
| if (!span) { | ||
| return original.apply(this, [input, ...rest]); | ||
| } | ||
| const failSpan = (error) => { | ||
| captureToolError(span, message, error); | ||
| span.end(); | ||
| throw error; | ||
| }; | ||
| try { | ||
| const result = Promise.resolve(original.apply(this, [input, ...rest])); | ||
| return result.then((value) => { | ||
| message.result = { output: value }; | ||
| enrichSpanOnEnd(span, message, options); | ||
| span.end(); | ||
| return value; | ||
| }, failSpan); | ||
| } catch (error) { | ||
| return failSpan(error); | ||
| } | ||
| }; | ||
| } | ||
| function buildTextMessage(type) { | ||
| return (options, telemetry) => ({ | ||
| type, | ||
| event: { | ||
| callId: nextCallId(), | ||
| operationId: `ai.${type}`, | ||
| functionId: asString(telemetry.functionId), | ||
| ...modelFields(options.model), | ||
| maxRetries: options.maxRetries, | ||
| // The `ai` SDK takes the system prompt as a top-level `system` option (all of v4/v5/v6); the | ||
| // shared core lifts `event.instructions` into the system-instructions attribute, matching v7's | ||
| // native channel (which carries it as a distinct field rather than inside the messages array). | ||
| instructions: asString(options.system), | ||
| // Normalize to the message-array shape the shared core (and v7's channel) expects: a bare string | ||
| // `prompt` becomes a single user message, matching the SDK's own normalization. | ||
| messages: normalizePromptMessages(options), | ||
| ...recording(telemetry) | ||
| } | ||
| }); | ||
| } | ||
| function normalizePromptMessages(options) { | ||
| if (Array.isArray(options.messages)) { | ||
| return options.messages; | ||
| } | ||
| if (typeof options.prompt === "string") { | ||
| return [{ role: "user", content: options.prompt }]; | ||
| } | ||
| return options.messages ?? options.prompt; | ||
| } | ||
| function recording(telemetry) { | ||
| const enabledDefault = telemetry.isEnabled === true ? true : void 0; | ||
| return { | ||
| recordInputs: telemetry.recordInputs ?? enabledDefault, | ||
| recordOutputs: telemetry.recordOutputs ?? enabledDefault | ||
| }; | ||
| } | ||
| function modelFields(model) { | ||
| return { provider: modelField(model, "provider"), modelId: modelField(model, "modelId") }; | ||
| } | ||
| function modelField(model, field) { | ||
| return isObjectLike(model) ? asString(model[field]) : void 0; | ||
| } | ||
| export { subscribeVercelAiOrchestrionChannels }; | ||
| //# sourceMappingURL=vercel-ai-orchestrion-subscriber.js.map |
| {"version":3,"file":"vercel-ai-orchestrion-subscriber.js","sources":["../../../src/vercel-ai/vercel-ai-orchestrion-subscriber.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Span } from '@sentry/core';\nimport { debug, getActiveSpan, isObjectLike, SPAN_STATUS_ERROR, withActiveSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { CHANNELS } from '../orchestrion/channels';\nimport { bindTracingChannelToSpan, type TracingChannelPayloadWithSpan } from '../tracing-channel';\nimport {\n captureToolError,\n clearOperationCallId,\n clearOperationId,\n createSpanFromMessage,\n enrichSpanOnEnd,\n streamedResultToChannelResult,\n type VercelAiChannelMessage,\n type VercelAiChannelOptions,\n type VercelAiTracingChannelFactory,\n} from './vercel-ai-dc-subscriber';\nimport { asString, isReadableStream, tapModelCallStream } from './util';\n\n/**\n * v4, v5 & v6 channel adapter for the Vercel AI (`ai`) SDK.\n *\n * `ai` >= 7 publishes a normalized `ai:telemetry` tracing channel natively\n * (consumed by `subscribeVercelAiTracingChannel`). v4/v5/v6 have no such channel, so\n * orchestrion injects `orchestrion:ai:*` channels around the top-level\n * functions (see `orchestrion/config/index.ts`). The injected channels carry only the\n * wrapped call's `{ arguments, result, error }` — NOT v7's normalized `event`\n * object — so this adapter reconstructs an equivalent {@link VercelAiChannelMessage}\n * from each version's argument/result shapes and delegates to the SAME span-building core\n * (`createSpanFromMessage` / `enrichSpanOnEnd`) the v7 subscriber uses, so the\n * emitted spans are identical between v4, v5, v6 and v7. The shared core reads both v4-style\n * (`promptTokens`/`completionTokens`) and v5+-style (`inputTokens`/`outputTokens`) token names.\n *\n * The model call (`languageModelCall` / `generate_content` span) has no\n * injectable definition in `ai`. On v5/v6 we wrap `resolveLanguageModel` (the\n * single chokepoint every model call flows through) and monkey-patch\n * `doGenerate`/`doStream` on the returned model. v4 has no `resolveLanguageModel`\n * — the passed `LanguageModelV1` (`specificationVersion: 'v1'`) is called directly —\n * so we patch its `doGenerate`/`doStream` at the operation start instead (see\n * `buildOperationSpan`), gated on `'v1'` so it never touches v5/v6 models.\n *\n * Tool-call spans differ by version: v6 exposes a per-call `executeToolCall`\n * function orchestrion wraps into its own channel. v4/v5 have no such export (only a\n * batch `executeTools`), so instead we monkey-patch each tool's `execute` from\n * the operation's `tools`. On v6 that patch is inert: `executeToolCall` runs\n * `execute` inside its own tool-call span (the active async context), so the\n * patch sees that span as its parent and skips — only v4/v5 (where the parent is\n * the enclosing `invoke_agent` span) emit the patched span, so v6 never\n * double-counts tool spans.\n */\n\n/** Shape orchestrion's transform attaches to the tracing-channel context. */\ninterface OrchestrionContext {\n arguments: unknown[];\n result?: unknown;\n error?: unknown;\n}\n\n/** Builds the normalized message for a channel from the wrapped call's first-arg options. */\ntype MessageBuilder = (options: Record<string, unknown>, telemetry: Record<string, unknown>) => VercelAiChannelMessage;\n\n/** A resolved `ai` language model — has `doGenerate`/`doStream` and identity fields. */\ninterface ResolvedModel {\n modelId?: string;\n provider?: string;\n doGenerate?: (...args: unknown[]) => Promise<unknown>;\n doStream?: (...args: unknown[]) => Promise<unknown>;\n}\n\nconst PATCHED = Symbol('SentryVercelAiModelPatched');\nconst TOOL_PATCHED = Symbol('SentryVercelAiToolPatched');\n\n/** A resolved model with our patch bookkeeping (idempotency flag). */\ntype PatchableModel = ResolvedModel & { [PATCHED]?: boolean };\n\n/** A tool definition off an operation's `tools`, with our patch bookkeeping. */\ntype PatchableTool = { execute?: (...args: unknown[]) => unknown; [TOOL_PATCHED]?: boolean };\n\n// Per-operation correlation id. No Date/random (unavailable / non-deterministic) — a counter is enough.\nlet callIdCounter = 0;\nfunction nextCallId(): string {\n return `v6-${++callIdCounter}`;\n}\n\n// The message built on `start` for each operation, keyed by the (stable-identity) channel context, so\n// the `beforeSpanEnd` handler can enrich the span from the settled result and clear the `callId` maps.\nconst messages = new WeakMap<object, VercelAiChannelMessage>();\n// The spans we opened for top-level operations, and each one's `callId`. A model call resolves its\n// parent against this set (so it never mis-attributes to the enclosing `main`/user span) and reads the\n// parent's `callId` so its span can be named after the operation (e.g. `ai.streamText.doStream`).\nconst operationSpans = new WeakSet<Span>();\n// Tool-call spans — only v6's `executeToolCall` channel opens these. The v5 tool-`execute` patch\n// consults this to avoid double-counting: `executeToolCall` runs `execute` inside its span (which is\n// therefore the active parent), so a resolved parent in this set means v6 already spanned the call.\nconst toolCallSpans = new WeakSet<Span>();\nconst callIdBySpan = new WeakMap<Span, string>();\n// The operation's per-call recording flags (`experimental_telemetry.recordInputs/recordOutputs`), keyed by\n// its span. A model call carries no telemetry of its own, so it inherits the enclosing operation's flags —\n// otherwise a per-call `recordInputs: true` would record inputs on the `invoke_agent` span but not on the\n// child `generate_content` span (whose event would fall back to the global default). v7's channel forwards\n// these flags on every event, so this keeps v6 identical.\nconst recordingBySpan = new WeakMap<Span, ReturnType<typeof recording>>();\n\n// The `experimental_telemetry` objects we swapped in to suppress `ai`'s native OTel spans (see\n// `suppressNativeTelemetry`). Our skip logic treats `isEnabled === false` as \"user disabled telemetry,\n// emit no span\"; without this set, a call whose options object we already neutralized — or a user-shared\n// telemetry object we replaced on a prior call — would be misread as user-disabled and lose its span.\nconst suppressedTelemetry = new WeakSet<object>();\n\nlet subscribed = false;\n\n/**\n * Subscribe the v6 orchestrion channel adapter. Safe to always call: inert on\n * `ai` >= 7 (those channels are never published) and when orchestrion injection\n * isn't active. Idempotent.\n *\n * `tracingChannel` is the platform-provided factory (the same one passed to\n * `subscribeVercelAiTracingChannel`); `options` pins the recording settings at\n * subscribe time so we never look the integration up per event.\n */\nexport function subscribeVercelAiOrchestrionChannels(\n tracingChannel: VercelAiTracingChannelFactory,\n options: VercelAiChannelOptions = {},\n): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n try {\n bindOperation(tracingChannel, CHANNELS.VERCEL_AI_GENERATE_TEXT, buildTextMessage('generateText'), options);\n bindOperation(tracingChannel, CHANNELS.VERCEL_AI_STREAM_TEXT, buildTextMessage('streamText'), options);\n bindOperation(tracingChannel, CHANNELS.VERCEL_AI_GENERATE_OBJECT, buildTextMessage('generateObject'), options);\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EMBED,\n (callOptions, telemetry) => ({\n type: 'embed',\n event: {\n callId: nextCallId(),\n ...modelFields(callOptions.model),\n maxRetries: callOptions.maxRetries,\n value: callOptions.value,\n ...recording(telemetry),\n },\n }),\n options,\n );\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EMBED_MANY,\n // `embedMany` takes a `values` array (vs `embed`'s single `value`); the shared core reads it as the\n // embeddings input, matching the OTel path's batch `ai.embedMany` span.\n (callOptions, telemetry) => ({\n type: 'embedMany',\n event: {\n callId: nextCallId(),\n ...modelFields(callOptions.model),\n maxRetries: callOptions.maxRetries,\n values: callOptions.values,\n ...recording(telemetry),\n },\n }),\n options,\n );\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EXECUTE_TOOL_CALL,\n (callOptions, telemetry) => ({\n type: 'executeTool',\n // v6 carries the tool definitions on the executeToolCall args (a record keyed by name);\n // the shared core reads the matching tool's `description` for the span.\n event: {\n callId: nextCallId(),\n toolCall: callOptions.toolCall,\n tools: callOptions.tools,\n ...recording(telemetry),\n },\n }),\n options,\n );\n subscribeResolveLanguageModel(tracingChannel, CHANNELS.VERCEL_AI_RESOLVE_LANGUAGE_MODEL, options);\n } catch {\n DEBUG_BUILD && debug.log('Vercel AI orchestrion channel subscription failed.');\n }\n}\n\n/**\n * Bind one operation channel: `getSpan` opens a span from the message reconstructed out of the wrapped\n * call's first argument; `beforeSpanEnd` enriches it from the settled result (tokens, output messages,\n * finish reasons, …) before the helper ends the span.\n *\n * An operation whose `experimental_telemetry.isEnabled` is explicitly `false` is skipped entirely (no\n * span): the orchestrion channel fires regardless of that flag, whereas v7's native `ai:telemetry`\n * channel is simply not published in that case — so we reproduce v7's \"no telemetry → no span\".\n */\nfunction bindOperation(\n tracingChannel: VercelAiTracingChannelFactory,\n channelName: string,\n build: MessageBuilder,\n options: VercelAiChannelOptions,\n): void {\n const channel = tracingChannel<OrchestrionContext>(channelName);\n\n // Build the operation span from the wrapped call's first argument and track it (so a model call can\n // resolve it as its parent). `bindTracingChannelToSpan` calls this once at channel `start` and makes\n // the returned span the active async context for the operation's duration — that active span is what\n // `resolveModelCallParent` reads. It also sets `data._sentrySpan`, so we don't here.\n const buildOperationSpan = (data: TracingChannelPayloadWithSpan<OrchestrionContext>): Span | undefined => {\n const callOptions = isObjectLike(data.arguments[0]) ? data.arguments[0] : {};\n const telemetry = isObjectLike(callOptions.experimental_telemetry) ? callOptions.experimental_telemetry : {};\n // `isEnabled === false` means the user opted out — emit no span. But `isEnabled` is also `false` on\n // the telemetry object we swap in to suppress native spans, so don't mistake our own object for a\n // user opt-out (which would drop the span on a call whose options we already neutralized).\n if (telemetry.isEnabled === false && !suppressedTelemetry.has(telemetry)) {\n return undefined;\n }\n const message = build(callOptions, telemetry);\n // Stop `ai` from emitting its own native OTel spans for this call — we build the equivalent spans\n // from the channels, so the SDK's would be duplicates. Reads above have already captured everything\n // we need off `telemetry`.\n suppressNativeTelemetry(callOptions, telemetry);\n const span = createSpanFromMessage(message, options);\n if (span) {\n messages.set(data, message);\n operationSpans.add(span);\n // v6's `executeToolCall` span: tracked so the v5 tool-`execute` patch can recognize (and skip)\n // tool calls it runs, since that patch sees this span as its active parent (see `patchToolExecute`).\n if (message.type === 'executeTool') {\n toolCallSpans.add(span);\n }\n const callId = asString(message.event.callId);\n if (callId) {\n callIdBySpan.set(span, callId);\n }\n recordingBySpan.set(span, recording(telemetry));\n // v5 has no `executeToolCall` channel, so patch each tool's `execute` to emit the tool-call span.\n // Inert on v6 (guarded inside `patchToolExecute` when the parent is `executeToolCall`'s own span).\n if (isObjectLike(callOptions.tools)) {\n patchOperationTools(callOptions.tools, options);\n }\n // v4 has no `resolveLanguageModel` chokepoint — the passed `LanguageModelV1`\n // (`specificationVersion: 'v1'`) is called directly — so patch its `doGenerate`/`doStream`\n // here, at the operation start, instead. v5/v6 models are `'v2'` and are patched via\n // `resolveLanguageModel`, so restricting to `'v1'` keeps this strictly additive and avoids\n // double-patching. Embedding models are also `'v1'` but expose no `doGenerate`/`doStream`, so\n // the patch is a no-op for `embed`/`embedMany`.\n if (isObjectLike(callOptions.model) && callOptions.model.specificationVersion === 'v1') {\n patchModelMethods(callOptions.model as PatchableModel, options);\n }\n }\n return span;\n };\n\n bindTracingChannelToSpan(\n channel,\n (data: TracingChannelPayloadWithSpan<OrchestrionContext>) => buildOperationSpan(data),\n {\n beforeSpanEnd: (span, data) => {\n const message = messages.get(data);\n if (!message) {\n return;\n }\n // The helper's `error` handler already set the span status; only enrich from a successful result.\n if (!('error' in data)) {\n // v6's `executeToolCall` returns the tool result/error object directly, whereas the shared core\n // (matching v7) expects it nested under `output`; wrap it so tool-error detection works.\n message.result = message.type === 'executeTool' ? { output: data.result } : data.result;\n enrichSpanOnEnd(span, message, options);\n }\n // A `streamText` model call runs after this (synchronously-returning) operation's span has\n // already ended, so its `callId` entry must outlive the operation — it's cleared once the model\n // call settles (see `patchModelMethod`). Every other operation can clear here.\n if (message.type !== 'streamText') {\n clearOperationId(message);\n }\n messages.delete(data);\n },\n // `streamText` returns synchronously, so its operation span would otherwise end before the stream\n // drains — losing the aggregate usage/output. Defer the end and await the result's completion\n // promises (`totalUsage`/`text`/…, which resolve on drain), mirroring how v7's channel defers the\n // operation span on the SDK's total-usage promise.\n deferSpanEnd: ({ data, end }) => deferStreamTextOperationEnd(data, end),\n },\n );\n}\n\n/**\n * Keep a streamed `streamText` operation span open until the stream drains, then enrich it from the\n * `StreamTextResult`'s completion promises (usage/output/finish reason) and end it. Returns `false` for\n * anything that isn't a streamed `streamText` result, so the helper ends the span as usual.\n */\nfunction deferStreamTextOperationEnd(\n data: TracingChannelPayloadWithSpan<OrchestrionContext>,\n end: (error?: unknown) => void,\n): boolean {\n if (messages.get(data)?.type !== 'streamText' || 'error' in data || !isStreamingResult(data.result)) {\n return false;\n }\n\n const streamResult = data.result;\n void (async () => {\n try {\n const [usage, text, toolCalls, finishReason, response] = await Promise.all([\n streamResult.totalUsage ?? streamResult.usage,\n streamResult.text,\n streamResult.toolCalls,\n streamResult.finishReason,\n streamResult.response,\n ]);\n // Feed the resolved values back through the shared enrichment: `beforeSpanEnd` reads `data.result`.\n data.result = { usage, text, toolCalls, finishReason, response };\n end();\n } catch (error) {\n end(error);\n }\n })();\n\n return true;\n}\n\n/** A `StreamTextResult` exposes its aggregates as promises (`totalUsage`/`usage`); a settled result does not. */\nfunction isStreamingResult(result: unknown): result is Record<string, PromiseLike<unknown> | undefined> {\n return isObjectLike(result) && (isThenable(result.totalUsage) || isThenable(result.usage));\n}\n\nfunction isThenable(value: unknown): boolean {\n return isObjectLike(value) && typeof value.then === 'function';\n}\n\n/**\n * Neutralize `ai`'s native OpenTelemetry instrumentation for this call by pointing\n * `experimental_telemetry` at a copy with `isEnabled: false`. `ai`'s `getTracer` then returns its\n * internal no-op tracer, so it never creates (nor sets active) the duplicate `ai.*` spans we'd\n * otherwise have to drop.\n *\n * Only a call that explicitly enabled telemetry emits native spans — otherwise `ai` already uses its\n * no-op tracer, so there's nothing to suppress and we leave the user's options untouched. When\n * `isEnabled === true`, `telemetry` is `callOptions.experimental_telemetry` and `callOptions` is the\n * real first argument the SDK will read, so the reassignment takes effect for the wrapped call. We\n * replace rather than mutate in place, so a telemetry object the user shares across calls keeps its own\n * `isEnabled: true`; the replacement is tracked in `suppressedTelemetry` so our skip logic doesn't\n * later read it back as a user opt-out.\n */\nfunction suppressNativeTelemetry(callOptions: Record<string, unknown>, telemetry: Record<string, unknown>): void {\n if (telemetry.isEnabled !== true) {\n return;\n }\n const suppressed = { ...telemetry, isEnabled: false };\n suppressedTelemetry.add(suppressed);\n callOptions.experimental_telemetry = suppressed;\n}\n\n/**\n * `resolveLanguageModel` returns the model every call flows through. We don't span it — on `end` we\n * monkey-patch `doGenerate`/`doStream` on the returned model so each invocation produces a\n * `languageModelCall` span parented to the enclosing invoke_agent span.\n */\nfunction subscribeResolveLanguageModel(\n tracingChannel: VercelAiTracingChannelFactory,\n channelName: string,\n options: VercelAiChannelOptions,\n): void {\n tracingChannel<OrchestrionContext>(channelName).subscribe({\n end(rawCtx) {\n const ctx = rawCtx as OrchestrionContext;\n if (!isObjectLike(ctx.result)) {\n return;\n }\n // Patch the model's `doGenerate`/`doStream` once. The model call recovers its parent from the\n // active async context at call time (the operation span `bindTracingChannelToSpan` bound), which\n // propagates into the model call for `streamText` too, so there is nothing to capture on the model here.\n patchModelMethods(ctx.result as PatchableModel, options);\n },\n start() {\n /* no-op */\n },\n asyncStart() {\n /* no-op */\n },\n asyncEnd() {\n /* no-op */\n },\n error() {\n /* no-op */\n },\n });\n}\n\n/**\n * Pick the invoke_agent span a model call should hang under: the operation span that\n * `bindTracingChannelToSpan` planted as the active async context for the enclosing operation.\n *\n * Because we suppress `ai`'s native telemetry (so it never installs its own span as active), the active\n * span inside `doGenerate`/`doStream` is our operation span. The `operationSpans` gate makes this return\n * `undefined` when the active span isn't one of ours — e.g. telemetry was disabled for the call so we\n * opened no operation span and the active span is the user's enclosing span — so the model call is\n * skipped rather than mis-parented.\n *\n * This covers `generateText`/`embed` (whose model call is awaited inside the operation body) and\n * `streamText` alike — `ai` initiates the stream synchronously within the operation's bound context, so\n * the later `doStream` continuation restores the same active span even though the operation's span has\n * already ended. The per-operation binding also disambiguates concurrent calls that share one model\n * instance (a single mutable slot on the shared model could not — it would hold whichever operation\n * resolved the model last).\n */\nfunction resolveModelCallParent(): Span | undefined {\n const active = getActiveSpan();\n return active && operationSpans.has(active) ? active : undefined;\n}\n\n/**\n * Idempotently patch a resolved model's `doGenerate`/`doStream` so each invocation emits a\n * `languageModelCall` span. Shared by the v5/v6 `resolveLanguageModel` path and the v4 path (which\n * patches the passed model at the operation start, as v4 has no `resolveLanguageModel`).\n */\nfunction patchModelMethods(model: PatchableModel, options: VercelAiChannelOptions): void {\n if (model[PATCHED]) {\n return;\n }\n model[PATCHED] = true;\n patchModelMethod(model, 'doGenerate', options);\n patchModelMethod(model, 'doStream', options);\n}\n\nfunction patchModelMethod(\n model: PatchableModel,\n method: 'doGenerate' | 'doStream',\n options: VercelAiChannelOptions,\n): void {\n const original = model[method];\n if (typeof original !== 'function') {\n return;\n }\n model[method] = function (this: unknown, ...args: unknown[]): Promise<unknown> {\n const parent = resolveModelCallParent();\n // No enclosing operation span (e.g. telemetry disabled for the call) → don't open a model-call span.\n if (!parent) {\n return Promise.resolve(original.apply(this, args));\n }\n\n const callArgs = isObjectLike(args[0]) ? args[0] : {};\n // Carry the operation's `callId` so the shared core can name the span after it\n // (`ai.generateText.doGenerate` / `ai.streamText.doStream`).\n const callId = callIdBySpan.get(parent);\n const message: VercelAiChannelMessage = {\n type: 'languageModelCall',\n event: {\n callId,\n provider: model.provider,\n modelId: model.modelId,\n // v4 nests the tool list under `mode.tools` (the `LanguageModelV1` call shape); v5+ passes a\n // top-level `tools` array. Reading both keeps `available_tools` populated on the model-call span.\n tools: callArgs.tools ?? (isObjectLike(callArgs.mode) ? callArgs.mode.tools : undefined),\n messages: callArgs.prompt,\n // Inherit the enclosing operation's per-call recording flags so inputs/tools/outputs are recorded on\n // the model-call span whenever they are on the parent `invoke_agent` span.\n ...recordingBySpan.get(parent),\n },\n };\n const span = withActiveSpan(parent, () => createSpanFromMessage(message, options));\n // `languageModelCall` always opens a span; the guard just keeps the wrapper safe if that changes.\n if (!span) {\n return Promise.resolve(original.apply(this, args));\n }\n\n // `streamText` ends its operation span synchronously, so its `callId` entry was deliberately left in\n // place for this later model call; drop it now that we've used it.\n const clearStreamCallId = (): void => {\n if (method === 'doStream' && callId) {\n clearOperationCallId(callId);\n }\n };\n\n // Both the synchronous throw and the async rejection of the model call must end the span with an\n // error status (an `async` `doGenerate`/`doStream` that throws rejects rather than throwing here).\n const failSpan = (error: unknown): never => {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: error instanceof Error ? error.message : 'unknown_error' });\n span.end();\n clearStreamCallId();\n throw error;\n };\n\n try {\n const result = Promise.resolve(original.apply(this, args));\n return result.then(value => {\n // A streamed model call resolves to `{ stream, ... }` before the stream is consumed, so its\n // usage/finish/output only arrive as the stream drains. Tap it (same helper as the v7 path) and\n // defer ending this model-call span until then. The parent `invoke_agent` span is enriched\n // separately by `deferStreamTextOperationEnd`, which awaits the operation's own result promises.\n if (method === 'doStream' && isObjectLike(value) && isReadableStream(value.stream)) {\n value.stream = tapModelCallStream(\n value.stream,\n final => {\n message.result = { ...value, ...streamedResultToChannelResult(final) };\n enrichSpanOnEnd(span, message, options);\n span.end();\n clearStreamCallId();\n },\n error => {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: error instanceof Error ? error.message : 'unknown_error',\n });\n span.end();\n clearStreamCallId();\n },\n );\n\n return value;\n }\n // `doGenerate` (and any non-stream result) settles with the full result; end here, start/end\n // bracket the call to match the channel timing.\n message.result = value;\n enrichSpanOnEnd(span, message, options);\n span.end();\n clearStreamCallId();\n\n return value;\n }, failSpan);\n } catch (error) {\n return failSpan(error);\n }\n };\n}\n\n/**\n * Patch every executable tool on an operation's `tools` (a record keyed by tool name) so each\n * invocation emits a `gen_ai.execute_tool` span. v5 routes tool execution through the batch\n * `executeTools`, which binds `tool.execute` at call time — so replacing the `execute` property here\n * (before the call runs) is picked up. Tools without an `execute` (client-side tools) are skipped,\n * matching `executeTools`.\n */\nfunction patchOperationTools(tools: Record<string, unknown>, options: VercelAiChannelOptions): void {\n // This runs inside the channel `start` transform (no upstream try/catch), so a throw here would break\n // the user's `ai` call. Instrumentation must never do that — degrade to no tool span instead.\n try {\n for (const [toolName, tool] of Object.entries(tools)) {\n if (isObjectLike(tool)) {\n patchToolExecute(toolName, tool as PatchableTool, tools, options);\n }\n }\n } catch {\n DEBUG_BUILD && debug.log('Vercel AI orchestrion tool patching failed.');\n }\n}\n\nfunction patchToolExecute(\n toolName: string,\n tool: PatchableTool,\n tools: Record<string, unknown>,\n options: VercelAiChannelOptions,\n): void {\n const original = tool.execute;\n if (typeof original !== 'function' || tool[TOOL_PATCHED]) {\n return;\n }\n tool[TOOL_PATCHED] = true;\n tool.execute = function (this: unknown, input: unknown, ...rest: unknown[]): unknown {\n // Skip if there's no enclosing operation span (telemetry disabled for the call), or if that parent\n // is itself a tool-call span — the latter means v6's `executeToolCall` already opened a span for\n // this call and is running `execute` inside it, so spanning again here would double-count. On v5\n // the parent is the enclosing `invoke_agent` operation span, so we proceed.\n const parent = resolveModelCallParent();\n if (!parent || toolCallSpans.has(parent)) {\n return original.apply(this, [input, ...rest]);\n }\n\n // v5 passes `{ toolCallId, messages, abortSignal, ... }` as the second argument to `execute`.\n const callOptions = isObjectLike(rest[0]) ? rest[0] : {};\n const message: VercelAiChannelMessage = {\n type: 'executeTool',\n event: {\n callId: callIdBySpan.get(parent),\n toolCall: { toolName, toolCallId: asString(callOptions.toolCallId), input },\n // The `tools` record (keyed by name) lets the shared core backfill the tool's `description`.\n tools,\n // Inherit the enclosing operation's per-call recording flags so tool inputs/outputs are recorded\n // whenever they are on the parent `invoke_agent` span.\n ...recordingBySpan.get(parent),\n },\n };\n const span = withActiveSpan(parent, () => createSpanFromMessage(message, options));\n // `executeTool` always opens a span; the guard just keeps the wrapper safe if that changes.\n if (!span) {\n return original.apply(this, [input, ...rest]);\n }\n\n // v5's `executeTools` catches a thrown tool error and turns it into `tool-error` content rather\n // than rejecting, so the user never sees a rejection — we must capture it here. Rethrow so\n // `executeTools` still produces its `tool-error` result and the operation continues normally.\n const failSpan = (error: unknown): never => {\n captureToolError(span, message, error);\n span.end();\n throw error;\n };\n\n try {\n const result = Promise.resolve(original.apply(this, [input, ...rest]));\n return result.then(value => {\n // The shared core (matching v6/v7) expects the tool result nested under `output`.\n message.result = { output: value };\n enrichSpanOnEnd(span, message, options);\n span.end();\n return value;\n }, failSpan);\n } catch (error) {\n return failSpan(error);\n }\n };\n}\n\nfunction buildTextMessage(type: 'generateText' | 'streamText' | 'generateObject'): MessageBuilder {\n return (options, telemetry) => ({\n type,\n event: {\n callId: nextCallId(),\n operationId: `ai.${type}`,\n functionId: asString(telemetry.functionId),\n ...modelFields(options.model),\n maxRetries: options.maxRetries,\n // The `ai` SDK takes the system prompt as a top-level `system` option (all of v4/v5/v6); the\n // shared core lifts `event.instructions` into the system-instructions attribute, matching v7's\n // native channel (which carries it as a distinct field rather than inside the messages array).\n instructions: asString(options.system),\n // Normalize to the message-array shape the shared core (and v7's channel) expects: a bare string\n // `prompt` becomes a single user message, matching the SDK's own normalization.\n messages: normalizePromptMessages(options),\n ...recording(telemetry),\n },\n });\n}\n\nfunction normalizePromptMessages(options: Record<string, unknown>): unknown {\n if (Array.isArray(options.messages)) {\n return options.messages;\n }\n if (typeof options.prompt === 'string') {\n return [{ role: 'user', content: options.prompt }];\n }\n return options.messages ?? options.prompt;\n}\n\nfunction recording(telemetry: Record<string, unknown>): { recordInputs: unknown; recordOutputs: unknown } {\n // Match the OTel integration's per-call default: an explicit `recordInputs`/`recordOutputs` wins, but a\n // call that merely enables telemetry (`isEnabled: true`, no explicit flags) defaults both to `true`.\n // Unlike v7's native `ai:telemetry` channel, the orchestrion channels expose `isEnabled`, so — as noted\n // in `resolveRecording` — we CAN reproduce that default here (the native-channel subscriber cannot).\n const enabledDefault = telemetry.isEnabled === true ? true : undefined;\n return {\n recordInputs: telemetry.recordInputs ?? enabledDefault,\n recordOutputs: telemetry.recordOutputs ?? enabledDefault,\n };\n}\n\nfunction modelFields(model: unknown): { provider?: string; modelId?: string } {\n return { provider: modelField(model, 'provider'), modelId: modelField(model, 'modelId') };\n}\n\nfunction modelField(model: unknown, field: 'modelId' | 'provider'): string | undefined {\n return isObjectLike(model) ? asString(model[field]) : undefined;\n}\n"],"names":[],"mappings":"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"} |
| import { TracingChannel } from 'node:diagnostics_channel'; | ||
| import { GraphqlDocumentNode } from './utils'; | ||
| export declare const GRAPHQL_DC_CHANNEL_PARSE = "graphql:parse"; | ||
| export declare const GRAPHQL_DC_CHANNEL_VALIDATE = "graphql:validate"; | ||
| export declare const GRAPHQL_DC_CHANNEL_EXECUTE = "graphql:execute"; | ||
| export declare const GRAPHQL_DC_CHANNEL_SUBSCRIBE = "graphql:subscribe"; | ||
| export declare const GRAPHQL_DC_CHANNEL_RESOLVE = "graphql:resolve"; | ||
| /** Context published on the sync-only `graphql:parse` channel. */ | ||
| export interface GraphqlParseData { | ||
| source: string | { | ||
| body?: string; | ||
| }; | ||
| result?: GraphqlDocumentNode; | ||
| error?: unknown; | ||
| } | ||
| /** Context published on the sync-only `graphql:validate` channel. */ | ||
| export interface GraphqlValidateData { | ||
| document: GraphqlDocumentNode; | ||
| /** Validation errors returned by validation; an empty array means the document is valid. */ | ||
| result?: ReadonlyArray<unknown>; | ||
| error?: unknown; | ||
| } | ||
| /** | ||
| * Context published on the `graphql:execute` and `graphql:subscribe` channels. | ||
| * | ||
| * `result` carries an `ExecutionResult` (or, for subscriptions, an async generator); GraphQL errors | ||
| * collected during execution surface on `result.errors` rather than as the channel's `error` | ||
| * lifecycle event, which only fires on an abrupt throw. | ||
| */ | ||
| export interface GraphqlOperationData { | ||
| document: GraphqlDocumentNode; | ||
| operationName?: string; | ||
| operationType?: string; | ||
| result?: unknown; | ||
| error?: unknown; | ||
| } | ||
| /** | ||
| * Context published on the per-field `graphql:resolve` channel. | ||
| * | ||
| * A resolver throw or rejection publishes the `error` lifecycle event here; the same failure also | ||
| * surfaces in the enclosing execution result. | ||
| */ | ||
| export interface GraphqlResolveData { | ||
| fieldName: string; | ||
| parentType: string; | ||
| fieldType: string; | ||
| fieldPath: string; | ||
| /** Whether the field is handled by graphql's default property resolver (vs. a user resolver). */ | ||
| isDefaultResolver: boolean; | ||
| alias?: string; | ||
| args?: unknown; | ||
| result?: unknown; | ||
| error?: unknown; | ||
| } | ||
| /** Options controlling which graphql channels the subscriber emits spans for. */ | ||
| export interface GraphqlDiagnosticChannelsOptions { | ||
| /** | ||
| * Do not create spans for resolvers. Resolver spans are per-field and can be very high volume. | ||
| * Defaults to `true`. | ||
| */ | ||
| ignoreResolveSpans?: boolean; | ||
| /** | ||
| * When resolver spans are enabled, do not create them for graphql's default property resolver | ||
| * (fields without a user-defined resolver), which are rarely interesting. Defaults to `true`. | ||
| */ | ||
| ignoreTrivialResolveSpans?: boolean; | ||
| /** | ||
| * Rename the enclosing root span to include the operation name(s), e.g. | ||
| * `GET /graphql` -> `GET /graphql (query GetUser)`. Defaults to `true`. | ||
| */ | ||
| useOperationNameForRootSpan?: boolean; | ||
| } | ||
| /** | ||
| * Platform-provided factory that creates a native tracing channel for the given name. The | ||
| * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`, | ||
| * which propagates the active span through the runtime's async context. | ||
| * | ||
| * Node passes `node:diagnostics_channel`'s `tracingChannel` directly. | ||
| */ | ||
| export type GraphqlTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>; | ||
| /** | ||
| * Subscribe Sentry span handlers to graphql's diagnostics-channel events | ||
| * (`graphql:parse`, `:validate`, `:execute`, `:subscribe`), published by graphql >= 17.0.0. | ||
| * | ||
| * On older graphql versions the channels are never published to, so the subscribers are inert — | ||
| * there is no double-instrumentation against the vendored OTel patcher, which is gated to `< 17`. | ||
| * | ||
| * The per-field `graphql:resolve` channel is only subscribed when `ignoreResolveSpans` is `false`: | ||
| * resolver spans are per-field and can be extremely high-volume, so they are off by default (matching | ||
| * the legacy OTel path). When enabled, `ignoreTrivialResolveSpans` (default `true`) additionally skips | ||
| * graphql's default property resolver. | ||
| */ | ||
| export declare function subscribeGraphqlDiagnosticChannels(tracingChannel: GraphqlTracingChannelFactory, options?: GraphqlDiagnosticChannelsOptions): void; | ||
| //# sourceMappingURL=graphql-dc-subscriber.d.ts.map |
| import { GraphqlDiagnosticChannelsOptions } from './graphql-dc-subscriber'; | ||
| /** | ||
| * Auto-instrument the [graphql](https://www.npmjs.com/package/graphql) library via its native | ||
| * `node:diagnostics_channel` tracing channels (graphql >= 17). | ||
| * | ||
| * On older graphql versions the channels are never published to, so this integration is inert and | ||
| * the vendored OTel instrumentation (gated to `< 17`) handles instrumentation instead. | ||
| */ | ||
| export declare const graphqlIntegration: (options?: GraphqlDiagnosticChannelsOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Graphql"; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
| import { Span } from '@sentry/core'; | ||
| /** Minimal shape of a graphql-js lexer token, enough to locate literal spans for redaction. */ | ||
| interface GraphqlToken { | ||
| kind: string; | ||
| start: number; | ||
| end: number; | ||
| next?: GraphqlToken | null; | ||
| } | ||
| /** Minimal shape of a parsed graphql-js `DocumentNode`, enough to read its source and tokens. */ | ||
| export interface GraphqlDocumentNode { | ||
| loc?: { | ||
| startToken?: GraphqlToken; | ||
| source?: { | ||
| body?: string; | ||
| }; | ||
| }; | ||
| } | ||
| /** | ||
| * Rename the enclosing root span to include the operation name(s), e.g. `GET /graphql (query GetUser)`. | ||
| * Mirrors the legacy OTel `useOperationNameForRootSpan` behavior; `parseSpanDescription` reads the same | ||
| * `sentry.graphql.operation` attribute on the OTel export path. | ||
| */ | ||
| export declare function renameRootSpanWithOperation(span: Span, operationType: string, operationName?: string): void; | ||
| /** | ||
| * Span name follows the GraphQL semantic conventions: `<operation.type> <operation.name>` when both | ||
| * are available, `<operation.type>` when only the type is, otherwise a static fallback. | ||
| */ | ||
| export declare function getOperationSpanName(operationType: string | undefined, operationName: string | undefined, fallbackName: string): string; | ||
| /** Whether a graphql execution result carries GraphQL errors (returned on `result.errors`). */ | ||
| export declare function hasResultErrors(result: unknown): boolean; | ||
| /** | ||
| * Serialize a parsed document into `graphql.document` while redacting every literal argument value: | ||
| * the original source text is preserved verbatim except that string/number literal spans are | ||
| * replaced (`"foo"` -> `"*"`, `42` -> `*`). graphql does not sanitize its channel payload, so this | ||
| * prevents raw inline values (potential PII) from leaving the process. Variable values are never | ||
| * included. Returns `undefined` (rather than throwing) on anything it cannot serialize. | ||
| */ | ||
| export declare function redactGraphqlDocument(document: GraphqlDocumentNode | undefined): string | undefined; | ||
| export {}; | ||
| //# sourceMappingURL=utils.d.ts.map |
| /** | ||
| * EXPERIMENTAL: orchestrion-driven `amqplib` integration. | ||
| * | ||
| * Subscribes to the `orchestrion:amqplib:*` diagnostics_channels that the orchestrion code transform | ||
| * injects into `amqplib`'s channel/connection methods. Requires the orchestrion runtime hook or | ||
| * bundler plugin to be active. | ||
| */ | ||
| export declare const amqplibChannelIntegration: () => import("@sentry/core").Integration & { | ||
| name: "Amqplib"; | ||
| }; | ||
| //# sourceMappingURL=amqplib.d.ts.map |
| import { ExpressIntegrationOptions } from './types'; | ||
| /** | ||
| * EXPERIMENTAL — orchestrion-driven Express integration. | ||
| * | ||
| * Subscribes to the `orchestrion:express:handle` (Express v4) and | ||
| * `orchestrion:router:handle` (Express v5, via the `router` package) | ||
| * diagnostics_channels that the orchestrion code transform injects into the | ||
| * routing layer's request handler (`Layer.prototype.handle_request` / | ||
| * `handleRequest`). One span is opened per layer invocation — producing the | ||
| * same spans as the OTel Express instrumentation. | ||
| * | ||
| * Requires the orchestrion runtime hook or bundler plugin to be active — wire | ||
| * that up via `experimentalUseDiagnosticsChannelInjection()`. | ||
| */ | ||
| export declare const expressChannelIntegration: (options?: ExpressIntegrationOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Express"; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { ExpressIntegrationOptions } from './types'; | ||
| export declare function instrumentExpress(options: ExpressIntegrationOptions, tracingChannel: typeof diagnosticsChannel.tracingChannel): void; | ||
| //# sourceMappingURL=instrumentation.d.ts.map |
| import { ExpressLayer, ExpressRequest } from './types'; | ||
| /** Record the path pattern a layer was registered with. */ | ||
| export declare function setLayerRegisteredPath(layer: ExpressLayer, path: string | undefined): void; | ||
| /** Read the path pattern a layer was registered with, if any. */ | ||
| export declare function getLayerRegisteredPath(layer: ExpressLayer): string | undefined; | ||
| /** Push a layer's registered path onto the request's chain. */ | ||
| export declare function pushLayerPath(req: ExpressRequest, path: string): void; | ||
| /** Pop the most recently pushed layer path off the request's chain. */ | ||
| export declare function popLayerPath(req: ExpressRequest): void; | ||
| /** | ||
| * The path pattern a `route`/`use` call registered, derived from its arguments. | ||
| * A leading string/RegExp/number path becomes the pattern (arrays are joined | ||
| * with `,`); a bare handler function yields `undefined`. Kept in sync with | ||
| * `@sentry/core`'s Express `getLayerPath`. | ||
| */ | ||
| export declare function getLayerPath(args: unknown[]): string | undefined; | ||
| /** | ||
| * Concatenate the stored layer paths into the full route pattern (parameters | ||
| * preserved), e.g. `/api/:version/user`. Mirrors `@sentry/core`. | ||
| */ | ||
| export declare function getConstructedRoute(req: ExpressRequest): string; | ||
| /** | ||
| * Validate the constructed route against the request URL, returning it only | ||
| * when it plausibly corresponds to a real match (otherwise `undefined`). Mirrors | ||
| * `@sentry/core`'s `getActualMatchedRoute` — used for the `http.route` attribute. | ||
| */ | ||
| export declare function getActualMatchedRoute(req: ExpressRequest, constructedRoute: string): string | undefined; | ||
| //# sourceMappingURL=route.d.ts.map |
| export type ExpressLayerType = 'router' | 'middleware' | 'request_handler'; | ||
| /** | ||
| * The subset of an Express routing `Layer` we read at handle time. `handle` is | ||
| * the user's middleware/handler; `route` is only set on route-dispatch layers | ||
| * (and carries the parameterized path). | ||
| */ | ||
| export interface ExpressLayer { | ||
| handle?: { | ||
| length?: number; | ||
| }; | ||
| name?: string; | ||
| path?: string; | ||
| route?: { | ||
| path?: unknown; | ||
| }; | ||
| method?: string; | ||
| } | ||
| /** Minimal Express request/response shapes — avoids a hard dep on `node:http`. */ | ||
| export interface ExpressRequest { | ||
| method?: string; | ||
| baseUrl?: string; | ||
| originalUrl?: string; | ||
| } | ||
| export interface ExpressResponse { | ||
| once(event: string, listener: () => void): unknown; | ||
| removeListener(event: string, listener: () => void): unknown; | ||
| } | ||
| /** | ||
| * The shape orchestrion's transform attaches to the tracing-channel `context` | ||
| * object for `Layer.prototype.handle_request`/`handleRequest`: `self` is the | ||
| * Layer the method was invoked on and `arguments` are `[req, res, next]`. | ||
| * | ||
| * `_sentryCleanup` is ours: a teardown for the `res.on('finish')` listener we | ||
| * register, invoked from `beforeSpanEnd` when the span ends via `next()`. | ||
| * `_sentryStoredLayer` marks that this invocation pushed a layer path (so the | ||
| * matching pop on `asyncStart` stays symmetric). | ||
| */ | ||
| export interface HandleChannelContext { | ||
| self?: ExpressLayer; | ||
| arguments?: unknown[]; | ||
| _sentryCleanup?: () => void; | ||
| _sentryStoredLayer?: boolean; | ||
| } | ||
| /** | ||
| * The context orchestrion attaches to the `route`/`use` registration channels: | ||
| * `self` is the router the method was invoked on (its freshly-pushed layer is | ||
| * the last entry in `stack`) and `arguments` are the registration args (the | ||
| * first of which is the path pattern). | ||
| */ | ||
| export interface RegistrationChannelContext { | ||
| self?: { | ||
| stack?: ExpressLayer[]; | ||
| }; | ||
| arguments?: unknown[]; | ||
| } | ||
| type IgnoreMatcher = string | RegExp | ((name: string) => boolean); | ||
| export interface ExpressIntegrationOptions { | ||
| /** Ignore specific based on their name */ | ||
| ignoreLayers?: IgnoreMatcher[]; | ||
| /** Ignore specific layers based on their type */ | ||
| ignoreLayersType?: ExpressLayerType[]; | ||
| } | ||
| export {}; | ||
| //# sourceMappingURL=types.d.ts.map |
| import { GoogleGenAIOptions } from '@sentry/core'; | ||
| /** | ||
| * EXPERIMENTAL — orchestrion-driven Google GenAI integration. Subscribes to the | ||
| * `orchestrion:@google/genai:*` diagnostics_channels injected into the SDK's `Models` | ||
| * (`generateContent`/`generateContentStream`/`embedContent`) and `Chat` | ||
| * (`sendMessage`/`sendMessageStream`) methods, so it requires the orchestrion runtime hook or | ||
| * bundler plugin. | ||
| */ | ||
| export declare const googleGenAIChannelIntegration: (options?: GoogleGenAIOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Google_GenAI"; | ||
| }; | ||
| //# sourceMappingURL=google-genai.d.ts.map |
| export declare const ORIGIN = "auto.graphql.diagnostic_channel"; | ||
| export declare const SPAN_NAME_PARSE = "graphql.parse"; | ||
| export declare const SPAN_NAME_VALIDATE = "graphql.validate"; | ||
| export declare const SPAN_NAME_EXECUTE = "graphql.execute"; | ||
| export declare const SPAN_NAME_RESOLVE = "graphql.resolve"; | ||
| export declare const GRAPHQL_FIELD_NAME = "graphql.field.name"; | ||
| export declare const GRAPHQL_FIELD_PATH = "graphql.field.path"; | ||
| export declare const GRAPHQL_FIELD_TYPE = "graphql.field.type"; | ||
| export declare const GRAPHQL_PARENT_NAME = "graphql.parent.name"; | ||
| export declare const GRAPHQL_DATA_SYMBOL: unique symbol; | ||
| export declare const GRAPHQL_PATCHED_SYMBOL: unique symbol; | ||
| //# sourceMappingURL=constants.d.ts.map |
| export type PromiseOrValue<T> = T | Promise<T>; | ||
| export type Maybe<T> = null | undefined | T; | ||
| export interface Location { | ||
| start: number; | ||
| end: number; | ||
| startToken: Token; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface Token { | ||
| kind: string; | ||
| start: number; | ||
| end: number; | ||
| line: number; | ||
| column: number; | ||
| value: string; | ||
| prev: Token | null; | ||
| next: Token | null; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface Source { | ||
| body: string; | ||
| name: string; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface DocumentNode { | ||
| kind: string; | ||
| definitions: ReadonlyArray<DefinitionNode>; | ||
| loc?: Location; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface DefinitionNode { | ||
| kind: string; | ||
| operation?: string; | ||
| name?: { | ||
| kind: string; | ||
| value: string; | ||
| loc?: Location; | ||
| }; | ||
| loc?: Location; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface OperationDefinitionNode extends DefinitionNode { | ||
| operation: string; | ||
| name?: { | ||
| kind: string; | ||
| value: string; | ||
| loc?: Location; | ||
| }; | ||
| } | ||
| export interface ParseOptions { | ||
| noLocation?: boolean; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface ExecutionArgs { | ||
| schema: GraphQLSchema; | ||
| document: DocumentNode; | ||
| rootValue?: unknown; | ||
| contextValue?: unknown; | ||
| variableValues?: Maybe<Record<string, unknown>>; | ||
| operationName?: Maybe<string>; | ||
| fieldResolver?: Maybe<GraphQLFieldResolver>; | ||
| typeResolver?: Maybe<GraphQLTypeResolver>; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface ExecutionResult { | ||
| errors?: ReadonlyArray<GraphQLError>; | ||
| data?: Record<string, unknown> | null; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLError { | ||
| message: string; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLSchema { | ||
| getQueryType(): GraphQLObjectType | undefined | null; | ||
| getMutationType(): GraphQLObjectType | undefined | null; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLObjectType { | ||
| name: string; | ||
| getFields(): Record<string, GraphQLField | undefined>; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLField { | ||
| name: string; | ||
| type: GraphQLOutputType; | ||
| resolve?: GraphQLFieldResolver; | ||
| [key: string]: unknown; | ||
| } | ||
| export type GraphQLOutputType = GraphQLNamedOutputType | GraphQLWrappingType; | ||
| interface GraphQLNamedOutputType { | ||
| name?: string; | ||
| [key: string]: unknown; | ||
| } | ||
| interface GraphQLWrappingType { | ||
| ofType: GraphQLOutputType; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLUnionType { | ||
| name: string; | ||
| getTypes(): ReadonlyArray<GraphQLObjectType>; | ||
| [key: string]: unknown; | ||
| } | ||
| export type GraphQLType = GraphQLOutputType | GraphQLUnionType; | ||
| export type GraphQLFieldResolver<TSource = unknown, TContext = unknown, TArgs = unknown> = (source: TSource, args: TArgs, context: TContext, info: GraphQLResolveInfo) => unknown; | ||
| export type GraphQLTypeResolver<TSource = unknown, TContext = unknown> = (value: TSource, context: TContext, info: GraphQLResolveInfo, abstractType: unknown) => unknown; | ||
| export interface GraphQLResolveInfo { | ||
| fieldName: string; | ||
| fieldNodes: ReadonlyArray<{ | ||
| kind: string; | ||
| loc?: Location; | ||
| [key: string]: unknown; | ||
| }>; | ||
| returnType: { | ||
| toString(): string; | ||
| [key: string]: unknown; | ||
| }; | ||
| parentType: { | ||
| name: string; | ||
| [key: string]: unknown; | ||
| }; | ||
| path: GraphQLPath; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLPath { | ||
| prev: GraphQLPath | undefined; | ||
| key: string | number; | ||
| typename?: string | undefined; | ||
| } | ||
| export type ValidationRule = unknown; | ||
| export interface TypeInfo { | ||
| [key: string]: unknown; | ||
| } | ||
| export {}; | ||
| //# sourceMappingURL=graphql-types.d.ts.map |
| import { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber'; | ||
| /** | ||
| * EXPERIMENTAL — orchestrion-driven graphql integration for graphql v14–16 (v17 publishes native | ||
| * `diagnostics_channel` events handled by `@sentry/server-utils`'s graphql integration instead). | ||
| * | ||
| * Subscribes to the `orchestrion:graphql:{parse,validate,execute}` channels the orchestrion code | ||
| * transform injects into `graphql`'s `language/parser.js`, `validation/validate.js` and | ||
| * `execution/execute.js`, emitting spans identical to the native path. Requires the orchestrion | ||
| * runtime hook or bundler plugin — wire it up via `experimentalUseDiagnosticsChannelInjection()`. | ||
| * | ||
| * @experimental | ||
| */ | ||
| export declare const graphqlChannelIntegration: (options?: GraphqlDiagnosticChannelsOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Graphql"; | ||
| }; | ||
| /** | ||
| * The complete graphql diagnostics-channel integration: the native subscriber (graphql v17) composed | ||
| * with the orchestrion subscriber (v14–16), so opting into injection instruments every supported | ||
| * version via diagnostics channels without the OTel patcher. Reuses the OTel `Graphql` name so | ||
| * enabling injection swaps this in for it. | ||
| */ | ||
| export declare const graphqlDiagnosticsChannelIntegration: (options?: GraphqlDiagnosticChannelsOptions) => Pick<import("@sentry/core").Integration & { | ||
| name: "Graphql"; | ||
| }, Exclude<keyof (import("@sentry/core").Integration & { | ||
| name: "Graphql"; | ||
| }), "name" | "setupOnce">> & { | ||
| name: "Graphql"; | ||
| setupOnce: () => void | undefined; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
| import { DefinitionNode, DocumentNode, GraphQLFieldResolver, GraphQLObjectType, GraphqlResolvedConfig, Maybe, Patched } from './types'; | ||
| /** | ||
| * Walks the query/mutation type tree and swaps each field's `resolve` for a span-creating proxy. | ||
| * Idempotent per type via {@link GRAPHQL_PATCHED_SYMBOL}. | ||
| */ | ||
| export declare function wrapFields(type: Maybe<GraphQLObjectType & Patched>, getConfig: () => GraphqlResolvedConfig): void; | ||
| export declare function wrapFieldResolver(getConfig: () => GraphqlResolvedConfig, fieldResolver: Maybe<GraphQLFieldResolver & Patched>, isDefaultResolver?: boolean): GraphQLFieldResolver & Patched; | ||
| /** | ||
| * Returns the operation definition for `operationName` (or the first operation) from a parsed | ||
| * document, or `undefined` for schema documents / when no operation is present. | ||
| */ | ||
| export declare function getOperation(document: DocumentNode, operationName?: Maybe<string>): DefinitionNode | undefined; | ||
| //# sourceMappingURL=resolvers.d.ts.map |
| import { Span } from '@sentry/core'; | ||
| import { GraphqlResolvedConfig } from './types'; | ||
| export declare function startParseSpan(): Span; | ||
| /** `documentAST` is the 2nd argument to `validate(schema, documentAST, …)`. */ | ||
| export declare function startValidateSpan(documentAST: unknown): Span; | ||
| /** `result` is validation's return value: a (possibly empty) array of errors. */ | ||
| export declare function finalizeValidateSpan(span: Span, result: unknown): void; | ||
| /** | ||
| * Opens the execute span and, unless resolver spans are disabled, swaps the schema's field resolvers | ||
| * (and the default field resolver) for span-creating proxies — mutating the live `arguments` in place | ||
| * so the wrapped `execute` call runs with them. Always returns a span; the caller guards against | ||
| * throws (see `safe` in `index.ts`). | ||
| */ | ||
| export declare function startExecuteSpan(argsArray: unknown[], self: unknown, config: GraphqlResolvedConfig, getConfig: () => GraphqlResolvedConfig): Span; | ||
| /** `result` is the settled `ExecutionResult`; GraphQL errors surface on `result.errors`, not a throw. */ | ||
| export declare function finalizeExecuteSpan(span: Span, result: unknown): void; | ||
| //# sourceMappingURL=spans.d.ts.map |
| import { Span } from '@sentry/core'; | ||
| import { GRAPHQL_DATA_SYMBOL, GRAPHQL_PATCHED_SYMBOL } from './constants'; | ||
| import { DocumentNode } from './graphql-types'; | ||
| export * from './graphql-types'; | ||
| /** Bookkeeping we attach to `contextValue` to parent resolver spans under the execute span. */ | ||
| interface GraphQLSpanData { | ||
| source?: DocumentNode; | ||
| span: Span; | ||
| fields: Record<string, { | ||
| span: Span; | ||
| } | undefined>; | ||
| } | ||
| export interface ObjectWithGraphQLData { | ||
| [GRAPHQL_DATA_SYMBOL]?: GraphQLSpanData; | ||
| } | ||
| export interface Patched { | ||
| [GRAPHQL_PATCHED_SYMBOL]?: boolean; | ||
| } | ||
| /** Resolved integration config (defaults applied), shared by the span + resolver builders. */ | ||
| export interface GraphqlResolvedConfig { | ||
| ignoreResolveSpans: boolean; | ||
| ignoreTrivialResolveSpans: boolean; | ||
| useOperationNameForRootSpan: boolean; | ||
| } | ||
| //# sourceMappingURL=types.d.ts.map |
| import { PostgresConnectionContext, Span } from '@sentry/core'; | ||
| export interface PostgresJsChannelIntegrationOptions { | ||
| /** | ||
| * Only create spans when there's already an active parent span. Defaults to | ||
| * `true`, matching the OTel `postgresJsIntegration`. | ||
| */ | ||
| requireParentSpan?: boolean; | ||
| /** | ||
| * Hook to modify the query span before the query runs. Receives the span, the | ||
| * sanitized SQL, and (when resolvable) the connection context. | ||
| */ | ||
| requestHook?: (span: Span, sanitizedSqlQuery: string, postgresConnectionContext?: PostgresConnectionContext) => void; | ||
| } | ||
| /** | ||
| * EXPERIMENTAL — orchestrion-driven postgres.js (`postgres` v3.x) integration. | ||
| * | ||
| * Subscribes to the `orchestrion:postgres:handle` / `:connection` / `:execute` / | ||
| * `:connect` diagnostics channels injected into postgres.js' `Query.prototype.handle` | ||
| * and `Connection`/`execute`/`connect` (in `src/*` and `cjs/src/*`) and creates db | ||
| * spans matching the OTel `postgresJsIntegration`. Requires the orchestrion runtime | ||
| * hook or bundler plugin. | ||
| */ | ||
| export declare const postgresJsChannelIntegration: (options?: PostgresJsChannelIntegrationOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "PostgresJs"; | ||
| }; | ||
| //# sourceMappingURL=postgres-js.d.ts.map |
| import { Span } from '@sentry/core'; | ||
| /** Mirrors `@opentelemetry/instrumentation-redis`' response hook. Not called for failed commands. */ | ||
| export type RedisResponseHook = (span: Span, command: string, args: Array<string | Buffer>, result: unknown) => void; | ||
| export interface RedisChannelIntegrationOptions { | ||
| responseHook?: RedisResponseHook; | ||
| } | ||
| /** | ||
| * EXPERIMENTAL — orchestrion-driven redis integration for `redis` v2-v3 and | ||
| * node-redis v4/v5 `<5.12.0` (`@redis/client`). Covers single commands, `connect`, | ||
| * and multi/pipeline batches, fully replacing `@opentelemetry/instrumentation-redis`. | ||
| * Requires the orchestrion runtime hook or bundler plugin. | ||
| */ | ||
| export declare const redisChannelIntegration: (options?: RedisChannelIntegrationOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "RedisChannel"; | ||
| }; | ||
| //# sourceMappingURL=redis.d.ts.map |
| /** | ||
| * Auto-instrument the [mysql2](https://www.npmjs.com/package/mysql2) library via its native | ||
| * `node:diagnostics_channel` tracing channels (mysql2 >= 3.20.0). | ||
| * | ||
| * On older mysql2 versions the channels are never published to, so this integration is inert and | ||
| * the vendored OTel instrumentation (gated to `< 3.20.0`) handles instrumentation instead. | ||
| */ | ||
| export declare const mysql2Integration: () => import("@sentry/core").Integration & { | ||
| name: string; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
| import { TracingChannel } from 'node:diagnostics_channel'; | ||
| export declare const MYSQL2_DC_CHANNEL_QUERY = "mysql2:query"; | ||
| export declare const MYSQL2_DC_CHANNEL_EXECUTE = "mysql2:execute"; | ||
| export declare const MYSQL2_DC_CHANNEL_CONNECT = "mysql2:connect"; | ||
| export declare const MYSQL2_DC_CHANNEL_POOL_CONNECT = "mysql2:pool:connect"; | ||
| /** | ||
| * Shape of the context object mysql2 >= 3.20.0 publishes on its query/execute | ||
| * tracing channels (see mysql2 `lib/base/connection.js`). | ||
| * | ||
| * Node's `traceCallback`/`tracePromise` mutate this same object with | ||
| * `result`/`error` once the operation settles, which `bindTracingChannelToSpan` | ||
| * reads in its lifecycle handlers — hence both are declared optional here. | ||
| * | ||
| * `query` is the SQL statement. On the `query` channel mysql2 has already | ||
| * inlined `values` into it (`Connection.format`), so it carries raw user data; | ||
| * on the `execute` channel it keeps `?` placeholders. Either way we sanitize it | ||
| * before emitting `db.query.text` and never attach `values`. | ||
| */ | ||
| export interface MySQL2QueryData { | ||
| query?: string; | ||
| values?: unknown; | ||
| database?: string; | ||
| serverAddress?: string; | ||
| /** Absent for unix-socket connections, where `serverAddress` is the socket path. */ | ||
| serverPort?: number; | ||
| result?: unknown; | ||
| error?: Error; | ||
| } | ||
| /** | ||
| * Shape of the context object mysql2 >= 3.20.0 publishes on its | ||
| * `connect`/`pool:connect` channels. | ||
| */ | ||
| export interface MySQL2ConnectData { | ||
| database?: string; | ||
| serverAddress?: string; | ||
| serverPort?: number; | ||
| user?: string; | ||
| result?: unknown; | ||
| error?: Error; | ||
| } | ||
| /** | ||
| * Platform-provided factory that creates a native tracing channel for the given name. The | ||
| * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`, | ||
| * which propagates the active span through the runtime's async context. | ||
| * | ||
| * Node passes `node:diagnostics_channel`'s `tracingChannel` directly. | ||
| */ | ||
| export type MySQL2TracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>; | ||
| /** | ||
| * Subscribe Sentry span handlers to mysql2's diagnostics-channel events | ||
| * (`mysql2:query`, `:execute`, `:connect`, `:pool:connect`), published by | ||
| * mysql2 >= 3.20.0. | ||
| * | ||
| * On older mysql2 versions the channels are never published to, so the | ||
| * subscribers are inert — there is no double-instrumentation against the | ||
| * vendored OTel patcher, which is gated to `< 3.20.0`. | ||
| */ | ||
| export declare function subscribeMysql2DiagnosticChannels(tracingChannel: MySQL2TracingChannelFactory): void; | ||
| //# sourceMappingURL=mysql2-dc-subscriber.d.ts.map |
| export declare const amqplibConfig: ({ | ||
| channelName: string; | ||
| module: { | ||
| filePath: string; | ||
| name: "amqplib"; | ||
| versionRange: ">=0.5.5 <2"; | ||
| }; | ||
| functionQuery: { | ||
| className: string; | ||
| methodName: string; | ||
| kind: "Sync"; | ||
| functionName?: undefined; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| filePath: string; | ||
| name: "amqplib"; | ||
| versionRange: ">=0.5.5 <2"; | ||
| }; | ||
| functionQuery: { | ||
| className: string; | ||
| methodName: string; | ||
| kind: "Callback"; | ||
| functionName?: undefined; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| filePath: string; | ||
| name: "amqplib"; | ||
| versionRange: ">=0.5.5 <2"; | ||
| }; | ||
| functionQuery: { | ||
| className: string; | ||
| methodName: string; | ||
| kind: "Async"; | ||
| functionName?: undefined; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| filePath: string; | ||
| name: "amqplib"; | ||
| versionRange: ">=0.5.5 <2"; | ||
| }; | ||
| functionQuery: { | ||
| functionName: string; | ||
| kind: "Callback"; | ||
| className?: undefined; | ||
| methodName?: undefined; | ||
| }; | ||
| })[]; | ||
| export declare const amqplibChannels: { | ||
| readonly AMQPLIB_PUBLISH: "orchestrion:amqplib:publish"; | ||
| readonly AMQPLIB_CONFIRM_PUBLISH: "orchestrion:amqplib:confirmPublish"; | ||
| readonly AMQPLIB_CONSUME: "orchestrion:amqplib:consume"; | ||
| readonly AMQPLIB_DISPATCH: "orchestrion:amqplib:dispatch"; | ||
| readonly AMQPLIB_ACK: "orchestrion:amqplib:ack"; | ||
| readonly AMQPLIB_NACK: "orchestrion:amqplib:nack"; | ||
| readonly AMQPLIB_REJECT: "orchestrion:amqplib:reject"; | ||
| readonly AMQPLIB_ACK_ALL: "orchestrion:amqplib:ackAll"; | ||
| readonly AMQPLIB_NACK_ALL: "orchestrion:amqplib:nackAll"; | ||
| readonly AMQPLIB_CONNECT: "orchestrion:amqplib:connect"; | ||
| }; | ||
| //# sourceMappingURL=amqplib.d.ts.map |
| export declare const expressConfig: ({ | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| expressionName: string; | ||
| kind: "Callback"; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| expressionName: string; | ||
| kind: "Sync"; | ||
| }; | ||
| })[]; | ||
| export declare const expressChannels: { | ||
| readonly EXPRESS_HANDLE: "orchestrion:express:handle"; | ||
| readonly ROUTER_HANDLE: "orchestrion:router:handle"; | ||
| readonly EXPRESS_ROUTE: "orchestrion:express:route"; | ||
| readonly EXPRESS_USE: "orchestrion:express:use"; | ||
| readonly ROUTER_ROUTE: "orchestrion:router:route"; | ||
| readonly ROUTER_USE: "orchestrion:router:use"; | ||
| }; | ||
| //# sourceMappingURL=express.d.ts.map |
| export declare const googleGenAiConfig: ({ | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| expressionName: "generateContent" | "generateContentStream"; | ||
| kind: "Auto"; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| className: string; | ||
| methodName: string; | ||
| kind: "Auto"; | ||
| }; | ||
| })[]; | ||
| export declare const googleGenAiChannels: { | ||
| readonly GOOGLE_GENAI_GENERATE_CONTENT: "orchestrion:@google/genai:generate-content"; | ||
| readonly GOOGLE_GENAI_EMBED_CONTENT: "orchestrion:@google/genai:embed-content"; | ||
| readonly GOOGLE_GENAI_CHAT: "orchestrion:@google/genai:chat"; | ||
| }; | ||
| //# sourceMappingURL=google-genai.d.ts.map |
| export declare const graphqlConfig: ({ | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| functionName: string; | ||
| kind: "Sync"; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| functionName: string; | ||
| kind: "Auto"; | ||
| }; | ||
| })[]; | ||
| export declare const graphqlChannels: { | ||
| readonly GRAPHQL_PARSE: "orchestrion:graphql:parse"; | ||
| readonly GRAPHQL_VALIDATE: "orchestrion:graphql:validate"; | ||
| readonly GRAPHQL_EXECUTE: "orchestrion:graphql:execute"; | ||
| }; | ||
| //# sourceMappingURL=graphql.d.ts.map |
| import { InstrumentationConfig } from '@apm-js-collab/code-transformer'; | ||
| export declare const postgresJsConfig: InstrumentationConfig[]; | ||
| export declare const postgresJsChannels: { | ||
| readonly POSTGRESJS_HANDLE: "orchestrion:postgres:handle"; | ||
| readonly POSTGRESJS_CONNECTION: "orchestrion:postgres:connection"; | ||
| readonly POSTGRESJS_EXECUTE: "orchestrion:postgres:execute"; | ||
| readonly POSTGRESJS_CONNECT: "orchestrion:postgres:connect"; | ||
| }; | ||
| //# sourceMappingURL=postgres.d.ts.map |
| export declare const redisConfig: ({ | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| expressionName: string; | ||
| kind: "Sync"; | ||
| className?: undefined; | ||
| methodName?: undefined; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| className: string; | ||
| methodName: string; | ||
| kind: "Async"; | ||
| expressionName?: undefined; | ||
| }; | ||
| })[]; | ||
| export declare const redisChannels: { | ||
| readonly REDIS_COMMAND: "orchestrion:redis:command"; | ||
| readonly NODE_REDIS_COMMAND: "orchestrion:@redis/client:command"; | ||
| readonly NODE_REDIS_EXECUTOR: "orchestrion:@redis/client:executor"; | ||
| readonly NODE_REDIS_CONNECT: "orchestrion:@redis/client:connect"; | ||
| readonly NODE_REDIS_MULTI: "orchestrion:@redis/client:multi"; | ||
| readonly NODE_REDIS_PIPELINE: "orchestrion:@redis/client:pipeline"; | ||
| readonly NODE_REDIS_BATCH: "orchestrion:@redis/client:batch"; | ||
| }; | ||
| //# sourceMappingURL=redis.d.ts.map |
| import { RedisDiagnosticChannelResponseHook } from './redis-dc-subscriber'; | ||
| /** Options controlling the redis diagnostics-channel subscription. */ | ||
| export interface RedisDiagnosticChannelsOptions { | ||
| /** | ||
| * Optional hook invoked once the redis command response arrives. Useful for attaching | ||
| * response-derived attributes (e.g. cache hit/miss, payload size). | ||
| */ | ||
| responseHook?: RedisDiagnosticChannelResponseHook; | ||
| } | ||
| /** | ||
| * Auto-instrument the [redis](https://www.npmjs.com/package/redis) and | ||
| * [ioredis](https://www.npmjs.com/package/ioredis) libraries via their native | ||
| * `node:diagnostics_channel` tracing channels (node-redis >= 5.12.0, ioredis >= 5.11.0). | ||
| */ | ||
| export declare const redisIntegration: (options?: RedisDiagnosticChannelsOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: string; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
| /** Shared, state-free helpers for the Vercel AI (`ai`) channel subscribers, plus the streamed model-call tap. */ | ||
| /** Narrow to a string, or `undefined` for anything else. */ | ||
| export declare function asString(value: unknown): string | undefined; | ||
| /** Narrow to a finite number, or `undefined` for anything else (including `NaN`). */ | ||
| export declare function asNumber(value: unknown): number | undefined; | ||
| /** Add two optional numbers, treating a missing operand as `0` but returning `undefined` when both are absent. */ | ||
| export declare function sum(a: number | undefined, b: number | undefined): number | undefined; | ||
| /** Stringify a value, passing strings through and falling back to a placeholder on circular/unserializable input. */ | ||
| export declare function safeStringify(value: unknown): string; | ||
| /** The aggregate handed back once a streamed model call finishes, in the shape `enrichSpanOnEnd` expects. */ | ||
| export interface StreamedModelCallResult { | ||
| text?: string; | ||
| toolCalls: Array<Record<string, unknown>>; | ||
| usage?: unknown; | ||
| finishReason?: unknown; | ||
| responseId?: string; | ||
| responseModel?: string; | ||
| providerMetadata?: unknown; | ||
| } | ||
| /** A minimal structural check — the streamed model call exposes a web `ReadableStream` on `result.stream`. */ | ||
| export declare function isReadableStream(value: unknown): value is ReadableStream<unknown>; | ||
| /** | ||
| * Wrap a streamed model call's `ReadableStream` so its chunks are observed as the SDK consumes them, | ||
| * without altering what the SDK sees. Returns a replacement stream to swap onto `result.stream`. | ||
| * | ||
| * `onFinal` runs exactly once when the stream drains cleanly; `onError` runs exactly once if it errors | ||
| * or is cancelled. Reading one source chunk per `pull` preserves the SDK's backpressure. The | ||
| * `try/catch` around every read guarantees the owning span is always ended — a leaked open span on a | ||
| * mid-stream failure would be worse than a slightly-less-enriched one. | ||
| */ | ||
| export declare function tapModelCallStream(stream: ReadableStream<unknown>, onFinal: (result: StreamedModelCallResult) => void, onError: (error: unknown) => void): ReadableStream<unknown>; | ||
| //# sourceMappingURL=util.d.ts.map |
| import { VercelAiChannelOptions, VercelAiTracingChannelFactory } from './vercel-ai-dc-subscriber'; | ||
| /** | ||
| * Subscribe the v6 orchestrion channel adapter. Safe to always call: inert on | ||
| * `ai` >= 7 (those channels are never published) and when orchestrion injection | ||
| * isn't active. Idempotent. | ||
| * | ||
| * `tracingChannel` is the platform-provided factory (the same one passed to | ||
| * `subscribeVercelAiTracingChannel`); `options` pins the recording settings at | ||
| * subscribe time so we never look the integration up per event. | ||
| */ | ||
| export declare function subscribeVercelAiOrchestrionChannels(tracingChannel: VercelAiTracingChannelFactory, options?: VercelAiChannelOptions): void; | ||
| //# sourceMappingURL=vercel-ai-orchestrion-subscriber.d.ts.map |
| import type { TracingChannel } from 'node:diagnostics_channel'; | ||
| import type { GraphqlDocumentNode } from './utils'; | ||
| export declare const GRAPHQL_DC_CHANNEL_PARSE = "graphql:parse"; | ||
| export declare const GRAPHQL_DC_CHANNEL_VALIDATE = "graphql:validate"; | ||
| export declare const GRAPHQL_DC_CHANNEL_EXECUTE = "graphql:execute"; | ||
| export declare const GRAPHQL_DC_CHANNEL_SUBSCRIBE = "graphql:subscribe"; | ||
| export declare const GRAPHQL_DC_CHANNEL_RESOLVE = "graphql:resolve"; | ||
| /** Context published on the sync-only `graphql:parse` channel. */ | ||
| export interface GraphqlParseData { | ||
| source: string | { | ||
| body?: string; | ||
| }; | ||
| result?: GraphqlDocumentNode; | ||
| error?: unknown; | ||
| } | ||
| /** Context published on the sync-only `graphql:validate` channel. */ | ||
| export interface GraphqlValidateData { | ||
| document: GraphqlDocumentNode; | ||
| /** Validation errors returned by validation; an empty array means the document is valid. */ | ||
| result?: ReadonlyArray<unknown>; | ||
| error?: unknown; | ||
| } | ||
| /** | ||
| * Context published on the `graphql:execute` and `graphql:subscribe` channels. | ||
| * | ||
| * `result` carries an `ExecutionResult` (or, for subscriptions, an async generator); GraphQL errors | ||
| * collected during execution surface on `result.errors` rather than as the channel's `error` | ||
| * lifecycle event, which only fires on an abrupt throw. | ||
| */ | ||
| export interface GraphqlOperationData { | ||
| document: GraphqlDocumentNode; | ||
| operationName?: string; | ||
| operationType?: string; | ||
| result?: unknown; | ||
| error?: unknown; | ||
| } | ||
| /** | ||
| * Context published on the per-field `graphql:resolve` channel. | ||
| * | ||
| * A resolver throw or rejection publishes the `error` lifecycle event here; the same failure also | ||
| * surfaces in the enclosing execution result. | ||
| */ | ||
| export interface GraphqlResolveData { | ||
| fieldName: string; | ||
| parentType: string; | ||
| fieldType: string; | ||
| fieldPath: string; | ||
| /** Whether the field is handled by graphql's default property resolver (vs. a user resolver). */ | ||
| isDefaultResolver: boolean; | ||
| alias?: string; | ||
| args?: unknown; | ||
| result?: unknown; | ||
| error?: unknown; | ||
| } | ||
| /** Options controlling which graphql channels the subscriber emits spans for. */ | ||
| export interface GraphqlDiagnosticChannelsOptions { | ||
| /** | ||
| * Do not create spans for resolvers. Resolver spans are per-field and can be very high volume. | ||
| * Defaults to `true`. | ||
| */ | ||
| ignoreResolveSpans?: boolean; | ||
| /** | ||
| * When resolver spans are enabled, do not create them for graphql's default property resolver | ||
| * (fields without a user-defined resolver), which are rarely interesting. Defaults to `true`. | ||
| */ | ||
| ignoreTrivialResolveSpans?: boolean; | ||
| /** | ||
| * Rename the enclosing root span to include the operation name(s), e.g. | ||
| * `GET /graphql` -> `GET /graphql (query GetUser)`. Defaults to `true`. | ||
| */ | ||
| useOperationNameForRootSpan?: boolean; | ||
| } | ||
| /** | ||
| * Platform-provided factory that creates a native tracing channel for the given name. The | ||
| * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`, | ||
| * which propagates the active span through the runtime's async context. | ||
| * | ||
| * Node passes `node:diagnostics_channel`'s `tracingChannel` directly. | ||
| */ | ||
| export type GraphqlTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>; | ||
| /** | ||
| * Subscribe Sentry span handlers to graphql's diagnostics-channel events | ||
| * (`graphql:parse`, `:validate`, `:execute`, `:subscribe`), published by graphql >= 17.0.0. | ||
| * | ||
| * On older graphql versions the channels are never published to, so the subscribers are inert — | ||
| * there is no double-instrumentation against the vendored OTel patcher, which is gated to `< 17`. | ||
| * | ||
| * The per-field `graphql:resolve` channel is only subscribed when `ignoreResolveSpans` is `false`: | ||
| * resolver spans are per-field and can be extremely high-volume, so they are off by default (matching | ||
| * the legacy OTel path). When enabled, `ignoreTrivialResolveSpans` (default `true`) additionally skips | ||
| * graphql's default property resolver. | ||
| */ | ||
| export declare function subscribeGraphqlDiagnosticChannels(tracingChannel: GraphqlTracingChannelFactory, options?: GraphqlDiagnosticChannelsOptions): void; | ||
| //# sourceMappingURL=graphql-dc-subscriber.d.ts.map |
| {"version":3,"file":"graphql-dc-subscriber.d.ts","sourceRoot":"","sources":["../../../src/graphql/graphql-dc-subscriber.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAU/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAMnD,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,2BAA2B,qBAAqB,CAAC;AAC9D,eAAO,MAAM,0BAA0B,oBAAoB,CAAC;AAC5D,eAAO,MAAM,4BAA4B,sBAAsB,CAAC;AAChE,eAAO,MAAM,0BAA0B,oBAAoB,CAAC;AAiB5D,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qEAAqE;AACrE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,4FAA4F;IAC5F,MAAM,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,iGAAiG;IACjG,iBAAiB,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,iFAAiF;AACjF,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;OAGG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IAEpC;;;OAGG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEpG;;;;;;;;;;;GAWG;AACH,wBAAgB,kCAAkC,CAChD,cAAc,EAAE,4BAA4B,EAC5C,OAAO,GAAE,gCAAqC,GAC7C,IAAI,CAaN"} |
| import { type GraphqlDiagnosticChannelsOptions } from './graphql-dc-subscriber'; | ||
| /** | ||
| * Auto-instrument the [graphql](https://www.npmjs.com/package/graphql) library via its native | ||
| * `node:diagnostics_channel` tracing channels (graphql >= 17). | ||
| * | ||
| * On older graphql versions the channels are never published to, so this integration is inert and | ||
| * the vendored OTel instrumentation (gated to `< 17`) handles instrumentation instead. | ||
| */ | ||
| export declare const graphqlIntegration: (options?: GraphqlDiagnosticChannelsOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Graphql"; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/graphql/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,gCAAgC,EAAsC,MAAM,yBAAyB,CAAC;AAoBpH;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB;;CAAyC,CAAC"} |
| import type { Span } from '@sentry/core'; | ||
| /** Minimal shape of a graphql-js lexer token, enough to locate literal spans for redaction. */ | ||
| interface GraphqlToken { | ||
| kind: string; | ||
| start: number; | ||
| end: number; | ||
| next?: GraphqlToken | null; | ||
| } | ||
| /** Minimal shape of a parsed graphql-js `DocumentNode`, enough to read its source and tokens. */ | ||
| export interface GraphqlDocumentNode { | ||
| loc?: { | ||
| startToken?: GraphqlToken; | ||
| source?: { | ||
| body?: string; | ||
| }; | ||
| }; | ||
| } | ||
| /** | ||
| * Rename the enclosing root span to include the operation name(s), e.g. `GET /graphql (query GetUser)`. | ||
| * Mirrors the legacy OTel `useOperationNameForRootSpan` behavior; `parseSpanDescription` reads the same | ||
| * `sentry.graphql.operation` attribute on the OTel export path. | ||
| */ | ||
| export declare function renameRootSpanWithOperation(span: Span, operationType: string, operationName?: string): void; | ||
| /** | ||
| * Span name follows the GraphQL semantic conventions: `<operation.type> <operation.name>` when both | ||
| * are available, `<operation.type>` when only the type is, otherwise a static fallback. | ||
| */ | ||
| export declare function getOperationSpanName(operationType: string | undefined, operationName: string | undefined, fallbackName: string): string; | ||
| /** Whether a graphql execution result carries GraphQL errors (returned on `result.errors`). */ | ||
| export declare function hasResultErrors(result: unknown): boolean; | ||
| /** | ||
| * Serialize a parsed document into `graphql.document` while redacting every literal argument value: | ||
| * the original source text is preserved verbatim except that string/number literal spans are | ||
| * replaced (`"foo"` -> `"*"`, `42` -> `*`). graphql does not sanitize its channel payload, so this | ||
| * prevents raw inline values (potential PII) from leaving the process. Variable values are never | ||
| * included. Returns `undefined` (rather than throwing) on anything it cannot serialize. | ||
| */ | ||
| export declare function redactGraphqlDocument(document: GraphqlDocumentNode | undefined): string | undefined; | ||
| export {}; | ||
| //# sourceMappingURL=utils.d.ts.map |
| {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/graphql/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAWzC,+FAA+F;AAC/F,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CAC5B;AAED,iGAAiG;AACjG,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE;QACJ,UAAU,CAAC,EAAE,YAAY,CAAC;QAC1B,MAAM,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC5B,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CA+B3G;AAiBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,YAAY,EAAE,MAAM,GACnB,MAAM,CASR;AAED,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAQxD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CA6BnG"} |
| /** | ||
| * EXPERIMENTAL: orchestrion-driven `amqplib` integration. | ||
| * | ||
| * Subscribes to the `orchestrion:amqplib:*` diagnostics_channels that the orchestrion code transform | ||
| * injects into `amqplib`'s channel/connection methods. Requires the orchestrion runtime hook or | ||
| * bundler plugin to be active. | ||
| */ | ||
| export declare const amqplibChannelIntegration: () => import("@sentry/core").Integration & { | ||
| name: "Amqplib"; | ||
| }; | ||
| //# sourceMappingURL=amqplib.d.ts.map |
| {"version":3,"file":"amqplib.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/amqplib.ts"],"names":[],"mappings":"AAqoBA;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB;;CAAgD,CAAC"} |
| import type { ExpressIntegrationOptions } from './types'; | ||
| /** | ||
| * EXPERIMENTAL — orchestrion-driven Express integration. | ||
| * | ||
| * Subscribes to the `orchestrion:express:handle` (Express v4) and | ||
| * `orchestrion:router:handle` (Express v5, via the `router` package) | ||
| * diagnostics_channels that the orchestrion code transform injects into the | ||
| * routing layer's request handler (`Layer.prototype.handle_request` / | ||
| * `handleRequest`). One span is opened per layer invocation — producing the | ||
| * same spans as the OTel Express instrumentation. | ||
| * | ||
| * Requires the orchestrion runtime hook or bundler plugin to be active — wire | ||
| * that up via `experimentalUseDiagnosticsChannelInjection()`. | ||
| */ | ||
| export declare const expressChannelIntegration: (options?: ExpressIntegrationOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Express"; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/express/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAuBzD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB;;CAAgD,CAAC"} |
| import type * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import type { ExpressIntegrationOptions } from './types'; | ||
| export declare function instrumentExpress(options: ExpressIntegrationOptions, tracingChannel: typeof diagnosticsChannel.tracingChannel): void; | ||
| //# sourceMappingURL=instrumentation.d.ts.map |
| {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/express/instrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,kBAAkB,MAAM,0BAA0B,CAAC;AA2BpE,OAAO,KAAK,EACV,yBAAyB,EAO1B,MAAM,SAAS,CAAC;AAcjB,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,yBAAyB,EAClC,cAAc,EAAE,OAAO,kBAAkB,CAAC,cAAc,GACvD,IAAI,CAgDN"} |
| import type { ExpressLayer, ExpressRequest } from './types'; | ||
| /** Record the path pattern a layer was registered with. */ | ||
| export declare function setLayerRegisteredPath(layer: ExpressLayer, path: string | undefined): void; | ||
| /** Read the path pattern a layer was registered with, if any. */ | ||
| export declare function getLayerRegisteredPath(layer: ExpressLayer): string | undefined; | ||
| /** Push a layer's registered path onto the request's chain. */ | ||
| export declare function pushLayerPath(req: ExpressRequest, path: string): void; | ||
| /** Pop the most recently pushed layer path off the request's chain. */ | ||
| export declare function popLayerPath(req: ExpressRequest): void; | ||
| /** | ||
| * The path pattern a `route`/`use` call registered, derived from its arguments. | ||
| * A leading string/RegExp/number path becomes the pattern (arrays are joined | ||
| * with `,`); a bare handler function yields `undefined`. Kept in sync with | ||
| * `@sentry/core`'s Express `getLayerPath`. | ||
| */ | ||
| export declare function getLayerPath(args: unknown[]): string | undefined; | ||
| /** | ||
| * Concatenate the stored layer paths into the full route pattern (parameters | ||
| * preserved), e.g. `/api/:version/user`. Mirrors `@sentry/core`. | ||
| */ | ||
| export declare function getConstructedRoute(req: ExpressRequest): string; | ||
| /** | ||
| * Validate the constructed route against the request URL, returning it only | ||
| * when it plausibly corresponds to a real match (otherwise `undefined`). Mirrors | ||
| * `@sentry/core`'s `getActualMatchedRoute` — used for the `http.route` attribute. | ||
| */ | ||
| export declare function getActualMatchedRoute(req: ExpressRequest, constructedRoute: string): string | undefined; | ||
| //# sourceMappingURL=route.d.ts.map |
| {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/express/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAc5D,2DAA2D;AAC3D,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAE1F;AAED,iEAAiE;AACjE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAE9E;AAoBD,+DAA+D;AAC/D,wBAAgB,aAAa,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAErE;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,SAAS,CAMhE;AAUD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAY/D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAoCvG"} |
| export type ExpressLayerType = 'router' | 'middleware' | 'request_handler'; | ||
| /** | ||
| * The subset of an Express routing `Layer` we read at handle time. `handle` is | ||
| * the user's middleware/handler; `route` is only set on route-dispatch layers | ||
| * (and carries the parameterized path). | ||
| */ | ||
| export interface ExpressLayer { | ||
| handle?: { | ||
| length?: number; | ||
| }; | ||
| name?: string; | ||
| path?: string; | ||
| route?: { | ||
| path?: unknown; | ||
| }; | ||
| method?: string; | ||
| } | ||
| /** Minimal Express request/response shapes — avoids a hard dep on `node:http`. */ | ||
| export interface ExpressRequest { | ||
| method?: string; | ||
| baseUrl?: string; | ||
| originalUrl?: string; | ||
| } | ||
| export interface ExpressResponse { | ||
| once(event: string, listener: () => void): unknown; | ||
| removeListener(event: string, listener: () => void): unknown; | ||
| } | ||
| /** | ||
| * The shape orchestrion's transform attaches to the tracing-channel `context` | ||
| * object for `Layer.prototype.handle_request`/`handleRequest`: `self` is the | ||
| * Layer the method was invoked on and `arguments` are `[req, res, next]`. | ||
| * | ||
| * `_sentryCleanup` is ours: a teardown for the `res.on('finish')` listener we | ||
| * register, invoked from `beforeSpanEnd` when the span ends via `next()`. | ||
| * `_sentryStoredLayer` marks that this invocation pushed a layer path (so the | ||
| * matching pop on `asyncStart` stays symmetric). | ||
| */ | ||
| export interface HandleChannelContext { | ||
| self?: ExpressLayer; | ||
| arguments?: unknown[]; | ||
| _sentryCleanup?: () => void; | ||
| _sentryStoredLayer?: boolean; | ||
| } | ||
| /** | ||
| * The context orchestrion attaches to the `route`/`use` registration channels: | ||
| * `self` is the router the method was invoked on (its freshly-pushed layer is | ||
| * the last entry in `stack`) and `arguments` are the registration args (the | ||
| * first of which is the path pattern). | ||
| */ | ||
| export interface RegistrationChannelContext { | ||
| self?: { | ||
| stack?: ExpressLayer[]; | ||
| }; | ||
| arguments?: unknown[]; | ||
| } | ||
| type IgnoreMatcher = string | RegExp | ((name: string) => boolean); | ||
| export interface ExpressIntegrationOptions { | ||
| /** Ignore specific based on their name */ | ||
| ignoreLayers?: IgnoreMatcher[]; | ||
| /** Ignore specific layers based on their type */ | ||
| ignoreLayersType?: ExpressLayerType[]; | ||
| } | ||
| export {}; | ||
| //# sourceMappingURL=types.d.ts.map |
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/express/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,YAAY,GAAG,iBAAiB,CAAC;AAE3E;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAG3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AACD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;IACnD,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CAC9D;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACzC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IAClC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;CACvB;AAED,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;AACnE,MAAM,WAAW,yBAAyB;IACxC,0CAA0C;IAC1C,YAAY,CAAC,EAAE,aAAa,EAAE,CAAC;IAC/B,iDAAiD;IACjD,gBAAgB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACvC"} |
| import type { GoogleGenAIOptions } from '@sentry/core'; | ||
| /** | ||
| * EXPERIMENTAL — orchestrion-driven Google GenAI integration. Subscribes to the | ||
| * `orchestrion:@google/genai:*` diagnostics_channels injected into the SDK's `Models` | ||
| * (`generateContent`/`generateContentStream`/`embedContent`) and `Chat` | ||
| * (`sendMessage`/`sendMessageStream`) methods, so it requires the orchestrion runtime hook or | ||
| * bundler plugin. | ||
| */ | ||
| export declare const googleGenAIChannelIntegration: (options?: GoogleGenAIOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Google_GenAI"; | ||
| }; | ||
| //# sourceMappingURL=google-genai.d.ts.map |
| {"version":3,"file":"google-genai.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/google-genai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAA4C,MAAM,cAAc,CAAC;AAiKjG;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B;;CAAoD,CAAC"} |
| export declare const ORIGIN = "auto.graphql.diagnostic_channel"; | ||
| export declare const SPAN_NAME_PARSE = "graphql.parse"; | ||
| export declare const SPAN_NAME_VALIDATE = "graphql.validate"; | ||
| export declare const SPAN_NAME_EXECUTE = "graphql.execute"; | ||
| export declare const SPAN_NAME_RESOLVE = "graphql.resolve"; | ||
| export declare const GRAPHQL_FIELD_NAME = "graphql.field.name"; | ||
| export declare const GRAPHQL_FIELD_PATH = "graphql.field.path"; | ||
| export declare const GRAPHQL_FIELD_TYPE = "graphql.field.type"; | ||
| export declare const GRAPHQL_PARENT_NAME = "graphql.parent.name"; | ||
| export declare const GRAPHQL_DATA_SYMBOL: unique symbol; | ||
| export declare const GRAPHQL_PATCHED_SYMBOL: unique symbol; | ||
| //# sourceMappingURL=constants.d.ts.map |
| {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/graphql/constants.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,MAAM,oCAAoC,CAAC;AAExD,eAAO,MAAM,eAAe,kBAAkB,CAAC;AAC/C,eAAO,MAAM,kBAAkB,qBAAqB,CAAC;AACrD,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AACnD,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAGnD,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AACvD,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AACvD,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AACvD,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAKzD,eAAO,MAAM,mBAAmB,eAA2C,CAAC;AAC5E,eAAO,MAAM,sBAAsB,eAAsC,CAAC"} |
| export type PromiseOrValue<T> = T | Promise<T>; | ||
| export type Maybe<T> = null | undefined | T; | ||
| export interface Location { | ||
| start: number; | ||
| end: number; | ||
| startToken: Token; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface Token { | ||
| kind: string; | ||
| start: number; | ||
| end: number; | ||
| line: number; | ||
| column: number; | ||
| value: string; | ||
| prev: Token | null; | ||
| next: Token | null; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface Source { | ||
| body: string; | ||
| name: string; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface DocumentNode { | ||
| kind: string; | ||
| definitions: ReadonlyArray<DefinitionNode>; | ||
| loc?: Location; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface DefinitionNode { | ||
| kind: string; | ||
| operation?: string; | ||
| name?: { | ||
| kind: string; | ||
| value: string; | ||
| loc?: Location; | ||
| }; | ||
| loc?: Location; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface OperationDefinitionNode extends DefinitionNode { | ||
| operation: string; | ||
| name?: { | ||
| kind: string; | ||
| value: string; | ||
| loc?: Location; | ||
| }; | ||
| } | ||
| export interface ParseOptions { | ||
| noLocation?: boolean; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface ExecutionArgs { | ||
| schema: GraphQLSchema; | ||
| document: DocumentNode; | ||
| rootValue?: unknown; | ||
| contextValue?: unknown; | ||
| variableValues?: Maybe<Record<string, unknown>>; | ||
| operationName?: Maybe<string>; | ||
| fieldResolver?: Maybe<GraphQLFieldResolver>; | ||
| typeResolver?: Maybe<GraphQLTypeResolver>; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface ExecutionResult { | ||
| errors?: ReadonlyArray<GraphQLError>; | ||
| data?: Record<string, unknown> | null; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLError { | ||
| message: string; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLSchema { | ||
| getQueryType(): GraphQLObjectType | undefined | null; | ||
| getMutationType(): GraphQLObjectType | undefined | null; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLObjectType { | ||
| name: string; | ||
| getFields(): Record<string, GraphQLField | undefined>; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLField { | ||
| name: string; | ||
| type: GraphQLOutputType; | ||
| resolve?: GraphQLFieldResolver; | ||
| [key: string]: unknown; | ||
| } | ||
| export type GraphQLOutputType = GraphQLNamedOutputType | GraphQLWrappingType; | ||
| interface GraphQLNamedOutputType { | ||
| name?: string; | ||
| [key: string]: unknown; | ||
| } | ||
| interface GraphQLWrappingType { | ||
| ofType: GraphQLOutputType; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLUnionType { | ||
| name: string; | ||
| getTypes(): ReadonlyArray<GraphQLObjectType>; | ||
| [key: string]: unknown; | ||
| } | ||
| export type GraphQLType = GraphQLOutputType | GraphQLUnionType; | ||
| export type GraphQLFieldResolver<TSource = unknown, TContext = unknown, TArgs = unknown> = (source: TSource, args: TArgs, context: TContext, info: GraphQLResolveInfo) => unknown; | ||
| export type GraphQLTypeResolver<TSource = unknown, TContext = unknown> = (value: TSource, context: TContext, info: GraphQLResolveInfo, abstractType: unknown) => unknown; | ||
| export interface GraphQLResolveInfo { | ||
| fieldName: string; | ||
| fieldNodes: ReadonlyArray<{ | ||
| kind: string; | ||
| loc?: Location; | ||
| [key: string]: unknown; | ||
| }>; | ||
| returnType: { | ||
| toString(): string; | ||
| [key: string]: unknown; | ||
| }; | ||
| parentType: { | ||
| name: string; | ||
| [key: string]: unknown; | ||
| }; | ||
| path: GraphQLPath; | ||
| [key: string]: unknown; | ||
| } | ||
| export interface GraphQLPath { | ||
| prev: GraphQLPath | undefined; | ||
| key: string | number; | ||
| typename?: string | undefined; | ||
| } | ||
| export type ValidationRule = unknown; | ||
| export interface TypeInfo { | ||
| [key: string]: unknown; | ||
| } | ||
| export {}; | ||
| //# sourceMappingURL=graphql-types.d.ts.map |
| {"version":3,"file":"graphql-types.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/graphql/graphql-types.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAC/C,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,IAAI,GAAG,SAAS,GAAG,CAAC,CAAC;AAE5C,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,KAAK,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAC3C,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,QAAQ,CAAA;KAAE,CAAC;IACvD,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,uBAAwB,SAAQ,cAAc;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,QAAQ,CAAA;KAAE,CAAC;CACxD;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,aAAa,CAAC,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC5C,YAAY,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAC1C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,IAAI,iBAAiB,GAAG,SAAS,GAAG,IAAI,CAAC;IACrD,eAAe,IAAI,iBAAiB,GAAG,SAAS,GAAG,IAAI,CAAC;IACxD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,CAAC,CAAC;IACtD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,GAAG,mBAAmB,CAAC;AAE7E,UAAU,sBAAsB;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,UAAU,mBAAmB;IAC3B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,IAAI,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAC7C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAE/D,MAAM,MAAM,oBAAoB,CAAC,OAAO,GAAG,OAAO,EAAE,QAAQ,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,IAAI,CACzF,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,kBAAkB,KACrB,OAAO,CAAC;AAEb,MAAM,MAAM,mBAAmB,CAAC,OAAO,GAAG,OAAO,EAAE,QAAQ,GAAG,OAAO,IAAI,CACvE,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,kBAAkB,EACxB,YAAY,EAAE,OAAO,KAClB,OAAO,CAAC;AAEb,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,QAAQ,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;IACpF,UAAU,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC3D,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACrD,IAAI,EAAE,WAAW,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,GAAG,SAAS,CAAC;IAC9B,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC;AAErC,MAAM,WAAW,QAAQ;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"} |
| import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber'; | ||
| /** | ||
| * EXPERIMENTAL — orchestrion-driven graphql integration for graphql v14–16 (v17 publishes native | ||
| * `diagnostics_channel` events handled by `@sentry/server-utils`'s graphql integration instead). | ||
| * | ||
| * Subscribes to the `orchestrion:graphql:{parse,validate,execute}` channels the orchestrion code | ||
| * transform injects into `graphql`'s `language/parser.js`, `validation/validate.js` and | ||
| * `execution/execute.js`, emitting spans identical to the native path. Requires the orchestrion | ||
| * runtime hook or bundler plugin — wire it up via `experimentalUseDiagnosticsChannelInjection()`. | ||
| * | ||
| * @experimental | ||
| */ | ||
| export declare const graphqlChannelIntegration: (options?: GraphqlDiagnosticChannelsOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Graphql"; | ||
| }; | ||
| /** | ||
| * The complete graphql diagnostics-channel integration: the native subscriber (graphql v17) composed | ||
| * with the orchestrion subscriber (v14–16), so opting into injection instruments every supported | ||
| * version via diagnostics channels without the OTel patcher. Reuses the OTel `Graphql` name so | ||
| * enabling injection swaps this in for it. | ||
| */ | ||
| export declare const graphqlDiagnosticsChannelIntegration: (options?: GraphqlDiagnosticChannelsOptions) => Omit<import("@sentry/core").Integration & { | ||
| name: "Graphql"; | ||
| }, "name" | "setupOnce"> & { | ||
| name: "Graphql"; | ||
| setupOnce: () => void | undefined; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/graphql/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,wCAAwC,CAAC;AA8E/F;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB;;CAAgD,CAAC;AAEvF;;;;;GAKG;AACH,eAAO,MAAM,oCAAoC,GAAI,UAAU,gCAAgC;;;;;CAM9F,CAAC"} |
| import type { DefinitionNode, DocumentNode, GraphQLFieldResolver, GraphQLObjectType, GraphqlResolvedConfig, Maybe, Patched } from './types'; | ||
| /** | ||
| * Walks the query/mutation type tree and swaps each field's `resolve` for a span-creating proxy. | ||
| * Idempotent per type via {@link GRAPHQL_PATCHED_SYMBOL}. | ||
| */ | ||
| export declare function wrapFields(type: Maybe<GraphQLObjectType & Patched>, getConfig: () => GraphqlResolvedConfig): void; | ||
| export declare function wrapFieldResolver(getConfig: () => GraphqlResolvedConfig, fieldResolver: Maybe<GraphQLFieldResolver & Patched>, isDefaultResolver?: boolean): GraphQLFieldResolver & Patched; | ||
| /** | ||
| * Returns the operation definition for `operationName` (or the first operation) from a parsed | ||
| * document, or `undefined` for schema documents / when no operation is present. | ||
| */ | ||
| export declare function getOperation(document: DocumentNode, operationName?: Maybe<string>): DefinitionNode | undefined; | ||
| //# sourceMappingURL=resolvers.d.ts.map |
| {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/graphql/resolvers.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EAMjB,qBAAqB,EACrB,KAAK,EAEL,OAAO,EACR,MAAM,SAAS,CAAC;AAMjB;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,iBAAiB,GAAG,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,qBAAqB,GAAG,IAAI,CAwBjH;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,qBAAqB,EACtC,aAAa,EAAE,KAAK,CAAC,oBAAoB,GAAG,OAAO,CAAC,EACpD,iBAAiB,UAAQ,GACxB,oBAAoB,GAAG,OAAO,CAqEhC;AA6FD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,cAAc,GAAG,SAAS,CAa9G"} |
| import type { Span } from '@sentry/core'; | ||
| import type { GraphqlResolvedConfig } from './types'; | ||
| export declare function startParseSpan(): Span; | ||
| /** `documentAST` is the 2nd argument to `validate(schema, documentAST, …)`. */ | ||
| export declare function startValidateSpan(documentAST: unknown): Span; | ||
| /** `result` is validation's return value: a (possibly empty) array of errors. */ | ||
| export declare function finalizeValidateSpan(span: Span, result: unknown): void; | ||
| /** | ||
| * Opens the execute span and, unless resolver spans are disabled, swaps the schema's field resolvers | ||
| * (and the default field resolver) for span-creating proxies — mutating the live `arguments` in place | ||
| * so the wrapped `execute` call runs with them. Always returns a span; the caller guards against | ||
| * throws (see `safe` in `index.ts`). | ||
| */ | ||
| export declare function startExecuteSpan(argsArray: unknown[], self: unknown, config: GraphqlResolvedConfig, getConfig: () => GraphqlResolvedConfig): Span; | ||
| /** `result` is the settled `ExecutionResult`; GraphQL errors surface on `result.errors`, not a throw. */ | ||
| export declare function finalizeExecuteSpan(span: Span, result: unknown): void; | ||
| //# sourceMappingURL=spans.d.ts.map |
| {"version":3,"file":"spans.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/graphql/spans.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAgBzC,OAAO,KAAK,EAIV,qBAAqB,EAGtB,MAAM,SAAS,CAAC;AAOjB,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI,CAK5D;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAItE;AA6DD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,OAAO,EAAE,EACpB,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,qBAAqB,EAC7B,SAAS,EAAE,MAAM,qBAAqB,GACrC,IAAI,CAgDN;AAED,yGAAyG;AACzG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAIrE"} |
| import type { Span } from '@sentry/core'; | ||
| import type { GRAPHQL_DATA_SYMBOL, GRAPHQL_PATCHED_SYMBOL } from './constants'; | ||
| import type { DocumentNode } from './graphql-types'; | ||
| export type * from './graphql-types'; | ||
| /** Bookkeeping we attach to `contextValue` to parent resolver spans under the execute span. */ | ||
| interface GraphQLSpanData { | ||
| source?: DocumentNode; | ||
| span: Span; | ||
| fields: Record<string, { | ||
| span: Span; | ||
| } | undefined>; | ||
| } | ||
| export interface ObjectWithGraphQLData { | ||
| [GRAPHQL_DATA_SYMBOL]?: GraphQLSpanData; | ||
| } | ||
| export interface Patched { | ||
| [GRAPHQL_PATCHED_SYMBOL]?: boolean; | ||
| } | ||
| /** Resolved integration config (defaults applied), shared by the span + resolver builders. */ | ||
| export interface GraphqlResolvedConfig { | ||
| ignoreResolveSpans: boolean; | ||
| ignoreTrivialResolveSpans: boolean; | ||
| useOperationNameForRootSpan: boolean; | ||
| } | ||
| //# sourceMappingURL=types.d.ts.map |
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/graphql/types.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,mBAAmB,iBAAiB,CAAC;AAErC,+FAA+F;AAC/F,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,qBAAqB;IACpC,CAAC,mBAAmB,CAAC,CAAC,EAAE,eAAe,CAAC;CACzC;AAED,MAAM,WAAW,OAAO;IACtB,CAAC,sBAAsB,CAAC,CAAC,EAAE,OAAO,CAAC;CACpC;AAED,8FAA8F;AAC9F,MAAM,WAAW,qBAAqB;IACpC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,yBAAyB,EAAE,OAAO,CAAC;IACnC,2BAA2B,EAAE,OAAO,CAAC;CACtC"} |
| import type { PostgresConnectionContext, Span } from '@sentry/core'; | ||
| export interface PostgresJsChannelIntegrationOptions { | ||
| /** | ||
| * Only create spans when there's already an active parent span. Defaults to | ||
| * `true`, matching the OTel `postgresJsIntegration`. | ||
| */ | ||
| requireParentSpan?: boolean; | ||
| /** | ||
| * Hook to modify the query span before the query runs. Receives the span, the | ||
| * sanitized SQL, and (when resolvable) the connection context. | ||
| */ | ||
| requestHook?: (span: Span, sanitizedSqlQuery: string, postgresConnectionContext?: PostgresConnectionContext) => void; | ||
| } | ||
| /** | ||
| * EXPERIMENTAL — orchestrion-driven postgres.js (`postgres` v3.x) integration. | ||
| * | ||
| * Subscribes to the `orchestrion:postgres:handle` / `:connection` / `:execute` / | ||
| * `:connect` diagnostics channels injected into postgres.js' `Query.prototype.handle` | ||
| * and `Connection`/`execute`/`connect` (in `src/*` and `cjs/src/*`) and creates db | ||
| * spans matching the OTel `postgresJsIntegration`. Requires the orchestrion runtime | ||
| * hook or bundler plugin. | ||
| */ | ||
| export declare const postgresJsChannelIntegration: (options?: PostgresJsChannelIntegrationOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "PostgresJs"; | ||
| }; | ||
| //# sourceMappingURL=postgres-js.d.ts.map |
| {"version":3,"file":"postgres-js.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/postgres-js.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,yBAAyB,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AA4CnF,MAAM,WAAW,mCAAmC;IAClD;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,yBAAyB,CAAC,EAAE,yBAAyB,KAAK,IAAI,CAAC;CACtH;AA6QD;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B;;CAAmD,CAAC"} |
| import type { Span } from '@sentry/core'; | ||
| /** Mirrors `@opentelemetry/instrumentation-redis`' response hook. Not called for failed commands. */ | ||
| export type RedisResponseHook = (span: Span, command: string, args: Array<string | Buffer>, result: unknown) => void; | ||
| export interface RedisChannelIntegrationOptions { | ||
| responseHook?: RedisResponseHook; | ||
| } | ||
| /** | ||
| * EXPERIMENTAL — orchestrion-driven redis integration for `redis` v2-v3 and | ||
| * node-redis v4/v5 `<5.12.0` (`@redis/client`). Covers single commands, `connect`, | ||
| * and multi/pipeline batches, fully replacing `@opentelemetry/instrumentation-redis`. | ||
| * Requires the orchestrion runtime hook or bundler plugin. | ||
| */ | ||
| export declare const redisChannelIntegration: (options?: RedisChannelIntegrationOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "RedisChannel"; | ||
| }; | ||
| //# sourceMappingURL=redis.d.ts.map |
| {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/redis.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAiB,IAAI,EAAkB,MAAM,cAAc,CAAC;AA+BxE,qGAAqG;AACrG,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAErH,MAAM,WAAW,8BAA8B;IAC7C,YAAY,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAsSD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;CAA8C,CAAC"} |
| /** | ||
| * Auto-instrument the [mysql2](https://www.npmjs.com/package/mysql2) library via its native | ||
| * `node:diagnostics_channel` tracing channels (mysql2 >= 3.20.0). | ||
| * | ||
| * On older mysql2 versions the channels are never published to, so this integration is inert and | ||
| * the vendored OTel instrumentation (gated to `< 3.20.0`) handles instrumentation instead. | ||
| */ | ||
| export declare const mysql2Integration: () => import("@sentry/core").Integration & { | ||
| name: string; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mysql2/index.ts"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB;;CAAwC,CAAC"} |
| import type { TracingChannel } from 'node:diagnostics_channel'; | ||
| export declare const MYSQL2_DC_CHANNEL_QUERY = "mysql2:query"; | ||
| export declare const MYSQL2_DC_CHANNEL_EXECUTE = "mysql2:execute"; | ||
| export declare const MYSQL2_DC_CHANNEL_CONNECT = "mysql2:connect"; | ||
| export declare const MYSQL2_DC_CHANNEL_POOL_CONNECT = "mysql2:pool:connect"; | ||
| /** | ||
| * Shape of the context object mysql2 >= 3.20.0 publishes on its query/execute | ||
| * tracing channels (see mysql2 `lib/base/connection.js`). | ||
| * | ||
| * Node's `traceCallback`/`tracePromise` mutate this same object with | ||
| * `result`/`error` once the operation settles, which `bindTracingChannelToSpan` | ||
| * reads in its lifecycle handlers — hence both are declared optional here. | ||
| * | ||
| * `query` is the SQL statement. On the `query` channel mysql2 has already | ||
| * inlined `values` into it (`Connection.format`), so it carries raw user data; | ||
| * on the `execute` channel it keeps `?` placeholders. Either way we sanitize it | ||
| * before emitting `db.query.text` and never attach `values`. | ||
| */ | ||
| export interface MySQL2QueryData { | ||
| query?: string; | ||
| values?: unknown; | ||
| database?: string; | ||
| serverAddress?: string; | ||
| /** Absent for unix-socket connections, where `serverAddress` is the socket path. */ | ||
| serverPort?: number; | ||
| result?: unknown; | ||
| error?: Error; | ||
| } | ||
| /** | ||
| * Shape of the context object mysql2 >= 3.20.0 publishes on its | ||
| * `connect`/`pool:connect` channels. | ||
| */ | ||
| export interface MySQL2ConnectData { | ||
| database?: string; | ||
| serverAddress?: string; | ||
| serverPort?: number; | ||
| user?: string; | ||
| result?: unknown; | ||
| error?: Error; | ||
| } | ||
| /** | ||
| * Platform-provided factory that creates a native tracing channel for the given name. The | ||
| * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`, | ||
| * which propagates the active span through the runtime's async context. | ||
| * | ||
| * Node passes `node:diagnostics_channel`'s `tracingChannel` directly. | ||
| */ | ||
| export type MySQL2TracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>; | ||
| /** | ||
| * Subscribe Sentry span handlers to mysql2's diagnostics-channel events | ||
| * (`mysql2:query`, `:execute`, `:connect`, `:pool:connect`), published by | ||
| * mysql2 >= 3.20.0. | ||
| * | ||
| * On older mysql2 versions the channels are never published to, so the | ||
| * subscribers are inert — there is no double-instrumentation against the | ||
| * vendored OTel patcher, which is gated to `< 3.20.0`. | ||
| */ | ||
| export declare function subscribeMysql2DiagnosticChannels(tracingChannel: MySQL2TracingChannelFactory): void; | ||
| //# sourceMappingURL=mysql2-dc-subscriber.d.ts.map |
| {"version":3,"file":"mysql2-dc-subscriber.d.ts","sourceRoot":"","sources":["../../../src/mysql2/mysql2-dc-subscriber.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAoB/D,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AACtD,eAAO,MAAM,yBAAyB,mBAAmB,CAAC;AAC1D,eAAO,MAAM,yBAAyB,mBAAmB,CAAC;AAC1D,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AAQpE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEnG;;;;;;;;GAQG;AACH,wBAAgB,iCAAiC,CAAC,cAAc,EAAE,2BAA2B,GAAG,IAAI,CAKnG"} |
| export declare const amqplibConfig: ({ | ||
| channelName: string; | ||
| module: { | ||
| filePath: string; | ||
| name: "amqplib"; | ||
| versionRange: ">=0.5.5 <2"; | ||
| }; | ||
| functionQuery: { | ||
| className: string; | ||
| methodName: string; | ||
| kind: "Sync"; | ||
| functionName?: undefined; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| filePath: string; | ||
| name: "amqplib"; | ||
| versionRange: ">=0.5.5 <2"; | ||
| }; | ||
| functionQuery: { | ||
| className: string; | ||
| methodName: string; | ||
| kind: "Callback"; | ||
| functionName?: undefined; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| filePath: string; | ||
| name: "amqplib"; | ||
| versionRange: ">=0.5.5 <2"; | ||
| }; | ||
| functionQuery: { | ||
| className: string; | ||
| methodName: string; | ||
| kind: "Async"; | ||
| functionName?: undefined; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| filePath: string; | ||
| name: "amqplib"; | ||
| versionRange: ">=0.5.5 <2"; | ||
| }; | ||
| functionQuery: { | ||
| functionName: string; | ||
| kind: "Callback"; | ||
| className?: undefined; | ||
| methodName?: undefined; | ||
| }; | ||
| })[]; | ||
| export declare const amqplibChannels: { | ||
| readonly AMQPLIB_PUBLISH: "orchestrion:amqplib:publish"; | ||
| readonly AMQPLIB_CONFIRM_PUBLISH: "orchestrion:amqplib:confirmPublish"; | ||
| readonly AMQPLIB_CONSUME: "orchestrion:amqplib:consume"; | ||
| readonly AMQPLIB_DISPATCH: "orchestrion:amqplib:dispatch"; | ||
| readonly AMQPLIB_ACK: "orchestrion:amqplib:ack"; | ||
| readonly AMQPLIB_NACK: "orchestrion:amqplib:nack"; | ||
| readonly AMQPLIB_REJECT: "orchestrion:amqplib:reject"; | ||
| readonly AMQPLIB_ACK_ALL: "orchestrion:amqplib:ackAll"; | ||
| readonly AMQPLIB_NACK_ALL: "orchestrion:amqplib:nackAll"; | ||
| readonly AMQPLIB_CONNECT: "orchestrion:amqplib:connect"; | ||
| }; | ||
| //# sourceMappingURL=amqplib.d.ts.map |
| {"version":3,"file":"amqplib.d.ts","sourceRoot":"","sources":["../../../../src/orchestrion/config/amqplib.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA6DS,CAAC;AAEpC,eAAO,MAAM,eAAe;;;;;;;;;;;CAWlB,CAAC"} |
| export declare const expressConfig: ({ | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| expressionName: string; | ||
| kind: "Callback"; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| expressionName: string; | ||
| kind: "Sync"; | ||
| }; | ||
| })[]; | ||
| export declare const expressChannels: { | ||
| readonly EXPRESS_HANDLE: "orchestrion:express:handle"; | ||
| readonly ROUTER_HANDLE: "orchestrion:router:handle"; | ||
| readonly EXPRESS_ROUTE: "orchestrion:express:route"; | ||
| readonly EXPRESS_USE: "orchestrion:express:use"; | ||
| readonly ROUTER_ROUTE: "orchestrion:router:route"; | ||
| readonly ROUTER_USE: "orchestrion:router:use"; | ||
| }; | ||
| //# sourceMappingURL=express.d.ts.map |
| {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../../../src/orchestrion/config/express.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;IAsDS,CAAC;AAEpC,eAAO,MAAM,eAAe;;;;;;;CAclB,CAAC"} |
| export declare const googleGenAiConfig: ({ | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| expressionName: "generateContent" | "generateContentStream"; | ||
| kind: "Auto"; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| className: string; | ||
| methodName: string; | ||
| kind: "Auto"; | ||
| }; | ||
| })[]; | ||
| export declare const googleGenAiChannels: { | ||
| readonly GOOGLE_GENAI_GENERATE_CONTENT: "orchestrion:@google/genai:generate-content"; | ||
| readonly GOOGLE_GENAI_EMBED_CONTENT: "orchestrion:@google/genai:embed-content"; | ||
| readonly GOOGLE_GENAI_CHAT: "orchestrion:@google/genai:chat"; | ||
| }; | ||
| //# sourceMappingURL=google-genai.d.ts.map |
| {"version":3,"file":"google-genai.d.ts","sourceRoot":"","sources":["../../../../src/orchestrion/config/google-genai.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;IAyBK,CAAC;AAEpC,eAAO,MAAM,mBAAmB;;;;CAItB,CAAC"} |
| export declare const graphqlConfig: ({ | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| functionName: string; | ||
| kind: "Sync"; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| functionName: string; | ||
| kind: "Auto"; | ||
| }; | ||
| })[]; | ||
| export declare const graphqlChannels: { | ||
| readonly GRAPHQL_PARSE: "orchestrion:graphql:parse"; | ||
| readonly GRAPHQL_VALIDATE: "orchestrion:graphql:validate"; | ||
| readonly GRAPHQL_EXECUTE: "orchestrion:graphql:execute"; | ||
| }; | ||
| //# sourceMappingURL=graphql.d.ts.map |
| {"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../../../src/orchestrion/config/graphql.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;IAgBS,CAAC;AAEpC,eAAO,MAAM,eAAe;;;;CAIlB,CAAC"} |
| import type { InstrumentationConfig } from '@apm-js-collab/code-transformer'; | ||
| export declare const postgresJsConfig: InstrumentationConfig[]; | ||
| export declare const postgresJsChannels: { | ||
| readonly POSTGRESJS_HANDLE: "orchestrion:postgres:handle"; | ||
| readonly POSTGRESJS_CONNECTION: "orchestrion:postgres:connection"; | ||
| readonly POSTGRESJS_EXECUTE: "orchestrion:postgres:execute"; | ||
| readonly POSTGRESJS_CONNECT: "orchestrion:postgres:connect"; | ||
| }; | ||
| //# sourceMappingURL=postgres.d.ts.map |
| {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../../src/orchestrion/config/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AA+C7E,eAAO,MAAM,gBAAgB,yBAA8D,CAAC;AAE5F,eAAO,MAAM,kBAAkB;;;;;CAKrB,CAAC"} |
| export declare const redisConfig: ({ | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| expressionName: string; | ||
| kind: "Sync"; | ||
| className?: undefined; | ||
| methodName?: undefined; | ||
| }; | ||
| } | { | ||
| channelName: string; | ||
| module: { | ||
| name: string; | ||
| versionRange: string; | ||
| filePath: string; | ||
| }; | ||
| functionQuery: { | ||
| className: string; | ||
| methodName: string; | ||
| kind: "Async"; | ||
| expressionName?: undefined; | ||
| }; | ||
| })[]; | ||
| export declare const redisChannels: { | ||
| readonly REDIS_COMMAND: "orchestrion:redis:command"; | ||
| readonly NODE_REDIS_COMMAND: "orchestrion:@redis/client:command"; | ||
| readonly NODE_REDIS_EXECUTOR: "orchestrion:@redis/client:executor"; | ||
| readonly NODE_REDIS_CONNECT: "orchestrion:@redis/client:connect"; | ||
| readonly NODE_REDIS_MULTI: "orchestrion:@redis/client:multi"; | ||
| readonly NODE_REDIS_PIPELINE: "orchestrion:@redis/client:pipeline"; | ||
| readonly NODE_REDIS_BATCH: "orchestrion:@redis/client:batch"; | ||
| }; | ||
| //# sourceMappingURL=redis.d.ts.map |
| {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../../../src/orchestrion/config/redis.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;IA4DW,CAAC;AAEpC,eAAO,MAAM,aAAa;;;;;;;;CAQhB,CAAC"} |
| import { type RedisDiagnosticChannelResponseHook } from './redis-dc-subscriber'; | ||
| /** Options controlling the redis diagnostics-channel subscription. */ | ||
| export interface RedisDiagnosticChannelsOptions { | ||
| /** | ||
| * Optional hook invoked once the redis command response arrives. Useful for attaching | ||
| * response-derived attributes (e.g. cache hit/miss, payload size). | ||
| */ | ||
| responseHook?: RedisDiagnosticChannelResponseHook; | ||
| } | ||
| /** | ||
| * Auto-instrument the [redis](https://www.npmjs.com/package/redis) and | ||
| * [ioredis](https://www.npmjs.com/package/ioredis) libraries via their native | ||
| * `node:diagnostics_channel` tracing channels (node-redis >= 5.12.0, ioredis >= 5.11.0). | ||
| */ | ||
| export declare const redisIntegration: (options?: RedisDiagnosticChannelsOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: string; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/redis/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,kCAAkC,EAAoC,MAAM,uBAAuB,CAAC;AAElH,sEAAsE;AACtE,MAAM,WAAW,8BAA8B;IAC7C;;;OAGG;IACH,YAAY,CAAC,EAAE,kCAAkC,CAAC;CACnD;AAkBD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;CAAuC,CAAC"} |
| /** Shared, state-free helpers for the Vercel AI (`ai`) channel subscribers, plus the streamed model-call tap. */ | ||
| /** Narrow to a string, or `undefined` for anything else. */ | ||
| export declare function asString(value: unknown): string | undefined; | ||
| /** Narrow to a finite number, or `undefined` for anything else (including `NaN`). */ | ||
| export declare function asNumber(value: unknown): number | undefined; | ||
| /** Add two optional numbers, treating a missing operand as `0` but returning `undefined` when both are absent. */ | ||
| export declare function sum(a: number | undefined, b: number | undefined): number | undefined; | ||
| /** Stringify a value, passing strings through and falling back to a placeholder on circular/unserializable input. */ | ||
| export declare function safeStringify(value: unknown): string; | ||
| /** The aggregate handed back once a streamed model call finishes, in the shape `enrichSpanOnEnd` expects. */ | ||
| export interface StreamedModelCallResult { | ||
| text?: string; | ||
| toolCalls: Array<Record<string, unknown>>; | ||
| usage?: unknown; | ||
| finishReason?: unknown; | ||
| responseId?: string; | ||
| responseModel?: string; | ||
| providerMetadata?: unknown; | ||
| } | ||
| /** A minimal structural check — the streamed model call exposes a web `ReadableStream` on `result.stream`. */ | ||
| export declare function isReadableStream(value: unknown): value is ReadableStream<unknown>; | ||
| /** | ||
| * Wrap a streamed model call's `ReadableStream` so its chunks are observed as the SDK consumes them, | ||
| * without altering what the SDK sees. Returns a replacement stream to swap onto `result.stream`. | ||
| * | ||
| * `onFinal` runs exactly once when the stream drains cleanly; `onError` runs exactly once if it errors | ||
| * or is cancelled. Reading one source chunk per `pull` preserves the SDK's backpressure. The | ||
| * `try/catch` around every read guarantees the owning span is always ended — a leaked open span on a | ||
| * mid-stream failure would be worse than a slightly-less-enriched one. | ||
| */ | ||
| export declare function tapModelCallStream(stream: ReadableStream<unknown>, onFinal: (result: StreamedModelCallResult) => void, onError: (error: unknown) => void): ReadableStream<unknown>; | ||
| //# sourceMappingURL=util.d.ts.map |
| {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../src/vercel-ai/util.ts"],"names":[],"mappings":"AAAA,iHAAiH;AAIjH,4DAA4D;AAC5D,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE3D;AAED,qFAAqF;AACrF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE3D;AAED,kHAAkH;AAClH,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAEpF;AAED,qHAAqH;AACrH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASpD;AAoCD,6GAA6G;AAC7G,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,8GAA8G;AAC9G,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAAC,OAAO,CAAC,CAMjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,EAC/B,OAAO,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,EAClD,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAChC,cAAc,CAAC,OAAO,CAAC,CAkDzB"} |
| import { type VercelAiChannelOptions, type VercelAiTracingChannelFactory } from './vercel-ai-dc-subscriber'; | ||
| /** | ||
| * Subscribe the v6 orchestrion channel adapter. Safe to always call: inert on | ||
| * `ai` >= 7 (those channels are never published) and when orchestrion injection | ||
| * isn't active. Idempotent. | ||
| * | ||
| * `tracingChannel` is the platform-provided factory (the same one passed to | ||
| * `subscribeVercelAiTracingChannel`); `options` pins the recording settings at | ||
| * subscribe time so we never look the integration up per event. | ||
| */ | ||
| export declare function subscribeVercelAiOrchestrionChannels(tracingChannel: VercelAiTracingChannelFactory, options?: VercelAiChannelOptions): void; | ||
| //# sourceMappingURL=vercel-ai-orchestrion-subscriber.d.ts.map |
| {"version":3,"file":"vercel-ai-orchestrion-subscriber.d.ts","sourceRoot":"","sources":["../../../src/vercel-ai/vercel-ai-orchestrion-subscriber.ts"],"names":[],"mappings":"AAMA,OAAO,EAQL,KAAK,sBAAsB,EAC3B,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;AA+FnC;;;;;;;;GAQG;AACH,wBAAgB,oCAAoC,CAClD,cAAc,EAAE,6BAA6B,EAC7C,OAAO,GAAE,sBAA2B,GACnC,IAAI,CA8DN"} |
+10
-11
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const index$1 = require('./mongoose/index.js'); | ||
| const redisDcSubscriber = require('./redis/redis-dc-subscriber.js'); | ||
| const index$1 = require('./graphql/index.js'); | ||
| const index$2 = require('./mongoose/index.js'); | ||
| const index$3 = require('./mysql2/index.js'); | ||
| const index$4 = require('./redis/index.js'); | ||
| const redisStatementSerializer = require('./redis/redis-statement-serializer.js'); | ||
| const tracingChannel = require('./tracing-channel.js'); | ||
| const index$2 = require('./vercel-ai/index.js'); | ||
| const index$5 = require('./vercel-ai/index.js'); | ||
| const index = require('./integrations/tracing-channel/fastify/index.js'); | ||
@@ -12,12 +14,9 @@ | ||
| exports.mongooseIntegration = index$1.mongooseIntegration; | ||
| exports.IOREDIS_DC_CHANNEL_COMMAND = redisDcSubscriber.IOREDIS_DC_CHANNEL_COMMAND; | ||
| exports.IOREDIS_DC_CHANNEL_CONNECT = redisDcSubscriber.IOREDIS_DC_CHANNEL_CONNECT; | ||
| exports.REDIS_DC_CHANNEL_BATCH = redisDcSubscriber.REDIS_DC_CHANNEL_BATCH; | ||
| exports.REDIS_DC_CHANNEL_COMMAND = redisDcSubscriber.REDIS_DC_CHANNEL_COMMAND; | ||
| exports.REDIS_DC_CHANNEL_CONNECT = redisDcSubscriber.REDIS_DC_CHANNEL_CONNECT; | ||
| exports.subscribeRedisDiagnosticChannels = redisDcSubscriber.subscribeRedisDiagnosticChannels; | ||
| exports.graphqlIntegration = index$1.graphqlIntegration; | ||
| exports.mongooseIntegration = index$2.mongooseIntegration; | ||
| exports.mysql2Integration = index$3.mysql2Integration; | ||
| exports.redisIntegration = index$4.redisIntegration; | ||
| exports.defaultDbStatementSerializer = redisStatementSerializer.defaultDbStatementSerializer; | ||
| exports.bindTracingChannelToSpan = tracingChannel.bindTracingChannelToSpan; | ||
| exports.vercelAiIntegration = index$2.vercelAiIntegration; | ||
| exports.vercelAiIntegration = index$5.vercelAiIntegration; | ||
| exports.fastifyIntegration = index.fastifyIntegration; | ||
@@ -24,0 +23,0 @@ exports.handleFastifyError = index.handleFastifyError; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;"} | ||
| {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;"} |
@@ -41,3 +41,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| function isFastifyRequest(arg) { | ||
| return !!arg && typeof arg === "object" && !!arg.method && !!arg.url && (!!arg.routeOptions || "routerPath" in arg); | ||
| return core.isObjectLike(arg) && !!arg.method && !!arg.url && (!!arg.routeOptions || "routerPath" in arg); | ||
| } | ||
@@ -44,0 +44,0 @@ function fastifyOtelPlugin(instance, _opts, done) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing-channel/fastify/instrumentation.ts"],"sourcesContent":["/*\n * Copyright (c) 2024-present The Fastify team <https://github.com/fastify/fastify#team>\n * SPDX-License-Identifier: MIT\n *\n * NOTICE from the Sentry authors:\n * - Based on: https://github.com/fastify/otel/tree/bae80d6caef4287e7f01ff3c8dc753243706ea86 (@fastify/otel@0.18.1)\n * - Streamlined to the Sentry SDK's needs: dropped the `InstrumentationBase` wrapper, the unused\n * request/lifecycle hooks, `ignorePaths`/`minimatch` support and the OpenTelemetry tracer/context\n * APIs in favor of the Sentry span API.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-this-alias */\n/* eslint-disable max-lines */\n\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { HTTP_REQUEST_METHOD, HTTP_RESPONSE_STATUS_CODE, HTTP_ROUTE, URL_PATH } from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport {\n debug,\n getActiveSpan,\n getIsolationScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n spanToJSON,\n startInactiveSpan,\n startSpan,\n withActiveSpan,\n} from '@sentry/core';\nimport type { FastifyInstance, FastifyRequest } from './types';\nimport { DEBUG_BUILD } from '../../../debug-build';\n\nconst PACKAGE_NAME = '@sentry/instrumentation-fastify';\nconst SUPPORTED_VERSIONS = '>=3.21.0 <6';\n\nconst ORIGIN = 'auto.http.otel.fastify';\nconst HOOK_OP = 'hook.fastify';\nconst REQUEST_HANDLER_OP = 'request_handler.fastify';\n\nconst FASTIFY_HOOKS = [\n 'onRequest',\n 'preParsing',\n 'preValidation',\n 'preHandler',\n 'preSerialization',\n 'onSend',\n 'onResponse',\n 'onError',\n];\nconst ATTRIBUTE_HOOK_NAME = 'hook.name' as const;\nconst ATTRIBUTE_FASTIFY_TYPE = 'fastify.type' as const;\nconst ATTRIBUTE_HOOK_CALLBACK_NAME = 'hook.callback.name' as const;\nconst ATTRIBUTE_FASTIFY_ROOT = 'fastify.root' as const;\n\nconst HOOK_TYPE_ROUTE = 'route-hook' as const;\nconst HOOK_TYPE_INSTANCE = 'hook' as const;\nconst HOOK_TYPE_HANDLER = 'request-handler' as const;\nconst ANONYMOUS_FUNCTION_NAME = 'anonymous';\n\nconst kRequestSpan = Symbol('sentry fastify request span');\nconst kAddHookOriginal = Symbol('sentry fastify addHook original');\nconst kSetNotFoundOriginal = Symbol('sentry fastify setNotFoundHandler original');\n\ntype AnyFn = (...args: any[]) => any;\n\n/**\n * Read the matched route URL off a request. Fastify >=4 exposes it on `request.routeOptions.url`,\n * while v3 only has the (since-removed-in-v5) `request.routerPath`.\n */\nfunction getRequestRouteUrl(request: any): string | undefined {\n return request.routeOptions?.url ?? request.routerPath;\n}\n\n/**\n * Read the per-route config off a request. Fastify >=4 exposes it on `request.routeOptions.config`,\n * while v3 uses `request.routeConfig`. Used to honor the `{ config: { otel: false } }` opt-out.\n */\nfunction getRequestRouteConfig(request: any): { otel?: boolean } | undefined {\n return request.routeOptions?.config ?? request.routeConfig;\n}\n\n/**\n * Detect whether one of a wrapped handler's arguments is the Fastify request. We can't rely on a\n * single property since the route metadata moved from `routerPath` (v3) to `routeOptions` (>=4),\n * so we accept either shape.\n */\nfunction isFastifyRequest(arg: any): boolean {\n return !!arg && typeof arg === 'object' && !!arg.method && !!arg.url && (!!arg.routeOptions || 'routerPath' in arg);\n}\n\n/**\n * The Fastify plugin that wires up the request/hook/handler spans. It is registered on every Fastify\n * instance via the `fastify.initialization` diagnostics channel.\n */\nfunction fastifyOtelPlugin(this: unknown, instance: any, _opts: unknown, done: () => void): void {\n instance.decorate(kAddHookOriginal, instance.addHook);\n instance.decorate(kSetNotFoundOriginal, instance.setNotFoundHandler);\n instance.decorateRequest('opentelemetry', function opentelemetry(this: any) {\n return { span: this[kRequestSpan] as Span | null };\n });\n instance.decorateRequest(kRequestSpan, null);\n\n instance.addHook('onRoute', otelWireRoute);\n instance.addHook('onRequest', startRequestSpanHook);\n instance.addHook('onResponse', finalizeNotFoundSpanHook);\n\n instance.addHook = addHookPatched;\n instance.setNotFoundHandler = setNotFoundHandlerPatched;\n\n done();\n}\n\nconst pluginSymbols = fastifyOtelPlugin as unknown as Record<symbol, unknown>;\npluginSymbols[Symbol.for('skip-override')] = true;\npluginSymbols[Symbol.for('fastify.display-name')] = PACKAGE_NAME;\npluginSymbols[Symbol.for('plugin-meta')] = {\n fastify: SUPPORTED_VERSIONS,\n name: PACKAGE_NAME,\n};\n\nfunction otelWireRoute(this: any, routeOptions: any): void {\n if (routeOptions.config?.otel === false) {\n return;\n }\n\n for (const hook of FASTIFY_HOOKS) {\n const handlerLike = routeOptions[hook];\n\n if (typeof handlerLike === 'function') {\n routeOptions[hook] = handlerWrapper(\n handlerLike,\n hook,\n routeHookAttributes(this.pluginName, hook, handlerLike, routeOptions.url),\n );\n } else if (Array.isArray(handlerLike)) {\n routeOptions[hook] = handlerLike.map((handler: AnyFn) =>\n handlerWrapper(handler, hook, routeHookAttributes(this.pluginName, hook, handler, routeOptions.url)),\n );\n }\n }\n\n routeOptions.onSend = appendRouteHook(routeOptions.onSend, finalizeResponseSpanHook);\n routeOptions.onError = appendRouteHook(routeOptions.onError, recordErrorInSpanHook);\n\n routeOptions.handler = handlerWrapper(routeOptions.handler, 'handler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - route-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_HANDLER,\n [HTTP_ROUTE]: routeOptions.url,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n routeOptions.handler.name.length > 0 ? routeOptions.handler.name : ANONYMOUS_FUNCTION_NAME,\n });\n}\n\nfunction routeHookAttributes(pluginName: string, hook: string, handler: AnyFn, url: string): Record<string, string> {\n return {\n [ATTRIBUTE_HOOK_NAME]: `${pluginName} - route -> ${hook}`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_ROUTE,\n [HTTP_ROUTE]: url,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: handler.name?.length > 0 ? handler.name : ANONYMOUS_FUNCTION_NAME,\n };\n}\n\nfunction appendRouteHook(existing: AnyFn | AnyFn[] | undefined, hook: AnyFn): AnyFn | AnyFn[] {\n if (existing == null) {\n return hook;\n }\n return Array.isArray(existing) ? [...existing, hook] : [existing, hook];\n}\n\nfunction startRequestSpanHook(this: any, request: any, _reply: any, hookDone: () => void): void {\n if (getRequestRouteConfig(request)?.otel === false) {\n return hookDone();\n }\n\n const attributes: Record<string, string> = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [ATTRIBUTE_FASTIFY_ROOT]: PACKAGE_NAME,\n [HTTP_REQUEST_METHOD]: request.method,\n [URL_PATH]: request.url,\n };\n\n const route = getRequestRouteUrl(request);\n if (route != null) {\n attributes[HTTP_ROUTE] = route;\n\n // Update the route of the request on the root span, if it is a http.server span\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n if (rootSpan && spanToJSON(rootSpan).data[SEMANTIC_ATTRIBUTE_SENTRY_OP] === 'http.server') {\n rootSpan.setAttribute(HTTP_ROUTE, route);\n }\n }\n\n const requestSpan = startInactiveSpan({ name: 'request', op: REQUEST_HANDLER_OP, attributes });\n request[kRequestSpan] = requestSpan;\n\n // Set the request span as the active span for the remainder of the request lifecycle, so that\n // downstream hooks/handlers as well as errors captured via the error diagnostics channel are\n // parented to it (otherwise they would attach to the root `http.server` span instead).\n withActiveSpan(requestSpan, () => {\n hookDone();\n });\n}\n\nfunction finalizeNotFoundSpanHook(request: any, reply: any, hookDone: () => void): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n span.setAttributes({ [HTTP_RESPONSE_STATUS_CODE]: reply.statusCode });\n span.end();\n }\n\n request[kRequestSpan] = null;\n\n hookDone();\n}\n\nfunction finalizeResponseSpanHook(\n request: any,\n reply: any,\n payload: any,\n hookDone: (err: null, payload: any) => void,\n): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n if (reply.statusCode >= 500) {\n span.setStatus({ code: SPAN_STATUS_ERROR });\n }\n span.setAttributes({ [HTTP_RESPONSE_STATUS_CODE]: reply.statusCode });\n span.end();\n }\n\n request[kRequestSpan] = null;\n\n hookDone(null, payload);\n}\n\nfunction recordErrorInSpanHook(request: any, _reply: any, error: any, hookDone: () => void): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: error.message });\n }\n\n hookDone();\n}\n\nfunction addHookPatched(this: any, name: string, hook: AnyFn): unknown {\n const addHookOriginal = this[kAddHookOriginal];\n\n if (FASTIFY_HOOKS.includes(name)) {\n return addHookOriginal.call(\n this,\n name,\n handlerWrapper(hook, name, {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - ${name}`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: hook.name?.length > 0 ? hook.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n }\n\n return addHookOriginal.call(this, name, hook);\n}\n\nfunction setNotFoundHandlerPatched(this: any, hooks: any, handler?: any): void {\n const setNotFoundHandlerOriginal = this[kSetNotFoundOriginal];\n\n if (typeof hooks === 'function') {\n setNotFoundHandlerOriginal.call(\n this,\n handlerWrapper(hooks, 'notFoundHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: hooks.name?.length > 0 ? hooks.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n return;\n }\n\n if (hooks.preValidation != null) {\n hooks.preValidation = handlerWrapper(hooks.preValidation, 'notFoundHandler - preValidation', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler - preValidation`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n hooks.preValidation.name?.length > 0 ? hooks.preValidation.name : ANONYMOUS_FUNCTION_NAME,\n });\n }\n\n if (hooks.preHandler != null) {\n hooks.preHandler = handlerWrapper(hooks.preHandler, 'notFoundHandler - preHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler - preHandler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n hooks.preHandler.name?.length > 0 ? hooks.preHandler.name : ANONYMOUS_FUNCTION_NAME,\n });\n }\n\n // Fastify allows `setNotFoundHandler(opts)` without a handler, falling back to its built-in 404\n // handler. Forward the (already-wrapped) hooks unchanged so that fallback still applies.\n if (handler == null) {\n setNotFoundHandlerOriginal.call(this, hooks);\n return;\n }\n\n setNotFoundHandlerOriginal.call(\n this,\n hooks,\n handlerWrapper(handler, 'notFoundHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: handler.name?.length > 0 ? handler.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n}\n\nfunction getRequestFromArgs(args: any[]): any | null {\n for (const arg of args) {\n if (isFastifyRequest(arg)) {\n return arg;\n }\n }\n return null;\n}\n\nfunction handlerWrapper(handler: AnyFn, hookName: string, spanAttributes: Record<string, string> = {}): AnyFn {\n return function handlerWrapped(this: any, ...args: any[]) {\n const request = getRequestFromArgs(args);\n\n if (request === null || getRequestRouteConfig(request)?.otel === false) {\n return handler.call(this, ...args);\n }\n\n const parentSpan = (request[kRequestSpan] as Span | null) ?? undefined;\n const handlerName = handler.name?.length > 0 ? handler.name : (this.pluginName ?? ANONYMOUS_FUNCTION_NAME);\n\n const hookType = spanAttributes[ATTRIBUTE_FASTIFY_TYPE];\n const op =\n hookType === HOOK_TYPE_INSTANCE ? HOOK_OP : hookType === HOOK_TYPE_HANDLER ? REQUEST_HANDLER_OP : undefined;\n\n const name = op ? stripFastifyPrefix(spanAttributes[ATTRIBUTE_HOOK_NAME]) : `${hookName} - ${handlerName}`;\n\n return startSpan(\n {\n name,\n op,\n attributes: {\n ...spanAttributes,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n },\n parentSpan,\n },\n () => handler.call(this, ...args),\n );\n };\n}\n\n/**\n * Strip the framework/plugin prefixes from a Fastify `hook.name` to derive a readable span name.\n * This is a bit of a hack and does not always work for all spans, but it's the best we can do without a proper API.\n */\nfunction stripFastifyPrefix(hookName = ''): string {\n return hookName\n .replace(/^fastify -> /, '')\n .replace(/^@fastify\\/otel -> /, '')\n .replace(/^@sentry\\/instrumentation-fastify -> /, '');\n}\n\nfunction instrumentOnRequest(fastify: FastifyInstance): void {\n fastify.addHook('onRequest', async (request: FastifyRequest, _reply) => {\n const routeName = getRequestRouteUrl(request);\n const method = request.method || 'GET';\n\n getIsolationScope().setTransactionName(`${method} ${routeName}`);\n });\n}\n\nlet _isInstrumented = false;\n\n/**\n * Set up the Fastify (>= 3.21.0 < 6) instrumentation by subscribing to the `fastify.initialization`\n * diagnostics channel and registering the span-creating plugin on every Fastify instance.\n *\n * Idempotent and exposes an `id` so it can participate in the OpenTelemetry preload list.\n */\nexport const instrumentFastify = Object.assign(\n function instrumentFastify(): void {\n if (_isInstrumented) {\n return;\n }\n _isInstrumented = true;\n\n diagnosticsChannel.subscribe('fastify.initialization', message => {\n const fastifyInstance = (message as { fastify?: FastifyInstance }).fastify;\n\n fastifyInstance?.register(fastifyOtelPlugin).after(err => {\n if (err) {\n DEBUG_BUILD && debug.error('Failed to setup Fastify instrumentation', err);\n } else if (fastifyInstance) {\n instrumentOnRequest(fastifyInstance);\n }\n });\n });\n },\n { id: 'Fastify.v5' },\n);\n"],"names":["HTTP_ROUTE","attributes","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","HTTP_REQUEST_METHOD","URL_PATH","getActiveSpan","getRootSpan","spanToJSON","SEMANTIC_ATTRIBUTE_SENTRY_OP","startInactiveSpan","withActiveSpan","HTTP_RESPONSE_STATUS_CODE","SPAN_STATUS_ERROR","startSpan","getIsolationScope","instrumentFastify","DEBUG_BUILD","debug"],"mappings":"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"} | ||
| {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing-channel/fastify/instrumentation.ts"],"sourcesContent":["/*\n * Copyright (c) 2024-present The Fastify team <https://github.com/fastify/fastify#team>\n * SPDX-License-Identifier: MIT\n *\n * NOTICE from the Sentry authors:\n * - Based on: https://github.com/fastify/otel/tree/bae80d6caef4287e7f01ff3c8dc753243706ea86 (@fastify/otel@0.18.1)\n * - Streamlined to the Sentry SDK's needs: dropped the `InstrumentationBase` wrapper, the unused\n * request/lifecycle hooks, `ignorePaths`/`minimatch` support and the OpenTelemetry tracer/context\n * APIs in favor of the Sentry span API.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-this-alias */\n/* eslint-disable max-lines */\n\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { HTTP_REQUEST_METHOD, HTTP_RESPONSE_STATUS_CODE, HTTP_ROUTE, URL_PATH } from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport {\n isObjectLike,\n debug,\n getActiveSpan,\n getIsolationScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n spanToJSON,\n startInactiveSpan,\n startSpan,\n withActiveSpan,\n} from '@sentry/core';\nimport type { FastifyInstance, FastifyRequest } from './types';\nimport { DEBUG_BUILD } from '../../../debug-build';\n\nconst PACKAGE_NAME = '@sentry/instrumentation-fastify';\nconst SUPPORTED_VERSIONS = '>=3.21.0 <6';\n\nconst ORIGIN = 'auto.http.otel.fastify';\nconst HOOK_OP = 'hook.fastify';\nconst REQUEST_HANDLER_OP = 'request_handler.fastify';\n\nconst FASTIFY_HOOKS = [\n 'onRequest',\n 'preParsing',\n 'preValidation',\n 'preHandler',\n 'preSerialization',\n 'onSend',\n 'onResponse',\n 'onError',\n];\nconst ATTRIBUTE_HOOK_NAME = 'hook.name' as const;\nconst ATTRIBUTE_FASTIFY_TYPE = 'fastify.type' as const;\nconst ATTRIBUTE_HOOK_CALLBACK_NAME = 'hook.callback.name' as const;\nconst ATTRIBUTE_FASTIFY_ROOT = 'fastify.root' as const;\n\nconst HOOK_TYPE_ROUTE = 'route-hook' as const;\nconst HOOK_TYPE_INSTANCE = 'hook' as const;\nconst HOOK_TYPE_HANDLER = 'request-handler' as const;\nconst ANONYMOUS_FUNCTION_NAME = 'anonymous';\n\nconst kRequestSpan = Symbol('sentry fastify request span');\nconst kAddHookOriginal = Symbol('sentry fastify addHook original');\nconst kSetNotFoundOriginal = Symbol('sentry fastify setNotFoundHandler original');\n\ntype AnyFn = (...args: any[]) => any;\n\n/**\n * Read the matched route URL off a request. Fastify >=4 exposes it on `request.routeOptions.url`,\n * while v3 only has the (since-removed-in-v5) `request.routerPath`.\n */\nfunction getRequestRouteUrl(request: any): string | undefined {\n return request.routeOptions?.url ?? request.routerPath;\n}\n\n/**\n * Read the per-route config off a request. Fastify >=4 exposes it on `request.routeOptions.config`,\n * while v3 uses `request.routeConfig`. Used to honor the `{ config: { otel: false } }` opt-out.\n */\nfunction getRequestRouteConfig(request: any): { otel?: boolean } | undefined {\n return request.routeOptions?.config ?? request.routeConfig;\n}\n\n/**\n * Detect whether one of a wrapped handler's arguments is the Fastify request. We can't rely on a\n * single property since the route metadata moved from `routerPath` (v3) to `routeOptions` (>=4),\n * so we accept either shape.\n */\nfunction isFastifyRequest(arg: any): boolean {\n return isObjectLike(arg) && !!arg.method && !!arg.url && (!!arg.routeOptions || 'routerPath' in arg);\n}\n\n/**\n * The Fastify plugin that wires up the request/hook/handler spans. It is registered on every Fastify\n * instance via the `fastify.initialization` diagnostics channel.\n */\nfunction fastifyOtelPlugin(this: unknown, instance: any, _opts: unknown, done: () => void): void {\n instance.decorate(kAddHookOriginal, instance.addHook);\n instance.decorate(kSetNotFoundOriginal, instance.setNotFoundHandler);\n instance.decorateRequest('opentelemetry', function opentelemetry(this: any) {\n return { span: this[kRequestSpan] as Span | null };\n });\n instance.decorateRequest(kRequestSpan, null);\n\n instance.addHook('onRoute', otelWireRoute);\n instance.addHook('onRequest', startRequestSpanHook);\n instance.addHook('onResponse', finalizeNotFoundSpanHook);\n\n instance.addHook = addHookPatched;\n instance.setNotFoundHandler = setNotFoundHandlerPatched;\n\n done();\n}\n\nconst pluginSymbols = fastifyOtelPlugin as unknown as Record<symbol, unknown>;\npluginSymbols[Symbol.for('skip-override')] = true;\npluginSymbols[Symbol.for('fastify.display-name')] = PACKAGE_NAME;\npluginSymbols[Symbol.for('plugin-meta')] = {\n fastify: SUPPORTED_VERSIONS,\n name: PACKAGE_NAME,\n};\n\nfunction otelWireRoute(this: any, routeOptions: any): void {\n if (routeOptions.config?.otel === false) {\n return;\n }\n\n for (const hook of FASTIFY_HOOKS) {\n const handlerLike = routeOptions[hook];\n\n if (typeof handlerLike === 'function') {\n routeOptions[hook] = handlerWrapper(\n handlerLike,\n hook,\n routeHookAttributes(this.pluginName, hook, handlerLike, routeOptions.url),\n );\n } else if (Array.isArray(handlerLike)) {\n routeOptions[hook] = handlerLike.map((handler: AnyFn) =>\n handlerWrapper(handler, hook, routeHookAttributes(this.pluginName, hook, handler, routeOptions.url)),\n );\n }\n }\n\n routeOptions.onSend = appendRouteHook(routeOptions.onSend, finalizeResponseSpanHook);\n routeOptions.onError = appendRouteHook(routeOptions.onError, recordErrorInSpanHook);\n\n routeOptions.handler = handlerWrapper(routeOptions.handler, 'handler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - route-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_HANDLER,\n [HTTP_ROUTE]: routeOptions.url,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n routeOptions.handler.name.length > 0 ? routeOptions.handler.name : ANONYMOUS_FUNCTION_NAME,\n });\n}\n\nfunction routeHookAttributes(pluginName: string, hook: string, handler: AnyFn, url: string): Record<string, string> {\n return {\n [ATTRIBUTE_HOOK_NAME]: `${pluginName} - route -> ${hook}`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_ROUTE,\n [HTTP_ROUTE]: url,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: handler.name?.length > 0 ? handler.name : ANONYMOUS_FUNCTION_NAME,\n };\n}\n\nfunction appendRouteHook(existing: AnyFn | AnyFn[] | undefined, hook: AnyFn): AnyFn | AnyFn[] {\n if (existing == null) {\n return hook;\n }\n return Array.isArray(existing) ? [...existing, hook] : [existing, hook];\n}\n\nfunction startRequestSpanHook(this: any, request: any, _reply: any, hookDone: () => void): void {\n if (getRequestRouteConfig(request)?.otel === false) {\n return hookDone();\n }\n\n const attributes: Record<string, string> = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [ATTRIBUTE_FASTIFY_ROOT]: PACKAGE_NAME,\n [HTTP_REQUEST_METHOD]: request.method,\n [URL_PATH]: request.url,\n };\n\n const route = getRequestRouteUrl(request);\n if (route != null) {\n attributes[HTTP_ROUTE] = route;\n\n // Update the route of the request on the root span, if it is a http.server span\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n if (rootSpan && spanToJSON(rootSpan).data[SEMANTIC_ATTRIBUTE_SENTRY_OP] === 'http.server') {\n rootSpan.setAttribute(HTTP_ROUTE, route);\n }\n }\n\n const requestSpan = startInactiveSpan({ name: 'request', op: REQUEST_HANDLER_OP, attributes });\n request[kRequestSpan] = requestSpan;\n\n // Set the request span as the active span for the remainder of the request lifecycle, so that\n // downstream hooks/handlers as well as errors captured via the error diagnostics channel are\n // parented to it (otherwise they would attach to the root `http.server` span instead).\n withActiveSpan(requestSpan, () => {\n hookDone();\n });\n}\n\nfunction finalizeNotFoundSpanHook(request: any, reply: any, hookDone: () => void): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n span.setAttributes({ [HTTP_RESPONSE_STATUS_CODE]: reply.statusCode });\n span.end();\n }\n\n request[kRequestSpan] = null;\n\n hookDone();\n}\n\nfunction finalizeResponseSpanHook(\n request: any,\n reply: any,\n payload: any,\n hookDone: (err: null, payload: any) => void,\n): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n if (reply.statusCode >= 500) {\n span.setStatus({ code: SPAN_STATUS_ERROR });\n }\n span.setAttributes({ [HTTP_RESPONSE_STATUS_CODE]: reply.statusCode });\n span.end();\n }\n\n request[kRequestSpan] = null;\n\n hookDone(null, payload);\n}\n\nfunction recordErrorInSpanHook(request: any, _reply: any, error: any, hookDone: () => void): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: error.message });\n }\n\n hookDone();\n}\n\nfunction addHookPatched(this: any, name: string, hook: AnyFn): unknown {\n const addHookOriginal = this[kAddHookOriginal];\n\n if (FASTIFY_HOOKS.includes(name)) {\n return addHookOriginal.call(\n this,\n name,\n handlerWrapper(hook, name, {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - ${name}`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: hook.name?.length > 0 ? hook.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n }\n\n return addHookOriginal.call(this, name, hook);\n}\n\nfunction setNotFoundHandlerPatched(this: any, hooks: any, handler?: any): void {\n const setNotFoundHandlerOriginal = this[kSetNotFoundOriginal];\n\n if (typeof hooks === 'function') {\n setNotFoundHandlerOriginal.call(\n this,\n handlerWrapper(hooks, 'notFoundHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: hooks.name?.length > 0 ? hooks.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n return;\n }\n\n if (hooks.preValidation != null) {\n hooks.preValidation = handlerWrapper(hooks.preValidation, 'notFoundHandler - preValidation', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler - preValidation`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n hooks.preValidation.name?.length > 0 ? hooks.preValidation.name : ANONYMOUS_FUNCTION_NAME,\n });\n }\n\n if (hooks.preHandler != null) {\n hooks.preHandler = handlerWrapper(hooks.preHandler, 'notFoundHandler - preHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler - preHandler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n hooks.preHandler.name?.length > 0 ? hooks.preHandler.name : ANONYMOUS_FUNCTION_NAME,\n });\n }\n\n // Fastify allows `setNotFoundHandler(opts)` without a handler, falling back to its built-in 404\n // handler. Forward the (already-wrapped) hooks unchanged so that fallback still applies.\n if (handler == null) {\n setNotFoundHandlerOriginal.call(this, hooks);\n return;\n }\n\n setNotFoundHandlerOriginal.call(\n this,\n hooks,\n handlerWrapper(handler, 'notFoundHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: handler.name?.length > 0 ? handler.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n}\n\nfunction getRequestFromArgs(args: any[]): any | null {\n for (const arg of args) {\n if (isFastifyRequest(arg)) {\n return arg;\n }\n }\n return null;\n}\n\nfunction handlerWrapper(handler: AnyFn, hookName: string, spanAttributes: Record<string, string> = {}): AnyFn {\n return function handlerWrapped(this: any, ...args: any[]) {\n const request = getRequestFromArgs(args);\n\n if (request === null || getRequestRouteConfig(request)?.otel === false) {\n return handler.call(this, ...args);\n }\n\n const parentSpan = (request[kRequestSpan] as Span | null) ?? undefined;\n const handlerName = handler.name?.length > 0 ? handler.name : (this.pluginName ?? ANONYMOUS_FUNCTION_NAME);\n\n const hookType = spanAttributes[ATTRIBUTE_FASTIFY_TYPE];\n const op =\n hookType === HOOK_TYPE_INSTANCE ? HOOK_OP : hookType === HOOK_TYPE_HANDLER ? REQUEST_HANDLER_OP : undefined;\n\n const name = op ? stripFastifyPrefix(spanAttributes[ATTRIBUTE_HOOK_NAME]) : `${hookName} - ${handlerName}`;\n\n return startSpan(\n {\n name,\n op,\n attributes: {\n ...spanAttributes,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n },\n parentSpan,\n },\n () => handler.call(this, ...args),\n );\n };\n}\n\n/**\n * Strip the framework/plugin prefixes from a Fastify `hook.name` to derive a readable span name.\n * This is a bit of a hack and does not always work for all spans, but it's the best we can do without a proper API.\n */\nfunction stripFastifyPrefix(hookName = ''): string {\n return hookName\n .replace(/^fastify -> /, '')\n .replace(/^@fastify\\/otel -> /, '')\n .replace(/^@sentry\\/instrumentation-fastify -> /, '');\n}\n\nfunction instrumentOnRequest(fastify: FastifyInstance): void {\n fastify.addHook('onRequest', async (request: FastifyRequest, _reply) => {\n const routeName = getRequestRouteUrl(request);\n const method = request.method || 'GET';\n\n getIsolationScope().setTransactionName(`${method} ${routeName}`);\n });\n}\n\nlet _isInstrumented = false;\n\n/**\n * Set up the Fastify (>= 3.21.0 < 6) instrumentation by subscribing to the `fastify.initialization`\n * diagnostics channel and registering the span-creating plugin on every Fastify instance.\n *\n * Idempotent and exposes an `id` so it can participate in the OpenTelemetry preload list.\n */\nexport const instrumentFastify = Object.assign(\n function instrumentFastify(): void {\n if (_isInstrumented) {\n return;\n }\n _isInstrumented = true;\n\n diagnosticsChannel.subscribe('fastify.initialization', message => {\n const fastifyInstance = (message as { fastify?: FastifyInstance }).fastify;\n\n fastifyInstance?.register(fastifyOtelPlugin).after(err => {\n if (err) {\n DEBUG_BUILD && debug.error('Failed to setup Fastify instrumentation', err);\n } else if (fastifyInstance) {\n instrumentOnRequest(fastifyInstance);\n }\n });\n });\n },\n { id: 'Fastify.v5' },\n);\n"],"names":["isObjectLike","HTTP_ROUTE","attributes","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","HTTP_REQUEST_METHOD","URL_PATH","getActiveSpan","getRootSpan","spanToJSON","SEMANTIC_ATTRIBUTE_SENTRY_OP","startInactiveSpan","withActiveSpan","HTTP_RESPONSE_STATUS_CODE","SPAN_STATUS_ERROR","startSpan","getIsolationScope","instrumentFastify","DEBUG_BUILD","debug"],"mappings":"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"} |
@@ -45,5 +45,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| (data) => { | ||
| if (!core.getActiveSpan()) { | ||
| return void 0; | ||
| } | ||
| const command = data.arguments?.[0]; | ||
@@ -62,2 +59,4 @@ if (!command || typeof command !== "object") { | ||
| { | ||
| // ioredis' `requireParentSpan` default: only create a span under an active span. | ||
| requiresParentSpan: true, | ||
| beforeSpanEnd(span, data) { | ||
@@ -74,13 +73,14 @@ if ("error" in data || !responseHook) { | ||
| ); | ||
| tracingChannel.bindTracingChannelToSpan(connectChannel, (data) => { | ||
| if (!core.getActiveSpan()) { | ||
| return void 0; | ||
| } | ||
| const { host, port } = getConnectionOptions(data.self); | ||
| return core.startInactiveSpan({ | ||
| name: "connect", | ||
| op: "db", | ||
| attributes: { ...connectionAttributes(host, port), [attributes.DB_STATEMENT]: "connect" } | ||
| }); | ||
| }); | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| connectChannel, | ||
| (data) => { | ||
| const { host, port } = getConnectionOptions(data.self); | ||
| return core.startInactiveSpan({ | ||
| name: "connect", | ||
| op: "db", | ||
| attributes: { ...connectionAttributes(host, port), [attributes.DB_STATEMENT]: "connect" } | ||
| }); | ||
| }, | ||
| { requiresParentSpan: true } | ||
| ); | ||
| }); | ||
@@ -87,0 +87,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"ioredis.js","sources":["../../../../src/integrations/tracing-channel/ioredis.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-deprecated -- we intentionally emit the OLD db/net semconv\n to match `@opentelemetry/instrumentation-ioredis` (and Sentry's `inferDbSpanData`, which keys off\n `db.statement`). TODO(v11): switch to the non-deprecated `db.system.name`/`db.query.text`/\n `server.address`/`server.port` conventions and drop this disable. */\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { DB_STATEMENT, DB_SYSTEM, NET_PEER_NAME, NET_PEER_PORT } from '@sentry/conventions/attributes';\nimport type { IntegrationFn, Span } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n getActiveSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { defaultDbStatementSerializer } from '../../redis/redis-statement-serializer';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Distinct from the OTel `Redis` integration, which is composite (node-redis +\n// ioredis + the >=5.11.0 diagnostics_channel subscriber) and stays in the set;\n// only its ioredis monkey-patch is gated off in the node SDK when this is active.\nconst INTEGRATION_NAME = 'IORedis' as const;\n\nconst ORIGIN = 'auto.db.orchestrion.redis';\n\n// todo(v11): Let's drop this as this is already covered with host and port\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\n\n/** Mirrors `@opentelemetry/instrumentation-ioredis`' response hook. Not called for failed commands. */\nexport type IORedisResponseHook = (span: Span, command: string, args: Array<string | Buffer>, result: unknown) => void;\n\nexport interface IORedisChannelIntegrationOptions {\n responseHook?: IORedisResponseHook;\n}\n\n/** Structural type for the command object ioredis passes to `sendCommand`. */\ninterface RedisCommand {\n name: string;\n args: Array<string | Buffer>;\n}\n\ninterface RedisClientLike {\n options?: { host?: string; port?: number };\n}\n\ninterface IORedisCommandContext {\n arguments?: unknown[];\n self?: RedisClientLike;\n result?: unknown;\n error?: unknown;\n}\n\ntype IORedisConnectContext = Omit<IORedisCommandContext, 'arguments'>;\n\nfunction getConnectionOptions(self: RedisClientLike | undefined): { host?: string; port?: number } {\n return { host: self?.options?.host, port: self?.options?.port };\n}\n\nfunction connectionAttributes(host: string | undefined, port: number | undefined): Record<string, unknown> {\n return {\n [DB_SYSTEM]: 'redis',\n [ATTR_DB_CONNECTION_STRING]: `redis://${host}:${port}`,\n [NET_PEER_NAME]: host,\n [NET_PEER_PORT]: port,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n };\n}\n\nconst _ioredisChannelIntegration = ((options: IORedisChannelIntegrationOptions = {}) => {\n const responseHook = options.responseHook;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD &&\n debug.log(`[orchestrion:ioredis] subscribing to \"${CHANNELS.IOREDIS_COMMAND}\"/\"${CHANNELS.IOREDIS_CONNECT}\"`);\n\n const commandChannel = diagnosticsChannel.tracingChannel<IORedisCommandContext, IORedisCommandContext>(\n CHANNELS.IOREDIS_COMMAND,\n );\n const connectChannel = diagnosticsChannel.tracingChannel<IORedisConnectContext, IORedisConnectContext>(\n CHANNELS.IOREDIS_CONNECT,\n );\n\n // `bindTracingChannelToSpan` uses `bindStore`, which needs the async-context\n // binding that `initOpenTelemetry()` registers after integration `setupOnce` —\n // defer until it's available (matches the native redis diagnostics-channel subscriber).\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan(\n commandChannel,\n data => {\n // ioredis' `requireParentSpan` default: only create a span under an active span.\n if (!getActiveSpan()) {\n return undefined;\n }\n const command = data.arguments?.[0] as RedisCommand | undefined;\n if (!command || typeof command !== 'object') {\n return undefined;\n }\n const { host, port } = getConnectionOptions(data.self);\n const statement = defaultDbStatementSerializer(command.name, command.args ?? []);\n return startInactiveSpan({\n name: statement,\n op: 'db',\n attributes: { ...connectionAttributes(host, port), [DB_STATEMENT]: statement },\n });\n },\n {\n beforeSpanEnd(span, data) {\n if ('error' in data || !responseHook) {\n return;\n }\n const command = data.arguments?.[0] as RedisCommand | undefined;\n if (command) {\n runResponseHook(responseHook, span, command, data.result);\n }\n },\n },\n );\n\n bindTracingChannelToSpan(connectChannel, data => {\n if (!getActiveSpan()) {\n return undefined;\n }\n const { host, port } = getConnectionOptions(data.self);\n return startInactiveSpan({\n name: 'connect',\n op: 'db',\n attributes: { ...connectionAttributes(host, port), [DB_STATEMENT]: 'connect' },\n });\n });\n });\n },\n };\n}) satisfies IntegrationFn;\n\nfunction runResponseHook(hook: IORedisResponseHook, span: Span, command: RedisCommand, result: unknown): void {\n try {\n hook(span, command.name, command.args, result);\n } catch {\n // never let a user-provided response hook break instrumentation\n }\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven ioredis integration. Subscribes to\n * `orchestrion:ioredis:command` / `:connect` (injected into ioredis' `<5.11.0`\n * `sendCommand`/`connect`) and creates db spans matching\n * `@opentelemetry/instrumentation-ioredis`. Requires the orchestrion runtime hook\n * or bundler plugin.\n */\nexport const ioredisChannelIntegration = defineIntegration(_ioredisChannelIntegration);\n"],"names":["DB_SYSTEM","NET_PEER_NAME","NET_PEER_PORT","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","DEBUG_BUILD","debug","CHANNELS","waitForTracingChannelBinding","bindTracingChannelToSpan","getActiveSpan","defaultDbStatementSerializer","startInactiveSpan","DB_STATEMENT","defineIntegration"],"mappings":";;;;;;;;;;AAuBA,MAAM,gBAAA,GAAmB,SAAA;AAEzB,MAAM,MAAA,GAAS,2BAAA;AAGf,MAAM,yBAAA,GAA4B,sBAAA;AA4BlC,SAAS,qBAAqB,IAAA,EAAqE;AACjG,EAAA,OAAO,EAAE,MAAM,IAAA,EAAM,OAAA,EAAS,MAAM,IAAA,EAAM,IAAA,EAAM,SAAS,IAAA,EAAK;AAChE;AAEA,SAAS,oBAAA,CAAqB,MAA0B,IAAA,EAAmD;AACzG,EAAA,OAAO;AAAA,IACL,CAACA,oBAAS,GAAG,OAAA;AAAA,IACb,CAAC,yBAAyB,GAAG,CAAA,QAAA,EAAW,IAAI,IAAI,IAAI,CAAA,CAAA;AAAA,IACpD,CAACC,wBAAa,GAAG,IAAA;AAAA,IACjB,CAACC,wBAAa,GAAG,IAAA;AAAA,IACjB,CAACC,qCAAgC,GAAG;AAAA,GACtC;AACF;AAEA,MAAM,0BAAA,IAA8B,CAAC,OAAA,GAA4C,EAAC,KAAM;AACtF,EAAA,MAAM,eAAe,OAAA,CAAQ,YAAA;AAE7B,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,mBAAmB,cAAA,EAAgB;AACtC,QAAA;AAAA,MACF;AAEA,MAAAC,sBAAA,IACEC,UAAA,CAAM,IAAI,CAAA,sCAAA,EAAyCC,iBAAA,CAAS,eAAe,CAAA,GAAA,EAAMA,iBAAA,CAAS,eAAe,CAAA,CAAA,CAAG,CAAA;AAE9G,MAAA,MAAM,iBAAiB,kBAAA,CAAmB,cAAA;AAAA,QACxCA,iBAAA,CAAS;AAAA,OACX;AACA,MAAA,MAAM,iBAAiB,kBAAA,CAAmB,cAAA;AAAA,QACxCA,iBAAA,CAAS;AAAA,OACX;AAKA,MAAAC,iCAAA,CAA6B,MAAM;AACjC,QAAAC,uCAAA;AAAA,UACE,cAAA;AAAA,UACA,CAAA,IAAA,KAAQ;AAEN,YAAA,IAAI,CAACC,oBAAc,EAAG;AACpB,cAAA,OAAO,MAAA;AAAA,YACT;AACA,YAAA,MAAM,OAAA,GAAU,IAAA,CAAK,SAAA,GAAY,CAAC,CAAA;AAClC,YAAA,IAAI,CAAC,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,EAAU;AAC3C,cAAA,OAAO,MAAA;AAAA,YACT;AACA,YAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,oBAAA,CAAqB,KAAK,IAAI,CAAA;AACrD,YAAA,MAAM,YAAYC,qDAAA,CAA6B,OAAA,CAAQ,MAAM,OAAA,CAAQ,IAAA,IAAQ,EAAE,CAAA;AAC/E,YAAA,OAAOC,sBAAA,CAAkB;AAAA,cACvB,IAAA,EAAM,SAAA;AAAA,cACN,EAAA,EAAI,IAAA;AAAA,cACJ,UAAA,EAAY,EAAE,GAAG,oBAAA,CAAqB,IAAA,EAAM,IAAI,CAAA,EAAG,CAACC,uBAAY,GAAG,SAAA;AAAU,aAC9E,CAAA;AAAA,UACH,CAAA;AAAA,UACA;AAAA,YACE,aAAA,CAAc,MAAM,IAAA,EAAM;AACxB,cAAA,IAAI,OAAA,IAAW,IAAA,IAAQ,CAAC,YAAA,EAAc;AACpC,gBAAA;AAAA,cACF;AACA,cAAA,MAAM,OAAA,GAAU,IAAA,CAAK,SAAA,GAAY,CAAC,CAAA;AAClC,cAAA,IAAI,OAAA,EAAS;AACX,gBAAA,eAAA,CAAgB,YAAA,EAAc,IAAA,EAAM,OAAA,EAAS,IAAA,CAAK,MAAM,CAAA;AAAA,cAC1D;AAAA,YACF;AAAA;AACF,SACF;AAEA,QAAAJ,uCAAA,CAAyB,gBAAgB,CAAA,IAAA,KAAQ;AAC/C,UAAA,IAAI,CAACC,oBAAc,EAAG;AACpB,YAAA,OAAO,MAAA;AAAA,UACT;AACA,UAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,oBAAA,CAAqB,KAAK,IAAI,CAAA;AACrD,UAAA,OAAOE,sBAAA,CAAkB;AAAA,YACvB,IAAA,EAAM,SAAA;AAAA,YACN,EAAA,EAAI,IAAA;AAAA,YACJ,UAAA,EAAY,EAAE,GAAG,oBAAA,CAAqB,IAAA,EAAM,IAAI,CAAA,EAAG,CAACC,uBAAY,GAAG,SAAA;AAAU,WAC9E,CAAA;AAAA,QACH,CAAC,CAAA;AAAA,MACH,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,eAAA,CAAgB,IAAA,EAA2B,IAAA,EAAY,OAAA,EAAuB,MAAA,EAAuB;AAC5G,EAAA,IAAI;AACF,IAAA,IAAA,CAAK,IAAA,EAAM,OAAA,CAAQ,IAAA,EAAM,OAAA,CAAQ,MAAM,MAAM,CAAA;AAAA,EAC/C,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AASO,MAAM,yBAAA,GAA4BC,uBAAkB,0BAA0B;;;;"} | ||
| {"version":3,"file":"ioredis.js","sources":["../../../../src/integrations/tracing-channel/ioredis.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-deprecated -- we intentionally emit the OLD db/net semconv\n to match `@opentelemetry/instrumentation-ioredis` (and Sentry's `inferDbSpanData`, which keys off\n `db.statement`). TODO(v11): switch to the non-deprecated `db.system.name`/`db.query.text`/\n `server.address`/`server.port` conventions and drop this disable. */\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { DB_STATEMENT, DB_SYSTEM, NET_PEER_NAME, NET_PEER_PORT } from '@sentry/conventions/attributes';\nimport type { IntegrationFn, Span } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { defaultDbStatementSerializer } from '../../redis/redis-statement-serializer';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Distinct from the OTel `Redis` integration, which is composite (node-redis +\n// ioredis + the >=5.11.0 diagnostics_channel subscriber) and stays in the set;\n// only its ioredis monkey-patch is gated off in the node SDK when this is active.\nconst INTEGRATION_NAME = 'IORedis' as const;\n\nconst ORIGIN = 'auto.db.orchestrion.redis';\n\n// todo(v11): Let's drop this as this is already covered with host and port\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\n\n/** Mirrors `@opentelemetry/instrumentation-ioredis`' response hook. Not called for failed commands. */\nexport type IORedisResponseHook = (span: Span, command: string, args: Array<string | Buffer>, result: unknown) => void;\n\nexport interface IORedisChannelIntegrationOptions {\n responseHook?: IORedisResponseHook;\n}\n\n/** Structural type for the command object ioredis passes to `sendCommand`. */\ninterface RedisCommand {\n name: string;\n args: Array<string | Buffer>;\n}\n\ninterface RedisClientLike {\n options?: { host?: string; port?: number };\n}\n\ninterface IORedisCommandContext {\n arguments?: unknown[];\n self?: RedisClientLike;\n result?: unknown;\n error?: unknown;\n}\n\ntype IORedisConnectContext = Omit<IORedisCommandContext, 'arguments'>;\n\nfunction getConnectionOptions(self: RedisClientLike | undefined): { host?: string; port?: number } {\n return { host: self?.options?.host, port: self?.options?.port };\n}\n\nfunction connectionAttributes(host: string | undefined, port: number | undefined): Record<string, unknown> {\n return {\n [DB_SYSTEM]: 'redis',\n [ATTR_DB_CONNECTION_STRING]: `redis://${host}:${port}`,\n [NET_PEER_NAME]: host,\n [NET_PEER_PORT]: port,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n };\n}\n\nconst _ioredisChannelIntegration = ((options: IORedisChannelIntegrationOptions = {}) => {\n const responseHook = options.responseHook;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD &&\n debug.log(`[orchestrion:ioredis] subscribing to \"${CHANNELS.IOREDIS_COMMAND}\"/\"${CHANNELS.IOREDIS_CONNECT}\"`);\n\n const commandChannel = diagnosticsChannel.tracingChannel<IORedisCommandContext, IORedisCommandContext>(\n CHANNELS.IOREDIS_COMMAND,\n );\n const connectChannel = diagnosticsChannel.tracingChannel<IORedisConnectContext, IORedisConnectContext>(\n CHANNELS.IOREDIS_CONNECT,\n );\n\n // `bindTracingChannelToSpan` uses `bindStore`, which needs the async-context\n // binding that `initOpenTelemetry()` registers after integration `setupOnce` —\n // defer until it's available (matches the native redis diagnostics-channel subscriber).\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan(\n commandChannel,\n data => {\n const command = data.arguments?.[0] as RedisCommand | undefined;\n if (!command || typeof command !== 'object') {\n return undefined;\n }\n const { host, port } = getConnectionOptions(data.self);\n const statement = defaultDbStatementSerializer(command.name, command.args ?? []);\n return startInactiveSpan({\n name: statement,\n op: 'db',\n attributes: { ...connectionAttributes(host, port), [DB_STATEMENT]: statement },\n });\n },\n {\n // ioredis' `requireParentSpan` default: only create a span under an active span.\n requiresParentSpan: true,\n beforeSpanEnd(span, data) {\n if ('error' in data || !responseHook) {\n return;\n }\n const command = data.arguments?.[0] as RedisCommand | undefined;\n if (command) {\n runResponseHook(responseHook, span, command, data.result);\n }\n },\n },\n );\n\n bindTracingChannelToSpan(\n connectChannel,\n data => {\n const { host, port } = getConnectionOptions(data.self);\n return startInactiveSpan({\n name: 'connect',\n op: 'db',\n attributes: { ...connectionAttributes(host, port), [DB_STATEMENT]: 'connect' },\n });\n },\n { requiresParentSpan: true },\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\nfunction runResponseHook(hook: IORedisResponseHook, span: Span, command: RedisCommand, result: unknown): void {\n try {\n hook(span, command.name, command.args, result);\n } catch {\n // never let a user-provided response hook break instrumentation\n }\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven ioredis integration. Subscribes to\n * `orchestrion:ioredis:command` / `:connect` (injected into ioredis' `<5.11.0`\n * `sendCommand`/`connect`) and creates db spans matching\n * `@opentelemetry/instrumentation-ioredis`. Requires the orchestrion runtime hook\n * or bundler plugin.\n */\nexport const ioredisChannelIntegration = defineIntegration(_ioredisChannelIntegration);\n"],"names":["DB_SYSTEM","NET_PEER_NAME","NET_PEER_PORT","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","DEBUG_BUILD","debug","CHANNELS","waitForTracingChannelBinding","bindTracingChannelToSpan","defaultDbStatementSerializer","startInactiveSpan","DB_STATEMENT","defineIntegration"],"mappings":"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"} |
@@ -79,3 +79,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| } | ||
| if (firstArg && typeof firstArg === "object" && "sql" in firstArg) { | ||
| if (core.isObjectLike(firstArg) && "sql" in firstArg) { | ||
| const sql = firstArg.sql; | ||
@@ -82,0 +82,0 @@ return typeof sql === "string" ? sql : void 0; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"mysql.js","sources":["../../../../src/integrations/tracing-channel/mysql.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope } from '@sentry/core';\nimport {\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, OTel 'Mysql' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Mysql' as const;\n\n// OTel \"OLD\" db/net semantic-conventions, inlined to keep this integration free of OTel deps. Matches\n// `@opentelemetry/instrumentation-mysql`'s default and the SDK's `inferDbSpanData` (which renames spans\n// off `db.statement`).\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\n\n/**\n * The shape orchestrion's transform attaches to the tracing-channel `context` object. Documented here\n * rather than imported because orchestrion's runtime doesn't export it.\n */\ninterface MysqlQueryChannelContext {\n // The live args array passed to the wrapped `connection.query` call; `arguments[0]` is the SQL.\n arguments: unknown[];\n self?: MysqlConnection;\n moduleVersion?: string;\n result?: unknown;\n error?: unknown;\n // The caller's scope, captured at `start` and replayed onto the streamed `Query` emitter (see below).\n _sentryCallerScope?: Scope;\n}\n\ninterface MysqlConnectionConfig {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n // Pool connections nest the real config one level deeper.\n connectionConfig?: MysqlConnectionConfig;\n}\n\ninterface MysqlConnection {\n config?: MysqlConnectionConfig;\n}\n\nconst _mysqlChannelIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD && debug.log(`[orchestrion:mysql] subscribing to channel \"${CHANNELS.MYSQL_QUERY}\"`);\n\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<MysqlQueryChannelContext>(CHANNELS.MYSQL_QUERY),\n data => {\n const sql = extractSql(data.arguments[0]);\n const { host, port, database, user } = getConnectionConfig(data.self);\n const portNumber = typeof port === 'string' ? parseInt(port, 10) : port;\n const portIsNumber = typeof portNumber === 'number' && !isNaN(portNumber);\n\n // For the streamed path: mysql emits the `Query` emitter's events from its socket data\n // handler with the caller's context lost. `deferSpanEnd` replays this scope onto the emitter.\n data._sentryCallerScope = getCurrentScope();\n\n return startInactiveSpan({\n name: sql ?? 'mysql.query',\n kind: SPAN_KIND.CLIENT,\n op: 'db',\n attributes: {\n [ATTR_DB_SYSTEM]: 'mysql',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.orchestrion.mysql',\n [ATTR_DB_CONNECTION_STRING]: getJDBCString(host, portIsNumber ? portNumber : undefined, database),\n ...(database ? { [ATTR_DB_NAME]: database } : {}),\n ...(user ? { [ATTR_DB_USER]: user } : {}),\n ...(sql ? { [ATTR_DB_STATEMENT]: sql } : {}),\n ...(host ? { [ATTR_NET_PEER_NAME]: host } : {}),\n ...(portIsNumber ? { [ATTR_NET_PEER_PORT]: portNumber } : {}),\n },\n });\n },\n {\n // No-callback `query(sql)` returns a streamable `Query` emitter as `result`; it settles on the\n // emitter's `'end'`/`'error'`, not the channel, so defer ending to those.\n deferSpanEnd({ data, end }) {\n const result = data.result;\n if (!result || typeof result !== 'object' || !hasOnMethod(result)) {\n return false;\n }\n\n // Replay the caller's scope so user listeners on the emitter nest under it, not a new trace.\n const callerScope = data._sentryCallerScope;\n if (callerScope) {\n bindScopeToEmitter(result, callerScope);\n }\n\n result.on('error', err => end(err));\n result.on('end', () => end());\n\n return true;\n },\n },\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\nfunction extractSql(firstArg: unknown): string | undefined {\n if (typeof firstArg === 'string') {\n return firstArg;\n }\n if (firstArg && typeof firstArg === 'object' && 'sql' in firstArg) {\n const sql = (firstArg as { sql?: unknown }).sql;\n return typeof sql === 'string' ? sql : undefined;\n }\n return undefined;\n}\n\nfunction getConnectionConfig(connection: MysqlConnection | undefined): {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n} {\n // Pool connections nest the real config under `.connectionConfig`; single\n // connections expose it directly. Matches `@opentelemetry/instrumentation-mysql`.\n const config = connection?.config?.connectionConfig ?? connection?.config ?? {};\n return {\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n };\n}\n\nfunction getJDBCString(host: string | undefined, port: number | undefined, database: string | undefined): string {\n let s = `jdbc:mysql://${host || 'localhost'}`;\n if (typeof port === 'number') {\n s += `:${port}`;\n }\n if (database) {\n s += `/${database}`;\n }\n return s;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven mysql integration.\n *\n * Subscribes to the `orchestrion:mysql:query` diagnostics_channel that the\n * orchestrion code transform injects into `mysql/lib/Connection.js`'s\n * `Connection.prototype.query`. Requires the orchestrion runtime hook or\n * bundler plugin to be active — wire that up via `_experimentalSetupOrchestrion`.\n */\nexport const mysqlChannelIntegration = defineIntegration(_mysqlChannelIntegration);\n"],"names":["DEBUG_BUILD","debug","CHANNELS","waitForTracingChannelBinding","bindTracingChannelToSpan","getCurrentScope","startInactiveSpan","SPAN_KIND","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","bindScopeToEmitter","defineIntegration"],"mappings":"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"} | ||
| {"version":3,"file":"mysql.js","sources":["../../../../src/integrations/tracing-channel/mysql.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope } from '@sentry/core';\nimport {\n isObjectLike,\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, OTel 'Mysql' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Mysql' as const;\n\n// OTel \"OLD\" db/net semantic-conventions, inlined to keep this integration free of OTel deps. Matches\n// `@opentelemetry/instrumentation-mysql`'s default and the SDK's `inferDbSpanData` (which renames spans\n// off `db.statement`).\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\n\n/**\n * The shape orchestrion's transform attaches to the tracing-channel `context` object. Documented here\n * rather than imported because orchestrion's runtime doesn't export it.\n */\ninterface MysqlQueryChannelContext {\n // The live args array passed to the wrapped `connection.query` call; `arguments[0]` is the SQL.\n arguments: unknown[];\n self?: MysqlConnection;\n moduleVersion?: string;\n result?: unknown;\n error?: unknown;\n // The caller's scope, captured at `start` and replayed onto the streamed `Query` emitter (see below).\n _sentryCallerScope?: Scope;\n}\n\ninterface MysqlConnectionConfig {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n // Pool connections nest the real config one level deeper.\n connectionConfig?: MysqlConnectionConfig;\n}\n\ninterface MysqlConnection {\n config?: MysqlConnectionConfig;\n}\n\nconst _mysqlChannelIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD && debug.log(`[orchestrion:mysql] subscribing to channel \"${CHANNELS.MYSQL_QUERY}\"`);\n\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<MysqlQueryChannelContext>(CHANNELS.MYSQL_QUERY),\n data => {\n const sql = extractSql(data.arguments[0]);\n const { host, port, database, user } = getConnectionConfig(data.self);\n const portNumber = typeof port === 'string' ? parseInt(port, 10) : port;\n const portIsNumber = typeof portNumber === 'number' && !isNaN(portNumber);\n\n // For the streamed path: mysql emits the `Query` emitter's events from its socket data\n // handler with the caller's context lost. `deferSpanEnd` replays this scope onto the emitter.\n data._sentryCallerScope = getCurrentScope();\n\n return startInactiveSpan({\n name: sql ?? 'mysql.query',\n kind: SPAN_KIND.CLIENT,\n op: 'db',\n attributes: {\n [ATTR_DB_SYSTEM]: 'mysql',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.orchestrion.mysql',\n [ATTR_DB_CONNECTION_STRING]: getJDBCString(host, portIsNumber ? portNumber : undefined, database),\n ...(database ? { [ATTR_DB_NAME]: database } : {}),\n ...(user ? { [ATTR_DB_USER]: user } : {}),\n ...(sql ? { [ATTR_DB_STATEMENT]: sql } : {}),\n ...(host ? { [ATTR_NET_PEER_NAME]: host } : {}),\n ...(portIsNumber ? { [ATTR_NET_PEER_PORT]: portNumber } : {}),\n },\n });\n },\n {\n // No-callback `query(sql)` returns a streamable `Query` emitter as `result`; it settles on the\n // emitter's `'end'`/`'error'`, not the channel, so defer ending to those.\n deferSpanEnd({ data, end }) {\n const result = data.result;\n if (!result || typeof result !== 'object' || !hasOnMethod(result)) {\n return false;\n }\n\n // Replay the caller's scope so user listeners on the emitter nest under it, not a new trace.\n const callerScope = data._sentryCallerScope;\n if (callerScope) {\n bindScopeToEmitter(result, callerScope);\n }\n\n result.on('error', err => end(err));\n result.on('end', () => end());\n\n return true;\n },\n },\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\nfunction extractSql(firstArg: unknown): string | undefined {\n if (typeof firstArg === 'string') {\n return firstArg;\n }\n if (isObjectLike(firstArg) && 'sql' in firstArg) {\n const sql = (firstArg as { sql?: unknown }).sql;\n return typeof sql === 'string' ? sql : undefined;\n }\n return undefined;\n}\n\nfunction getConnectionConfig(connection: MysqlConnection | undefined): {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n} {\n // Pool connections nest the real config under `.connectionConfig`; single\n // connections expose it directly. Matches `@opentelemetry/instrumentation-mysql`.\n const config = connection?.config?.connectionConfig ?? connection?.config ?? {};\n return {\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n };\n}\n\nfunction getJDBCString(host: string | undefined, port: number | undefined, database: string | undefined): string {\n let s = `jdbc:mysql://${host || 'localhost'}`;\n if (typeof port === 'number') {\n s += `:${port}`;\n }\n if (database) {\n s += `/${database}`;\n }\n return s;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven mysql integration.\n *\n * Subscribes to the `orchestrion:mysql:query` diagnostics_channel that the\n * orchestrion code transform injects into `mysql/lib/Connection.js`'s\n * `Connection.prototype.query`. Requires the orchestrion runtime hook or\n * bundler plugin to be active — wire that up via `_experimentalSetupOrchestrion`.\n */\nexport const mysqlChannelIntegration = defineIntegration(_mysqlChannelIntegration);\n"],"names":["DEBUG_BUILD","debug","CHANNELS","waitForTracingChannelBinding","bindTracingChannelToSpan","getCurrentScope","startInactiveSpan","SPAN_KIND","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","bindScopeToEmitter","isObjectLike","defineIntegration"],"mappings":"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"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"openai.js","sources":["../../../../src/integrations/tracing-channel/openai.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, OpenAiOptions, Span, SpanAttributeValue } from '@sentry/core';\nimport {\n _INTERNAL_shouldSkipAiProviderWrapping,\n addOpenAiRequestAttributes,\n addOpenAiResponseAttributes,\n debug,\n defineIntegration,\n extractOpenAiRequestAttributes,\n instrumentOpenAiStream,\n resolveAIRecordingOptions,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Same name as the OTel integration by design: when enabled, the OTel 'OpenAI'\n// integration is dropped from the default set (see the Node opt-in loader).\nconst INTEGRATION_NAME = 'OpenAI' as const;\n\n// Distinct from the proxy's `auto.ai.openai` so spans from the orchestrion path\n// are attributable separately from the OTel/proxy one.\nconst ORIGIN = 'auto.ai.orchestrion.openai';\n\n// Each instrumented `create` method maps to the gen_ai operation its span reports.\nconst INSTRUMENTED_CHANNELS = [\n { channel: CHANNELS.OPENAI_CHAT, operation: 'chat' },\n { channel: CHANNELS.OPENAI_RESPONSES, operation: 'chat' },\n { channel: CHANNELS.OPENAI_EMBEDDINGS, operation: 'embeddings' },\n { channel: CHANNELS.OPENAI_CONVERSATIONS, operation: 'chat' },\n] as const;\n\n/**\n * The context object orchestrion shares across the tracing-channel lifecycle hooks: `arguments` is the\n * live args array passed to `Completions.create(body, options)`, and Node's `tracingChannel` attaches\n * `result` when the returned promise settles.\n */\ninterface OpenAiChatChannelContext {\n arguments: unknown[];\n result?: unknown;\n}\n\nlet subscribed = false;\n\nconst _openaiChannelIntegration = ((options: OpenAiOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19, and a second `init()` would double-subscribe.\n if (!diagnosticsChannel.tracingChannel || subscribed) {\n return;\n }\n subscribed = true;\n\n // `bindTracingChannelToSpan` needs the async-context binding that `initOpenTelemetry()` registers\n // after `setupOnce` runs, so wait for it before subscribing.\n waitForTracingChannelBinding(() => {\n for (const { channel, operation } of INSTRUMENTED_CHANNELS) {\n DEBUG_BUILD && debug.log(`[orchestrion:openai] subscribing to channel \"${channel}\"`);\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<OpenAiChatChannelContext>(channel),\n data => createGenAiSpan(data, operation, options),\n {\n beforeSpanEnd: (span, data) => {\n addOpenAiResponseAttributes(span, data.result, resolveAIRecordingOptions(options).recordOutputs);\n },\n // Streaming: the result is a `Stream` consumed later, so instrument it and let it end the span.\n deferSpanEnd: ({ span, data }) => wrapStreamResult(span, data, options),\n },\n );\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Build the span for an instrumented `create` call.\n * Returning `undefined` opts the payload out so no span is opened.\n */\nfunction createGenAiSpan(data: OpenAiChatChannelContext, operation: string, options: OpenAiOptions): Span | undefined {\n // langchain drives the openai SDK itself and instruments at its own layer; skip here to avoid double spans.\n if (_INTERNAL_shouldSkipAiProviderWrapping(INTEGRATION_NAME)) {\n return undefined;\n }\n\n const args = data.arguments ?? [];\n const params = args[0] as Record<string, unknown> | undefined;\n\n const { recordInputs } = resolveAIRecordingOptions(options);\n const enableTruncation = shouldEnableTruncation(options.enableTruncation);\n\n const attributes = extractOpenAiRequestAttributes(args, operation);\n attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN;\n const model = (params?.model as string) || 'unknown';\n\n const span = startInactiveSpan({\n name: `${operation} ${model}`,\n op: `gen_ai.${operation}`,\n attributes: attributes as Record<string, SpanAttributeValue>,\n });\n\n if (recordInputs && params) {\n addOpenAiRequestAttributes(span, params, operation, enableTruncation);\n }\n\n return span;\n}\n\ntype AsyncIterableStream = { [Symbol.asyncIterator]: () => AsyncIterator<unknown> };\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterableStream {\n return !!value && typeof (value as AsyncIterableStream)[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * For a streaming `create({ stream: true })` the result is a `Stream` the caller consumes later. We can't\n * swap what `create` returns, but the `Stream` in `data.result` is the same instance the caller holds and\n * `asyncEnd` fires before the caller iterates — so we patch its async iterator in place to run through\n * `instrumentOpenAiStream`, which accumulates the streamed attributes and ends the span when iteration finishes.\n * Only a streaming call resolves to an async-iterable, so that check alone distinguishes it. Returns `true`\n * to hand span-ending ownership to `instrumentOpenAiStream`; `false` for non-streaming/errored results, which end\n * via the normal `beforeSpanEnd` path.\n */\nfunction wrapStreamResult(span: Span, data: OpenAiChatChannelContext, options: OpenAiOptions): boolean {\n const result = data.result;\n if (!isAsyncIterable(result)) {\n return false;\n }\n\n const { recordOutputs } = resolveAIRecordingOptions(options);\n const iterate = result[Symbol.asyncIterator].bind(result);\n const instrumented = instrumentOpenAiStream({ [Symbol.asyncIterator]: iterate }, span, recordOutputs ?? false);\n result[Symbol.asyncIterator] = () => instrumented;\n\n return true;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven OpenAI integration. Subscribes to the `orchestrion:openai:*`\n * diagnostics_channels injected into `openai`'s `create` methods (chat completions, responses, embeddings,\n * conversations), so it requires the orchestrion runtime hook or bundler plugin.\n */\nexport const openaiChannelIntegration = defineIntegration(_openaiChannelIntegration);\n"],"names":["CHANNELS","waitForTracingChannelBinding","DEBUG_BUILD","debug","bindTracingChannelToSpan","addOpenAiResponseAttributes","resolveAIRecordingOptions","_INTERNAL_shouldSkipAiProviderWrapping","shouldEnableTruncation","extractOpenAiRequestAttributes","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startInactiveSpan","addOpenAiRequestAttributes","instrumentOpenAiStream","defineIntegration"],"mappings":"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"} | ||
| {"version":3,"file":"openai.js","sources":["../../../../src/integrations/tracing-channel/openai.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, OpenAiOptions, Span, SpanAttributeValue } from '@sentry/core';\nimport {\n _INTERNAL_shouldSkipAiProviderWrapping,\n addOpenAiRequestAttributes,\n addOpenAiResponseAttributes,\n debug,\n defineIntegration,\n extractOpenAiRequestAttributes,\n instrumentOpenAiStream,\n resolveAIRecordingOptions,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Same name as the OTel integration by design: when enabled, the OTel 'OpenAI'\n// integration is dropped from the default set (see the Node opt-in loader).\nconst INTEGRATION_NAME = 'OpenAI' as const;\n\n// Distinct from the proxy's `auto.ai.openai` so spans from the orchestrion path\n// are attributable separately from the OTel/proxy one.\nconst ORIGIN = 'auto.ai.orchestrion.openai';\n\n// Each instrumented `create` method maps to the gen_ai operation its span reports.\nconst INSTRUMENTED_CHANNELS = [\n { channel: CHANNELS.OPENAI_CHAT, operation: 'chat' },\n { channel: CHANNELS.OPENAI_RESPONSES, operation: 'chat' },\n { channel: CHANNELS.OPENAI_EMBEDDINGS, operation: 'embeddings' },\n { channel: CHANNELS.OPENAI_CONVERSATIONS, operation: 'chat' },\n] as const;\n\n/**\n * The context object orchestrion shares across the tracing-channel lifecycle hooks: `arguments` is the\n * live args array passed to `Completions.create(body, options)`, and Node's `tracingChannel` attaches\n * `result` when the returned promise settles.\n */\ninterface OpenAiChatChannelContext {\n arguments: unknown[];\n result?: unknown;\n}\n\nlet subscribed = false;\n\nconst _openaiChannelIntegration = ((options: OpenAiOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19, and a second `init()` would double-subscribe.\n if (!diagnosticsChannel.tracingChannel || subscribed) {\n return;\n }\n subscribed = true;\n\n // `bindTracingChannelToSpan` needs the async-context binding that `initOpenTelemetry()` registers\n // after `setupOnce` runs, so wait for it before subscribing.\n waitForTracingChannelBinding(() => {\n for (const { channel, operation } of INSTRUMENTED_CHANNELS) {\n DEBUG_BUILD && debug.log(`[orchestrion:openai] subscribing to channel \"${channel}\"`);\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<OpenAiChatChannelContext>(channel),\n data => createGenAiSpan(data, operation, options),\n {\n beforeSpanEnd: (span, data) => {\n addOpenAiResponseAttributes(span, data.result, resolveAIRecordingOptions(options).recordOutputs);\n },\n // Streaming: the result is a `Stream` consumed later, so instrument it and let it end the span.\n deferSpanEnd: ({ span, data }) => wrapStreamResult(span, data, options),\n },\n );\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Build the span for an instrumented `create` call.\n * Returning `undefined` opts the payload out so no span is opened.\n */\nfunction createGenAiSpan(data: OpenAiChatChannelContext, operation: string, options: OpenAiOptions): Span | undefined {\n // When another provider (e.g. LangChain) is driving the SDK, it records the spans itself and marks this\n // provider as skipped; skip here to avoid double spans.\n if (_INTERNAL_shouldSkipAiProviderWrapping(INTEGRATION_NAME)) {\n return undefined;\n }\n\n const args = data.arguments ?? [];\n const params = args[0] as Record<string, unknown> | undefined;\n\n const { recordInputs } = resolveAIRecordingOptions(options);\n const enableTruncation = shouldEnableTruncation(options.enableTruncation);\n\n const attributes = extractOpenAiRequestAttributes(args, operation);\n attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN;\n const model = (params?.model as string) || 'unknown';\n\n const span = startInactiveSpan({\n name: `${operation} ${model}`,\n op: `gen_ai.${operation}`,\n attributes: attributes as Record<string, SpanAttributeValue>,\n });\n\n if (recordInputs && params) {\n addOpenAiRequestAttributes(span, params, operation, enableTruncation);\n }\n\n return span;\n}\n\ntype AsyncIterableStream = { [Symbol.asyncIterator]: () => AsyncIterator<unknown> };\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterableStream {\n return !!value && typeof (value as AsyncIterableStream)[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * For a streaming `create({ stream: true })` the result is a `Stream` the caller consumes later. We can't\n * swap what `create` returns, but the `Stream` in `data.result` is the same instance the caller holds and\n * `asyncEnd` fires before the caller iterates — so we patch its async iterator in place to run through\n * `instrumentOpenAiStream`, which accumulates the streamed attributes and ends the span when iteration finishes.\n * Only a streaming call resolves to an async-iterable, so that check alone distinguishes it. Returns `true`\n * to hand span-ending ownership to `instrumentOpenAiStream`; `false` for non-streaming/errored results, which end\n * via the normal `beforeSpanEnd` path.\n */\nfunction wrapStreamResult(span: Span, data: OpenAiChatChannelContext, options: OpenAiOptions): boolean {\n const result = data.result;\n if (!isAsyncIterable(result)) {\n return false;\n }\n\n const { recordOutputs } = resolveAIRecordingOptions(options);\n const iterate = result[Symbol.asyncIterator].bind(result);\n const instrumented = instrumentOpenAiStream({ [Symbol.asyncIterator]: iterate }, span, recordOutputs ?? false);\n result[Symbol.asyncIterator] = () => instrumented;\n\n return true;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven OpenAI integration. Subscribes to the `orchestrion:openai:*`\n * diagnostics_channels injected into `openai`'s `create` methods (chat completions, responses, embeddings,\n * conversations), so it requires the orchestrion runtime hook or bundler plugin.\n */\nexport const openaiChannelIntegration = defineIntegration(_openaiChannelIntegration);\n"],"names":["CHANNELS","waitForTracingChannelBinding","DEBUG_BUILD","debug","bindTracingChannelToSpan","addOpenAiResponseAttributes","resolveAIRecordingOptions","_INTERNAL_shouldSkipAiProviderWrapping","shouldEnableTruncation","extractOpenAiRequestAttributes","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startInactiveSpan","addOpenAiRequestAttributes","instrumentOpenAiStream","defineIntegration"],"mappings":"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"} |
@@ -47,5 +47,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| (data) => { | ||
| if (!core.getActiveSpan()) { | ||
| return void 0; | ||
| } | ||
| data._sentryCallerScope = core.getCurrentScope(); | ||
@@ -60,2 +57,5 @@ return core.startInactiveSpan({ ...getSpanOptions(data), kind: core.SPAN_KIND.CLIENT }); | ||
| deferStreamedResult ? { | ||
| // Only instrument under an active span, leaving the context untouched otherwise | ||
| // (e.g. connects issued during app startup). | ||
| requiresParentSpan: true, | ||
| // Streamable `Submittable` (e.g. `client.query(new Query())`) | ||
@@ -80,3 +80,3 @@ // returns an emitter that orchestrion stores on `ctx.result` while | ||
| } | ||
| } : void 0 | ||
| } : { requiresParentSpan: true } | ||
| ); | ||
@@ -115,3 +115,3 @@ } | ||
| } | ||
| if (arg0 && typeof arg0 === "object" && typeof arg0.text === "string") { | ||
| if (core.isObjectLike(arg0) && typeof arg0.text === "string") { | ||
| const obj = arg0; | ||
@@ -118,0 +118,0 @@ return { text: obj.text, name: obj.name }; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"postgres.js","sources":["../../../../src/integrations/tracing-channel/postgres.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope, SpanAttributes } from '@sentry/core';\nimport {\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getActiveSpan,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, the OTel 'Postgres' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Postgres' as const;\n\n// Only the query span carries an origin (the connect/pool-connect spans don't,\n// so they default to 'manual').\nconst ORIGIN = 'auto.db.orchestrion.postgres';\n\n// OpenTelemetry \"OLD\" db/net semantic-conventions, inlined to keep this\n// integration free of `@opentelemetry/*` deps.\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\nconst ATTR_PG_PLAN = 'db.postgresql.plan';\nconst ATTR_PG_IDLE_TIMEOUT = 'db.postgresql.idle.timeout.millis';\nconst ATTR_PG_MAX_CLIENT = 'db.postgresql.max.client';\nconst DB_SYSTEM_POSTGRESQL = 'postgresql';\n\n// We set `op: 'db'` and the SQL description directly here (same as mysql\n// orchestrion) rather than relying on the OTel pipeline's `inferDbSpanData`\n// processor, which only runs in the node SDK, so setting them here is what\n// makes the spans correct on the other runtimes\n//\n// The user-visible span is identical to OTel: query spans are named after\n// `db.statement`; connect/pool-connect spans keep these names.\nconst SPAN_QUERY_FALLBACK = 'pg.query';\nconst SPAN_CONNECT = 'pg.connect';\nconst SPAN_POOL_CONNECT = 'pg-pool.connect';\n\n/**\n * The shape orchestrion's transform attaches to the tracing-channel `context`. Documented here rather\n * than imported because orchestrion's runtime doesn't export it.\n */\ninterface PgChannelContext {\n // The live args array passed to the wrapped `query`/`connect` call.\n arguments: unknown[];\n self?: unknown;\n result?: unknown;\n error?: unknown;\n // The caller's scope, captured at `start` and replayed onto a streamed `Submittable` emitter (see below).\n _sentryCallerScope?: Scope;\n}\n\ninterface PgConnectionParams {\n database?: string;\n host?: string;\n port?: number;\n user?: string;\n connectionString?: string;\n}\n\ninterface PgPoolOptions extends PgConnectionParams {\n idleTimeoutMillis?: number;\n // pg-pool stores the max pool size as `max` (defaulting it to 10 in its\n // constructor). The OTel pg instrumentation reads a `maxClient` field that\n // pg-pool never sets, so its `db.postgresql.max.client` attribute is always\n // dropped; we read the real `max` so the attribute is actually populated.\n max?: number;\n}\n\nconst _postgresChannelIntegration = ((options: { ignoreConnectSpans?: boolean } = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n // Query spans: `pg`/native `Client.prototype.query`. Only this channel can return a streamable\n // `Submittable` result, so it's the only one that defers span-ending to the emitter (see below).\n subscribeQueryLikeChannel(CHANNELS.PG_QUERY, querySpanOptions, { deferStreamedResult: true });\n\n // Connect spans, gated by `ignoreConnectSpans` (same as OTel pg).\n // `Client.prototype.connect` (pg + native)\n // and `Pool.prototype.connect` (pg-pool).\n if (!options.ignoreConnectSpans) {\n subscribeQueryLikeChannel(CHANNELS.PG_CONNECT, connectSpanOptions);\n subscribeQueryLikeChannel(CHANNELS.PGPOOL_CONNECT, poolConnectSpanOptions);\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Subscribe to a pg tracing-channel and manage a span across its lifecycle.\n * Shared by the query/connect/pool-connect channels. They differ only in how\n * the span's name + attributes are built (`getSpanOptions`), and whether the\n * result can be a streamable emitter (`deferStreamedResult`, query-only).\n */\nfunction subscribeQueryLikeChannel(\n channelName: string,\n getSpanOptions: (ctx: PgChannelContext) => { name: string; op: string; attributes: SpanAttributes },\n { deferStreamedResult = false }: { deferStreamedResult?: boolean } = {},\n): void {\n DEBUG_BUILD && debug.log(`[orchestrion:pg] subscribing to channel \"${channelName}\"`);\n\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<PgChannelContext>(channelName),\n data => {\n // Only instrument when there's an active span; returning `undefined` opts this call out entirely,\n // leaving the active context untouched (e.g. connects issued during app startup).\n if (!getActiveSpan()) {\n return undefined;\n }\n\n // Capture the caller's scope while still synchronously inside the call, for the streamed path:\n // pg dispatches a `Submittable` emitter's events outside the original async scope, so `deferSpanEnd`\n // replays this scope onto that emitter.\n data._sentryCallerScope = getCurrentScope();\n\n // `kind: CLIENT` mirrors the OTel pg instrumentation, so the emitted\n // `otel.kind` matches across the OTel and diagnostics-channel paths.\n return startInactiveSpan({ ...getSpanOptions(data), kind: SPAN_KIND.CLIENT });\n },\n // `connect`/`pool-connect` resolve with a persistent `Client` (itself an\n // `EventEmitter`), which is NOT a streamed result. Deferring their span\n // to that emitter's `'end'`/`'error'` would keep it open for the whole\n // connection lifetime, so it never ends in time and is dropped. Only\n // `query` can return a streamable `Submittable`, so only it defers.\n deferStreamedResult\n ? {\n // Streamable `Submittable` (e.g. `client.query(new Query())`)\n // returns an emitter that orchestrion stores on `ctx.result` while\n // firing no async events; the query isn't done until the emitter\n // emits `'end'`/`'error'`. Defer ending to those events for that\n // path; the callback, promise, and sync-throw paths carry no\n // emitter, so the helper ends the span as usual.\n deferSpanEnd({ data, end }) {\n const result = data.result;\n if (!result || typeof result !== 'object' || !hasOnMethod(result)) {\n return false;\n }\n\n // Replay the caller's scope onto the emitter so listeners theu\n // user attaches after the call returns (and any spans they start)\n // nest under the caller, not a fresh root trace.\n const callerScope = data._sentryCallerScope;\n if (callerScope) {\n bindScopeToEmitter(result, callerScope);\n }\n\n result.on('error', err => end(err));\n result.on('end', () => end());\n\n return true;\n },\n }\n : undefined,\n );\n}\n\nfunction querySpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const params = (ctx.self as { connectionParameters?: PgConnectionParams } | undefined)?.connectionParameters ?? {};\n const queryConfig = extractQueryConfig(ctx.arguments);\n return {\n // The description is the SQL statement\n name: queryConfig?.text ?? SPAN_QUERY_FALLBACK,\n op: 'db',\n attributes: {\n ...getConnectionAttributes(params),\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [ATTR_DB_STATEMENT]: queryConfig?.text || undefined,\n [ATTR_PG_PLAN]: typeof queryConfig?.name === 'string' ? queryConfig.name : undefined,\n },\n };\n}\n\nfunction connectSpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const params = (ctx.self as { connectionParameters?: PgConnectionParams } | undefined)?.connectionParameters ?? {};\n // No origin set -> defaults to 'manual'\n return { name: SPAN_CONNECT, op: 'db', attributes: getConnectionAttributes(params) };\n}\n\nfunction poolConnectSpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const opts = (ctx.self as { options?: PgPoolOptions } | undefined)?.options ?? {};\n return { name: SPAN_POOL_CONNECT, op: 'db', attributes: getPoolConnectionAttributes(opts) };\n}\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\n// `client.query(text, cb?)`, `client.query(text, values, cb?)`, and\n// `client.query(configObj, cb?)` are all valid; normalize to `{ text, name }`\n// (the only fields the span needs). Returns undefined for invalid args.\nfunction extractQueryConfig(args: unknown[]): { text: string; name?: unknown } | undefined {\n const arg0 = args[0];\n if (typeof arg0 === 'string') {\n return { text: arg0 };\n }\n if (arg0 && typeof arg0 === 'object' && typeof (arg0 as { text?: unknown }).text === 'string') {\n const obj = arg0 as { text: string; name?: unknown };\n return { text: obj.text, name: obj.name };\n }\n return undefined;\n}\n\nfunction getConnectionAttributes(params: PgConnectionParams): SpanAttributes {\n return {\n [ATTR_DB_SYSTEM]: DB_SYSTEM_POSTGRESQL,\n [ATTR_DB_CONNECTION_STRING]: getConnectionString(params),\n [ATTR_DB_NAME]: params.database,\n [ATTR_DB_USER]: params.user,\n [ATTR_NET_PEER_NAME]: params.host,\n [ATTR_NET_PEER_PORT]: Number.isInteger(params.port) ? params.port : undefined,\n };\n}\n\nfunction getPoolConnectionAttributes(opts: PgPoolOptions): SpanAttributes {\n let url: URL | undefined;\n try {\n url = opts.connectionString ? new URL(opts.connectionString) : undefined;\n } catch {\n url = undefined;\n }\n const database = url?.pathname.slice(1) || opts.database;\n const host = url?.hostname || opts.host;\n // Mirror OTel's `getSemanticAttributesFromPoolConnection`: prefer the URL's\n // port, but fall back to an explicit `opts.port` when the connection string\n // omits it (`Number('')` / `Number(undefined)` -> falsy).\n const port = Number(url?.port) || (Number.isInteger(opts.port) ? opts.port : undefined);\n const user = url?.username || opts.user;\n return {\n [ATTR_DB_SYSTEM]: DB_SYSTEM_POSTGRESQL,\n [ATTR_DB_CONNECTION_STRING]: getConnectionString(opts),\n [ATTR_PG_IDLE_TIMEOUT]: opts.idleTimeoutMillis,\n [ATTR_PG_MAX_CLIENT]: opts.max,\n [ATTR_DB_NAME]: database,\n [ATTR_NET_PEER_PORT]: port,\n // these two come from a url parse and slice, can be ''\n [ATTR_NET_PEER_NAME]: host || undefined,\n [ATTR_DB_USER]: user || undefined,\n };\n}\n\n// Builds `postgresql://host:port/database`, masking credentials when a raw\n// connection string was provided.\nfunction getConnectionString(params: PgConnectionParams): string {\n if (params.connectionString) {\n try {\n const url = new URL(params.connectionString);\n url.username = '';\n url.password = '';\n return url.toString();\n } catch {\n return 'postgresql://localhost:5432/';\n }\n }\n const host = params.host || 'localhost';\n const port = params.port || 5432;\n const database = params.database || '';\n return `postgresql://${host}:${port}/${database}`;\n}\n\n/**\n * EXPERIMENTAL: orchestrion-driven `pg` (node-postgres) integration.\n *\n * Subscribes to the `orchestrion:pg:query`/`:connect` and\n * `orchestrion:pg-pool:connect` diagnostics_channels that the orchestrion code\n * transform injects into `pg`'s `Client.prototype.query`/`connect`\n * and `pg-pool`'s `Pool.prototype.connect`. Requires the orchestrion runtime\n * hook or bundler plugin to be active.\n */\nexport const postgresChannelIntegration = defineIntegration(_postgresChannelIntegration);\n"],"names":["waitForTracingChannelBinding","CHANNELS","DEBUG_BUILD","debug","bindTracingChannelToSpan","getActiveSpan","getCurrentScope","startInactiveSpan","SPAN_KIND","bindScopeToEmitter","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","defineIntegration"],"mappings":"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"} | ||
| {"version":3,"file":"postgres.js","sources":["../../../../src/integrations/tracing-channel/postgres.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope, SpanAttributes } from '@sentry/core';\nimport {\n isObjectLike,\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, the OTel 'Postgres' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Postgres' as const;\n\n// Only the query span carries an origin (the connect/pool-connect spans don't,\n// so they default to 'manual').\nconst ORIGIN = 'auto.db.orchestrion.postgres';\n\n// OpenTelemetry \"OLD\" db/net semantic-conventions, inlined to keep this\n// integration free of `@opentelemetry/*` deps.\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\nconst ATTR_PG_PLAN = 'db.postgresql.plan';\nconst ATTR_PG_IDLE_TIMEOUT = 'db.postgresql.idle.timeout.millis';\nconst ATTR_PG_MAX_CLIENT = 'db.postgresql.max.client';\nconst DB_SYSTEM_POSTGRESQL = 'postgresql';\n\n// We set `op: 'db'` and the SQL description directly here (same as mysql\n// orchestrion) rather than relying on the OTel pipeline's `inferDbSpanData`\n// processor, which only runs in the node SDK, so setting them here is what\n// makes the spans correct on the other runtimes\n//\n// The user-visible span is identical to OTel: query spans are named after\n// `db.statement`; connect/pool-connect spans keep these names.\nconst SPAN_QUERY_FALLBACK = 'pg.query';\nconst SPAN_CONNECT = 'pg.connect';\nconst SPAN_POOL_CONNECT = 'pg-pool.connect';\n\n/**\n * The shape orchestrion's transform attaches to the tracing-channel `context`. Documented here rather\n * than imported because orchestrion's runtime doesn't export it.\n */\ninterface PgChannelContext {\n // The live args array passed to the wrapped `query`/`connect` call.\n arguments: unknown[];\n self?: unknown;\n result?: unknown;\n error?: unknown;\n // The caller's scope, captured at `start` and replayed onto a streamed `Submittable` emitter (see below).\n _sentryCallerScope?: Scope;\n}\n\ninterface PgConnectionParams {\n database?: string;\n host?: string;\n port?: number;\n user?: string;\n connectionString?: string;\n}\n\ninterface PgPoolOptions extends PgConnectionParams {\n idleTimeoutMillis?: number;\n // pg-pool stores the max pool size as `max` (defaulting it to 10 in its\n // constructor). The OTel pg instrumentation reads a `maxClient` field that\n // pg-pool never sets, so its `db.postgresql.max.client` attribute is always\n // dropped; we read the real `max` so the attribute is actually populated.\n max?: number;\n}\n\nconst _postgresChannelIntegration = ((options: { ignoreConnectSpans?: boolean } = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n // Query spans: `pg`/native `Client.prototype.query`. Only this channel can return a streamable\n // `Submittable` result, so it's the only one that defers span-ending to the emitter (see below).\n subscribeQueryLikeChannel(CHANNELS.PG_QUERY, querySpanOptions, { deferStreamedResult: true });\n\n // Connect spans, gated by `ignoreConnectSpans` (same as OTel pg).\n // `Client.prototype.connect` (pg + native)\n // and `Pool.prototype.connect` (pg-pool).\n if (!options.ignoreConnectSpans) {\n subscribeQueryLikeChannel(CHANNELS.PG_CONNECT, connectSpanOptions);\n subscribeQueryLikeChannel(CHANNELS.PGPOOL_CONNECT, poolConnectSpanOptions);\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Subscribe to a pg tracing-channel and manage a span across its lifecycle.\n * Shared by the query/connect/pool-connect channels. They differ only in how\n * the span's name + attributes are built (`getSpanOptions`), and whether the\n * result can be a streamable emitter (`deferStreamedResult`, query-only).\n */\nfunction subscribeQueryLikeChannel(\n channelName: string,\n getSpanOptions: (ctx: PgChannelContext) => { name: string; op: string; attributes: SpanAttributes },\n { deferStreamedResult = false }: { deferStreamedResult?: boolean } = {},\n): void {\n DEBUG_BUILD && debug.log(`[orchestrion:pg] subscribing to channel \"${channelName}\"`);\n\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<PgChannelContext>(channelName),\n data => {\n // Capture the caller's scope while still synchronously inside the call, for the streamed path:\n // pg dispatches a `Submittable` emitter's events outside the original async scope, so `deferSpanEnd`\n // replays this scope onto that emitter.\n data._sentryCallerScope = getCurrentScope();\n\n // `kind: CLIENT` mirrors the OTel pg instrumentation, so the emitted\n // `otel.kind` matches across the OTel and diagnostics-channel paths.\n return startInactiveSpan({ ...getSpanOptions(data), kind: SPAN_KIND.CLIENT });\n },\n // `connect`/`pool-connect` resolve with a persistent `Client` (itself an\n // `EventEmitter`), which is NOT a streamed result. Deferring their span\n // to that emitter's `'end'`/`'error'` would keep it open for the whole\n // connection lifetime, so it never ends in time and is dropped. Only\n // `query` can return a streamable `Submittable`, so only it defers.\n deferStreamedResult\n ? {\n // Only instrument under an active span, leaving the context untouched otherwise\n // (e.g. connects issued during app startup).\n requiresParentSpan: true,\n // Streamable `Submittable` (e.g. `client.query(new Query())`)\n // returns an emitter that orchestrion stores on `ctx.result` while\n // firing no async events; the query isn't done until the emitter\n // emits `'end'`/`'error'`. Defer ending to those events for that\n // path; the callback, promise, and sync-throw paths carry no\n // emitter, so the helper ends the span as usual.\n deferSpanEnd({ data, end }) {\n const result = data.result;\n if (!result || typeof result !== 'object' || !hasOnMethod(result)) {\n return false;\n }\n\n // Replay the caller's scope onto the emitter so listeners theu\n // user attaches after the call returns (and any spans they start)\n // nest under the caller, not a fresh root trace.\n const callerScope = data._sentryCallerScope;\n if (callerScope) {\n bindScopeToEmitter(result, callerScope);\n }\n\n result.on('error', err => end(err));\n result.on('end', () => end());\n\n return true;\n },\n }\n : { requiresParentSpan: true },\n );\n}\n\nfunction querySpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const params = (ctx.self as { connectionParameters?: PgConnectionParams } | undefined)?.connectionParameters ?? {};\n const queryConfig = extractQueryConfig(ctx.arguments);\n return {\n // The description is the SQL statement\n name: queryConfig?.text ?? SPAN_QUERY_FALLBACK,\n op: 'db',\n attributes: {\n ...getConnectionAttributes(params),\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [ATTR_DB_STATEMENT]: queryConfig?.text || undefined,\n [ATTR_PG_PLAN]: typeof queryConfig?.name === 'string' ? queryConfig.name : undefined,\n },\n };\n}\n\nfunction connectSpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const params = (ctx.self as { connectionParameters?: PgConnectionParams } | undefined)?.connectionParameters ?? {};\n // No origin set -> defaults to 'manual'\n return { name: SPAN_CONNECT, op: 'db', attributes: getConnectionAttributes(params) };\n}\n\nfunction poolConnectSpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const opts = (ctx.self as { options?: PgPoolOptions } | undefined)?.options ?? {};\n return { name: SPAN_POOL_CONNECT, op: 'db', attributes: getPoolConnectionAttributes(opts) };\n}\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\n// `client.query(text, cb?)`, `client.query(text, values, cb?)`, and\n// `client.query(configObj, cb?)` are all valid; normalize to `{ text, name }`\n// (the only fields the span needs). Returns undefined for invalid args.\nfunction extractQueryConfig(args: unknown[]): { text: string; name?: unknown } | undefined {\n const arg0 = args[0];\n if (typeof arg0 === 'string') {\n return { text: arg0 };\n }\n if (isObjectLike(arg0) && typeof (arg0 as { text?: unknown }).text === 'string') {\n const obj = arg0 as { text: string; name?: unknown };\n return { text: obj.text, name: obj.name };\n }\n return undefined;\n}\n\nfunction getConnectionAttributes(params: PgConnectionParams): SpanAttributes {\n return {\n [ATTR_DB_SYSTEM]: DB_SYSTEM_POSTGRESQL,\n [ATTR_DB_CONNECTION_STRING]: getConnectionString(params),\n [ATTR_DB_NAME]: params.database,\n [ATTR_DB_USER]: params.user,\n [ATTR_NET_PEER_NAME]: params.host,\n [ATTR_NET_PEER_PORT]: Number.isInteger(params.port) ? params.port : undefined,\n };\n}\n\nfunction getPoolConnectionAttributes(opts: PgPoolOptions): SpanAttributes {\n let url: URL | undefined;\n try {\n url = opts.connectionString ? new URL(opts.connectionString) : undefined;\n } catch {\n url = undefined;\n }\n const database = url?.pathname.slice(1) || opts.database;\n const host = url?.hostname || opts.host;\n // Mirror OTel's `getSemanticAttributesFromPoolConnection`: prefer the URL's\n // port, but fall back to an explicit `opts.port` when the connection string\n // omits it (`Number('')` / `Number(undefined)` -> falsy).\n const port = Number(url?.port) || (Number.isInteger(opts.port) ? opts.port : undefined);\n const user = url?.username || opts.user;\n return {\n [ATTR_DB_SYSTEM]: DB_SYSTEM_POSTGRESQL,\n [ATTR_DB_CONNECTION_STRING]: getConnectionString(opts),\n [ATTR_PG_IDLE_TIMEOUT]: opts.idleTimeoutMillis,\n [ATTR_PG_MAX_CLIENT]: opts.max,\n [ATTR_DB_NAME]: database,\n [ATTR_NET_PEER_PORT]: port,\n // these two come from a url parse and slice, can be ''\n [ATTR_NET_PEER_NAME]: host || undefined,\n [ATTR_DB_USER]: user || undefined,\n };\n}\n\n// Builds `postgresql://host:port/database`, masking credentials when a raw\n// connection string was provided.\nfunction getConnectionString(params: PgConnectionParams): string {\n if (params.connectionString) {\n try {\n const url = new URL(params.connectionString);\n url.username = '';\n url.password = '';\n return url.toString();\n } catch {\n return 'postgresql://localhost:5432/';\n }\n }\n const host = params.host || 'localhost';\n const port = params.port || 5432;\n const database = params.database || '';\n return `postgresql://${host}:${port}/${database}`;\n}\n\n/**\n * EXPERIMENTAL: orchestrion-driven `pg` (node-postgres) integration.\n *\n * Subscribes to the `orchestrion:pg:query`/`:connect` and\n * `orchestrion:pg-pool:connect` diagnostics_channels that the orchestrion code\n * transform injects into `pg`'s `Client.prototype.query`/`connect`\n * and `pg-pool`'s `Pool.prototype.connect`. Requires the orchestrion runtime\n * hook or bundler plugin to be active.\n */\nexport const postgresChannelIntegration = defineIntegration(_postgresChannelIntegration);\n"],"names":["waitForTracingChannelBinding","CHANNELS","DEBUG_BUILD","debug","bindTracingChannelToSpan","getCurrentScope","startInactiveSpan","SPAN_KIND","bindScopeToEmitter","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","isObjectLike","defineIntegration"],"mappings":"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"} |
@@ -6,3 +6,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const vercelAiOrchestrionV6Subscriber = require('../../vercel-ai/vercel-ai-orchestrion-v6-subscriber.js'); | ||
| const vercelAiOrchestrionSubscriber = require('../../vercel-ai/vercel-ai-orchestrion-subscriber.js'); | ||
@@ -18,3 +18,3 @@ const _vercelAiChannelIntegration = ((options = {}) => { | ||
| core.waitForTracingChannelBinding(() => { | ||
| vercelAiOrchestrionV6Subscriber.subscribeVercelAiOrchestrionChannels(diagnosticsChannel.tracingChannel, options); | ||
| vercelAiOrchestrionSubscriber.subscribeVercelAiOrchestrionChannels(diagnosticsChannel.tracingChannel, options); | ||
| }); | ||
@@ -21,0 +21,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"vercel-ai.js","sources":["../../../../src/integrations/tracing-channel/vercel-ai.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';\nimport { vercelAiIntegration as baseVercelAiIntegration } from '../../vercel-ai';\nimport * as dc from 'node:diagnostics_channel';\nimport { subscribeVercelAiOrchestrionChannels } from '../../vercel-ai/vercel-ai-orchestrion-v6-subscriber';\n\ntype VercelAiOptions = Parameters<typeof baseVercelAiIntegration>[0];\n\n// In channel-based (orchestrion) mode we emit our own `gen_ai.*` spans from the\n// diagnostics channels. The `ai` SDK would otherwise emit its own native\n// OpenTelemetry spans whenever the user enables `experimental_telemetry`, which\n// would be duplicates. Rather than dropping those after the fact, the v6\n// subscriber suppresses them at the source: it flips the wrapped call's\n// `experimental_telemetry.isEnabled` to `false`, so `ai` falls back to its\n// internal no-op tracer and never creates the native spans in the first place.\n// See `subscribeVercelAiOrchestrionChannels`.\nconst _vercelAiChannelIntegration = ((options: VercelAiOptions = {}) => {\n const parentIntegration = baseVercelAiIntegration(options);\n\n return extendIntegration(parentIntegration, {\n options,\n setupOnce() {\n // Bail if this is not available\n if (!dc.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n subscribeVercelAiOrchestrionChannels(dc.tracingChannel, options);\n });\n },\n });\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the `ai` SDK. Supported are:\n * - v7 via native `ai:telemetry` tracing channel\n * - v6 via orchestrion `orchestrion:ai:*` channels\n */\nexport const vercelAiChannelIntegration = defineIntegration(_vercelAiChannelIntegration);\n"],"names":["baseVercelAiIntegration","extendIntegration","dc","waitForTracingChannelBinding","subscribeVercelAiOrchestrionChannels","defineIntegration"],"mappings":";;;;;;;AAgBA,MAAM,2BAAA,IAA+B,CAAC,OAAA,GAA2B,EAAC,KAAM;AACtE,EAAA,MAAM,iBAAA,GAAoBA,0BAAwB,OAAO,CAAA;AAEzD,EAAA,OAAOC,uBAAkB,iBAAA,EAAmB;AAAA,IAC1C,OAAA;AAAA,IACA,SAAA,GAAY;AAEV,MAAA,IAAI,CAACC,mBAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAEA,MAAAC,iCAAA,CAA6B,MAAM;AACjC,QAAAC,oEAAA,CAAqCF,kBAAA,CAAG,gBAAgB,OAAO,CAAA;AAAA,MACjE,CAAC,CAAA;AAAA,IACH;AAAA,GACD,CAAA;AACH,CAAA,CAAA;AAOO,MAAM,0BAAA,GAA6BG,uBAAkB,2BAA2B;;;;"} | ||
| {"version":3,"file":"vercel-ai.js","sources":["../../../../src/integrations/tracing-channel/vercel-ai.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';\nimport { vercelAiIntegration as baseVercelAiIntegration } from '../../vercel-ai';\nimport * as dc from 'node:diagnostics_channel';\nimport { subscribeVercelAiOrchestrionChannels } from '../../vercel-ai/vercel-ai-orchestrion-subscriber';\n\ntype VercelAiOptions = Parameters<typeof baseVercelAiIntegration>[0];\n\n// In channel-based (orchestrion) mode we emit our own `gen_ai.*` spans from the\n// diagnostics channels. The `ai` SDK would otherwise emit its own native\n// OpenTelemetry spans whenever the user enables `experimental_telemetry`, which\n// would be duplicates. Rather than dropping those after the fact, the v6\n// subscriber suppresses them at the source: it flips the wrapped call's\n// `experimental_telemetry.isEnabled` to `false`, so `ai` falls back to its\n// internal no-op tracer and never creates the native spans in the first place.\n// See `subscribeVercelAiOrchestrionChannels`.\nconst _vercelAiChannelIntegration = ((options: VercelAiOptions = {}) => {\n const parentIntegration = baseVercelAiIntegration(options);\n\n return extendIntegration(parentIntegration, {\n options,\n setupOnce() {\n // Bail if this is not available\n if (!dc.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n subscribeVercelAiOrchestrionChannels(dc.tracingChannel, options);\n });\n },\n });\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the `ai` SDK. Supported are:\n * - v7 via native `ai:telemetry` tracing channel\n * - v4, v5 & v6 via orchestrion `orchestrion:ai:*` channels\n */\nexport const vercelAiChannelIntegration = defineIntegration(_vercelAiChannelIntegration);\n"],"names":["baseVercelAiIntegration","extendIntegration","dc","waitForTracingChannelBinding","subscribeVercelAiOrchestrionChannels","defineIntegration"],"mappings":";;;;;;;AAgBA,MAAM,2BAAA,IAA+B,CAAC,OAAA,GAA2B,EAAC,KAAM;AACtE,EAAA,MAAM,iBAAA,GAAoBA,0BAAwB,OAAO,CAAA;AAEzD,EAAA,OAAOC,uBAAkB,iBAAA,EAAmB;AAAA,IAC1C,OAAA;AAAA,IACA,SAAA,GAAY;AAEV,MAAA,IAAI,CAACC,mBAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAEA,MAAAC,iCAAA,CAA6B,MAAM;AACjC,QAAAC,kEAAA,CAAqCF,kBAAA,CAAG,gBAAgB,OAAO,CAAA;AAAA,MACjE,CAAC,CAAA;AAAA,IACH;AAAA,GACD,CAAA;AACH,CAAA,CAAA;AAOO,MAAM,0BAAA,GAA6BG,uBAAkB,2BAA2B;;;;"} |
@@ -80,3 +80,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| } | ||
| if (value && typeof value === "object") { | ||
| if (core.isObjectLike(value)) { | ||
| const out = {}; | ||
@@ -83,0 +83,0 @@ for (const key of Object.keys(value)) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"mongoose-dc-subscriber.js","sources":["../../../src/mongoose/mongoose-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport {\n DB_COLLECTION_NAME,\n DB_NAMESPACE,\n DB_OPERATION_BATCH_SIZE,\n DB_OPERATION_NAME,\n DB_QUERY_TEXT,\n DB_SYSTEM_NAME,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport { debug, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n// Channel names published by mongoose >= 9.7.0 (see mongoose `lib/tracing.js`,\n// `lib/query.js`, `lib/aggregate.js`, `lib/model.js` and `lib/cursor/*`).\n// Hardcoded so the subscriber does not have to import mongoose — the channels\n// just have to be subscribed to before the user's mongoose code publishes.\nexport const MONGOOSE_DC_CHANNEL_QUERY = 'mongoose:query';\nexport const MONGOOSE_DC_CHANNEL_AGGREGATE = 'mongoose:aggregate';\nexport const MONGOOSE_DC_CHANNEL_MODEL_SAVE = 'mongoose:model:save';\nexport const MONGOOSE_DC_CHANNEL_MODEL_INSERT_MANY = 'mongoose:model:insertMany';\nexport const MONGOOSE_DC_CHANNEL_MODEL_BULK_WRITE = 'mongoose:model:bulkWrite';\nexport const MONGOOSE_DC_CHANNEL_CURSOR_NEXT = 'mongoose:cursor:next';\n\nconst ORIGIN = 'auto.db.mongoose.diagnostic_channel';\nconst DB_SYSTEM_NAME_VALUE_MONGODB = 'mongodb';\n\n// Cap recursion so a self-referential or pathologically deep query can never\n// stall (or blow the stack in) instrumentation.\nconst MAX_REDACTION_DEPTH = 10;\n\n/**\n * Shape of the context object mongoose >= 9.7.0 publishes on its tracing\n * channels (mongoose's `TracingContext`, see `types/tracing.d.ts`).\n *\n * Node's `tracePromise` mutates this same object with `result`/`error` once the\n * operation settles, which `bindTracingChannelToSpan` reads in its lifecycle\n * handlers — hence both are declared optional here.\n *\n * Unlike redis/ioredis, mongoose does NOT pre-sanitize the payload, so `args`\n * carries the raw user query. We redact it before emitting `db.query.text`.\n */\nexport interface MongooseTracingData {\n operation: string;\n /** Absent for connection-level `aggregate()` calls, which have no model/collection. */\n collection?: string;\n database?: string;\n serverAddress?: string;\n serverPort?: number;\n /** Cursor channels only: the cursor's fetch batch size and tailable flag. */\n batchSize?: number;\n tailable?: boolean;\n /**\n * Operation-specific arguments. `filter` (queries/cursors) and `pipeline`\n * (aggregations) carry the query shape; `docs`/`ops` carry batch payloads.\n */\n args?: {\n filter?: unknown;\n pipeline?: unknown;\n docs?: unknown;\n ops?: unknown;\n [key: string]: unknown;\n };\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Node passes `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type MongooseTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\nlet subscribed = false;\n\n/**\n * Subscribe Sentry span handlers to mongoose's diagnostics-channel events\n * (`mongoose:query`, `:aggregate`, `:model:save`, `:model:insertMany`,\n * `:model:bulkWrite`, `:cursor:next`), published by mongoose >= 9.7.0.\n *\n * On older mongoose versions the channels are never published to, so the\n * subscribers are inert — there is no double-instrumentation against the\n * IITM-based vendored patcher, which is gated to `< 9.7.0`.\n *\n * Idempotent: subsequent calls are a no-op.\n */\nexport function subscribeMongooseDiagnosticChannels(tracingChannel: MongooseTracingChannelFactory): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n try {\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_QUERY);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_AGGREGATE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_SAVE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_INSERT_MANY);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_BULK_WRITE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_CURSOR_NEXT);\n } catch {\n // The factory relies on `node:diagnostics_channel`, which isn't always\n // available. Fail closed; the SDK simply won't emit mongoose spans here.\n DEBUG_BUILD && debug.log('Mongoose node:diagnostics_channel subscription failed.');\n }\n}\n\nfunction setupChannel(tracingChannel: MongooseTracingChannelFactory, channelName: string): void {\n bindTracingChannelToSpan(tracingChannel<MongooseTracingData>(channelName), data => {\n const collection = data.collection;\n const queryText = redactMongoQuery(data.args?.pipeline ?? data.args?.filter);\n const batchSize = getBatchSize(data);\n\n return startInactiveSpan({\n name: collection ? `mongoose.${collection}.${data.operation}` : `mongoose.${data.operation}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MONGODB,\n [DB_OPERATION_NAME]: data.operation,\n [DB_COLLECTION_NAME]: collection ?? undefined,\n [DB_NAMESPACE]: data.database ?? undefined,\n [DB_QUERY_TEXT]: queryText ?? undefined,\n [DB_OPERATION_BATCH_SIZE]: batchSize ?? undefined,\n [SERVER_ADDRESS]: data.serverAddress ?? undefined,\n [SERVER_PORT]: data.serverPort ?? undefined,\n },\n });\n });\n}\n\n/**\n * `db.operation.batch.size` is only meaningful for genuine batch operations.\n * Mongoose's cursor `batchSize` is a fetch-tuning option, not a batch operation\n * size, so it is intentionally excluded here.\n */\nfunction getBatchSize(data: MongooseTracingData): number | undefined {\n const args = data.args;\n const batch = data.operation === 'insertMany' ? args?.docs : data.operation === 'bulkWrite' ? args?.ops : undefined;\n\n return Array.isArray(batch) && batch.length > 1 ? batch.length : undefined;\n}\n\n/**\n * Serialize a mongoose filter/pipeline into `db.query.text` while stripping every\n * value: keys and Mongo operators (`$gt`, `$in`, …) are preserved, leaf values\n * become `'?'`. Mongoose does not sanitize its channel payload, so this prevents\n * raw user data (potential PII) from leaving the process. Returns `undefined`\n * (rather than throwing) on anything it cannot serialize.\n */\nfunction redactMongoQuery(value: unknown): string | undefined {\n if (value == null) {\n return undefined;\n }\n\n try {\n const redacted = redactValue(value, 0);\n const text = JSON.stringify(redacted);\n\n // Skip empty/uninformative shapes (e.g. a `findOne()` with no filter).\n return text == null || text === '{}' || text === '[]' ? undefined : text;\n } catch {\n return undefined;\n }\n}\n\nfunction redactValue(value: unknown, depth: number): unknown {\n if (depth > MAX_REDACTION_DEPTH) {\n return '?';\n }\n\n if (Array.isArray(value)) {\n return value.map(item => redactValue(item, depth + 1));\n }\n\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n out[key] = redactValue((value as Record<string, unknown>)[key], depth + 1);\n }\n\n return out;\n }\n\n return '?';\n}\n"],"names":["DEBUG_BUILD","debug","tracingChannel","bindTracingChannelToSpan","startInactiveSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","DB_SYSTEM_NAME","DB_OPERATION_NAME","DB_COLLECTION_NAME","DB_NAMESPACE","DB_QUERY_TEXT","DB_OPERATION_BATCH_SIZE","SERVER_ADDRESS","SERVER_PORT"],"mappings":"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"} | ||
| {"version":3,"file":"mongoose-dc-subscriber.js","sources":["../../../src/mongoose/mongoose-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport {\n DB_COLLECTION_NAME,\n DB_NAMESPACE,\n DB_OPERATION_BATCH_SIZE,\n DB_OPERATION_NAME,\n DB_QUERY_TEXT,\n DB_SYSTEM_NAME,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport {\n isObjectLike,\n debug,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startInactiveSpan,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n// Channel names published by mongoose >= 9.7.0 (see mongoose `lib/tracing.js`,\n// `lib/query.js`, `lib/aggregate.js`, `lib/model.js` and `lib/cursor/*`).\n// Hardcoded so the subscriber does not have to import mongoose — the channels\n// just have to be subscribed to before the user's mongoose code publishes.\nexport const MONGOOSE_DC_CHANNEL_QUERY = 'mongoose:query';\nexport const MONGOOSE_DC_CHANNEL_AGGREGATE = 'mongoose:aggregate';\nexport const MONGOOSE_DC_CHANNEL_MODEL_SAVE = 'mongoose:model:save';\nexport const MONGOOSE_DC_CHANNEL_MODEL_INSERT_MANY = 'mongoose:model:insertMany';\nexport const MONGOOSE_DC_CHANNEL_MODEL_BULK_WRITE = 'mongoose:model:bulkWrite';\nexport const MONGOOSE_DC_CHANNEL_CURSOR_NEXT = 'mongoose:cursor:next';\n\nconst ORIGIN = 'auto.db.mongoose.diagnostic_channel';\nconst DB_SYSTEM_NAME_VALUE_MONGODB = 'mongodb';\n\n// Cap recursion so a self-referential or pathologically deep query can never\n// stall (or blow the stack in) instrumentation.\nconst MAX_REDACTION_DEPTH = 10;\n\n/**\n * Shape of the context object mongoose >= 9.7.0 publishes on its tracing\n * channels (mongoose's `TracingContext`, see `types/tracing.d.ts`).\n *\n * Node's `tracePromise` mutates this same object with `result`/`error` once the\n * operation settles, which `bindTracingChannelToSpan` reads in its lifecycle\n * handlers — hence both are declared optional here.\n *\n * Unlike redis/ioredis, mongoose does NOT pre-sanitize the payload, so `args`\n * carries the raw user query. We redact it before emitting `db.query.text`.\n */\nexport interface MongooseTracingData {\n operation: string;\n /** Absent for connection-level `aggregate()` calls, which have no model/collection. */\n collection?: string;\n database?: string;\n serverAddress?: string;\n serverPort?: number;\n /** Cursor channels only: the cursor's fetch batch size and tailable flag. */\n batchSize?: number;\n tailable?: boolean;\n /**\n * Operation-specific arguments. `filter` (queries/cursors) and `pipeline`\n * (aggregations) carry the query shape; `docs`/`ops` carry batch payloads.\n */\n args?: {\n filter?: unknown;\n pipeline?: unknown;\n docs?: unknown;\n ops?: unknown;\n [key: string]: unknown;\n };\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Node passes `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type MongooseTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\nlet subscribed = false;\n\n/**\n * Subscribe Sentry span handlers to mongoose's diagnostics-channel events\n * (`mongoose:query`, `:aggregate`, `:model:save`, `:model:insertMany`,\n * `:model:bulkWrite`, `:cursor:next`), published by mongoose >= 9.7.0.\n *\n * On older mongoose versions the channels are never published to, so the\n * subscribers are inert — there is no double-instrumentation against the\n * IITM-based vendored patcher, which is gated to `< 9.7.0`.\n *\n * Idempotent: subsequent calls are a no-op.\n */\nexport function subscribeMongooseDiagnosticChannels(tracingChannel: MongooseTracingChannelFactory): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n try {\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_QUERY);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_AGGREGATE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_SAVE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_INSERT_MANY);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_BULK_WRITE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_CURSOR_NEXT);\n } catch {\n // The factory relies on `node:diagnostics_channel`, which isn't always\n // available. Fail closed; the SDK simply won't emit mongoose spans here.\n DEBUG_BUILD && debug.log('Mongoose node:diagnostics_channel subscription failed.');\n }\n}\n\nfunction setupChannel(tracingChannel: MongooseTracingChannelFactory, channelName: string): void {\n bindTracingChannelToSpan(tracingChannel<MongooseTracingData>(channelName), data => {\n const collection = data.collection;\n const queryText = redactMongoQuery(data.args?.pipeline ?? data.args?.filter);\n const batchSize = getBatchSize(data);\n\n return startInactiveSpan({\n name: collection ? `mongoose.${collection}.${data.operation}` : `mongoose.${data.operation}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MONGODB,\n [DB_OPERATION_NAME]: data.operation,\n [DB_COLLECTION_NAME]: collection ?? undefined,\n [DB_NAMESPACE]: data.database ?? undefined,\n [DB_QUERY_TEXT]: queryText ?? undefined,\n [DB_OPERATION_BATCH_SIZE]: batchSize ?? undefined,\n [SERVER_ADDRESS]: data.serverAddress ?? undefined,\n [SERVER_PORT]: data.serverPort ?? undefined,\n },\n });\n });\n}\n\n/**\n * `db.operation.batch.size` is only meaningful for genuine batch operations.\n * Mongoose's cursor `batchSize` is a fetch-tuning option, not a batch operation\n * size, so it is intentionally excluded here.\n */\nfunction getBatchSize(data: MongooseTracingData): number | undefined {\n const args = data.args;\n const batch = data.operation === 'insertMany' ? args?.docs : data.operation === 'bulkWrite' ? args?.ops : undefined;\n\n return Array.isArray(batch) && batch.length > 1 ? batch.length : undefined;\n}\n\n/**\n * Serialize a mongoose filter/pipeline into `db.query.text` while stripping every\n * value: keys and Mongo operators (`$gt`, `$in`, …) are preserved, leaf values\n * become `'?'`. Mongoose does not sanitize its channel payload, so this prevents\n * raw user data (potential PII) from leaving the process. Returns `undefined`\n * (rather than throwing) on anything it cannot serialize.\n */\nfunction redactMongoQuery(value: unknown): string | undefined {\n if (value == null) {\n return undefined;\n }\n\n try {\n const redacted = redactValue(value, 0);\n const text = JSON.stringify(redacted);\n\n // Skip empty/uninformative shapes (e.g. a `findOne()` with no filter).\n return text == null || text === '{}' || text === '[]' ? undefined : text;\n } catch {\n return undefined;\n }\n}\n\nfunction redactValue(value: unknown, depth: number): unknown {\n if (depth > MAX_REDACTION_DEPTH) {\n return '?';\n }\n\n if (Array.isArray(value)) {\n return value.map(item => redactValue(item, depth + 1));\n }\n\n if (isObjectLike(value)) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n out[key] = redactValue((value as Record<string, unknown>)[key], depth + 1);\n }\n\n return out;\n }\n\n return '?';\n}\n"],"names":["DEBUG_BUILD","debug","tracingChannel","bindTracingChannelToSpan","startInactiveSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","DB_SYSTEM_NAME","DB_OPERATION_NAME","DB_COLLECTION_NAME","DB_NAMESPACE","DB_QUERY_TEXT","DB_OPERATION_BATCH_SIZE","SERVER_ADDRESS","SERVER_PORT","isObjectLike"],"mappings":"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"} |
@@ -7,6 +7,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const pg = require('./config/pg.js'); | ||
| const postgres = require('./config/postgres.js'); | ||
| const openai = require('./config/openai.js'); | ||
| const anthropicAi = require('./config/anthropic-ai.js'); | ||
| const googleGenai = require('./config/google-genai.js'); | ||
| const vercelAi = require('./config/vercel-ai.js'); | ||
| const amqplib = require('./config/amqplib.js'); | ||
| const hapi = require('./config/hapi.js'); | ||
| const redis = require('./config/redis.js'); | ||
| const express = require('./config/express.js'); | ||
| const graphql = require('./config/graphql.js'); | ||
@@ -18,6 +24,12 @@ const CHANNELS = { | ||
| ...pg.pgChannels, | ||
| ...postgres.postgresJsChannels, | ||
| ...openai.openaiChannels, | ||
| ...anthropicAi.anthropicAiChannels, | ||
| ...googleGenai.googleGenAiChannels, | ||
| ...vercelAi.vercelAiChannels, | ||
| ...hapi.hapiChannels | ||
| ...amqplib.amqplibChannels, | ||
| ...hapi.hapiChannels, | ||
| ...redis.redisChannels, | ||
| ...express.expressChannels, | ||
| ...graphql.graphqlChannels | ||
| }; | ||
@@ -24,0 +36,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"channels.js","sources":["../../../src/orchestrion/channels.ts"],"sourcesContent":["import { mysqlChannels } from './config/mysql';\nimport { lruMemoizerChannels } from './config/lru-memoizer';\nimport { ioredisChannels } from './config/ioredis';\nimport { pgChannels } from './config/pg';\nimport { openaiChannels } from './config/openai';\nimport { anthropicAiChannels } from './config/anthropic-ai';\nimport { vercelAiChannels } from './config/vercel-ai';\nimport { hapiChannels } from './config/hapi';\n\n/**\n * Fully-qualified `diagnostics_channel` names that orchestrion publishes to.\n *\n * Orchestrion's transform always prefixes the configured `channelName` with\n * `orchestrion:${module.name}:`. So a config of\n * `{ channelName: 'query', module: { name: 'mysql' } }`\n * publishes to `orchestrion:mysql:query`.\n *\n * Subscribers (`integrations/<lib>/tracing-channel.ts`) consume the full\n * prefixed string from this map; the config files set only the unprefixed\n * suffix in `channelName`. Keeping both pieces in one file is what guarantees\n * they don't drift apart and silently stop firing.\n */\nexport const CHANNELS = {\n ...mysqlChannels,\n ...lruMemoizerChannels,\n ...ioredisChannels,\n ...pgChannels,\n ...openaiChannels,\n ...anthropicAiChannels,\n ...vercelAiChannels,\n ...hapiChannels,\n} as const;\n\nexport type ChannelName = (typeof CHANNELS)[keyof typeof CHANNELS];\n"],"names":["mysqlChannels","lruMemoizerChannels","ioredisChannels","pgChannels","openaiChannels","anthropicAiChannels","vercelAiChannels","hapiChannels"],"mappings":";;;;;;;;;;;AAsBO,MAAM,QAAA,GAAW;AAAA,EACtB,GAAGA,mBAAA;AAAA,EACH,GAAGC,+BAAA;AAAA,EACH,GAAGC,uBAAA;AAAA,EACH,GAAGC,aAAA;AAAA,EACH,GAAGC,qBAAA;AAAA,EACH,GAAGC,+BAAA;AAAA,EACH,GAAGC,yBAAA;AAAA,EACH,GAAGC;AACL;;;;"} | ||
| {"version":3,"file":"channels.js","sources":["../../../src/orchestrion/channels.ts"],"sourcesContent":["import { mysqlChannels } from './config/mysql';\nimport { lruMemoizerChannels } from './config/lru-memoizer';\nimport { ioredisChannels } from './config/ioredis';\nimport { pgChannels } from './config/pg';\nimport { postgresJsChannels } from './config/postgres';\nimport { openaiChannels } from './config/openai';\nimport { anthropicAiChannels } from './config/anthropic-ai';\nimport { googleGenAiChannels } from './config/google-genai';\nimport { vercelAiChannels } from './config/vercel-ai';\nimport { amqplibChannels } from './config/amqplib';\nimport { hapiChannels } from './config/hapi';\nimport { redisChannels } from './config/redis';\nimport { expressChannels } from './config/express';\nimport { graphqlChannels } from './config/graphql';\n\n/**\n * Fully-qualified `diagnostics_channel` names that orchestrion publishes to.\n *\n * Orchestrion's transform always prefixes the configured `channelName` with\n * `orchestrion:${module.name}:`. So a config of\n * `{ channelName: 'query', module: { name: 'mysql' } }`\n * publishes to `orchestrion:mysql:query`.\n *\n * Subscribers (`integrations/<lib>/tracing-channel.ts`) consume the full\n * prefixed string from this map; the config files set only the unprefixed\n * suffix in `channelName`. Keeping both pieces in one file is what guarantees\n * they don't drift apart and silently stop firing.\n */\nexport const CHANNELS = {\n ...mysqlChannels,\n ...lruMemoizerChannels,\n ...ioredisChannels,\n ...pgChannels,\n ...postgresJsChannels,\n ...openaiChannels,\n ...anthropicAiChannels,\n ...googleGenAiChannels,\n ...vercelAiChannels,\n ...amqplibChannels,\n ...hapiChannels,\n ...redisChannels,\n ...expressChannels,\n ...graphqlChannels,\n} as const;\n\nexport type ChannelName = (typeof CHANNELS)[keyof typeof CHANNELS];\n"],"names":["mysqlChannels","lruMemoizerChannels","ioredisChannels","pgChannels","postgresJsChannels","openaiChannels","anthropicAiChannels","googleGenAiChannels","vercelAiChannels","amqplibChannels","hapiChannels","redisChannels","expressChannels","graphqlChannels"],"mappings":";;;;;;;;;;;;;;;;;AA4BO,MAAM,QAAA,GAAW;AAAA,EACtB,GAAGA,mBAAA;AAAA,EACH,GAAGC,+BAAA;AAAA,EACH,GAAGC,uBAAA;AAAA,EACH,GAAGC,aAAA;AAAA,EACH,GAAGC,2BAAA;AAAA,EACH,GAAGC,qBAAA;AAAA,EACH,GAAGC,+BAAA;AAAA,EACH,GAAGC,+BAAA;AAAA,EACH,GAAGC,yBAAA;AAAA,EACH,GAAGC,uBAAA;AAAA,EACH,GAAGC,iBAAA;AAAA,EACH,GAAGC,mBAAA;AAAA,EACH,GAAGC,uBAAA;AAAA,EACH,GAAGC;AACL;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| const mysql = require('./mysql.js'); | ||
@@ -8,5 +9,11 @@ const lruMemoizer = require('./lru-memoizer.js'); | ||
| const pg = require('./pg.js'); | ||
| const postgres = require('./postgres.js'); | ||
| const anthropicAi = require('./anthropic-ai.js'); | ||
| const googleGenai = require('./google-genai.js'); | ||
| const vercelAi = require('./vercel-ai.js'); | ||
| const amqplib = require('./amqplib.js'); | ||
| const hapi = require('./hapi.js'); | ||
| const redis = require('./redis.js'); | ||
| const express = require('./express.js'); | ||
| const graphql = require('./graphql.js'); | ||
@@ -19,7 +26,13 @@ const SENTRY_INSTRUMENTATIONS = [ | ||
| ...pg.pgConfig, | ||
| ...postgres.postgresJsConfig, | ||
| ...anthropicAi.anthropicAiConfig, | ||
| ...googleGenai.googleGenAiConfig, | ||
| ...vercelAi.vercelAiConfig, | ||
| ...hapi.hapiConfig | ||
| ...hapi.hapiConfig, | ||
| ...amqplib.amqplibConfig, | ||
| ...redis.redisConfig, | ||
| ...express.expressConfig, | ||
| ...graphql.graphqlConfig | ||
| ]; | ||
| const INSTRUMENTED_MODULE_NAMES = Array.from(new Set(SENTRY_INSTRUMENTATIONS.map((i) => i.module.name))); | ||
| const INSTRUMENTED_MODULE_NAMES = core.uniq(SENTRY_INSTRUMENTATIONS.map((i) => i.module.name)); | ||
| function withoutInstrumentedExternals(external) { | ||
@@ -26,0 +39,0 @@ if (!external) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../../src/orchestrion/config/index.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\nimport { mysqlConfig } from './mysql';\nimport { lruMemoizerConfig } from './lru-memoizer';\nimport { ioredisConfig } from './ioredis';\nimport { openaiConfig } from './openai';\nimport { pgConfig } from './pg';\nimport { anthropicAiConfig } from './anthropic-ai';\nimport { vercelAiConfig } from './vercel-ai';\nimport { hapiConfig } from './hapi';\n\nexport const SENTRY_INSTRUMENTATIONS: InstrumentationConfig[] = [\n ...mysqlConfig,\n ...lruMemoizerConfig,\n ...ioredisConfig,\n ...openaiConfig,\n ...pgConfig,\n ...anthropicAiConfig,\n ...vercelAiConfig,\n ...hapiConfig,\n];\n\n/**\n * The unique set of package names instrumented by `SENTRY_INSTRUMENTATIONS`\n * (e.g. `['mysql']`).\n *\n * Bundler plugins MUST ensure these are actually bundled rather than\n * externalized: an externalized dependency is resolved from `node_modules` at\n * runtime and never passes through the code transform's `onLoad`, so its\n * diagnostics_channel calls are silently never injected.\n */\nexport const INSTRUMENTED_MODULE_NAMES: string[] = Array.from(new Set(SENTRY_INSTRUMENTATIONS.map(i => i.module.name)));\n\n/**\n * Returns `external` with any instrumented packages removed, so a bundler that\n * uses an \"external\" denylist (esbuild, Bun, Rollup) still bundles — and thus\n * transforms — them. Matches an exact package name (`'mysql'`) or a subpath\n * (`'mysql/lib/...'`); wildcard/other patterns are left untouched. `undefined`\n * is returned unchanged.\n *\n * (Vite uses an `ssr.noExternal` allowlist instead, so it consumes\n * `INSTRUMENTED_MODULE_NAMES` directly rather than this helper.)\n */\nexport function withoutInstrumentedExternals(external: readonly string[] | undefined): string[] | undefined {\n if (!external) {\n return undefined;\n }\n return external.filter(\n entry => !INSTRUMENTED_MODULE_NAMES.some(name => entry === name || entry.startsWith(`${name}/`)),\n );\n}\n"],"names":["mysqlConfig","lruMemoizerConfig","ioredisConfig","openaiConfig","pgConfig","anthropicAiConfig","vercelAiConfig","hapiConfig"],"mappings":";;;;;;;;;;;AAUO,MAAM,uBAAA,GAAmD;AAAA,EAC9D,GAAGA,iBAAA;AAAA,EACH,GAAGC,6BAAA;AAAA,EACH,GAAGC,qBAAA;AAAA,EACH,GAAGC,mBAAA;AAAA,EACH,GAAGC,WAAA;AAAA,EACH,GAAGC,6BAAA;AAAA,EACH,GAAGC,uBAAA;AAAA,EACH,GAAGC;AACL;AAWO,MAAM,yBAAA,GAAsC,KAAA,CAAM,IAAA,CAAK,IAAI,GAAA,CAAI,uBAAA,CAAwB,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,CAAO,IAAI,CAAC,CAAC;AAY/G,SAAS,6BAA6B,QAAA,EAA+D;AAC1G,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,QAAA,CAAS,MAAA;AAAA,IACd,CAAA,KAAA,KAAS,CAAC,yBAAA,CAA0B,IAAA,CAAK,CAAA,IAAA,KAAQ,KAAA,KAAU,IAAA,IAAQ,KAAA,CAAM,UAAA,CAAW,CAAA,EAAG,IAAI,CAAA,CAAA,CAAG,CAAC;AAAA,GACjG;AACF;;;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../../src/orchestrion/config/index.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\nimport { uniq } from '@sentry/core';\nimport { mysqlConfig } from './mysql';\nimport { lruMemoizerConfig } from './lru-memoizer';\nimport { ioredisConfig } from './ioredis';\nimport { openaiConfig } from './openai';\nimport { pgConfig } from './pg';\nimport { postgresJsConfig } from './postgres';\nimport { anthropicAiConfig } from './anthropic-ai';\nimport { googleGenAiConfig } from './google-genai';\nimport { vercelAiConfig } from './vercel-ai';\nimport { amqplibConfig } from './amqplib';\nimport { hapiConfig } from './hapi';\nimport { redisConfig } from './redis';\nimport { expressConfig } from './express';\nimport { graphqlConfig } from './graphql';\n\nexport const SENTRY_INSTRUMENTATIONS: InstrumentationConfig[] = [\n ...mysqlConfig,\n ...lruMemoizerConfig,\n ...ioredisConfig,\n ...openaiConfig,\n ...pgConfig,\n ...postgresJsConfig,\n ...anthropicAiConfig,\n ...googleGenAiConfig,\n ...vercelAiConfig,\n ...hapiConfig,\n ...amqplibConfig,\n ...redisConfig,\n ...expressConfig,\n ...graphqlConfig,\n];\n\n/**\n * The unique set of package names instrumented by `SENTRY_INSTRUMENTATIONS`\n * (e.g. `['mysql']`).\n *\n * Bundler plugins MUST ensure these are actually bundled rather than\n * externalized: an externalized dependency is resolved from `node_modules` at\n * runtime and never passes through the code transform's `onLoad`, so its\n * diagnostics_channel calls are silently never injected.\n */\nexport const INSTRUMENTED_MODULE_NAMES: string[] = uniq(SENTRY_INSTRUMENTATIONS.map(i => i.module.name));\n\n/**\n * Returns `external` with any instrumented packages removed, so a bundler that\n * uses an \"external\" denylist (esbuild, Bun, Rollup) still bundles — and thus\n * transforms — them. Matches an exact package name (`'mysql'`) or a subpath\n * (`'mysql/lib/...'`); wildcard/other patterns are left untouched. `undefined`\n * is returned unchanged.\n *\n * (Vite uses an `ssr.noExternal` allowlist instead, so it consumes\n * `INSTRUMENTED_MODULE_NAMES` directly rather than this helper.)\n */\nexport function withoutInstrumentedExternals(external: readonly string[] | undefined): string[] | undefined {\n if (!external) {\n return undefined;\n }\n return external.filter(\n entry => !INSTRUMENTED_MODULE_NAMES.some(name => entry === name || entry.startsWith(`${name}/`)),\n );\n}\n"],"names":["mysqlConfig","lruMemoizerConfig","ioredisConfig","openaiConfig","pgConfig","postgresJsConfig","anthropicAiConfig","googleGenAiConfig","vercelAiConfig","hapiConfig","amqplibConfig","redisConfig","expressConfig","graphqlConfig","uniq"],"mappings":";;;;;;;;;;;;;;;;;;AAiBO,MAAM,uBAAA,GAAmD;AAAA,EAC9D,GAAGA,iBAAA;AAAA,EACH,GAAGC,6BAAA;AAAA,EACH,GAAGC,qBAAA;AAAA,EACH,GAAGC,mBAAA;AAAA,EACH,GAAGC,WAAA;AAAA,EACH,GAAGC,yBAAA;AAAA,EACH,GAAGC,6BAAA;AAAA,EACH,GAAGC,6BAAA;AAAA,EACH,GAAGC,uBAAA;AAAA,EACH,GAAGC,eAAA;AAAA,EACH,GAAGC,qBAAA;AAAA,EACH,GAAGC,iBAAA;AAAA,EACH,GAAGC,qBAAA;AAAA,EACH,GAAGC;AACL;AAWO,MAAM,yBAAA,GAAsCC,UAAK,uBAAA,CAAwB,GAAA,CAAI,OAAK,CAAA,CAAE,MAAA,CAAO,IAAI,CAAC;AAYhG,SAAS,6BAA6B,QAAA,EAA+D;AAC1G,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,QAAA,CAAS,MAAA;AAAA,IACd,CAAA,KAAA,KAAS,CAAC,yBAAA,CAA0B,IAAA,CAAK,CAAA,IAAA,KAAQ,KAAA,KAAU,IAAA,IAAQ,KAAA,CAAM,UAAA,CAAW,CAAA,EAAG,IAAI,CAAA,CAAA,CAAG,CAAC;AAAA,GACjG;AACF;;;;;;"} |
@@ -12,28 +12,32 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| // ends it when the (synchronous) call returns. | ||
| ...vercelAiV6Entries("generateText", "generateText", "Async"), | ||
| ...vercelAiV6Entries("streamText", "streamText", "Sync"), | ||
| ...vercelAiV6Entries("embed", "embed", "Async"), | ||
| ...vercelAiV6Entries("embedMany", "embedMany", "Async"), | ||
| ...vercelAiV6Entries("executeToolCall", "executeToolCall", "Async"), | ||
| ...vercelAiV6Entries("resolveLanguageModel", "resolveLanguageModel", "Sync") | ||
| // The majority of entrypoints are present in all versions we support | ||
| ...vercelAiEntries(">=4.0.0 <7.0.0", "generateText", "generateText", "Async"), | ||
| ...vercelAiEntries(">=4.0.0 <7.0.0", "streamText", "streamText", "Sync"), | ||
| ...vercelAiEntries(">=4.0.0 <7.0.0", "generateObject", "generateObject", "Async"), | ||
| ...vercelAiEntries(">=4.0.0 <7.0.0", "embed", "embed", "Async"), | ||
| ...vercelAiEntries(">=4.0.0 <7.0.0", "embedMany", "embedMany", "Async"), | ||
| // The following entry is only present in v5 and later | ||
| ...vercelAiEntries(">=5.0.0 <7.0.0", "resolveLanguageModel", "resolveLanguageModel", "Sync"), | ||
| // The following entry is only present in v6 and later | ||
| ...vercelAiEntries(">=6.0.0 <7.0.0", "executeToolCall", "executeToolCall", "Async") | ||
| ]; | ||
| const vercelAiChannels = { | ||
| // Vercel AI (`ai`) v6: orchestrion injects these so the same channel-based | ||
| // Vercel AI (`ai`): orchestrion injects these so the same channel-based | ||
| // integration that consumes `ai`'s native `ai:telemetry` channel (v7) can | ||
| // also instrument v6. Each maps to a top-level function in `ai`'s bundle. | ||
| // also instrument v4/v5/v6. Each maps to a top-level function in `ai`'s bundle. | ||
| // All three versions share the same channel names (the subscriber is version-agnostic); | ||
| // `VERCEL_AI_EXECUTE_TOOL_CALL` is v6-only (v4/v5 have no `executeToolCall` export) and | ||
| // `VERCEL_AI_RESOLVE_LANGUAGE_MODEL` is v5/v6-only (v4 has no such chokepoint). | ||
| VERCEL_AI_GENERATE_TEXT: "orchestrion:ai:generateText", | ||
| VERCEL_AI_STREAM_TEXT: "orchestrion:ai:streamText", | ||
| VERCEL_AI_GENERATE_OBJECT: "orchestrion:ai:generateObject", | ||
| VERCEL_AI_EMBED: "orchestrion:ai:embed", | ||
| VERCEL_AI_EMBED_MANY: "orchestrion:ai:embedMany", | ||
| VERCEL_AI_EXECUTE_TOOL_CALL: "orchestrion:ai:executeToolCall", | ||
| // `resolveLanguageModel` is the single chokepoint every model call flows | ||
| // through; we wrap it to monkey-patch `doGenerate`/`doStream` on the returned | ||
| // model (the model-call site itself is an inline call with no injectable | ||
| // definition). | ||
| VERCEL_AI_RESOLVE_LANGUAGE_MODEL: "orchestrion:ai:resolveLanguageModel" | ||
| }; | ||
| function vercelAiV6Entries(channelName, functionName, kind) { | ||
| function vercelAiEntries(versionRange, channelName, functionName, kind) { | ||
| return ["dist/index.js", "dist/index.mjs"].map((filePath) => ({ | ||
| channelName, | ||
| module: { name: "ai", versionRange: ">=6.0.0 <7.0.0", filePath }, | ||
| module: { name: "ai", versionRange, filePath }, | ||
| functionQuery: { functionName, kind } | ||
@@ -40,0 +44,0 @@ })); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"vercel-ai.js","sources":["../../../../src/orchestrion/config/vercel-ai.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\nexport const vercelAiConfig = [\n // Vercel AI v6: mirror the v7 native `ai:telemetry` channel by injecting\n // channels into the top-level entry points. `resolveLanguageModel` is wrapped\n // not to span it, but so the subscriber can monkey-patch `doGenerate`/\n // `doStream` on the returned model (the only way to span the model call,\n // which is an inline call with no injectable definition in `ai`).\n // `streamText` returns its result synchronously (streaming is lazy), so it's\n // `Sync`; the subscriber binds the span via `bindTracingChannelToSpan`, which\n // ends it when the (synchronous) call returns.\n ...vercelAiV6Entries('generateText', 'generateText', 'Async'),\n ...vercelAiV6Entries('streamText', 'streamText', 'Sync'),\n ...vercelAiV6Entries('embed', 'embed', 'Async'),\n ...vercelAiV6Entries('embedMany', 'embedMany', 'Async'),\n ...vercelAiV6Entries('executeToolCall', 'executeToolCall', 'Async'),\n ...vercelAiV6Entries('resolveLanguageModel', 'resolveLanguageModel', 'Sync'),\n] satisfies InstrumentationConfig[];\n\nexport const vercelAiChannels = {\n // Vercel AI (`ai`) v6: orchestrion injects these so the same channel-based\n // integration that consumes `ai`'s native `ai:telemetry` channel (v7) can\n // also instrument v6. Each maps to a top-level function in `ai`'s bundle.\n VERCEL_AI_GENERATE_TEXT: 'orchestrion:ai:generateText',\n VERCEL_AI_STREAM_TEXT: 'orchestrion:ai:streamText',\n VERCEL_AI_EMBED: 'orchestrion:ai:embed',\n VERCEL_AI_EMBED_MANY: 'orchestrion:ai:embedMany',\n VERCEL_AI_EXECUTE_TOOL_CALL: 'orchestrion:ai:executeToolCall',\n // `resolveLanguageModel` is the single chokepoint every model call flows\n // through; we wrap it to monkey-patch `doGenerate`/`doStream` on the returned\n // model (the model-call site itself is an inline call with no injectable\n // definition).\n VERCEL_AI_RESOLVE_LANGUAGE_MODEL: 'orchestrion:ai:resolveLanguageModel',\n} as const;\n\n/**\n * The central list of channel injections orchestrion should perform.\n *\n * This module has NO side effects — it's the only thing both the runtime hook\n * (`runtime/import-hook.mjs`) and the bundler plugins (`bundler/vite.ts`, …)\n * import from. Adding a new instrumented method is one entry here plus one\n * subscriber in `integrations/<lib>/tracing-channel.ts`.\n *\n * `channelName` here is the unprefixed suffix; the actual diagnostics_channel\n * name is `orchestrion:${module.name}:${channelName}` (see `channels.ts`).\n */\n/**\n * `ai` ships a single bundled entry per module system, so each instrumented\n * function needs one config entry per file (the app loads whichever matches its\n * module system). This expands a single target into both.\n */\nfunction vercelAiV6Entries(channelName: string, functionName: string, kind: 'Async' | 'Sync'): InstrumentationConfig[] {\n return ['dist/index.js', 'dist/index.mjs'].map(filePath => ({\n channelName,\n module: { name: 'ai', versionRange: '>=6.0.0 <7.0.0', filePath },\n functionQuery: { functionName, kind },\n }));\n}\n"],"names":[],"mappings":";;AAEO,MAAM,cAAA,GAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS5B,GAAG,iBAAA,CAAkB,cAAA,EAAgB,cAAA,EAAgB,OAAO,CAAA;AAAA,EAC5D,GAAG,iBAAA,CAAkB,YAAA,EAAc,YAAA,EAAc,MAAM,CAAA;AAAA,EACvD,GAAG,iBAAA,CAAkB,OAAA,EAAS,OAAA,EAAS,OAAO,CAAA;AAAA,EAC9C,GAAG,iBAAA,CAAkB,WAAA,EAAa,WAAA,EAAa,OAAO,CAAA;AAAA,EACtD,GAAG,iBAAA,CAAkB,iBAAA,EAAmB,iBAAA,EAAmB,OAAO,CAAA;AAAA,EAClE,GAAG,iBAAA,CAAkB,sBAAA,EAAwB,sBAAA,EAAwB,MAAM;AAC7E;AAEO,MAAM,gBAAA,GAAmB;AAAA;AAAA;AAAA;AAAA,EAI9B,uBAAA,EAAyB,6BAAA;AAAA,EACzB,qBAAA,EAAuB,2BAAA;AAAA,EACvB,eAAA,EAAiB,sBAAA;AAAA,EACjB,oBAAA,EAAsB,0BAAA;AAAA,EACtB,2BAAA,EAA6B,gCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAK7B,gCAAA,EAAkC;AACpC;AAkBA,SAAS,iBAAA,CAAkB,WAAA,EAAqB,YAAA,EAAsB,IAAA,EAAiD;AACrH,EAAA,OAAO,CAAC,eAAA,EAAiB,gBAAgB,CAAA,CAAE,IAAI,CAAA,QAAA,MAAa;AAAA,IAC1D,WAAA;AAAA,IACA,QAAQ,EAAE,IAAA,EAAM,IAAA,EAAM,YAAA,EAAc,kBAAkB,QAAA,EAAS;AAAA,IAC/D,aAAA,EAAe,EAAE,YAAA,EAAc,IAAA;AAAK,GACtC,CAAE,CAAA;AACJ;;;;;"} | ||
| {"version":3,"file":"vercel-ai.js","sources":["../../../../src/orchestrion/config/vercel-ai.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\nexport const vercelAiConfig = [\n // Vercel AI v6: mirror the v7 native `ai:telemetry` channel by injecting\n // channels into the top-level entry points. `resolveLanguageModel` is wrapped\n // not to span it, but so the subscriber can monkey-patch `doGenerate`/\n // `doStream` on the returned model (the only way to span the model call,\n // which is an inline call with no injectable definition in `ai`).\n // `streamText` returns its result synchronously (streaming is lazy), so it's\n // `Sync`; the subscriber binds the span via `bindTracingChannelToSpan`, which\n // ends it when the (synchronous) call returns.\n\n // The majority of entrypoints are present in all versions we support\n ...vercelAiEntries('>=4.0.0 <7.0.0', 'generateText', 'generateText', 'Async'),\n ...vercelAiEntries('>=4.0.0 <7.0.0', 'streamText', 'streamText', 'Sync'),\n ...vercelAiEntries('>=4.0.0 <7.0.0', 'generateObject', 'generateObject', 'Async'),\n ...vercelAiEntries('>=4.0.0 <7.0.0', 'embed', 'embed', 'Async'),\n ...vercelAiEntries('>=4.0.0 <7.0.0', 'embedMany', 'embedMany', 'Async'),\n\n // The following entry is only present in v5 and later\n ...vercelAiEntries('>=5.0.0 <7.0.0', 'resolveLanguageModel', 'resolveLanguageModel', 'Sync'),\n\n // The following entry is only present in v6 and later\n ...vercelAiEntries('>=6.0.0 <7.0.0', 'executeToolCall', 'executeToolCall', 'Async'),\n] satisfies InstrumentationConfig[];\n\nexport const vercelAiChannels = {\n // Vercel AI (`ai`): orchestrion injects these so the same channel-based\n // integration that consumes `ai`'s native `ai:telemetry` channel (v7) can\n // also instrument v4/v5/v6. Each maps to a top-level function in `ai`'s bundle.\n // All three versions share the same channel names (the subscriber is version-agnostic);\n // `VERCEL_AI_EXECUTE_TOOL_CALL` is v6-only (v4/v5 have no `executeToolCall` export) and\n // `VERCEL_AI_RESOLVE_LANGUAGE_MODEL` is v5/v6-only (v4 has no such chokepoint).\n VERCEL_AI_GENERATE_TEXT: 'orchestrion:ai:generateText',\n VERCEL_AI_STREAM_TEXT: 'orchestrion:ai:streamText',\n VERCEL_AI_GENERATE_OBJECT: 'orchestrion:ai:generateObject',\n VERCEL_AI_EMBED: 'orchestrion:ai:embed',\n VERCEL_AI_EMBED_MANY: 'orchestrion:ai:embedMany',\n VERCEL_AI_EXECUTE_TOOL_CALL: 'orchestrion:ai:executeToolCall',\n VERCEL_AI_RESOLVE_LANGUAGE_MODEL: 'orchestrion:ai:resolveLanguageModel',\n} as const;\n\n/**\n * The central list of channel injections orchestrion should perform.\n *\n * This module has NO side effects — it's the only thing both the runtime hook\n * (`runtime/import-hook.mjs`) and the bundler plugins (`bundler/vite.ts`, …)\n * import from. Adding a new instrumented method is one entry here plus one\n * subscriber in `integrations/<lib>/tracing-channel.ts`.\n *\n * `channelName` here is the unprefixed suffix; the actual diagnostics_channel\n * name is `orchestrion:${module.name}:${channelName}` (see `channels.ts`).\n */\n/**\n * `ai` ships a single bundled entry per module system, so each instrumented\n * function needs one config entry per file (the app loads whichever matches its\n * module system). This expands a single target into both.\n */\nfunction vercelAiEntries(\n versionRange: string,\n channelName: string,\n functionName: string,\n kind: 'Async' | 'Sync',\n): InstrumentationConfig[] {\n return ['dist/index.js', 'dist/index.mjs'].map(filePath => ({\n channelName,\n module: { name: 'ai', versionRange, filePath },\n functionQuery: { functionName, kind },\n }));\n}\n"],"names":[],"mappings":";;AAEO,MAAM,cAAA,GAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW5B,GAAG,eAAA,CAAgB,gBAAA,EAAkB,cAAA,EAAgB,gBAAgB,OAAO,CAAA;AAAA,EAC5E,GAAG,eAAA,CAAgB,gBAAA,EAAkB,YAAA,EAAc,cAAc,MAAM,CAAA;AAAA,EACvE,GAAG,eAAA,CAAgB,gBAAA,EAAkB,gBAAA,EAAkB,kBAAkB,OAAO,CAAA;AAAA,EAChF,GAAG,eAAA,CAAgB,gBAAA,EAAkB,OAAA,EAAS,SAAS,OAAO,CAAA;AAAA,EAC9D,GAAG,eAAA,CAAgB,gBAAA,EAAkB,WAAA,EAAa,aAAa,OAAO,CAAA;AAAA;AAAA,EAGtE,GAAG,eAAA,CAAgB,gBAAA,EAAkB,sBAAA,EAAwB,wBAAwB,MAAM,CAAA;AAAA;AAAA,EAG3F,GAAG,eAAA,CAAgB,gBAAA,EAAkB,iBAAA,EAAmB,mBAAmB,OAAO;AACpF;AAEO,MAAM,gBAAA,GAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO9B,uBAAA,EAAyB,6BAAA;AAAA,EACzB,qBAAA,EAAuB,2BAAA;AAAA,EACvB,yBAAA,EAA2B,+BAAA;AAAA,EAC3B,eAAA,EAAiB,sBAAA;AAAA,EACjB,oBAAA,EAAsB,0BAAA;AAAA,EACtB,2BAAA,EAA6B,gCAAA;AAAA,EAC7B,gCAAA,EAAkC;AACpC;AAkBA,SAAS,eAAA,CACP,YAAA,EACA,WAAA,EACA,YAAA,EACA,IAAA,EACyB;AACzB,EAAA,OAAO,CAAC,eAAA,EAAiB,gBAAgB,CAAA,CAAE,IAAI,CAAA,QAAA,MAAa;AAAA,IAC1D,WAAA;AAAA,IACA,MAAA,EAAQ,EAAE,IAAA,EAAM,IAAA,EAAM,cAAc,QAAA,EAAS;AAAA,IAC7C,aAAA,EAAe,EAAE,YAAA,EAAc,IAAA;AAAK,GACtC,CAAE,CAAA;AACJ;;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const amqplib = require('../integrations/tracing-channel/amqplib.js'); | ||
| const anthropic = require('../integrations/tracing-channel/anthropic.js'); | ||
| const googleGenai = require('../integrations/tracing-channel/google-genai.js'); | ||
| const index$1 = require('../integrations/tracing-channel/graphql/index.js'); | ||
| const hapi = require('../integrations/tracing-channel/hapi.js'); | ||
@@ -10,7 +13,11 @@ const ioredis = require('../integrations/tracing-channel/ioredis.js'); | ||
| const postgres = require('../integrations/tracing-channel/postgres.js'); | ||
| const postgresJs = require('../integrations/tracing-channel/postgres-js.js'); | ||
| const vercelAi = require('../integrations/tracing-channel/vercel-ai.js'); | ||
| const index = require('../integrations/tracing-channel/express/index.js'); | ||
| const detect = require('./detect.js'); | ||
| const redis = require('../integrations/tracing-channel/redis.js'); | ||
| const channelIntegrations = { | ||
| postgresIntegration: postgres.postgresChannelIntegration, | ||
| postgresJsIntegration: postgresJs.postgresJsChannelIntegration, | ||
| mysqlIntegration: mysql.mysqlChannelIntegration, | ||
@@ -20,7 +27,14 @@ lruMemoizerIntegration: lruMemoizer.lruMemoizerChannelIntegration, | ||
| anthropicIntegration: anthropic.anthropicChannelIntegration, | ||
| googleGenAIIntegration: googleGenai.googleGenAIChannelIntegration, | ||
| vercelAiIntegration: vercelAi.vercelAiChannelIntegration, | ||
| hapiIntegration: hapi.hapiChannelIntegration | ||
| amqplibIntegration: amqplib.amqplibChannelIntegration, | ||
| hapiIntegration: hapi.hapiChannelIntegration, | ||
| expressIntegration: index.expressChannelIntegration, | ||
| graphqlIntegration: index$1.graphqlDiagnosticsChannelIntegration | ||
| }; | ||
| exports.amqplibChannelIntegration = amqplib.amqplibChannelIntegration; | ||
| exports.anthropicChannelIntegration = anthropic.anthropicChannelIntegration; | ||
| exports.googleGenAIChannelIntegration = googleGenai.googleGenAIChannelIntegration; | ||
| exports.graphqlChannelIntegration = index$1.graphqlChannelIntegration; | ||
| exports.hapiChannelIntegration = hapi.hapiChannelIntegration; | ||
@@ -32,6 +46,9 @@ exports.ioredisChannelIntegration = ioredis.ioredisChannelIntegration; | ||
| exports.postgresChannelIntegration = postgres.postgresChannelIntegration; | ||
| exports.postgresJsChannelIntegration = postgresJs.postgresJsChannelIntegration; | ||
| exports.vercelAiChannelIntegration = vercelAi.vercelAiChannelIntegration; | ||
| exports.expressChannelIntegration = index.expressChannelIntegration; | ||
| exports.detectOrchestrionSetup = detect.detectOrchestrionSetup; | ||
| exports.isOrchestrionInjected = detect.isOrchestrionInjected; | ||
| exports.redisChannelIntegration = redis.redisChannelIntegration; | ||
| exports.channelIntegrations = channelIntegrations; | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../src/orchestrion/index.ts"],"sourcesContent":["import { anthropicChannelIntegration } from '../integrations/tracing-channel/anthropic';\nimport { hapiChannelIntegration } from '../integrations/tracing-channel/hapi';\nimport { ioredisChannelIntegration } from '../integrations/tracing-channel/ioredis';\nimport { lruMemoizerChannelIntegration } from '../integrations/tracing-channel/lru-memoizer';\nimport { mysqlChannelIntegration } from '../integrations/tracing-channel/mysql';\nimport { openaiChannelIntegration } from '../integrations/tracing-channel/openai';\nimport { postgresChannelIntegration } from '../integrations/tracing-channel/postgres';\nimport { vercelAiChannelIntegration } from '../integrations/tracing-channel/vercel-ai';\n\nexport { detectOrchestrionSetup, isOrchestrionInjected } from './detect';\nexport {\n anthropicChannelIntegration,\n hapiChannelIntegration,\n ioredisChannelIntegration,\n lruMemoizerChannelIntegration,\n mysqlChannelIntegration,\n openaiChannelIntegration,\n postgresChannelIntegration,\n vercelAiChannelIntegration,\n};\nexport type { IORedisChannelIntegrationOptions, IORedisResponseHook } from '../integrations/tracing-channel/ioredis';\n\n/**\n * The canonical set of orchestrion diagnostics-channel integrations, keyed by their public\n * (OTel-parity) factory name.\n *\n * Single source of truth: add a new channel integration here and every consumer — the `@sentry/node`\n * opt-in helper (`experimentalUseDiagnosticsChannelInjection`) and its public\n * `diagnosticsChannelInjectionIntegrations()` map — picks it up automatically, so there's no separate\n * list to keep in sync.\n *\n * NOTE: `ioredisChannelIntegration` is intentionally NOT here. It only partially replaces the\n * composite OTel `Redis` integration and needs the node SDK's redis cache `responseHook` (which\n * can't live in `server-utils`), so `@sentry/node` wires it up separately.\n */\nexport const channelIntegrations = {\n postgresIntegration: postgresChannelIntegration,\n mysqlIntegration: mysqlChannelIntegration,\n lruMemoizerIntegration: lruMemoizerChannelIntegration,\n openaiIntegration: openaiChannelIntegration,\n anthropicIntegration: anthropicChannelIntegration,\n vercelAiIntegration: vercelAiChannelIntegration,\n hapiIntegration: hapiChannelIntegration,\n} as const;\n"],"names":["postgresChannelIntegration","mysqlChannelIntegration","lruMemoizerChannelIntegration","openaiChannelIntegration","anthropicChannelIntegration","vercelAiChannelIntegration","hapiChannelIntegration"],"mappings":";;;;;;;;;;;;AAmCO,MAAM,mBAAA,GAAsB;AAAA,EACjC,mBAAA,EAAqBA,mCAAA;AAAA,EACrB,gBAAA,EAAkBC,6BAAA;AAAA,EAClB,sBAAA,EAAwBC,yCAAA;AAAA,EACxB,iBAAA,EAAmBC,+BAAA;AAAA,EACnB,oBAAA,EAAsBC,qCAAA;AAAA,EACtB,mBAAA,EAAqBC,mCAAA;AAAA,EACrB,eAAA,EAAiBC;AACnB;;;;;;;;;;;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../src/orchestrion/index.ts"],"sourcesContent":["import { amqplibChannelIntegration } from '../integrations/tracing-channel/amqplib';\nimport { anthropicChannelIntegration } from '../integrations/tracing-channel/anthropic';\nimport { googleGenAIChannelIntegration } from '../integrations/tracing-channel/google-genai';\nimport {\n graphqlChannelIntegration,\n graphqlDiagnosticsChannelIntegration,\n} from '../integrations/tracing-channel/graphql';\nimport { hapiChannelIntegration } from '../integrations/tracing-channel/hapi';\nimport { ioredisChannelIntegration } from '../integrations/tracing-channel/ioredis';\nimport { lruMemoizerChannelIntegration } from '../integrations/tracing-channel/lru-memoizer';\nimport { mysqlChannelIntegration } from '../integrations/tracing-channel/mysql';\nimport { openaiChannelIntegration } from '../integrations/tracing-channel/openai';\nimport { postgresChannelIntegration } from '../integrations/tracing-channel/postgres';\nimport { postgresJsChannelIntegration } from '../integrations/tracing-channel/postgres-js';\nimport { vercelAiChannelIntegration } from '../integrations/tracing-channel/vercel-ai';\nimport { expressChannelIntegration } from '../integrations/tracing-channel/express';\n\nexport { detectOrchestrionSetup, isOrchestrionInjected } from './detect';\nexport {\n amqplibChannelIntegration,\n anthropicChannelIntegration,\n googleGenAIChannelIntegration,\n graphqlChannelIntegration,\n hapiChannelIntegration,\n ioredisChannelIntegration,\n lruMemoizerChannelIntegration,\n mysqlChannelIntegration,\n openaiChannelIntegration,\n postgresChannelIntegration,\n postgresJsChannelIntegration,\n vercelAiChannelIntegration,\n expressChannelIntegration,\n};\nexport type { IORedisChannelIntegrationOptions, IORedisResponseHook } from '../integrations/tracing-channel/ioredis';\nexport type { PostgresJsChannelIntegrationOptions } from '../integrations/tracing-channel/postgres-js';\nexport { redisChannelIntegration } from '../integrations/tracing-channel/redis';\nexport type { RedisChannelIntegrationOptions, RedisResponseHook } from '../integrations/tracing-channel/redis';\n\n// The structural `graphql` package types are the single source of truth shared with `@sentry/node`'s\n// vendored OTel graphql instrumentation (re-exported from here so the two can't drift).\nexport type * from '../integrations/tracing-channel/graphql/graphql-types';\n\n/**\n * The canonical set of orchestrion diagnostics-channel integrations, keyed by their public\n * (OTel-parity) factory name.\n *\n * Single source of truth: add a new channel integration here and every consumer — the `@sentry/node`\n * opt-in helper (`experimentalUseDiagnosticsChannelInjection`) and its public\n * `diagnosticsChannelInjectionIntegrations()` map — picks it up automatically, so there's no separate\n * list to keep in sync.\n *\n * NOTE: `ioredisChannelIntegration` and `redisChannelIntegration` are intentionally NOT here. They\n * only partially replace the composite OTel `Redis` integration and need the node SDK's redis cache\n * `responseHook` (which can't live in `server-utils`), so `@sentry/node` wires them up separately.\n */\nexport const channelIntegrations = {\n postgresIntegration: postgresChannelIntegration,\n postgresJsIntegration: postgresJsChannelIntegration,\n mysqlIntegration: mysqlChannelIntegration,\n lruMemoizerIntegration: lruMemoizerChannelIntegration,\n openaiIntegration: openaiChannelIntegration,\n anthropicIntegration: anthropicChannelIntegration,\n googleGenAIIntegration: googleGenAIChannelIntegration,\n vercelAiIntegration: vercelAiChannelIntegration,\n amqplibIntegration: amqplibChannelIntegration,\n hapiIntegration: hapiChannelIntegration,\n expressIntegration: expressChannelIntegration,\n graphqlIntegration: graphqlDiagnosticsChannelIntegration,\n} as const;\n"],"names":["postgresChannelIntegration","postgresJsChannelIntegration","mysqlChannelIntegration","lruMemoizerChannelIntegration","openaiChannelIntegration","anthropicChannelIntegration","googleGenAIChannelIntegration","vercelAiChannelIntegration","amqplibChannelIntegration","hapiChannelIntegration","expressChannelIntegration","graphqlDiagnosticsChannelIntegration"],"mappings":";;;;;;;;;;;;;;;;;;AAuDO,MAAM,mBAAA,GAAsB;AAAA,EACjC,mBAAA,EAAqBA,mCAAA;AAAA,EACrB,qBAAA,EAAuBC,uCAAA;AAAA,EACvB,gBAAA,EAAkBC,6BAAA;AAAA,EAClB,sBAAA,EAAwBC,yCAAA;AAAA,EACxB,iBAAA,EAAmBC,+BAAA;AAAA,EACnB,oBAAA,EAAsBC,qCAAA;AAAA,EACtB,sBAAA,EAAwBC,yCAAA;AAAA,EACxB,mBAAA,EAAqBC,mCAAA;AAAA,EACrB,kBAAA,EAAoBC,iCAAA;AAAA,EACpB,eAAA,EAAiBC,2BAAA;AAAA,EACjB,kBAAA,EAAoBC,+BAAA;AAAA,EACpB,kBAAA,EAAoBC;AACtB;;;;;;;;;;;;;;;;;;;;"} |
@@ -5,3 +5,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| const debugBuild = require('../debug-build.js'); | ||
| const tracingChannel = require('../tracing-channel.js'); | ||
@@ -16,30 +15,20 @@ | ||
| const DB_SYSTEM_NAME_VALUE_REDIS = "redis"; | ||
| let subscribed = false; | ||
| let currentResponseHook; | ||
| let activeUnbinds = []; | ||
| function subscribeRedisDiagnosticChannels(tracingChannel, responseHook) { | ||
| currentResponseHook = responseHook; | ||
| if (subscribed) return; | ||
| subscribed = true; | ||
| try { | ||
| activeUnbinds.push( | ||
| setupCommandChannel(tracingChannel, REDIS_DC_CHANNEL_COMMAND, (data) => data.args.slice(1)), | ||
| setupBatchChannel( | ||
| tracingChannel, | ||
| REDIS_DC_CHANNEL_BATCH, | ||
| (data) => data.batchMode === "PIPELINE" ? "PIPELINE" : "MULTI" | ||
| ), | ||
| setupConnectChannel(tracingChannel, REDIS_DC_CHANNEL_CONNECT), | ||
| // ioredis: args already exclude the command name; no slicing needed. And | ||
| // ioredis has no separate batch channel — pipeline/MULTI metadata rides | ||
| // on the per-command payload via `batchMode`/`batchSize`. | ||
| setupCommandChannel(tracingChannel, IOREDIS_DC_CHANNEL_COMMAND, (data) => data.args), | ||
| setupConnectChannel(tracingChannel, IOREDIS_DC_CHANNEL_CONNECT) | ||
| ); | ||
| } catch { | ||
| debugBuild.DEBUG_BUILD && core.debug.log("Redis node:diagnostics_channel subscription failed."); | ||
| } | ||
| setupCommandChannel( | ||
| tracingChannel, | ||
| REDIS_DC_CHANNEL_COMMAND, | ||
| (data) => data.args.slice(1), | ||
| responseHook | ||
| ); | ||
| setupBatchChannel( | ||
| tracingChannel, | ||
| REDIS_DC_CHANNEL_BATCH, | ||
| (data) => data.batchMode === "PIPELINE" ? "PIPELINE" : "MULTI" | ||
| ); | ||
| setupConnectChannel(tracingChannel, REDIS_DC_CHANNEL_CONNECT); | ||
| setupCommandChannel(tracingChannel, IOREDIS_DC_CHANNEL_COMMAND, (data) => data.args, responseHook); | ||
| setupConnectChannel(tracingChannel, IOREDIS_DC_CHANNEL_CONNECT); | ||
| } | ||
| function setupCommandChannel(tracingChannel$1, channelName, getCommandArgs) { | ||
| return tracingChannel.bindTracingChannelToSpan( | ||
| function setupCommandChannel(tracingChannel$1, channelName, getCommandArgs, responseHook) { | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel$1(channelName), | ||
@@ -67,9 +56,9 @@ (data) => { | ||
| if ("error" in data) return; | ||
| runResponseHook(span, data.command, getCommandArgs(data), data.result); | ||
| runResponseHook(responseHook, span, data.command, getCommandArgs(data), data.result); | ||
| } | ||
| } | ||
| ).unbind; | ||
| ); | ||
| } | ||
| function setupBatchChannel(tracingChannel$1, channelName, getOperationName) { | ||
| return tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel$1(channelName), | ||
@@ -92,6 +81,6 @@ (data) => { | ||
| { captureError: false } | ||
| ).unbind; | ||
| ); | ||
| } | ||
| function setupConnectChannel(tracingChannel$1, channelName) { | ||
| return tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel$1(channelName), | ||
@@ -111,6 +100,5 @@ (data) => { | ||
| { captureError: false } | ||
| ).unbind; | ||
| ); | ||
| } | ||
| function runResponseHook(span, command, args, result) { | ||
| const hook = currentResponseHook; | ||
| function runResponseHook(hook, span, command, args, result) { | ||
| if (!hook) return; | ||
@@ -117,0 +105,0 @@ try { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"redis-dc-subscriber.js","sources":["../../../src/redis/redis-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport {\n DB_OPERATION_BATCH_SIZE,\n DB_QUERY_TEXT,\n DB_SYSTEM_NAME,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport { debug, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n// Channel names published by node-redis >= 5.12.0 and ioredis >= 5.11.0.\n// Hardcoded so the subscriber does not have to import either library — the\n// channels just have to be subscribed to before the user's redis code\n// publishes.\nexport const REDIS_DC_CHANNEL_COMMAND = 'node-redis:command';\nexport const REDIS_DC_CHANNEL_BATCH = 'node-redis:batch';\nexport const REDIS_DC_CHANNEL_CONNECT = 'node-redis:connect';\nexport const IOREDIS_DC_CHANNEL_COMMAND = 'ioredis:command';\nexport const IOREDIS_DC_CHANNEL_CONNECT = 'ioredis:connect';\n\nconst ORIGIN = 'auto.db.redis.diagnostic_channel';\nconst DB_SYSTEM_NAME_VALUE_REDIS = 'redis';\n\n/**\n * Shape of the `node-redis:command` channel payload published by node-redis.\n *\n * Both `command` and `args` are already redacted by node-redis itself (see\n * `sanitizeArgs` in @redis/client) using the OTel `redis-common` rules. The\n * arg array is `[<command>, <safe arg>, ..., '?', ...]` — `?` replaces any\n * value the library considers sensitive. Subscribers can emit `args` directly\n * as `db.query.text` without further serialization.\n */\nexport interface RedisCommandData {\n command: string;\n /** First arg is the command name itself; consumers should slice it off. */\n args: string[];\n database?: number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Shape of the `ioredis:command` channel payload published by ioredis >= 5.11.0.\n *\n * As with node-redis, args are already sanitized by ioredis (`sanitizeArgs` in\n * `lib/tracing.ts`) before publishing. Unlike node-redis, the command name is\n * NOT prepended to `args`.\n */\nexport interface IORedisCommandData {\n command: string;\n args: string[];\n batchMode?: 'MULTI';\n batchSize?: number;\n database?: number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\n/** Shape of the `node-redis:batch` channel payload published by node-redis. */\nexport interface RedisBatchData {\n batchMode?: 'MULTI' | 'PIPELINE';\n batchSize?: number;\n database?: number;\n clientId?: string | number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown[];\n error?: Error;\n}\n\n/** Shape of the `*:connect` channel payload published by node-redis and ioredis. */\nexport interface RedisConnectData {\n serverAddress?: string;\n serverPort?: number;\n url?: string;\n error?: Error;\n}\n\n/**\n * Optional callback invoked once the redis command response arrives. Useful\n * for attaching response-derived attributes (e.g. cache hit/miss, payload size).\n *\n * Mirrors `@opentelemetry/instrumentation-ioredis`' response hook so existing\n * Sentry node code (`cacheResponseHook`) can be reused unchanged.\n */\nexport type RedisDiagnosticChannelResponseHook = (\n span: Span,\n cmdName: string,\n cmdArgs: string[],\n result: unknown,\n) => void;\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Both Node and Deno pass `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type RedisTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\nlet subscribed = false;\nlet currentResponseHook: RedisDiagnosticChannelResponseHook | undefined;\nlet activeUnbinds: Array<() => void> = [];\n\n/**\n * Subscribe Sentry span handlers to node-redis and ioredis diagnostics-channel\n * events: `node-redis:command`/`:batch`/`:connect` (published by node-redis\n * >= 5.12.0) and `ioredis:command`/`:connect` (published by ioredis >= 5.11.0).\n *\n * On older client versions the channels are never published to, so subscribers\n * are inert — there is no double-instrumentation against any IITM-based\n * patcher gated to those older versions.\n *\n * Idempotent: subsequent calls update the response hook but do not\n * re-subscribe.\n */\nexport function subscribeRedisDiagnosticChannels(\n tracingChannel: RedisTracingChannelFactory,\n responseHook?: RedisDiagnosticChannelResponseHook,\n): void {\n currentResponseHook = responseHook;\n if (subscribed) return;\n subscribed = true;\n\n try {\n // node-redis: command name appears as args[0] in the channel payload, so\n // strip it before the statement and response hook see it.\n activeUnbinds.push(\n setupCommandChannel<RedisCommandData>(tracingChannel, REDIS_DC_CHANNEL_COMMAND, data => data.args.slice(1)),\n setupBatchChannel(tracingChannel, REDIS_DC_CHANNEL_BATCH, data =>\n data.batchMode === 'PIPELINE' ? 'PIPELINE' : 'MULTI',\n ),\n setupConnectChannel(tracingChannel, REDIS_DC_CHANNEL_CONNECT),\n // ioredis: args already exclude the command name; no slicing needed. And\n // ioredis has no separate batch channel — pipeline/MULTI metadata rides\n // on the per-command payload via `batchMode`/`batchSize`.\n setupCommandChannel<IORedisCommandData>(tracingChannel, IOREDIS_DC_CHANNEL_COMMAND, data => data.args),\n setupConnectChannel(tracingChannel, IOREDIS_DC_CHANNEL_CONNECT),\n );\n } catch {\n // The factory may rely on `node:diagnostics_channel`, which isn't always\n // available. Fail closed; the SDK simply won't emit redis spans here.\n DEBUG_BUILD && debug.log('Redis node:diagnostics_channel subscription failed.');\n }\n}\n\nfunction setupCommandChannel<T extends RedisCommandData | IORedisCommandData>(\n tracingChannel: RedisTracingChannelFactory,\n channelName: string,\n getCommandArgs: (data: T) => string[],\n): () => void {\n return bindTracingChannelToSpan(\n tracingChannel<T>(channelName),\n data => {\n // `args` is already sanitized by the publishing library (node-redis /\n // ioredis call their own `sanitizeArgs` before publishing). Join with\n // spaces to mirror the format the libraries themselves intend.\n const args = getCommandArgs(data);\n const statement = args.length ? `${data.command} ${args.join(' ')}` : data.command;\n return startInactiveSpan({\n name: `redis-${data.command}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n [DB_QUERY_TEXT]: statement,\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n {\n // Command failures are surfaced to (and usually handled by) the caller; only annotate the\n // span so we don't emit a duplicate error event for every failed command.\n captureError: false,\n beforeSpanEnd(span, data) {\n if ('error' in data) return;\n runResponseHook(span, data.command, getCommandArgs(data), data.result);\n },\n },\n ).unbind;\n}\n\nfunction setupBatchChannel(\n tracingChannel: RedisTracingChannelFactory,\n channelName: string,\n getOperationName: (data: RedisBatchData) => string,\n): () => void {\n return bindTracingChannelToSpan(\n tracingChannel<RedisBatchData>(channelName),\n data => {\n return startInactiveSpan({\n name: getOperationName(data),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n // should only include batch size greater than 1,\n // or else it isn't properly considered a \"batch\"\n ...(Number(data.batchSize) > 1 ? { [DB_OPERATION_BATCH_SIZE]: data.batchSize } : {}),\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n { captureError: false },\n ).unbind;\n}\n\nfunction setupConnectChannel(tracingChannel: RedisTracingChannelFactory, channelName: string): () => void {\n return bindTracingChannelToSpan(\n tracingChannel<RedisConnectData>(channelName),\n data => {\n return startInactiveSpan({\n name: 'redis-connect',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis.connect',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n { captureError: false },\n ).unbind;\n}\n\nfunction runResponseHook(span: Span, command: string, args: string[], result: unknown): void {\n const hook = currentResponseHook;\n if (!hook) return;\n try {\n hook(span, command, args, result);\n } catch {\n // never let user hooks break instrumentation\n }\n}\n\n/** Test-only: detach all channel bindings and reset module-local subscribe state. */\nexport function _resetRedisDiagnosticChannelsForTesting(): void {\n activeUnbinds.forEach(unbind => unbind());\n activeUnbinds = [];\n subscribed = false;\n currentResponseHook = undefined;\n}\n"],"names":["DEBUG_BUILD","debug","tracingChannel","bindTracingChannelToSpan","startInactiveSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","DB_SYSTEM_NAME","DB_QUERY_TEXT","SERVER_ADDRESS","SERVER_PORT","DB_OPERATION_BATCH_SIZE"],"mappings":"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"} | ||
| {"version":3,"file":"redis-dc-subscriber.js","sources":["../../../src/redis/redis-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport {\n DB_OPERATION_BATCH_SIZE,\n DB_QUERY_TEXT,\n DB_SYSTEM_NAME,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan } from '@sentry/core';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n// Channel names published by node-redis >= 5.12.0 and ioredis >= 5.11.0.\n// Hardcoded so the subscriber does not have to import either library — the\n// channels just have to be subscribed to before the user's redis code\n// publishes.\nexport const REDIS_DC_CHANNEL_COMMAND = 'node-redis:command';\nexport const REDIS_DC_CHANNEL_BATCH = 'node-redis:batch';\nexport const REDIS_DC_CHANNEL_CONNECT = 'node-redis:connect';\nexport const IOREDIS_DC_CHANNEL_COMMAND = 'ioredis:command';\nexport const IOREDIS_DC_CHANNEL_CONNECT = 'ioredis:connect';\n\nconst ORIGIN = 'auto.db.redis.diagnostic_channel';\nconst DB_SYSTEM_NAME_VALUE_REDIS = 'redis';\n\n/**\n * Shape of the `node-redis:command` channel payload published by node-redis.\n *\n * Both `command` and `args` are already redacted by node-redis itself (see\n * `sanitizeArgs` in @redis/client) using the OTel `redis-common` rules. The\n * arg array is `[<command>, <safe arg>, ..., '?', ...]` — `?` replaces any\n * value the library considers sensitive. Subscribers can emit `args` directly\n * as `db.query.text` without further serialization.\n */\nexport interface RedisCommandData {\n command: string;\n /** First arg is the command name itself; consumers should slice it off. */\n args: string[];\n database?: number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Shape of the `ioredis:command` channel payload published by ioredis >= 5.11.0.\n *\n * As with node-redis, args are already sanitized by ioredis (`sanitizeArgs` in\n * `lib/tracing.ts`) before publishing. Unlike node-redis, the command name is\n * NOT prepended to `args`.\n */\nexport interface IORedisCommandData {\n command: string;\n args: string[];\n batchMode?: 'MULTI';\n batchSize?: number;\n database?: number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\n/** Shape of the `node-redis:batch` channel payload published by node-redis. */\nexport interface RedisBatchData {\n batchMode?: 'MULTI' | 'PIPELINE';\n batchSize?: number;\n database?: number;\n clientId?: string | number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown[];\n error?: Error;\n}\n\n/** Shape of the `*:connect` channel payload published by node-redis and ioredis. */\nexport interface RedisConnectData {\n serverAddress?: string;\n serverPort?: number;\n url?: string;\n error?: Error;\n}\n\n/**\n * Optional callback invoked once the redis command response arrives. Useful\n * for attaching response-derived attributes (e.g. cache hit/miss, payload size).\n *\n * Mirrors `@opentelemetry/instrumentation-ioredis`' response hook so existing\n * Sentry node code (`cacheResponseHook`) can be reused unchanged.\n */\nexport type RedisDiagnosticChannelResponseHook = (\n span: Span,\n cmdName: string,\n cmdArgs: string[],\n result: unknown,\n) => void;\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Both Node and Deno pass `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type RedisTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/**\n * Subscribe Sentry span handlers to node-redis and ioredis diagnostics-channel\n * events: `node-redis:command`/`:batch`/`:connect` (published by node-redis\n * >= 5.12.0) and `ioredis:command`/`:connect` (published by ioredis >= 5.11.0).\n *\n * On older client versions the channels are never published to, so subscribers\n * are inert — there is no double-instrumentation against any IITM-based\n * patcher gated to those older versions.\n */\nexport function subscribeRedisDiagnosticChannels(\n tracingChannel: RedisTracingChannelFactory,\n responseHook?: RedisDiagnosticChannelResponseHook,\n): void {\n // node-redis: command name appears as args[0] in the channel payload, so\n // strip it before the statement and response hook see it.\n setupCommandChannel<RedisCommandData>(\n tracingChannel,\n REDIS_DC_CHANNEL_COMMAND,\n data => data.args.slice(1),\n responseHook,\n );\n setupBatchChannel(tracingChannel, REDIS_DC_CHANNEL_BATCH, data =>\n data.batchMode === 'PIPELINE' ? 'PIPELINE' : 'MULTI',\n );\n setupConnectChannel(tracingChannel, REDIS_DC_CHANNEL_CONNECT);\n // ioredis: args already exclude the command name; no slicing needed. And\n // ioredis has no separate batch channel — pipeline/MULTI metadata rides\n // on the per-command payload via `batchMode`/`batchSize`.\n setupCommandChannel<IORedisCommandData>(tracingChannel, IOREDIS_DC_CHANNEL_COMMAND, data => data.args, responseHook);\n setupConnectChannel(tracingChannel, IOREDIS_DC_CHANNEL_CONNECT);\n}\n\nfunction setupCommandChannel<T extends RedisCommandData | IORedisCommandData>(\n tracingChannel: RedisTracingChannelFactory,\n channelName: string,\n getCommandArgs: (data: T) => string[],\n responseHook?: RedisDiagnosticChannelResponseHook,\n): void {\n bindTracingChannelToSpan(\n tracingChannel<T>(channelName),\n data => {\n // `args` is already sanitized by the publishing library (node-redis /\n // ioredis call their own `sanitizeArgs` before publishing). Join with\n // spaces to mirror the format the libraries themselves intend.\n const args = getCommandArgs(data);\n const statement = args.length ? `${data.command} ${args.join(' ')}` : data.command;\n return startInactiveSpan({\n name: `redis-${data.command}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n [DB_QUERY_TEXT]: statement,\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n {\n // Command failures are surfaced to (and usually handled by) the caller; only annotate the\n // span so we don't emit a duplicate error event for every failed command.\n captureError: false,\n beforeSpanEnd(span, data) {\n if ('error' in data) return;\n runResponseHook(responseHook, span, data.command, getCommandArgs(data), data.result);\n },\n },\n );\n}\n\nfunction setupBatchChannel(\n tracingChannel: RedisTracingChannelFactory,\n channelName: string,\n getOperationName: (data: RedisBatchData) => string,\n): void {\n bindTracingChannelToSpan(\n tracingChannel<RedisBatchData>(channelName),\n data => {\n return startInactiveSpan({\n name: getOperationName(data),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n // should only include batch size greater than 1,\n // or else it isn't properly considered a \"batch\"\n ...(Number(data.batchSize) > 1 ? { [DB_OPERATION_BATCH_SIZE]: data.batchSize } : {}),\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n { captureError: false },\n );\n}\n\nfunction setupConnectChannel(tracingChannel: RedisTracingChannelFactory, channelName: string): void {\n bindTracingChannelToSpan(\n tracingChannel<RedisConnectData>(channelName),\n data => {\n return startInactiveSpan({\n name: 'redis-connect',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis.connect',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n { captureError: false },\n );\n}\n\nfunction runResponseHook(\n hook: RedisDiagnosticChannelResponseHook | undefined,\n span: Span,\n command: string,\n args: string[],\n result: unknown,\n): void {\n if (!hook) return;\n try {\n hook(span, command, args, result);\n } catch {\n // never let user hooks break instrumentation\n }\n}\n"],"names":["tracingChannel","bindTracingChannelToSpan","startInactiveSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","DB_SYSTEM_NAME","DB_QUERY_TEXT","SERVER_ADDRESS","SERVER_PORT","DB_OPERATION_BATCH_SIZE"],"mappings":"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"} |
@@ -10,3 +10,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| function bindTracingChannelToSpan(channel, getSpan, opts) { | ||
| const handle = bindSpanToChannelStore(channel, getSpan); | ||
| const handle = bindSpanToChannelStore(channel, getSpan, opts); | ||
| const beforeSpanEnd = opts?.beforeSpanEnd; | ||
@@ -82,3 +82,3 @@ const deferSpanEnd = opts?.deferSpanEnd; | ||
| } | ||
| function bindSpanToChannelStore(channel, getSpan) { | ||
| function bindSpanToChannelStore(channel, getSpan, opts) { | ||
| const binding = core.getAsyncContextStrategy(core.getMainCarrier()).getTracingChannelBinding?.(); | ||
@@ -95,3 +95,4 @@ if (!binding) { | ||
| data._sentryCallerStore = asyncLocalStorage.getStore(); | ||
| const span = getSpan(data); | ||
| const shouldGetSpan = !opts?.requiresParentSpan || core.getActiveSpan(); | ||
| const span = shouldGetSpan ? getSpan(data) : void 0; | ||
| if (!span) { | ||
@@ -123,6 +124,6 @@ return data._sentryCallerStore; | ||
| function getErrorInfo(error) { | ||
| const isObject = !!error && typeof error === "object"; | ||
| const raw = isObject ? "message" in error ? error.message : void 0 : error; | ||
| const errorIsObject = core.isObjectLike(error); | ||
| const raw = errorIsObject ? "message" in error ? error.message : void 0 : error; | ||
| const message = raw ? String(raw) : void 0; | ||
| const type = isObject && "name" in error ? String(error.name) : "unknown"; | ||
| const type = errorIsObject && "name" in error ? String(error.name) : "unknown"; | ||
| return { | ||
@@ -129,0 +130,0 @@ message, |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"tracing-channel.js","sources":["../../src/tracing-channel.ts"],"sourcesContent":["import type { TracingChannel, TracingChannelSubscribers } from 'node:diagnostics_channel';\nimport type { AsyncLocalStorage } from 'node:async_hooks';\nimport type { ExclusiveEventHintOrCaptureContext, Span } from '@sentry/core';\nimport { debug, captureException, SPAN_STATUS_ERROR, getAsyncContextStrategy, getMainCarrier } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { ERROR_TYPE } from '@sentry/conventions/attributes';\n\nexport type TracingChannelPayloadWithSpan<TData extends object> = TData & {\n /**\n * The current active span for the traced call.\n */\n _sentrySpan?: Span;\n\n /**\n * The context's active store value, used to restore the context for asyncStart continuations for callback-based tracing.\n */\n _sentryCallerStore?: unknown;\n};\n\n/*\n * A type patch so that we don't have to handle all subscription types.\n */\nexport interface SentryTracingChannel<TData extends object = object> extends Omit<\n TracingChannel<TData, TracingChannelPayloadWithSpan<TData>>,\n 'subscribe' | 'unsubscribe'\n> {\n subscribe(subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>>): void;\n unsubscribe(subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>>): void;\n}\n\nexport interface TracingChannelLifeCycleOptions<TData extends object = object> {\n /**\n * Invoked with the span and the channel context object once the traced operation completes\n * Use it to enrich the span from the result/error (branch on `'error' in data` / `'result' in data`) or to run cleanup.\n */\n beforeSpanEnd?: (span: Span, data: TracingChannelPayloadWithSpan<TData>) => void;\n\n /**\n * Whether a thrown error is captured as a Sentry event. The span is always marked with error status regardless. Defaults to `false`.\n * You can alternatively pass a function that sets the ExclusiveEventHintOrCaptureContext on the captured error.\n * Set `true` for instrumentations that own the error boundary, (e.g: route handlers)\n * For database drivers, it is not recommended to set this at all.\n */\n captureError?: boolean | ((e: unknown) => ExclusiveEventHintOrCaptureContext);\n\n /**\n * Take ownership of *when* the span ends: return `true` and the helper won't end it on\n * `end`/`asyncEnd`. For results that settle out-of-band — e.g. a streamed `EventEmitter` that\n * completes via its own `'end'`/`'error'` events.\n *\n * Call `end` when it settles — `end()` on success, `end(error)` on failure. `end` owns *how* the span\n * ends (error status/attributes, `captureError`, `beforeSpanEnd`) and is idempotent. Default `false`\n * lets the helper end the span as usual.\n */\n deferSpanEnd?: (args: {\n span: Span;\n data: TracingChannelPayloadWithSpan<TData>;\n /** Ends the span: `end()` on success, `end(error)` on failure. Idempotent. */\n end: (error?: unknown) => void;\n }) => boolean;\n}\n\n/** Returned by {@link bindTracingChannelToSpan}: the bound channel plus a teardown handle. */\nexport interface TracingChannelBindingHandle<TData extends object = object> {\n /**\n * The tracing channel with the span bound into async context.\n */\n channel: SentryTracingChannel<TData>;\n\n /**\n * Tears down the binding: unsubscribes lifecycle handlers, when present, and unbinds the start store.\n * Idempotent, and a no-op when no async context binding was available.\n */\n unbind: () => void;\n}\n\nconst NOOP = (): void => {};\n\n/**\n * Bind a span and its lifecycle to a tracing channel so the span becomes the active async context\n * for the traced operation and is ended when the operation completes.\n *\n * `getSpan` may return `undefined` to opt a payload out entirely: nothing is bound, no span is\n * tracked, and the active context is left untouched. Use it for events that ride the same channel\n * but should reuse the enclosing span instead of opening (and ending) their own — e.g. an agent\n * loop's per-step events, where ending a freshly opened span would close the parent prematurely.\n */\nexport function bindTracingChannelToSpan<TData extends object>(\n channel: TracingChannel<TData, TData>,\n getSpan: (data: TracingChannelPayloadWithSpan<TData>) => Span | undefined,\n opts?: TracingChannelLifeCycleOptions<TData>,\n): TracingChannelBindingHandle<TData> {\n const handle = bindSpanToChannelStore(channel, getSpan);\n\n const beforeSpanEnd = opts?.beforeSpanEnd;\n const deferSpanEnd = opts?.deferSpanEnd;\n const getErrorHint = (e: unknown): ExclusiveEventHintOrCaptureContext => {\n if (typeof opts?.captureError === 'function') {\n return opts.captureError(e);\n }\n\n return {\n mechanism: {\n type: 'auto.diagnostic_channels.bind_span',\n handled: false,\n },\n };\n };\n\n // Apply Sentry error status + attributes (and capture, if configured) to a span. Shared by the\n // channel `error` lifecycle and the deferred `end` util so the two can't drift.\n const annotateSpanError = (span: Span, error: unknown): void => {\n if (opts?.captureError) {\n captureException(error, getErrorHint(error));\n }\n\n const { message, attributes } = getErrorInfo(error);\n span.setStatus({ code: SPAN_STATUS_ERROR, message });\n span.setAttributes(attributes);\n };\n\n // Creates an end fn for deferred handlers to use, ensures consistent span end behavior\n const makeDeferredEnd = (span: Span, data: TracingChannelPayloadWithSpan<TData>) => {\n let ended = false;\n\n return (error?: unknown): void => {\n if (ended) {\n return;\n }\n\n ended = true;\n if (error !== undefined) {\n annotateSpanError(span, error);\n }\n\n endBoundSpan(data, beforeSpanEnd);\n };\n };\n\n const subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>> = {\n start: NOOP,\n asyncStart: NOOP,\n end(data) {\n // The operation settled synchronously (returned or threw)\n // Presence checks because caller can return `undefined` result or throw a falsy value.\n if ('error' in data || 'result' in data) {\n const span = data._sentrySpan;\n if (span && deferSpanEnd?.({ span, data, end: makeDeferredEnd(span, data) })) {\n return;\n }\n endBoundSpan(data, beforeSpanEnd);\n }\n },\n error(data) {\n // No span was bound for this payload (`getSpan` returned undefined), so there is nothing to\n // annotate and no instrumentation that owns capturing this error.\n const span = data._sentrySpan;\n if (!span) {\n return;\n }\n\n annotateSpanError(span, data.error);\n },\n asyncEnd(data) {\n const span = data._sentrySpan;\n if (span && deferSpanEnd?.({ span, data, end: makeDeferredEnd(span, data) })) {\n return;\n }\n endBoundSpan(data, beforeSpanEnd);\n },\n };\n\n handle.channel.subscribe(subscribers);\n\n return {\n channel: handle.channel,\n unbind: () => {\n handle.channel.unsubscribe(subscribers);\n handle.unbind();\n },\n };\n}\n\n/**\n * Bind a span into the channel's async context so it becomes active for the traced operation,\n * without managing its lifecycle. The primitive behind {@link bindTracingChannelToSpan}, which\n * layers span-ending and error handling on top.\n *\n * `getSpan` may return `undefined` to leave the active context untouched for that payload.\n */\nfunction bindSpanToChannelStore<TData extends object>(\n channel: TracingChannel<TData, TData>,\n getSpan: (data: TracingChannelPayloadWithSpan<TData>) => Span | undefined,\n): TracingChannelBindingHandle<TData> {\n // Grabs the tracing channel binding defined by the AsyncContext strategy implementation\n const binding = getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.();\n\n // If no binding, then either the implementer doesn't support tracing channels or there is no active strategy\n // Failure mode here means we would still access the channel and potentially subscribe to it, but parenting will be off.\n if (!binding) {\n DEBUG_BUILD && debug.log('[TracingChannel] Could not access async context binding.');\n\n return {\n channel,\n unbind: NOOP,\n };\n }\n\n // Grab the ALS instance, we don't really care what is in it as long as the AsyncContext strategy can use its value to figure out parenting.\n const asyncLocalStorage = binding.asyncLocalStorage as AsyncLocalStorage<TData>;\n\n // bindStore activates the ALS for the traced call; any getStore() inside it returns the value bound for that context.\n // 1. Produce: getStoreWithActiveSpan(span) clones the current scope, plants the span via _INTERNAL_setSpanForScope, and returns { scope, isolationScope }, the active context carrying our span.\n // 2. Bind: the courier hands that opaque value to channel.start.bindStore(asyncLocalStorage, producer), which runs the traced op inside asyncLocalStorage.run(value, …); it never inspects the value.\n // 3. Read: inside the op, Sentry's scope machinery calls getScopes() → asyncStorage.getStore() on that same ALS, so getCurrentScope/getIsolationScope/getActiveSpan resolve to the scope carrying our span.\n // 4. Nest: any child span started in the traced op parents to that active span.\n channel.start.bindStore(asyncLocalStorage, (data: TracingChannelPayloadWithSpan<TData>) => {\n // Stash the caller's store before we swap in the span store, so `asyncStart` can restore it for\n // callback-style channels (see `_sentryCallerStore`).\n data._sentryCallerStore = asyncLocalStorage.getStore();\n\n const span = getSpan(data);\n if (!span) {\n // Leave the active context untouched so nested operations keep parenting to the enclosing span.\n return data._sentryCallerStore as TData;\n }\n data._sentrySpan = span;\n\n return binding.getStoreWithActiveSpan(span) as TData;\n });\n\n // Restore the caller's context for the async continuation. Only callback-style channels `runStores`\n // `asyncStart` (so the callback runs inside this store). promise channels `publish` it, leaving this\n // inert, their continuation already inherits the caller's context natively.\n channel.asyncStart.bindStore(asyncLocalStorage, (data: TracingChannelPayloadWithSpan<TData>) => {\n return data._sentryCallerStore as TData;\n });\n\n return {\n channel,\n unbind: () => {\n // Removes the stores\n channel.start.unbindStore(asyncLocalStorage);\n channel.asyncStart.unbindStore(asyncLocalStorage);\n },\n };\n}\n\nfunction endBoundSpan<TData extends object>(\n data: TracingChannelPayloadWithSpan<TData>,\n beforeSpanEnd: TracingChannelLifeCycleOptions<TData>['beforeSpanEnd'],\n): void {\n const span = data._sentrySpan;\n if (!span) {\n return;\n }\n beforeSpanEnd?.(span, data);\n span.end();\n}\n\ntype ErrorInfo = {\n message: string | undefined;\n attributes: Record<string, string>;\n};\n\n/**\n * Best-effort message and attribute extraction for thrown/rejected values.\n */\nfunction getErrorInfo(error: unknown): ErrorInfo {\n const isObject = !!error && typeof error === 'object';\n const raw = isObject ? ('message' in error ? error.message : undefined) : error;\n\n // Leave the status message unset if not set (e.g. an `AggregateError` from\n // ECONNREFUSED, whose `.message` is empty). Otherwise cast to string.\n const message = raw ? String(raw) : undefined;\n const type = isObject && 'name' in error ? String(error.name) : 'unknown';\n\n return {\n message,\n attributes: {\n [ERROR_TYPE]: type,\n },\n };\n}\n"],"names":["captureException","SPAN_STATUS_ERROR","getAsyncContextStrategy","getMainCarrier","DEBUG_BUILD","debug","ERROR_TYPE"],"mappings":"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"} | ||
| {"version":3,"file":"tracing-channel.js","sources":["../../src/tracing-channel.ts"],"sourcesContent":["import type { TracingChannel, TracingChannelSubscribers } from 'node:diagnostics_channel';\nimport type { AsyncLocalStorage } from 'node:async_hooks';\nimport type { ExclusiveEventHintOrCaptureContext, Span } from '@sentry/core';\nimport {\n debug,\n captureException,\n isObjectLike,\n SPAN_STATUS_ERROR,\n getAsyncContextStrategy,\n getMainCarrier,\n getActiveSpan,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { ERROR_TYPE } from '@sentry/conventions/attributes';\n\nexport type TracingChannelPayloadWithSpan<TData extends object> = TData & {\n /**\n * The current active span for the traced call.\n */\n _sentrySpan?: Span;\n\n /**\n * The context's active store value, used to restore the context for asyncStart continuations for callback-based tracing.\n */\n _sentryCallerStore?: unknown;\n};\n\n/*\n * A type patch so that we don't have to handle all subscription types.\n */\nexport interface SentryTracingChannel<TData extends object = object> extends Omit<\n TracingChannel<TData, TracingChannelPayloadWithSpan<TData>>,\n 'subscribe' | 'unsubscribe'\n> {\n subscribe(subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>>): void;\n unsubscribe(subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>>): void;\n}\n\nexport interface TracingChannelLifeCycleOptions<TData extends object = object> {\n /**\n * Invoked with the span and the channel context object once the traced operation completes\n * Use it to enrich the span from the result/error (branch on `'error' in data` / `'result' in data`) or to run cleanup.\n */\n beforeSpanEnd?: (span: Span, data: TracingChannelPayloadWithSpan<TData>) => void;\n\n /**\n * Whether a thrown error is captured as a Sentry event. The span is always marked with error status regardless. Defaults to `false`.\n * You can alternatively pass a function that sets the ExclusiveEventHintOrCaptureContext on the captured error.\n * Set `true` for instrumentations that own the error boundary, (e.g: route handlers)\n * For database drivers, it is not recommended to set this at all.\n */\n captureError?: boolean | ((e: unknown) => ExclusiveEventHintOrCaptureContext);\n\n /**\n * Take ownership of *when* the span ends: return `true` and the helper won't end it on\n * `end`/`asyncEnd`. For results that settle out-of-band — e.g. a streamed `EventEmitter` that\n * completes via its own `'end'`/`'error'` events.\n *\n * Call `end` when it settles — `end()` on success, `end(error)` on failure. `end` owns *how* the span\n * ends (error status/attributes, `captureError`, `beforeSpanEnd`) and is idempotent. Default `false`\n * lets the helper end the span as usual.\n */\n deferSpanEnd?: (args: {\n span: Span;\n data: TracingChannelPayloadWithSpan<TData>;\n /** Ends the span: `end()` on success, `end(error)` on failure. Idempotent. */\n end: (error?: unknown) => void;\n }) => boolean;\n\n /**\n * Apply span/scope binding only if there is a parent span, similar to returning `undefined` in the `getSpan` callback.\n * If you need to perform some conditional checking on the parent span before deciding, do it in the `getSpan` callback.\n */\n requiresParentSpan?: boolean;\n}\n\n/** Returned by {@link bindTracingChannelToSpan}: the bound channel plus a teardown handle. */\nexport interface TracingChannelBindingHandle<TData extends object = object> {\n /**\n * The tracing channel with the span bound into async context.\n */\n channel: SentryTracingChannel<TData>;\n\n /**\n * Tears down the binding: unsubscribes lifecycle handlers, when present, and unbinds the start store.\n * Idempotent, and a no-op when no async context binding was available.\n */\n unbind: () => void;\n}\n\nconst NOOP = (): void => {};\n\n/**\n * Bind a span and its lifecycle to a tracing channel so the span becomes the active async context\n * for the traced operation and is ended when the operation completes.\n *\n * `getSpan` may return `undefined` to opt a payload out entirely: nothing is bound, no span is\n * tracked, and the active context is left untouched. Use it for events that ride the same channel\n * but should reuse the enclosing span instead of opening (and ending) their own — e.g. an agent\n * loop's per-step events, where ending a freshly opened span would close the parent prematurely.\n */\nexport function bindTracingChannelToSpan<TData extends object>(\n channel: TracingChannel<TData, TData>,\n getSpan: (data: TracingChannelPayloadWithSpan<TData>) => Span | undefined,\n opts?: TracingChannelLifeCycleOptions<TData>,\n): TracingChannelBindingHandle<TData> {\n const handle = bindSpanToChannelStore(channel, getSpan, opts);\n\n const beforeSpanEnd = opts?.beforeSpanEnd;\n const deferSpanEnd = opts?.deferSpanEnd;\n const getErrorHint = (e: unknown): ExclusiveEventHintOrCaptureContext => {\n if (typeof opts?.captureError === 'function') {\n return opts.captureError(e);\n }\n\n return {\n mechanism: {\n type: 'auto.diagnostic_channels.bind_span',\n handled: false,\n },\n };\n };\n\n // Apply Sentry error status + attributes (and capture, if configured) to a span. Shared by the\n // channel `error` lifecycle and the deferred `end` util so the two can't drift.\n const annotateSpanError = (span: Span, error: unknown): void => {\n if (opts?.captureError) {\n captureException(error, getErrorHint(error));\n }\n\n const { message, attributes } = getErrorInfo(error);\n span.setStatus({ code: SPAN_STATUS_ERROR, message });\n span.setAttributes(attributes);\n };\n\n // Creates an end fn for deferred handlers to use, ensures consistent span end behavior\n const makeDeferredEnd = (span: Span, data: TracingChannelPayloadWithSpan<TData>) => {\n let ended = false;\n\n return (error?: unknown): void => {\n if (ended) {\n return;\n }\n\n ended = true;\n if (error !== undefined) {\n annotateSpanError(span, error);\n }\n\n endBoundSpan(data, beforeSpanEnd);\n };\n };\n\n const subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>> = {\n start: NOOP,\n asyncStart: NOOP,\n end(data) {\n // The operation settled synchronously (returned or threw)\n // Presence checks because caller can return `undefined` result or throw a falsy value.\n if ('error' in data || 'result' in data) {\n const span = data._sentrySpan;\n if (span && deferSpanEnd?.({ span, data, end: makeDeferredEnd(span, data) })) {\n return;\n }\n endBoundSpan(data, beforeSpanEnd);\n }\n },\n error(data) {\n // No span was bound for this payload (`getSpan` returned undefined), so there is nothing to\n // annotate and no instrumentation that owns capturing this error.\n const span = data._sentrySpan;\n if (!span) {\n return;\n }\n\n annotateSpanError(span, data.error);\n },\n asyncEnd(data) {\n const span = data._sentrySpan;\n if (span && deferSpanEnd?.({ span, data, end: makeDeferredEnd(span, data) })) {\n return;\n }\n endBoundSpan(data, beforeSpanEnd);\n },\n };\n\n handle.channel.subscribe(subscribers);\n\n return {\n channel: handle.channel,\n unbind: () => {\n handle.channel.unsubscribe(subscribers);\n handle.unbind();\n },\n };\n}\n\n/**\n * Bind a span into the channel's async context so it becomes active for the traced operation,\n * without managing its lifecycle. The primitive behind {@link bindTracingChannelToSpan}, which\n * layers span-ending and error handling on top.\n *\n * `getSpan` may return `undefined` to leave the active context untouched for that payload.\n */\nfunction bindSpanToChannelStore<TData extends object>(\n channel: TracingChannel<TData, TData>,\n getSpan: (data: TracingChannelPayloadWithSpan<TData>) => Span | undefined,\n opts?: Pick<TracingChannelLifeCycleOptions, 'requiresParentSpan'>,\n): TracingChannelBindingHandle<TData> {\n // Grabs the tracing channel binding defined by the AsyncContext strategy implementation\n const binding = getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.();\n\n // If no binding, then either the implementer doesn't support tracing channels or there is no active strategy\n // Failure mode here means we would still access the channel and potentially subscribe to it, but parenting will be off.\n if (!binding) {\n DEBUG_BUILD && debug.log('[TracingChannel] Could not access async context binding.');\n\n return {\n channel,\n unbind: NOOP,\n };\n }\n\n // Grab the ALS instance, we don't really care what is in it as long as the AsyncContext strategy can use its value to figure out parenting.\n const asyncLocalStorage = binding.asyncLocalStorage as AsyncLocalStorage<TData>;\n\n // bindStore activates the ALS for the traced call; any getStore() inside it returns the value bound for that context.\n // 1. Produce: getStoreWithActiveSpan(span) clones the current scope, plants the span via _INTERNAL_setSpanForScope, and returns { scope, isolationScope }, the active context carrying our span.\n // 2. Bind: the courier hands that opaque value to channel.start.bindStore(asyncLocalStorage, producer), which runs the traced op inside asyncLocalStorage.run(value, …); it never inspects the value.\n // 3. Read: inside the op, Sentry's scope machinery calls getScopes() → asyncStorage.getStore() on that same ALS, so getCurrentScope/getIsolationScope/getActiveSpan resolve to the scope carrying our span.\n // 4. Nest: any child span started in the traced op parents to that active span.\n channel.start.bindStore(asyncLocalStorage, (data: TracingChannelPayloadWithSpan<TData>) => {\n // Stash the caller's store before we swap in the span store, so `asyncStart` can restore it for\n // callback-style channels (see `_sentryCallerStore`).\n data._sentryCallerStore = asyncLocalStorage.getStore();\n\n const shouldGetSpan = !opts?.requiresParentSpan || getActiveSpan();\n const span = shouldGetSpan ? getSpan(data) : undefined;\n if (!span) {\n // Leave the active context untouched so nested operations keep parenting to the enclosing span.\n return data._sentryCallerStore as TData;\n }\n data._sentrySpan = span;\n\n return binding.getStoreWithActiveSpan(span) as TData;\n });\n\n // Restore the caller's context for the async continuation. Only callback-style channels `runStores`\n // `asyncStart` (so the callback runs inside this store). promise channels `publish` it, leaving this\n // inert, their continuation already inherits the caller's context natively.\n channel.asyncStart.bindStore(asyncLocalStorage, (data: TracingChannelPayloadWithSpan<TData>) => {\n return data._sentryCallerStore as TData;\n });\n\n return {\n channel,\n unbind: () => {\n // Removes the stores\n channel.start.unbindStore(asyncLocalStorage);\n channel.asyncStart.unbindStore(asyncLocalStorage);\n },\n };\n}\n\nfunction endBoundSpan<TData extends object>(\n data: TracingChannelPayloadWithSpan<TData>,\n beforeSpanEnd: TracingChannelLifeCycleOptions<TData>['beforeSpanEnd'],\n): void {\n const span = data._sentrySpan;\n if (!span) {\n return;\n }\n beforeSpanEnd?.(span, data);\n span.end();\n}\n\ntype ErrorInfo = {\n message: string | undefined;\n attributes: Record<string, string>;\n};\n\n/**\n * Best-effort message and attribute extraction for thrown/rejected values.\n */\nfunction getErrorInfo(error: unknown): ErrorInfo {\n const errorIsObject = isObjectLike(error);\n const raw = errorIsObject ? ('message' in error ? error.message : undefined) : error;\n\n // Leave the status message unset if not set (e.g. an `AggregateError` from\n // ECONNREFUSED, whose `.message` is empty). Otherwise cast to string.\n const message = raw ? String(raw) : undefined;\n const type = errorIsObject && 'name' in error ? String(error.name) : 'unknown';\n\n return {\n message,\n attributes: {\n [ERROR_TYPE]: type,\n },\n };\n}\n"],"names":["captureException","SPAN_STATUS_ERROR","getAsyncContextStrategy","getMainCarrier","DEBUG_BUILD","debug","getActiveSpan","isObjectLike","ERROR_TYPE"],"mappings":"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"} |
@@ -7,2 +7,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const tracingChannel = require('../tracing-channel.js'); | ||
| const util = require('./util.js'); | ||
@@ -21,3 +22,11 @@ const AI_SDK_TELEMETRY_TRACING_CHANNEL = "ai:telemetry"; | ||
| const toolDescriptionsByCallId = /* @__PURE__ */ new Map(); | ||
| const ROOT_OPERATION_TYPES = /* @__PURE__ */ new Set(["generateText", "streamText", "embed", "embedMany", "rerank"]); | ||
| const invokeAgentSpanByCallId = /* @__PURE__ */ new Map(); | ||
| const ROOT_OPERATION_TYPES = /* @__PURE__ */ new Set([ | ||
| "generateText", | ||
| "streamText", | ||
| "generateObject", | ||
| "embed", | ||
| "embedMany", | ||
| "rerank" | ||
| ]); | ||
| function clearOperationId(data) { | ||
@@ -27,3 +36,3 @@ if (!ROOT_OPERATION_TYPES.has(data.type)) { | ||
| } | ||
| const callId = asString(data.event.callId); | ||
| const callId = util.asString(data.event.callId); | ||
| if (callId) { | ||
@@ -36,2 +45,3 @@ clearOperationCallId(callId); | ||
| toolDescriptionsByCallId.delete(callId); | ||
| invokeAgentSpanByCallId.delete(callId); | ||
| } | ||
@@ -44,3 +54,3 @@ function recordToolDescriptions(callId, tools) { | ||
| for (const tool of tools) { | ||
| if (isRecord(tool) && typeof tool.name === "string" && typeof tool.description === "string") { | ||
| if (core.isObjectLike(tool) && typeof tool.name === "string" && typeof tool.description === "string") { | ||
| descriptions = descriptions ?? /* @__PURE__ */ new Map(); | ||
@@ -62,17 +72,12 @@ if (!descriptions.has(tool.name)) { | ||
| if (Array.isArray(tools)) { | ||
| const match = tools.find((tool) => isRecord(tool) && tool.name === toolName); | ||
| return isRecord(match) ? asString(match.description) : void 0; | ||
| const match = tools.find((tool) => core.isObjectLike(tool) && tool.name === toolName); | ||
| return core.isObjectLike(match) ? util.asString(match.description) : void 0; | ||
| } | ||
| if (isRecord(tools)) { | ||
| if (core.isObjectLike(tools)) { | ||
| const tool = tools[toolName]; | ||
| return isRecord(tool) ? asString(tool.description) : void 0; | ||
| return core.isObjectLike(tool) ? util.asString(tool.description) : void 0; | ||
| } | ||
| return void 0; | ||
| } | ||
| let subscribed = false; | ||
| function subscribeVercelAiTracingChannel(tracingChannel$1, options = {}) { | ||
| if (subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| tracingChannel.bindTracingChannelToSpan( | ||
@@ -87,6 +92,80 @@ tracingChannel$1(AI_SDK_TELEMETRY_TRACING_CHANNEL), | ||
| clearOperationId(data); | ||
| } | ||
| }, | ||
| // A streamed model call resolves before its stream is drained, so we tap the stream, keep the | ||
| // span open, and end it (via `end`) once the final usage/finish/output chunks arrive. | ||
| deferSpanEnd: ({ data, end }) => deferStreamedModelCallEnd(data, options, end) | ||
| } | ||
| ); | ||
| } | ||
| function deferStreamedModelCallEnd(data, options, end) { | ||
| if (data.type !== "languageModelCall" || !core.isObjectLike(data.result)) { | ||
| return false; | ||
| } | ||
| const result = data.result; | ||
| const stream = result.stream; | ||
| if (!util.isReadableStream(stream)) { | ||
| return false; | ||
| } | ||
| const callId = util.asString(data.event.callId); | ||
| const { recordOutputs } = getRecordingOptions(data.event, options); | ||
| result.stream = util.tapModelCallStream( | ||
| stream, | ||
| (final) => { | ||
| data.result = { ...result, ...streamedResultToChannelResult(final) }; | ||
| end(); | ||
| enrichInvokeAgentFromStream(callId, final, recordOutputs); | ||
| }, | ||
| (error) => end(error) | ||
| ); | ||
| return true; | ||
| } | ||
| function streamedResultToChannelResult(final) { | ||
| const content = []; | ||
| if (final.text) { | ||
| content.push({ type: "text", text: final.text }); | ||
| } | ||
| for (const toolCall of final.toolCalls) { | ||
| content.push({ type: "tool-call", ...toolCall }); | ||
| } | ||
| return { | ||
| content, | ||
| ...final.usage !== void 0 ? { usage: final.usage } : {}, | ||
| ...final.finishReason !== void 0 ? { finishReason: final.finishReason } : {}, | ||
| ...final.providerMetadata !== void 0 ? { providerMetadata: final.providerMetadata } : {}, | ||
| ...final.responseId || final.responseModel ? { | ||
| response: { | ||
| ...final.responseId ? { id: final.responseId } : {}, | ||
| ...final.responseModel ? { modelId: final.responseModel } : {} | ||
| } | ||
| } : {} | ||
| }; | ||
| } | ||
| function enrichInvokeAgentFromStream(callId, final, recordOutputs) { | ||
| const span = callId ? invokeAgentSpanByCallId.get(callId) : void 0; | ||
| if (!span) { | ||
| return; | ||
| } | ||
| const usage = core.isObjectLike(final.usage) ? final.usage : void 0; | ||
| if (usage) { | ||
| const input = tokenCount(usage.inputTokens) ?? tokenCount(usage.promptTokens) ?? tokenCount(usage.tokens); | ||
| const output = tokenCount(usage.outputTokens) ?? tokenCount(usage.completionTokens); | ||
| addTokensToSpan(span, attributes.GEN_AI_USAGE_INPUT_TOKENS, input); | ||
| addTokensToSpan(span, attributes.GEN_AI_USAGE_OUTPUT_TOKENS, output); | ||
| addTokensToSpan(span, attributes.GEN_AI_USAGE_TOTAL_TOKENS, tokenCount(usage.totalTokens) ?? util.sum(input, output)); | ||
| } | ||
| if (recordOutputs) { | ||
| const parts = partsFromTextAndToolCalls(final.text, final.toolCalls); | ||
| const outputMessages = buildOutputMessages(parts, getFinishReason({ finishReason: final.finishReason })); | ||
| if (outputMessages) { | ||
| span.setAttribute(attributes.GEN_AI_OUTPUT_MESSAGES, outputMessages); | ||
| } | ||
| } | ||
| } | ||
| function addTokensToSpan(span, attribute, value) { | ||
| if (value === void 0) { | ||
| return; | ||
| } | ||
| const current = core.spanToJSON(span).data[attribute]; | ||
| span.setAttribute(attribute, (typeof current === "number" ? current : 0) + value); | ||
| } | ||
| function createSpanFromMessage(data, channelOptions) { | ||
@@ -98,6 +177,6 @@ const { type, event } = data; | ||
| const { recordInputs, enableTruncation } = getRecordingOptions(event, channelOptions); | ||
| const provider = asString(event.provider); | ||
| const modelId = asString(event.modelId); | ||
| const callId = asString(event.callId); | ||
| const maxRetries = asNumber(event.maxRetries); | ||
| const provider = util.asString(event.provider); | ||
| const modelId = util.asString(event.modelId); | ||
| const callId = util.asString(event.callId); | ||
| const maxRetries = util.asNumber(event.maxRetries); | ||
| if (recordInputs) { | ||
@@ -115,2 +194,3 @@ recordToolDescriptions(callId, event.tools); | ||
| case "streamText": | ||
| case "generateObject": | ||
| return buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, type === "streamText"); | ||
@@ -126,3 +206,3 @@ case "languageModelCall": | ||
| ...baseAttributes, | ||
| ...recordInputs && input !== void 0 ? { [attributes.GEN_AI_EMBEDDINGS_INPUT]: safeStringify(input) } : {} | ||
| ...recordInputs && input !== void 0 ? { [attributes.GEN_AI_EMBEDDINGS_INPUT]: util.safeStringify(input) } : {} | ||
| }); | ||
@@ -144,8 +224,8 @@ } | ||
| function buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, isStream) { | ||
| const functionId = asString(event.functionId); | ||
| const operationId = asString(event.operationId) ?? (isStream ? "ai.streamText" : "ai.generateText"); | ||
| const functionId = util.asString(event.functionId); | ||
| const operationId = util.asString(event.operationId) ?? (isStream ? "ai.streamText" : "ai.generateText"); | ||
| if (callId) { | ||
| operationIdByCallId.set(callId, { operationId, isStream }); | ||
| } | ||
| return startGenAiSpan(op.GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, { | ||
| const span = startGenAiSpan(op.GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, { | ||
| ...baseAttributes, | ||
@@ -157,2 +237,6 @@ [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId, | ||
| }); | ||
| if (isStream && callId) { | ||
| invokeAgentSpanByCallId.set(callId, span); | ||
| } | ||
| return span; | ||
| } | ||
@@ -166,11 +250,11 @@ function buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId) { | ||
| ...recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}, | ||
| ...recordInputs && Array.isArray(event.tools) ? { [attributes.GEN_AI_REQUEST_AVAILABLE_TOOLS]: safeStringify(event.tools) } : {} | ||
| ...recordInputs && Array.isArray(event.tools) ? { [attributes.GEN_AI_REQUEST_AVAILABLE_TOOLS]: util.safeStringify(event.tools) } : {} | ||
| }); | ||
| } | ||
| function buildToolSpan(event, recordInputs) { | ||
| const toolCall = isRecord(event.toolCall) ? event.toolCall : {}; | ||
| const toolName = asString(toolCall.toolName); | ||
| const toolCallId = asString(event.toolCallId) ?? asString(toolCall.toolCallId); | ||
| const toolCall = core.isObjectLike(event.toolCall) ? event.toolCall : {}; | ||
| const toolName = util.asString(toolCall.toolName); | ||
| const toolCallId = util.asString(event.toolCallId) ?? util.asString(toolCall.toolCallId); | ||
| const toolInput = toolCall.input ?? toolCall.args; | ||
| const description = recordInputs && toolName ? resolveToolDescription(asString(event.callId), toolName, event.tools) : void 0; | ||
| const description = recordInputs && toolName ? resolveToolDescription(util.asString(event.callId), toolName, event.tools) : void 0; | ||
| return startGenAiSpan(op.GEN_AI_EXECUTE_TOOL_SPAN_OP, toolName, { | ||
@@ -182,3 +266,3 @@ [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| ...description ? { [GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE]: description } : {}, | ||
| ...recordInputs && toolInput !== void 0 ? { [attributes.GEN_AI_TOOL_INPUT]: safeStringify(toolInput) } : {} | ||
| ...recordInputs && toolInput !== void 0 ? { [attributes.GEN_AI_TOOL_INPUT]: util.safeStringify(toolInput) } : {} | ||
| }); | ||
@@ -188,3 +272,3 @@ } | ||
| const { type, result } = data; | ||
| if (!isRecord(result)) { | ||
| if (!core.isObjectLike(result)) { | ||
| return; | ||
@@ -195,5 +279,5 @@ } | ||
| if (recordOutputs) { | ||
| span.setAttribute(attributes.GEN_AI_TOOL_OUTPUT, safeStringify(result.output ?? result)); | ||
| span.setAttribute(attributes.GEN_AI_TOOL_OUTPUT, util.safeStringify(result.output ?? result)); | ||
| } | ||
| const output = isRecord(result.output) ? result.output : void 0; | ||
| const output = core.isObjectLike(result.output) ? result.output : void 0; | ||
| if (output?.type === "tool-error") { | ||
@@ -204,7 +288,7 @@ captureToolError(span, data, output.error); | ||
| } | ||
| const usage = isRecord(result.usage) ? result.usage : void 0; | ||
| const usage = core.isObjectLike(result.usage) ? result.usage : void 0; | ||
| if (usage) { | ||
| const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.tokens); | ||
| const outputTokens = tokenCount(usage.outputTokens); | ||
| const totalTokens = tokenCount(usage.totalTokens) ?? sum(inputTokens, outputTokens); | ||
| const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.promptTokens) ?? tokenCount(usage.tokens); | ||
| const outputTokens = tokenCount(usage.outputTokens) ?? tokenCount(usage.completionTokens); | ||
| const totalTokens = tokenCount(usage.totalTokens) ?? util.sum(inputTokens, outputTokens); | ||
| if (inputTokens !== void 0) { | ||
@@ -222,10 +306,10 @@ span.setAttribute(attributes.GEN_AI_USAGE_INPUT_TOKENS, inputTokens); | ||
| if (finishReason && type === "languageModelCall") { | ||
| span.setAttribute(attributes.GEN_AI_RESPONSE_FINISH_REASONS, safeStringify([finishReason])); | ||
| span.setAttribute(attributes.GEN_AI_RESPONSE_FINISH_REASONS, util.safeStringify([finishReason])); | ||
| } | ||
| const response = isRecord(result.response) ? result.response : void 0; | ||
| const responseId = asString(response?.id) ?? asString(result.responseId); | ||
| const response = core.isObjectLike(result.response) ? result.response : void 0; | ||
| const responseId = util.asString(response?.id) ?? util.asString(result.responseId); | ||
| if (responseId) { | ||
| span.setAttribute(attributes.GEN_AI_RESPONSE_ID, responseId); | ||
| } | ||
| const responseModel = asString(response?.modelId) ?? asString(data.event.modelId); | ||
| const responseModel = util.asString(response?.modelId) ?? util.asString(data.event.modelId); | ||
| if (responseModel) { | ||
@@ -256,6 +340,6 @@ span.setAttribute(attributes.GEN_AI_RESPONSE_MODEL, responseModel); | ||
| } | ||
| return isRecord(finishReason) ? asString(finishReason.unified) : void 0; | ||
| return core.isObjectLike(finishReason) ? util.asString(finishReason.unified) : void 0; | ||
| } | ||
| function tokenCount(value) { | ||
| return asNumber(value) ?? (isRecord(value) ? asNumber(value.total) : void 0); | ||
| return util.asNumber(value) ?? (core.isObjectLike(value) ? util.asNumber(value.total) : void 0); | ||
| } | ||
@@ -266,3 +350,3 @@ function buildOutputMessages(parts, finishReason) { | ||
| } | ||
| return safeStringify([{ role: "assistant", parts, finish_reason: normalizeFinishReason(finishReason) }]); | ||
| return util.safeStringify([{ role: "assistant", parts, finish_reason: normalizeFinishReason(finishReason) }]); | ||
| } | ||
@@ -273,5 +357,5 @@ function toolCallPart(toolCall) { | ||
| type: "tool_call", | ||
| id: asString(toolCall.toolCallId), | ||
| name: asString(toolCall.toolName), | ||
| arguments: typeof args === "string" ? args : safeStringify(args ?? {}) | ||
| id: util.asString(toolCall.toolCallId), | ||
| name: util.asString(toolCall.toolName), | ||
| arguments: typeof args === "string" ? args : util.safeStringify(args ?? {}) | ||
| }; | ||
@@ -282,3 +366,3 @@ } | ||
| for (const item of content) { | ||
| if (!isRecord(item)) { | ||
| if (!core.isObjectLike(item)) { | ||
| continue; | ||
@@ -301,3 +385,3 @@ } | ||
| for (const toolCall of toolCalls) { | ||
| if (isRecord(toolCall)) { | ||
| if (core.isObjectLike(toolCall)) { | ||
| parts.push(toolCallPart(toolCall)); | ||
@@ -314,5 +398,5 @@ } | ||
| }); | ||
| const toolCall = isRecord(data.event.toolCall) ? data.event.toolCall : {}; | ||
| const toolName = asString(toolCall.toolName); | ||
| const toolCallId = asString(data.event.toolCallId) ?? asString(toolCall.toolCallId); | ||
| const toolCall = core.isObjectLike(data.event.toolCall) ? data.event.toolCall : {}; | ||
| const toolName = util.asString(toolCall.toolName); | ||
| const toolCallId = util.asString(data.event.toolCallId) ?? util.asString(toolCall.toolCallId); | ||
| core.withScope((scope) => { | ||
@@ -354,9 +438,9 @@ scope.setContext("trace", core.spanToTraceContext(span)); | ||
| const attributes$1 = {}; | ||
| const instructions = asString(event.instructions); | ||
| const instructions = util.asString(event.instructions); | ||
| if (instructions) { | ||
| attributes$1[core.GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE] = safeStringify([{ type: "text", content: instructions }]); | ||
| attributes$1[core.GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE] = util.safeStringify([{ type: "text", content: instructions }]); | ||
| } | ||
| const messages = event.messages ?? event.prompt; | ||
| if (messages !== void 0) { | ||
| attributes$1[attributes.GEN_AI_INPUT_MESSAGES] = enableTruncation ? core.getTruncatedJsonString(messages) : safeStringify(messages); | ||
| attributes$1[attributes.GEN_AI_INPUT_MESSAGES] = enableTruncation ? core.getTruncatedJsonString(messages) : util.safeStringify(messages); | ||
| attributes$1[core.GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE] = Array.isArray(messages) ? messages.length : 1; | ||
@@ -366,25 +450,4 @@ } | ||
| } | ||
| function asString(value) { | ||
| return typeof value === "string" ? value : void 0; | ||
| } | ||
| function asNumber(value) { | ||
| return typeof value === "number" && !isNaN(value) ? value : void 0; | ||
| } | ||
| function sum(a, b) { | ||
| return a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0); | ||
| } | ||
| function isRecord(value) { | ||
| return typeof value === "object" && value !== null; | ||
| } | ||
| function safeStringify(value) { | ||
| if (typeof value === "string") { | ||
| return value; | ||
| } | ||
| try { | ||
| return JSON.stringify(value); | ||
| } catch { | ||
| return "[unserializable]"; | ||
| } | ||
| } | ||
| exports.captureToolError = captureToolError; | ||
| exports.clearOperationCallId = clearOperationCallId; | ||
@@ -394,3 +457,4 @@ exports.clearOperationId = clearOperationId; | ||
| exports.enrichSpanOnEnd = enrichSpanOnEnd; | ||
| exports.streamedResultToChannelResult = streamedResultToChannelResult; | ||
| exports.subscribeVercelAiTracingChannel = subscribeVercelAiTracingChannel; | ||
| //# sourceMappingURL=vercel-ai-dc-subscriber.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"vercel-ai-dc-subscriber.js","sources":["../../../src/vercel-ai/vercel-ai-dc-subscriber.ts"],"sourcesContent":["/* eslint-disable max-lines */\n// `@sentry/conventions` marks several gen_ai attributes (e.g. `GEN_AI_SYSTEM`, `GEN_AI_TOOL_*`,\n// `GEN_AI_REQUEST_AVAILABLE_TOOLS`) as deprecated in favour of newer semconv names. We intentionally\n// keep emitting the current names so these spans match the OTel-based (v6) integration and what the\n// Sentry product consumes today; migrating to the new names is a separate, coordinated change.\n/* eslint-disable typescript-eslint/no-deprecated */\nimport {\n GEN_AI_EMBEDDINGS_INPUT,\n GEN_AI_FUNCTION_ID,\n GEN_AI_INPUT_MESSAGES,\n GEN_AI_OPERATION_NAME,\n GEN_AI_OUTPUT_MESSAGES,\n GEN_AI_REQUEST_AVAILABLE_TOOLS,\n GEN_AI_REQUEST_MODEL,\n GEN_AI_RESPONSE_FINISH_REASONS,\n GEN_AI_RESPONSE_ID,\n GEN_AI_RESPONSE_MODEL,\n GEN_AI_RESPONSE_STREAMING,\n GEN_AI_SYSTEM,\n GEN_AI_TOOL_INPUT,\n GEN_AI_TOOL_NAME,\n GEN_AI_TOOL_OUTPUT,\n GEN_AI_TOOL_TYPE,\n GEN_AI_USAGE_INPUT_TOKENS,\n GEN_AI_USAGE_OUTPUT_TOKENS,\n GEN_AI_USAGE_TOTAL_TOKENS,\n} from '@sentry/conventions/attributes';\nimport { GEN_AI_EXECUTE_TOOL_SPAN_OP, GEN_AI_INVOKE_AGENT_SPAN_OP } from '@sentry/conventions/op';\nimport type { Span } from '@sentry/core';\nimport {\n captureException,\n GEN_AI_CONVERSATION_ID_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE,\n getClient,\n getProviderMetadataAttributes,\n getTruncatedJsonString,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n SPAN_STATUS_ERROR,\n spanToJSON,\n spanToTraceContext,\n startInactiveSpan,\n withScope,\n} from '@sentry/core';\nimport type { TracingChannel } from 'node:diagnostics_channel';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n/**\n * The single tracing channel the `ai` package (>= 7) publishes all telemetry lifecycle events to\n * via `node:diagnostics_channel`. Events are discriminated by their `type` field.\n * @see https://github.com/vercel/ai/pull/15660\n */\nconst AI_SDK_TELEMETRY_TRACING_CHANNEL = 'ai:telemetry';\n\nconst ORIGIN = 'auto.vercelai.channel';\n\n// `@sentry/conventions` does not expose these yet, so we keep the literals here.\nconst GEN_AI_TOOL_CALL_ID_ATTRIBUTE = 'gen_ai.tool.call.id';\nconst GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE = 'gen_ai.tool.description';\nconst GEN_AI_EMBEDDINGS_OPERATION = 'embeddings';\nconst GEN_AI_RERANK_OPERATION = 'rerank';\n// The model-call op matches the Vercel AI OTel integration (`gen_ai.generate_content`) rather than\n// the generic `gen_ai.chat`, so v6 (OTel) and v7 (channel) produce the same spans.\nconst GEN_AI_GENERATE_CONTENT_OPERATION = 'generate_content';\n\n// Subset of the `vercel.ai.*` passthrough attributes the OTel integration emits that we reproduce.\nconst VERCEL_AI_OPERATION_ID_ATTRIBUTE = 'vercel.ai.operationId';\nconst VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE = 'vercel.ai.model.provider';\nconst VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE = 'vercel.ai.settings.maxRetries';\n\n// Tracks the top-level operationId (and whether it streams) per `callId` so a model-call span can\n// name its `doGenerate`/`doStream` operation the same way the OTel integration does. `isStream` is\n// the authoritative event-type signal rather than a substring check on the (possibly custom)\n// operationId. Cleared when the top-level span ends.\nconst operationIdByCallId = new Map<string, { operationId: string; isStream: boolean }>();\n\n// Per-operation map of tool name → description, harvested from a model-call /\n// top-level event's `tools` (keyed by the shared `callId`). The AI SDK's\n// `executeTool` event doesn't carry the tool's description, so we backfill it\n// onto the tool span here — without relying on the OTel `vercelAiEventProcessor`\n// (which isn't registered in channel/orchestrion mode). Cleared with the\n// operation. Only populated when inputs are recorded, matching the OTel path\n// (which sources descriptions from the recorded `available_tools`).\nconst toolDescriptionsByCallId = new Map<string, Map<string, string>>();\n\n// Only top-level operations own the `callId` → operationId mapping; `step`/`languageModelCall`/\n// `executeTool` share the parent's `callId`, so they must not clear it.\nconst ROOT_OPERATION_TYPES = new Set<ChannelEventType>(['generateText', 'streamText', 'embed', 'embedMany', 'rerank']);\n\n/** Drop the per-operation `callId` maps once the owning top-level operation settles (success or error). */\nexport function clearOperationId(data: VercelAiChannelMessage): void {\n if (!ROOT_OPERATION_TYPES.has(data.type)) {\n return;\n }\n const callId = asString(data.event.callId);\n if (callId) {\n clearOperationCallId(callId);\n }\n}\n\n/**\n * Drop the per-operation `callId` maps for a single id. The v6 orchestrion adapter uses this to clear a\n * `streamText` operation only after its lazily-run model call settles — the operation's own span ends\n * synchronously (when `streamText` returns) but the model call runs later as the stream is consumed, and\n * it still needs the operation's `operationId`/`isStream` entry to name itself `ai.streamText.doStream`.\n */\nexport function clearOperationCallId(callId: string): void {\n operationIdByCallId.delete(callId);\n toolDescriptionsByCallId.delete(callId);\n}\n\n/** Record tool name → description from an event's `tools`, so tool spans can backfill the description. */\nfunction recordToolDescriptions(callId: string | undefined, tools: unknown): void {\n if (!callId || !Array.isArray(tools)) {\n return;\n }\n let descriptions = toolDescriptionsByCallId.get(callId);\n for (const tool of tools) {\n if (isRecord(tool) && typeof tool.name === 'string' && typeof tool.description === 'string') {\n descriptions = descriptions ?? new Map();\n if (!descriptions.has(tool.name)) {\n descriptions.set(tool.name, tool.description);\n }\n }\n }\n if (descriptions) {\n toolDescriptionsByCallId.set(callId, descriptions);\n }\n}\n\n/**\n * Resolve a tool's description, preferring the per-operation map (populated from the model-call /\n * top-level event's `tools`, v7) and falling back to a `tools` collection on the event itself —\n * which may be an array of `{ name, description }` or a record keyed by tool name (v6).\n */\nfunction resolveToolDescription(callId: string | undefined, toolName: string, tools: unknown): string | undefined {\n const fromMap = callId ? toolDescriptionsByCallId.get(callId)?.get(toolName) : undefined;\n if (fromMap) {\n return fromMap;\n }\n if (Array.isArray(tools)) {\n const match = tools.find(tool => isRecord(tool) && tool.name === toolName);\n return isRecord(match) ? asString(match.description) : undefined;\n }\n if (isRecord(tools)) {\n const tool = tools[toolName];\n return isRecord(tool) ? asString(tool.description) : undefined;\n }\n return undefined;\n}\n\n/** The lifecycle event types the `ai:telemetry` channel can carry. */\nexport type ChannelEventType =\n | 'generateText'\n | 'streamText'\n | 'step'\n | 'languageModelCall'\n | 'executeTool'\n | 'embed'\n | 'embedMany'\n | 'rerank';\n\n/**\n * The context object the AI SDK passes through one tracing-channel call. It is the same object\n * identity across `start`/`end`/`asyncEnd`/`error`, and Node's `tracingChannel` attaches\n * `result`/`error` to it as the traced promise settles.\n */\nexport interface VercelAiChannelMessage {\n type: ChannelEventType;\n event: Record<string, unknown>;\n result?: unknown;\n error?: unknown;\n}\n\n/**\n * Platform-provided factory that returns a tracing channel for the given channel name. The factory\n * is responsible for, when `start` fires, calling `transformStart(data)` and storing the returned\n * span on `data._sentrySpan` so the subscriber's `asyncEnd`/`error` handlers can read it.\n *\n * Node passes `@sentry/opentelemetry/tracing-channel`, which uses `bindStore` to additionally make\n * the span the active OTel context for the duration of the traced operation. That is what makes\n * nested AI SDK operations (model calls, tool calls) become children of the enclosing span without\n * any manual parent bookkeeping here.\n */\nexport type VercelAiTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/** Integration-level recording options, pinned at subscribe time so we never look the integration up per event. */\nexport interface VercelAiChannelOptions {\n recordInputs?: boolean;\n recordOutputs?: boolean;\n enableTruncation?: boolean;\n}\n\nlet subscribed = false;\n\n/**\n * Subscribe Sentry span handlers to the `ai` SDK's native telemetry tracing channel (`ai:telemetry`,\n * available in `ai` >= 7) and emit fully-formed `gen_ai.*` spans directly — no OpenTelemetry span\n * post-processing involved.\n *\n * The integration passes its options in directly (rather than us looking the integration up on every\n * event); the global `dataCollection.genAI` default is still read from the client per event.\n *\n * Safe to always call: on `ai` versions that don't publish to the channel (e.g. < 7) nothing is\n * ever emitted and this is inert, so there is no double-instrumentation against the OTel-based\n * patcher. Idempotent.\n */\nexport function subscribeVercelAiTracingChannel(\n tracingChannel: VercelAiTracingChannelFactory,\n options: VercelAiChannelOptions = {},\n): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n bindTracingChannelToSpan(\n tracingChannel<VercelAiChannelMessage>(AI_SDK_TELEMETRY_TRACING_CHANNEL),\n data => createSpanFromMessage(data, options),\n {\n // The helper ends the span; we enrich it from the settled result first (tokens, output messages,\n // finish reasons, response model/id, provider metadata) and drop the per-operation `callId` maps.\n beforeSpanEnd: (span, data) => {\n enrichSpanOnEnd(span, data, options);\n clearOperationId(data);\n },\n },\n );\n}\n\n/**\n * Transform a channel `start` payload into the span that should be active for the operation. For\n * `step` we deliberately don't open a span (model calls and tool calls are siblings under the\n * invoke_agent span, matching the OTel-based output), so we reuse the active span and mark the\n * payload to skip ending it.\n */\nexport function createSpanFromMessage(\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): Span | undefined {\n const { type, event } = data;\n\n if (type === 'step' || !event || typeof event !== 'object') {\n // Opt out: returning `undefined` leaves the enclosing `invoke_agent` span as the active context\n // (model-call and tool-call events nest under it) without opening — or ending — a span of its own.\n return undefined;\n }\n\n const { recordInputs, enableTruncation } = getRecordingOptions(event, channelOptions);\n const provider = asString(event.provider);\n const modelId = asString(event.modelId);\n const callId = asString(event.callId);\n const maxRetries = asNumber(event.maxRetries);\n\n // Harvest tool descriptions from the operation/model-call `tools` so tool spans can backfill them.\n // Gated on `recordInputs` to match the OTel path, which only records `available_tools` then.\n if (recordInputs) {\n recordToolDescriptions(callId, event.tools);\n }\n\n const baseAttributes: Record<string, string | number | boolean> = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n ...(provider ? { [GEN_AI_SYSTEM]: provider, [VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE]: provider } : {}),\n ...(modelId ? { [GEN_AI_REQUEST_MODEL]: modelId } : {}),\n ...(maxRetries !== undefined ? { [VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE]: maxRetries } : {}),\n };\n\n switch (type) {\n case 'generateText':\n case 'streamText':\n return buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, type === 'streamText');\n case 'languageModelCall':\n return buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId);\n case 'executeTool':\n return buildToolSpan(event, recordInputs);\n case 'embed':\n case 'embedMany': {\n // `embed` carries a single `value`; `embedMany` a `values` array — both map to the embeddings input.\n const input = type === 'embedMany' ? event.values : event.value;\n return startGenAiSpan(GEN_AI_EMBEDDINGS_OPERATION, modelId, {\n ...baseAttributes,\n ...(recordInputs && input !== undefined ? { [GEN_AI_EMBEDDINGS_INPUT]: safeStringify(input) } : {}),\n });\n }\n case 'rerank':\n return startGenAiSpan(GEN_AI_RERANK_OPERATION, modelId, baseAttributes);\n default:\n // Unknown event type: opt out rather than open a span we can't shape correctly.\n return undefined;\n }\n}\n\ntype Attributes = Record<string, string | number | boolean>;\n\n/** Start a `gen_ai.<operation>` span named `<operation> <suffix>` (or just `<operation>` when no suffix). */\nfunction startGenAiSpan(operation: string, suffix: string | undefined, attributes: Attributes): Span {\n return startInactiveSpan({\n name: suffix ? `${operation} ${suffix}` : operation,\n op: `gen_ai.${operation}`,\n attributes: { [GEN_AI_OPERATION_NAME]: operation, ...attributes },\n });\n}\n\nfunction buildInvokeAgentSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n isStream: boolean,\n): Span {\n const functionId = asString(event.functionId);\n const operationId = asString(event.operationId) ?? (isStream ? 'ai.streamText' : 'ai.generateText');\n if (callId) {\n operationIdByCallId.set(callId, { operationId, isStream });\n }\n return startGenAiSpan(GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n [GEN_AI_RESPONSE_STREAMING]: isStream,\n ...(functionId ? { [GEN_AI_FUNCTION_ID]: functionId } : {}),\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n });\n}\n\nfunction buildModelCallSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n modelId: string | undefined,\n): Span {\n const parent = callId ? operationIdByCallId.get(callId) : undefined;\n const operationId = parent\n ? `${parent.operationId}.${parent.isStream ? 'doStream' : 'doGenerate'}`\n : 'ai.generateText.doGenerate';\n return startGenAiSpan(GEN_AI_GENERATE_CONTENT_OPERATION, modelId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n ...(recordInputs && Array.isArray(event.tools)\n ? { [GEN_AI_REQUEST_AVAILABLE_TOOLS]: safeStringify(event.tools) }\n : {}),\n });\n}\n\nfunction buildToolSpan(event: Record<string, unknown>, recordInputs: boolean): Span {\n const toolCall = isRecord(event.toolCall) ? event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(event.toolCallId) ?? asString(toolCall.toolCallId);\n const toolInput = toolCall.input ?? toolCall.args;\n // The `executeTool` event has no description; backfill it from the operation's recorded tools.\n // Gated on `recordInputs` to match the OTel path (descriptions come from the recorded tools list).\n const description =\n recordInputs && toolName ? resolveToolDescription(asString(event.callId), toolName, event.tools) : undefined;\n return startGenAiSpan(GEN_AI_EXECUTE_TOOL_SPAN_OP, toolName, {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [GEN_AI_TOOL_TYPE]: 'function',\n ...(toolName ? { [GEN_AI_TOOL_NAME]: toolName } : {}),\n ...(toolCallId ? { [GEN_AI_TOOL_CALL_ID_ATTRIBUTE]: toolCallId } : {}),\n ...(description ? { [GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE]: description } : {}),\n ...(recordInputs && toolInput !== undefined ? { [GEN_AI_TOOL_INPUT]: safeStringify(toolInput) } : {}),\n });\n}\n\n/**\n * Best-effort enrichment from the resolved value the AI SDK attaches to the channel context.\n * Everything here is guarded: when a field is missing or the shape differs across `ai` versions,\n * we simply don't set the attribute rather than emit a malformed span.\n */\nexport function enrichSpanOnEnd(\n span: Span,\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): void {\n const { type, result } = data;\n if (!isRecord(result)) {\n return;\n }\n\n const { recordOutputs } = getRecordingOptions(data.event, channelOptions);\n\n if (type === 'executeTool') {\n if (recordOutputs) {\n span.setAttribute(GEN_AI_TOOL_OUTPUT, safeStringify(result.output ?? result));\n }\n // From V5 on, tool errors are not rejected (so the `error` channel verb never fires) — they\n // surface as `tool-error` content on the resolved result. Mirror the OTel path by marking the\n // span and capturing the error.\n const output = isRecord(result.output) ? result.output : undefined;\n if (output?.type === 'tool-error') {\n captureToolError(span, data, output.error);\n }\n return;\n }\n\n // `languageModelCall` results report usage as `{ total }` objects; top-level/step results report\n // flat numbers. `tokenCount` handles both.\n const usage = isRecord(result.usage) ? result.usage : undefined;\n if (usage) {\n const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.tokens);\n const outputTokens = tokenCount(usage.outputTokens);\n const totalTokens = tokenCount(usage.totalTokens) ?? sum(inputTokens, outputTokens);\n if (inputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_INPUT_TOKENS, inputTokens);\n }\n if (outputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens);\n }\n if (totalTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_TOTAL_TOKENS, totalTokens);\n }\n }\n\n // Match the OTel integration: finish reasons live on the model-call (`generate_content`) span, not\n // on the top-level `invoke_agent` span.\n const finishReason = getFinishReason(result);\n if (finishReason && type === 'languageModelCall') {\n span.setAttribute(GEN_AI_RESPONSE_FINISH_REASONS, safeStringify([finishReason]));\n }\n\n const response = isRecord(result.response) ? result.response : undefined;\n const responseId = asString(response?.id) ?? asString(result.responseId);\n if (responseId) {\n span.setAttribute(GEN_AI_RESPONSE_ID, responseId);\n }\n const responseModel = asString(response?.modelId) ?? asString(data.event.modelId);\n if (responseModel) {\n span.setAttribute(GEN_AI_RESPONSE_MODEL, responseModel);\n }\n\n // Provider-specific cache/reasoning/prediction token breakdowns and `gen_ai.conversation.id`.\n // The channel exposes `providerMetadata` as an object (the OTel path parses it from a string);\n // both share `getProviderMetadataAttributes` so the emitted shape is identical.\n const providerMetadata = (result as { providerMetadata?: unknown }).providerMetadata;\n const providerAttributes = getProviderMetadataAttributes(providerMetadata);\n // Don't overwrite a conversation id already set on span start (e.g. by `conversationIdIntegration`\n // from a user-set scope value); the provider-derived id is only a fallback. Matches the OTel path.\n if (\n GEN_AI_CONVERSATION_ID_ATTRIBUTE in providerAttributes &&\n spanToJSON(span).data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]\n ) {\n // oxlint-disable-next-line typescript/no-dynamic-delete\n delete providerAttributes[GEN_AI_CONVERSATION_ID_ATTRIBUTE];\n }\n span.setAttributes(providerAttributes);\n\n if (recordOutputs) {\n // `languageModelCall` exposes the response as a `content` parts array; top-level results expose\n // `text` + `toolCalls`. Both normalize into the OTel `gen_ai.output.messages` assistant message.\n const parts =\n type === 'languageModelCall' && Array.isArray(result.content)\n ? partsFromContent(result.content)\n : partsFromTextAndToolCalls(result.text, result.toolCalls);\n const outputMessages = buildOutputMessages(parts, finishReason);\n if (outputMessages) {\n span.setAttribute(GEN_AI_OUTPUT_MESSAGES, outputMessages);\n }\n }\n}\n\n/** Maps a Vercel AI finish reason to the OTel `gen_ai.output.messages` form (`tool-calls` → `tool_call`). */\nfunction normalizeFinishReason(finishReason: string | undefined): string {\n return finishReason === 'tool-calls' ? 'tool_call' : (finishReason ?? 'stop');\n}\n\n/** Reads the finish reason from a result — a string on top-level results, `{ unified }` on model calls. */\nfunction getFinishReason(result: Record<string, unknown>): string | undefined {\n const finishReason = result.finishReason;\n if (typeof finishReason === 'string') {\n return finishReason;\n }\n return isRecord(finishReason) ? asString(finishReason.unified) : undefined;\n}\n\n/** Reads a token count that may be a plain number or a `{ total }` object (model-call usage). */\nfunction tokenCount(value: unknown): number | undefined {\n return asNumber(value) ?? (isRecord(value) ? asNumber(value.total) : undefined);\n}\n\nfunction buildOutputMessages(\n parts: Array<Record<string, unknown>>,\n finishReason: string | undefined,\n): string | undefined {\n if (!parts.length) {\n return undefined;\n }\n return safeStringify([{ role: 'assistant', parts, finish_reason: normalizeFinishReason(finishReason) }]);\n}\n\nfunction toolCallPart(toolCall: Record<string, unknown>): Record<string, unknown> {\n const args = toolCall.input ?? toolCall.args;\n return {\n type: 'tool_call',\n id: asString(toolCall.toolCallId),\n name: asString(toolCall.toolName),\n arguments: typeof args === 'string' ? args : safeStringify(args ?? {}),\n };\n}\n\nfunction partsFromContent(content: unknown[]): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n for (const item of content) {\n if (!isRecord(item)) {\n continue;\n }\n if (item.type === 'text' && typeof item.text === 'string') {\n parts.push({ type: 'text', content: item.text });\n } else if (item.type === 'tool-call') {\n parts.push(toolCallPart(item));\n }\n }\n return parts;\n}\n\nfunction partsFromTextAndToolCalls(text: unknown, toolCalls: unknown): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n if (typeof text === 'string' && text.length) {\n parts.push({ type: 'text', content: text });\n }\n if (Array.isArray(toolCalls)) {\n for (const toolCall of toolCalls) {\n if (isRecord(toolCall)) {\n parts.push(toolCallPart(toolCall));\n }\n }\n }\n return parts;\n}\n\nfunction captureToolError(span: Span, data: VercelAiChannelMessage, error: unknown): void {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: error instanceof Error ? error.message : 'tool_error',\n });\n\n const toolCall = isRecord(data.event.toolCall) ? data.event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(data.event.toolCallId) ?? asString(toolCall.toolCallId);\n\n withScope(scope => {\n scope.setContext('trace', spanToTraceContext(span));\n if (toolName) {\n scope.setTag('vercel.ai.tool.name', toolName);\n }\n if (toolCallId) {\n scope.setTag('vercel.ai.tool.callId', toolCallId);\n }\n scope.setLevel('error');\n captureException(\n error instanceof Error ? error : new Error(typeof error === 'string' ? error : 'Tool execution failed'),\n {\n mechanism: { type: 'auto.vercelai.channel', handled: false },\n },\n );\n });\n}\n\nfunction getRecordingOptions(\n event: Record<string, unknown>,\n channelOptions: VercelAiChannelOptions,\n): {\n recordInputs: boolean;\n recordOutputs: boolean;\n enableTruncation: boolean;\n} {\n const genAI = getClient()?.getDataCollectionOptions().genAI;\n\n return {\n recordInputs: resolveRecording(channelOptions.recordInputs, event.recordInputs, genAI?.inputs),\n recordOutputs: resolveRecording(channelOptions.recordOutputs, event.recordOutputs, genAI?.outputs),\n enableTruncation: shouldEnableTruncation(channelOptions.enableTruncation),\n };\n}\n\n/**\n * Mirrors the OTel integration's `determineRecordingSettings` precedence: an integration-level option\n * wins, then the per-call `experimental_telemetry.recordInputs/recordOutputs` flag the AI SDK forwards\n * on the channel event, then the global `dataCollection.genAI` default.\n *\n * NOTE: the OTel integration also defaults recording to `true` for a call with\n * `experimental_telemetry: { isEnabled: true }`. The `ai:telemetry` channel does not expose `isEnabled`\n * (nor a resolved recording flag), so that per-call default cannot be reproduced here — v7 users who\n * want inputs/outputs recorded must enable `dataCollection.genAI` or set `recordInputs`/`recordOutputs`.\n */\nfunction resolveRecording(integrationOption: unknown, perCallOption: unknown, globalDefault: unknown): boolean {\n if (typeof integrationOption === 'boolean') {\n return integrationOption;\n }\n if (typeof perCallOption === 'boolean') {\n return perCallOption;\n }\n return globalDefault === true;\n}\n\nfunction buildInputMessageAttributes(\n event: Record<string, unknown>,\n enableTruncation: boolean,\n): Record<string, string | number> {\n const attributes: Record<string, string | number> = {};\n\n // `ai` >= 7 forbids system messages in `messages`/`prompt` and exposes the system prompt as a\n // separate `instructions` field. The OTel path lifts the system message out of the prompt into\n // `gen_ai.system_instructions` as `[{ type: 'text', content }]`; mirror that shape here.\n const instructions = asString(event.instructions);\n if (instructions) {\n attributes[GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE] = safeStringify([{ type: 'text', content: instructions }]);\n }\n\n // The AI SDK start events extend `StandardizedPrompt`; messages live on `messages`, otherwise the\n // simpler `prompt` field is used.\n const messages = event.messages ?? event.prompt;\n if (messages !== undefined) {\n attributes[GEN_AI_INPUT_MESSAGES] = enableTruncation ? getTruncatedJsonString(messages) : safeStringify(messages);\n // The original (pre-truncation) message count, so the product can show how many were dropped.\n attributes[GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE] = Array.isArray(messages) ? messages.length : 1;\n }\n\n return attributes;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && !isNaN(value) ? value : undefined;\n}\n\nfunction sum(a: number | undefined, b: number | undefined): number | undefined {\n return a === undefined && b === undefined ? undefined : (a ?? 0) + (b ?? 0);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '[unserializable]';\n }\n}\n"],"names":["tracingChannel","bindTracingChannelToSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","GEN_AI_SYSTEM","GEN_AI_REQUEST_MODEL","GEN_AI_EMBEDDINGS_INPUT","attributes","startInactiveSpan","GEN_AI_OPERATION_NAME","GEN_AI_INVOKE_AGENT_SPAN_OP","GEN_AI_RESPONSE_STREAMING","GEN_AI_FUNCTION_ID","GEN_AI_REQUEST_AVAILABLE_TOOLS","GEN_AI_EXECUTE_TOOL_SPAN_OP","GEN_AI_TOOL_TYPE","GEN_AI_TOOL_NAME","GEN_AI_TOOL_INPUT","GEN_AI_TOOL_OUTPUT","GEN_AI_USAGE_INPUT_TOKENS","GEN_AI_USAGE_OUTPUT_TOKENS","GEN_AI_USAGE_TOTAL_TOKENS","GEN_AI_RESPONSE_FINISH_REASONS","GEN_AI_RESPONSE_ID","GEN_AI_RESPONSE_MODEL","getProviderMetadataAttributes","GEN_AI_CONVERSATION_ID_ATTRIBUTE","spanToJSON","GEN_AI_OUTPUT_MESSAGES","SPAN_STATUS_ERROR","withScope","spanToTraceContext","captureException","getClient","shouldEnableTruncation","GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE","GEN_AI_INPUT_MESSAGES","getTruncatedJsonString","GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE"],"mappings":"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"} | ||
| {"version":3,"file":"vercel-ai-dc-subscriber.js","sources":["../../../src/vercel-ai/vercel-ai-dc-subscriber.ts"],"sourcesContent":["/* eslint-disable max-lines */\n// `@sentry/conventions` marks several gen_ai attributes (e.g. `GEN_AI_SYSTEM`, `GEN_AI_TOOL_*`,\n// `GEN_AI_REQUEST_AVAILABLE_TOOLS`) as deprecated in favour of newer semconv names. We intentionally\n// keep emitting the current names so these spans match the OTel-based (v6) integration and what the\n// Sentry product consumes today; migrating to the new names is a separate, coordinated change.\n/* eslint-disable typescript-eslint/no-deprecated */\nimport {\n GEN_AI_EMBEDDINGS_INPUT,\n GEN_AI_FUNCTION_ID,\n GEN_AI_INPUT_MESSAGES,\n GEN_AI_OPERATION_NAME,\n GEN_AI_OUTPUT_MESSAGES,\n GEN_AI_REQUEST_AVAILABLE_TOOLS,\n GEN_AI_REQUEST_MODEL,\n GEN_AI_RESPONSE_FINISH_REASONS,\n GEN_AI_RESPONSE_ID,\n GEN_AI_RESPONSE_MODEL,\n GEN_AI_RESPONSE_STREAMING,\n GEN_AI_SYSTEM,\n GEN_AI_TOOL_INPUT,\n GEN_AI_TOOL_NAME,\n GEN_AI_TOOL_OUTPUT,\n GEN_AI_TOOL_TYPE,\n GEN_AI_USAGE_INPUT_TOKENS,\n GEN_AI_USAGE_OUTPUT_TOKENS,\n GEN_AI_USAGE_TOTAL_TOKENS,\n} from '@sentry/conventions/attributes';\nimport { GEN_AI_EXECUTE_TOOL_SPAN_OP, GEN_AI_INVOKE_AGENT_SPAN_OP } from '@sentry/conventions/op';\nimport type { Span } from '@sentry/core';\nimport {\n captureException,\n GEN_AI_CONVERSATION_ID_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE,\n getClient,\n getProviderMetadataAttributes,\n getTruncatedJsonString,\n isObjectLike,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n SPAN_STATUS_ERROR,\n spanToJSON,\n spanToTraceContext,\n startInactiveSpan,\n withScope,\n} from '@sentry/core';\nimport type { TracingChannel } from 'node:diagnostics_channel';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\nimport {\n asNumber,\n asString,\n isReadableStream,\n safeStringify,\n type StreamedModelCallResult,\n sum,\n tapModelCallStream,\n} from './util';\n\n/**\n * The single tracing channel the `ai` package (>= 7) publishes all telemetry lifecycle events to\n * via `node:diagnostics_channel`. Events are discriminated by their `type` field.\n * @see https://github.com/vercel/ai/pull/15660\n */\nconst AI_SDK_TELEMETRY_TRACING_CHANNEL = 'ai:telemetry';\n\nconst ORIGIN = 'auto.vercelai.channel';\n\n// `@sentry/conventions` does not expose these yet, so we keep the literals here.\nconst GEN_AI_TOOL_CALL_ID_ATTRIBUTE = 'gen_ai.tool.call.id';\nconst GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE = 'gen_ai.tool.description';\nconst GEN_AI_EMBEDDINGS_OPERATION = 'embeddings';\nconst GEN_AI_RERANK_OPERATION = 'rerank';\n// The model-call op matches the Vercel AI OTel integration (`gen_ai.generate_content`) rather than\n// the generic `gen_ai.chat`, so v6 (OTel) and v7 (channel) produce the same spans.\nconst GEN_AI_GENERATE_CONTENT_OPERATION = 'generate_content';\n\n// Subset of the `vercel.ai.*` passthrough attributes the OTel integration emits that we reproduce.\nconst VERCEL_AI_OPERATION_ID_ATTRIBUTE = 'vercel.ai.operationId';\nconst VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE = 'vercel.ai.model.provider';\nconst VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE = 'vercel.ai.settings.maxRetries';\n\n// Tracks the top-level operationId (and whether it streams) per `callId` so a model-call span can\n// name its `doGenerate`/`doStream` operation the same way the OTel integration does. `isStream` is\n// the authoritative event-type signal rather than a substring check on the (possibly custom)\n// operationId. Cleared when the top-level span ends.\nconst operationIdByCallId = new Map<string, { operationId: string; isStream: boolean }>();\n\n// Per-operation map of tool name → description, harvested from a model-call /\n// top-level event's `tools` (keyed by the shared `callId`). The AI SDK's\n// `executeTool` event doesn't carry the tool's description, so we backfill it\n// onto the tool span here — without relying on the OTel `vercelAiEventProcessor`\n// (which isn't registered in channel/orchestrion mode). Cleared with the\n// operation. Only populated when inputs are recorded, matching the OTel path\n// (which sources descriptions from the recorded `available_tools`).\nconst toolDescriptionsByCallId = new Map<string, Map<string, string>>();\n\n// A streamed `streamText` operation's own channel result is always `undefined` (the SDK exposes the\n// stream only on the model call), so its `invoke_agent` span can't be enriched from the channel. We\n// key the span by `callId` and enrich it as each streamed model call drains — the model calls flush\n// before the operation's own span ends. The running token sum lives on the span's own attributes.\nconst invokeAgentSpanByCallId = new Map<string, Span>();\n\n// Only top-level operations own the `callId` → operationId mapping; `step`/`languageModelCall`/\n// `executeTool` share the parent's `callId`, so they must not clear it.\nconst ROOT_OPERATION_TYPES = new Set<ChannelEventType>([\n 'generateText',\n 'streamText',\n 'generateObject',\n 'embed',\n 'embedMany',\n 'rerank',\n]);\n\n/** Drop the per-operation `callId` maps once the owning top-level operation settles (success or error). */\nexport function clearOperationId(data: VercelAiChannelMessage): void {\n if (!ROOT_OPERATION_TYPES.has(data.type)) {\n return;\n }\n const callId = asString(data.event.callId);\n if (callId) {\n clearOperationCallId(callId);\n }\n}\n\n/**\n * Drop the per-operation `callId` maps for a single id. The v6 orchestrion adapter uses this to clear a\n * `streamText` operation only after its lazily-run model call settles — the operation's own span ends\n * synchronously (when `streamText` returns) but the model call runs later as the stream is consumed, and\n * it still needs the operation's `operationId`/`isStream` entry to name itself `ai.streamText.doStream`.\n */\nexport function clearOperationCallId(callId: string): void {\n operationIdByCallId.delete(callId);\n toolDescriptionsByCallId.delete(callId);\n invokeAgentSpanByCallId.delete(callId);\n}\n\n/** Record tool name → description from an event's `tools`, so tool spans can backfill the description. */\nfunction recordToolDescriptions(callId: string | undefined, tools: unknown): void {\n if (!callId || !Array.isArray(tools)) {\n return;\n }\n let descriptions = toolDescriptionsByCallId.get(callId);\n for (const tool of tools) {\n if (isObjectLike(tool) && typeof tool.name === 'string' && typeof tool.description === 'string') {\n descriptions = descriptions ?? new Map();\n if (!descriptions.has(tool.name)) {\n descriptions.set(tool.name, tool.description);\n }\n }\n }\n if (descriptions) {\n toolDescriptionsByCallId.set(callId, descriptions);\n }\n}\n\n/**\n * Resolve a tool's description, preferring the per-operation map (populated from the model-call /\n * top-level event's `tools`, v7) and falling back to a `tools` collection on the event itself —\n * which may be an array of `{ name, description }` or a record keyed by tool name (v6).\n */\nfunction resolveToolDescription(callId: string | undefined, toolName: string, tools: unknown): string | undefined {\n const fromMap = callId ? toolDescriptionsByCallId.get(callId)?.get(toolName) : undefined;\n if (fromMap) {\n return fromMap;\n }\n if (Array.isArray(tools)) {\n const match = tools.find(tool => isObjectLike(tool) && tool.name === toolName);\n return isObjectLike(match) ? asString(match.description) : undefined;\n }\n if (isObjectLike(tools)) {\n const tool = tools[toolName];\n return isObjectLike(tool) ? asString(tool.description) : undefined;\n }\n return undefined;\n}\n\n/** The lifecycle event types the `ai:telemetry` channel can carry. */\nexport type ChannelEventType =\n | 'generateText'\n | 'streamText'\n | 'generateObject'\n | 'step'\n | 'languageModelCall'\n | 'executeTool'\n | 'embed'\n | 'embedMany'\n | 'rerank';\n\n/**\n * The context object the AI SDK passes through one tracing-channel call. It is the same object\n * identity across `start`/`end`/`asyncEnd`/`error`, and Node's `tracingChannel` attaches\n * `result`/`error` to it as the traced promise settles.\n */\nexport interface VercelAiChannelMessage {\n type: ChannelEventType;\n event: Record<string, unknown>;\n result?: unknown;\n error?: unknown;\n}\n\n/**\n * Platform-provided factory that returns a tracing channel for the given channel name. The factory\n * is responsible for, when `start` fires, calling `transformStart(data)` and storing the returned\n * span on `data._sentrySpan` so the subscriber's `asyncEnd`/`error` handlers can read it.\n *\n * Node passes `@sentry/opentelemetry/tracing-channel`, which uses `bindStore` to additionally make\n * the span the active OTel context for the duration of the traced operation. That is what makes\n * nested AI SDK operations (model calls, tool calls) become children of the enclosing span without\n * any manual parent bookkeeping here.\n */\nexport type VercelAiTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/** Integration-level recording options, pinned at subscribe time so we never look the integration up per event. */\nexport interface VercelAiChannelOptions {\n recordInputs?: boolean;\n recordOutputs?: boolean;\n enableTruncation?: boolean;\n}\n\n/**\n * Subscribe Sentry span handlers to the `ai` SDK's native telemetry tracing channel (`ai:telemetry`,\n * available in `ai` >= 7) and emit fully-formed `gen_ai.*` spans directly — no OpenTelemetry span\n * post-processing involved.\n *\n * The integration passes its options in directly (rather than us looking the integration up on every\n * event); the global `dataCollection.genAI` default is still read from the client per event.\n *\n * Safe to always call: on `ai` versions that don't publish to the channel (e.g. < 7) nothing is\n * ever emitted and this is inert, so there is no double-instrumentation against the OTel-based\n * patcher. The integration's `setupOnce` guarantees this runs a single time.\n */\nexport function subscribeVercelAiTracingChannel(\n tracingChannel: VercelAiTracingChannelFactory,\n options: VercelAiChannelOptions = {},\n): void {\n bindTracingChannelToSpan(\n tracingChannel<VercelAiChannelMessage>(AI_SDK_TELEMETRY_TRACING_CHANNEL),\n data => createSpanFromMessage(data, options),\n {\n // The helper ends the span; we enrich it from the settled result first (tokens, output messages,\n // finish reasons, response model/id, provider metadata) and drop the per-operation `callId` maps.\n beforeSpanEnd: (span, data) => {\n enrichSpanOnEnd(span, data, options);\n clearOperationId(data);\n },\n // A streamed model call resolves before its stream is drained, so we tap the stream, keep the\n // span open, and end it (via `end`) once the final usage/finish/output chunks arrive.\n deferSpanEnd: ({ data, end }) => deferStreamedModelCallEnd(data, options, end),\n },\n );\n}\n\n/**\n * When a `languageModelCall` resolves to a live `ReadableStream`, defer ending its span: swap in a\n * passthrough that forwards chunks to the SDK untouched while aggregating usage/finish/output, then\n * enrich and end the span once the stream settles. Returns `false` for anything that isn't a streamed\n * model call so the helper ends the span as usual.\n */\nfunction deferStreamedModelCallEnd(\n data: VercelAiChannelMessage,\n options: VercelAiChannelOptions,\n end: (error?: unknown) => void,\n): boolean {\n if (data.type !== 'languageModelCall' || !isObjectLike(data.result)) {\n return false;\n }\n const result = data.result;\n const stream = result.stream;\n if (!isReadableStream(stream)) {\n return false;\n }\n\n const callId = asString(data.event.callId);\n const { recordOutputs } = getRecordingOptions(data.event, options);\n result.stream = tapModelCallStream(\n stream,\n final => {\n // Reshape the aggregate into the result `enrichSpanOnEnd` expects, then let `end` run it (it calls\n // `beforeSpanEnd`). Enriching the model-call span and the parent from the same aggregate keeps\n // streamed and non-streamed spans identical.\n data.result = { ...result, ...streamedResultToChannelResult(final) };\n end();\n enrichInvokeAgentFromStream(callId, final, recordOutputs);\n },\n error => end(error),\n );\n\n return true;\n}\n\n/** Map the tapped stream aggregate onto the `languageModelCall` result shape `enrichSpanOnEnd` reads. */\nexport function streamedResultToChannelResult(final: StreamedModelCallResult): Record<string, unknown> {\n const content: Array<Record<string, unknown>> = [];\n if (final.text) {\n content.push({ type: 'text', text: final.text });\n }\n for (const toolCall of final.toolCalls) {\n content.push({ type: 'tool-call', ...toolCall });\n }\n\n return {\n content,\n ...(final.usage !== undefined ? { usage: final.usage } : {}),\n ...(final.finishReason !== undefined ? { finishReason: final.finishReason } : {}),\n ...(final.providerMetadata !== undefined ? { providerMetadata: final.providerMetadata } : {}),\n ...(final.responseId || final.responseModel\n ? {\n response: {\n ...(final.responseId ? { id: final.responseId } : {}),\n ...(final.responseModel ? { modelId: final.responseModel } : {}),\n },\n }\n : {}),\n };\n}\n\n/**\n * Propagate a streamed model call's usage (summed across the operation's model calls) and output onto\n * the enclosing `invoke_agent` span, which has no channel result of its own. Runs before that span ends\n * because the operation's completion promise settles only after every model-call stream has drained.\n */\nfunction enrichInvokeAgentFromStream(\n callId: string | undefined,\n final: StreamedModelCallResult,\n recordOutputs: boolean,\n): void {\n const span = callId ? invokeAgentSpanByCallId.get(callId) : undefined;\n if (!span) {\n return;\n }\n\n const usage = isObjectLike(final.usage) ? final.usage : undefined;\n if (usage) {\n const input = tokenCount(usage.inputTokens) ?? tokenCount(usage.promptTokens) ?? tokenCount(usage.tokens);\n const output = tokenCount(usage.outputTokens) ?? tokenCount(usage.completionTokens);\n addTokensToSpan(span, GEN_AI_USAGE_INPUT_TOKENS, input);\n addTokensToSpan(span, GEN_AI_USAGE_OUTPUT_TOKENS, output);\n addTokensToSpan(span, GEN_AI_USAGE_TOTAL_TOKENS, tokenCount(usage.totalTokens) ?? sum(input, output));\n }\n\n if (recordOutputs) {\n const parts = partsFromTextAndToolCalls(final.text, final.toolCalls);\n const outputMessages = buildOutputMessages(parts, getFinishReason({ finishReason: final.finishReason }));\n if (outputMessages) {\n span.setAttribute(GEN_AI_OUTPUT_MESSAGES, outputMessages);\n }\n }\n}\n\n/** Add `value` into a span's numeric token attribute, using the span itself as the running sum. */\nfunction addTokensToSpan(span: Span, attribute: string, value: number | undefined): void {\n if (value === undefined) {\n return;\n }\n const current = spanToJSON(span).data[attribute];\n span.setAttribute(attribute, (typeof current === 'number' ? current : 0) + value);\n}\n\n/**\n * Transform a channel `start` payload into the span that should be active for the operation. For\n * `step` we deliberately don't open a span (model calls and tool calls are siblings under the\n * invoke_agent span, matching the OTel-based output), so we reuse the active span and mark the\n * payload to skip ending it.\n */\nexport function createSpanFromMessage(\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): Span | undefined {\n const { type, event } = data;\n\n if (type === 'step' || !event || typeof event !== 'object') {\n // Opt out: returning `undefined` leaves the enclosing `invoke_agent` span as the active context\n // (model-call and tool-call events nest under it) without opening — or ending — a span of its own.\n return undefined;\n }\n\n const { recordInputs, enableTruncation } = getRecordingOptions(event, channelOptions);\n const provider = asString(event.provider);\n const modelId = asString(event.modelId);\n const callId = asString(event.callId);\n const maxRetries = asNumber(event.maxRetries);\n\n // Harvest tool descriptions from the operation/model-call `tools` so tool spans can backfill them.\n // Gated on `recordInputs` to match the OTel path, which only records `available_tools` then.\n if (recordInputs) {\n recordToolDescriptions(callId, event.tools);\n }\n\n const baseAttributes: Record<string, string | number | boolean> = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n ...(provider ? { [GEN_AI_SYSTEM]: provider, [VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE]: provider } : {}),\n ...(modelId ? { [GEN_AI_REQUEST_MODEL]: modelId } : {}),\n ...(maxRetries !== undefined ? { [VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE]: maxRetries } : {}),\n };\n\n switch (type) {\n case 'generateText':\n case 'streamText':\n case 'generateObject':\n // `generateObject` builds the same `invoke_agent` span as `generateText` (non-streaming); its\n // distinct `ai.generateObject` operationId rides on `event.operationId`. The JSON-schema attribute\n // the OTel path derives from the SDK's Zod schema is not reconstructed on the channel path.\n return buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, type === 'streamText');\n case 'languageModelCall':\n return buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId);\n case 'executeTool':\n return buildToolSpan(event, recordInputs);\n case 'embed':\n case 'embedMany': {\n // `embed` carries a single `value`; `embedMany` a `values` array — both map to the embeddings input.\n const input = type === 'embedMany' ? event.values : event.value;\n return startGenAiSpan(GEN_AI_EMBEDDINGS_OPERATION, modelId, {\n ...baseAttributes,\n ...(recordInputs && input !== undefined ? { [GEN_AI_EMBEDDINGS_INPUT]: safeStringify(input) } : {}),\n });\n }\n case 'rerank':\n return startGenAiSpan(GEN_AI_RERANK_OPERATION, modelId, baseAttributes);\n default:\n // Unknown event type: opt out rather than open a span we can't shape correctly.\n return undefined;\n }\n}\n\ntype Attributes = Record<string, string | number | boolean>;\n\n/** Start a `gen_ai.<operation>` span named `<operation> <suffix>` (or just `<operation>` when no suffix). */\nfunction startGenAiSpan(operation: string, suffix: string | undefined, attributes: Attributes): Span {\n return startInactiveSpan({\n name: suffix ? `${operation} ${suffix}` : operation,\n op: `gen_ai.${operation}`,\n attributes: { [GEN_AI_OPERATION_NAME]: operation, ...attributes },\n });\n}\n\nfunction buildInvokeAgentSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n isStream: boolean,\n): Span {\n const functionId = asString(event.functionId);\n const operationId = asString(event.operationId) ?? (isStream ? 'ai.streamText' : 'ai.generateText');\n if (callId) {\n operationIdByCallId.set(callId, { operationId, isStream });\n }\n const span = startGenAiSpan(GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n [GEN_AI_RESPONSE_STREAMING]: isStream,\n ...(functionId ? { [GEN_AI_FUNCTION_ID]: functionId } : {}),\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n });\n if (isStream && callId) {\n invokeAgentSpanByCallId.set(callId, span);\n }\n\n return span;\n}\n\nfunction buildModelCallSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n modelId: string | undefined,\n): Span {\n const parent = callId ? operationIdByCallId.get(callId) : undefined;\n const operationId = parent\n ? `${parent.operationId}.${parent.isStream ? 'doStream' : 'doGenerate'}`\n : 'ai.generateText.doGenerate';\n return startGenAiSpan(GEN_AI_GENERATE_CONTENT_OPERATION, modelId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n ...(recordInputs && Array.isArray(event.tools)\n ? { [GEN_AI_REQUEST_AVAILABLE_TOOLS]: safeStringify(event.tools) }\n : {}),\n });\n}\n\nfunction buildToolSpan(event: Record<string, unknown>, recordInputs: boolean): Span {\n const toolCall = isObjectLike(event.toolCall) ? event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(event.toolCallId) ?? asString(toolCall.toolCallId);\n const toolInput = toolCall.input ?? toolCall.args;\n // The `executeTool` event has no description; backfill it from the operation's recorded tools.\n // Gated on `recordInputs` to match the OTel path (descriptions come from the recorded tools list).\n const description =\n recordInputs && toolName ? resolveToolDescription(asString(event.callId), toolName, event.tools) : undefined;\n return startGenAiSpan(GEN_AI_EXECUTE_TOOL_SPAN_OP, toolName, {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [GEN_AI_TOOL_TYPE]: 'function',\n ...(toolName ? { [GEN_AI_TOOL_NAME]: toolName } : {}),\n ...(toolCallId ? { [GEN_AI_TOOL_CALL_ID_ATTRIBUTE]: toolCallId } : {}),\n ...(description ? { [GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE]: description } : {}),\n ...(recordInputs && toolInput !== undefined ? { [GEN_AI_TOOL_INPUT]: safeStringify(toolInput) } : {}),\n });\n}\n\n/**\n * Best-effort enrichment from the resolved value the AI SDK attaches to the channel context.\n * Everything here is guarded: when a field is missing or the shape differs across `ai` versions,\n * we simply don't set the attribute rather than emit a malformed span.\n */\nexport function enrichSpanOnEnd(\n span: Span,\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): void {\n const { type, result } = data;\n if (!isObjectLike(result)) {\n return;\n }\n\n const { recordOutputs } = getRecordingOptions(data.event, channelOptions);\n\n if (type === 'executeTool') {\n if (recordOutputs) {\n span.setAttribute(GEN_AI_TOOL_OUTPUT, safeStringify(result.output ?? result));\n }\n // From V5 on, tool errors are not rejected (so the `error` channel verb never fires) — they\n // surface as `tool-error` content on the resolved result. Mirror the OTel path by marking the\n // span and capturing the error.\n const output = isObjectLike(result.output) ? result.output : undefined;\n if (output?.type === 'tool-error') {\n captureToolError(span, data, output.error);\n }\n return;\n }\n\n // `languageModelCall` results report usage as `{ total }` objects; top-level/step results report\n // flat numbers. `tokenCount` handles both. v4 names tokens `promptTokens`/`completionTokens`\n // (v5+ uses `inputTokens`/`outputTokens`); the fallbacks are `undefined`-inert on v5+.\n const usage = isObjectLike(result.usage) ? result.usage : undefined;\n if (usage) {\n const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.promptTokens) ?? tokenCount(usage.tokens);\n const outputTokens = tokenCount(usage.outputTokens) ?? tokenCount(usage.completionTokens);\n const totalTokens = tokenCount(usage.totalTokens) ?? sum(inputTokens, outputTokens);\n if (inputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_INPUT_TOKENS, inputTokens);\n }\n if (outputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens);\n }\n if (totalTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_TOTAL_TOKENS, totalTokens);\n }\n }\n\n // Match the OTel integration: finish reasons live on the model-call (`generate_content`) span, not\n // on the top-level `invoke_agent` span.\n const finishReason = getFinishReason(result);\n if (finishReason && type === 'languageModelCall') {\n span.setAttribute(GEN_AI_RESPONSE_FINISH_REASONS, safeStringify([finishReason]));\n }\n\n const response = isObjectLike(result.response) ? result.response : undefined;\n const responseId = asString(response?.id) ?? asString(result.responseId);\n if (responseId) {\n span.setAttribute(GEN_AI_RESPONSE_ID, responseId);\n }\n const responseModel = asString(response?.modelId) ?? asString(data.event.modelId);\n if (responseModel) {\n span.setAttribute(GEN_AI_RESPONSE_MODEL, responseModel);\n }\n\n // Provider-specific cache/reasoning/prediction token breakdowns and `gen_ai.conversation.id`.\n // The channel exposes `providerMetadata` as an object (the OTel path parses it from a string);\n // both share `getProviderMetadataAttributes` so the emitted shape is identical.\n const providerMetadata = (result as { providerMetadata?: unknown }).providerMetadata;\n const providerAttributes = getProviderMetadataAttributes(providerMetadata);\n // Don't overwrite a conversation id already set on span start (e.g. by `conversationIdIntegration`\n // from a user-set scope value); the provider-derived id is only a fallback. Matches the OTel path.\n if (\n GEN_AI_CONVERSATION_ID_ATTRIBUTE in providerAttributes &&\n spanToJSON(span).data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]\n ) {\n // oxlint-disable-next-line typescript/no-dynamic-delete\n delete providerAttributes[GEN_AI_CONVERSATION_ID_ATTRIBUTE];\n }\n span.setAttributes(providerAttributes);\n\n if (recordOutputs) {\n // `languageModelCall` exposes the response as a `content` parts array; top-level results expose\n // `text` + `toolCalls`. Both normalize into the OTel `gen_ai.output.messages` assistant message.\n const parts =\n type === 'languageModelCall' && Array.isArray(result.content)\n ? partsFromContent(result.content)\n : partsFromTextAndToolCalls(result.text, result.toolCalls);\n const outputMessages = buildOutputMessages(parts, finishReason);\n if (outputMessages) {\n span.setAttribute(GEN_AI_OUTPUT_MESSAGES, outputMessages);\n }\n }\n}\n\n/** Maps a Vercel AI finish reason to the OTel `gen_ai.output.messages` form (`tool-calls` → `tool_call`). */\nfunction normalizeFinishReason(finishReason: string | undefined): string {\n return finishReason === 'tool-calls' ? 'tool_call' : (finishReason ?? 'stop');\n}\n\n/** Reads the finish reason from a result — a string on top-level results, `{ unified }` on model calls. */\nfunction getFinishReason(result: Record<string, unknown>): string | undefined {\n const finishReason = result.finishReason;\n if (typeof finishReason === 'string') {\n return finishReason;\n }\n return isObjectLike(finishReason) ? asString(finishReason.unified) : undefined;\n}\n\n/** Reads a token count that may be a plain number or a `{ total }` object (model-call usage). */\nfunction tokenCount(value: unknown): number | undefined {\n return asNumber(value) ?? (isObjectLike(value) ? asNumber(value.total) : undefined);\n}\n\nfunction buildOutputMessages(\n parts: Array<Record<string, unknown>>,\n finishReason: string | undefined,\n): string | undefined {\n if (!parts.length) {\n return undefined;\n }\n return safeStringify([{ role: 'assistant', parts, finish_reason: normalizeFinishReason(finishReason) }]);\n}\n\nfunction toolCallPart(toolCall: Record<string, unknown>): Record<string, unknown> {\n const args = toolCall.input ?? toolCall.args;\n return {\n type: 'tool_call',\n id: asString(toolCall.toolCallId),\n name: asString(toolCall.toolName),\n arguments: typeof args === 'string' ? args : safeStringify(args ?? {}),\n };\n}\n\nfunction partsFromContent(content: unknown[]): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n for (const item of content) {\n if (!isObjectLike(item)) {\n continue;\n }\n if (item.type === 'text' && typeof item.text === 'string') {\n parts.push({ type: 'text', content: item.text });\n } else if (item.type === 'tool-call') {\n parts.push(toolCallPart(item));\n }\n }\n return parts;\n}\n\nfunction partsFromTextAndToolCalls(text: unknown, toolCalls: unknown): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n if (typeof text === 'string' && text.length) {\n parts.push({ type: 'text', content: text });\n }\n if (Array.isArray(toolCalls)) {\n for (const toolCall of toolCalls) {\n if (isObjectLike(toolCall)) {\n parts.push(toolCallPart(toolCall));\n }\n }\n }\n return parts;\n}\n\nexport function captureToolError(span: Span, data: VercelAiChannelMessage, error: unknown): void {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: error instanceof Error ? error.message : 'tool_error',\n });\n\n const toolCall = isObjectLike(data.event.toolCall) ? data.event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(data.event.toolCallId) ?? asString(toolCall.toolCallId);\n\n withScope(scope => {\n scope.setContext('trace', spanToTraceContext(span));\n if (toolName) {\n scope.setTag('vercel.ai.tool.name', toolName);\n }\n if (toolCallId) {\n scope.setTag('vercel.ai.tool.callId', toolCallId);\n }\n scope.setLevel('error');\n captureException(\n error instanceof Error ? error : new Error(typeof error === 'string' ? error : 'Tool execution failed'),\n {\n mechanism: { type: 'auto.vercelai.channel', handled: false },\n },\n );\n });\n}\n\nfunction getRecordingOptions(\n event: Record<string, unknown>,\n channelOptions: VercelAiChannelOptions,\n): {\n recordInputs: boolean;\n recordOutputs: boolean;\n enableTruncation: boolean;\n} {\n const genAI = getClient()?.getDataCollectionOptions().genAI;\n\n return {\n recordInputs: resolveRecording(channelOptions.recordInputs, event.recordInputs, genAI?.inputs),\n recordOutputs: resolveRecording(channelOptions.recordOutputs, event.recordOutputs, genAI?.outputs),\n enableTruncation: shouldEnableTruncation(channelOptions.enableTruncation),\n };\n}\n\n/**\n * Mirrors the OTel integration's `determineRecordingSettings` precedence: an integration-level option\n * wins, then the per-call `experimental_telemetry.recordInputs/recordOutputs` flag the AI SDK forwards\n * on the channel event, then the global `dataCollection.genAI` default.\n *\n * NOTE: the OTel integration also defaults recording to `true` for a call with\n * `experimental_telemetry: { isEnabled: true }`. The `ai:telemetry` channel does not expose `isEnabled`\n * (nor a resolved recording flag), so that per-call default cannot be reproduced here — v7 users who\n * want inputs/outputs recorded must enable `dataCollection.genAI` or set `recordInputs`/`recordOutputs`.\n */\nfunction resolveRecording(integrationOption: unknown, perCallOption: unknown, globalDefault: unknown): boolean {\n if (typeof integrationOption === 'boolean') {\n return integrationOption;\n }\n if (typeof perCallOption === 'boolean') {\n return perCallOption;\n }\n return globalDefault === true;\n}\n\nfunction buildInputMessageAttributes(\n event: Record<string, unknown>,\n enableTruncation: boolean,\n): Record<string, string | number> {\n const attributes: Record<string, string | number> = {};\n\n // `ai` >= 7 forbids system messages in `messages`/`prompt` and exposes the system prompt as a\n // separate `instructions` field. The OTel path lifts the system message out of the prompt into\n // `gen_ai.system_instructions` as `[{ type: 'text', content }]`; mirror that shape here.\n const instructions = asString(event.instructions);\n if (instructions) {\n attributes[GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE] = safeStringify([{ type: 'text', content: instructions }]);\n }\n\n // The AI SDK start events extend `StandardizedPrompt`; messages live on `messages`, otherwise the\n // simpler `prompt` field is used.\n const messages = event.messages ?? event.prompt;\n if (messages !== undefined) {\n attributes[GEN_AI_INPUT_MESSAGES] = enableTruncation ? getTruncatedJsonString(messages) : safeStringify(messages);\n // The original (pre-truncation) message count, so the product can show how many were dropped.\n attributes[GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE] = Array.isArray(messages) ? messages.length : 1;\n }\n\n return attributes;\n}\n"],"names":["asString","isObjectLike","tracingChannel","bindTracingChannelToSpan","isReadableStream","tapModelCallStream","GEN_AI_USAGE_INPUT_TOKENS","GEN_AI_USAGE_OUTPUT_TOKENS","GEN_AI_USAGE_TOTAL_TOKENS","sum","GEN_AI_OUTPUT_MESSAGES","spanToJSON","asNumber","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","GEN_AI_SYSTEM","GEN_AI_REQUEST_MODEL","GEN_AI_EMBEDDINGS_INPUT","safeStringify","attributes","startInactiveSpan","GEN_AI_OPERATION_NAME","GEN_AI_INVOKE_AGENT_SPAN_OP","GEN_AI_RESPONSE_STREAMING","GEN_AI_FUNCTION_ID","GEN_AI_REQUEST_AVAILABLE_TOOLS","GEN_AI_EXECUTE_TOOL_SPAN_OP","GEN_AI_TOOL_TYPE","GEN_AI_TOOL_NAME","GEN_AI_TOOL_INPUT","GEN_AI_TOOL_OUTPUT","GEN_AI_RESPONSE_FINISH_REASONS","GEN_AI_RESPONSE_ID","GEN_AI_RESPONSE_MODEL","getProviderMetadataAttributes","GEN_AI_CONVERSATION_ID_ATTRIBUTE","SPAN_STATUS_ERROR","withScope","spanToTraceContext","captureException","getClient","shouldEnableTruncation","GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE","GEN_AI_INPUT_MESSAGES","getTruncatedJsonString","GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE"],"mappings":";;;;;;;;AA+DA,MAAM,gCAAA,GAAmC,cAAA;AAEzC,MAAM,MAAA,GAAS,uBAAA;AAGf,MAAM,6BAAA,GAAgC,qBAAA;AACtC,MAAM,iCAAA,GAAoC,yBAAA;AAC1C,MAAM,2BAAA,GAA8B,YAAA;AACpC,MAAM,uBAAA,GAA0B,QAAA;AAGhC,MAAM,iCAAA,GAAoC,kBAAA;AAG1C,MAAM,gCAAA,GAAmC,uBAAA;AACzC,MAAM,kCAAA,GAAqC,0BAAA;AAC3C,MAAM,wCAAA,GAA2C,+BAAA;AAMjD,MAAM,mBAAA,uBAA0B,GAAA,EAAwD;AASxF,MAAM,wBAAA,uBAA+B,GAAA,EAAiC;AAMtE,MAAM,uBAAA,uBAA8B,GAAA,EAAkB;AAItD,MAAM,oBAAA,uBAA2B,GAAA,CAAsB;AAAA,EACrD,cAAA;AAAA,EACA,YAAA;AAAA,EACA,gBAAA;AAAA,EACA,OAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAAC,CAAA;AAGM,SAAS,iBAAiB,IAAA,EAAoC;AACnE,EAAA,IAAI,CAAC,oBAAA,CAAqB,GAAA,CAAI,IAAA,CAAK,IAAI,CAAA,EAAG;AACxC,IAAA;AAAA,EACF;AACA,EAAA,MAAM,MAAA,GAASA,aAAA,CAAS,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AACzC,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,oBAAA,CAAqB,MAAM,CAAA;AAAA,EAC7B;AACF;AAQO,SAAS,qBAAqB,MAAA,EAAsB;AACzD,EAAA,mBAAA,CAAoB,OAAO,MAAM,CAAA;AACjC,EAAA,wBAAA,CAAyB,OAAO,MAAM,CAAA;AACtC,EAAA,uBAAA,CAAwB,OAAO,MAAM,CAAA;AACvC;AAGA,SAAS,sBAAA,CAAuB,QAA4B,KAAA,EAAsB;AAChF,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACpC,IAAA;AAAA,EACF;AACA,EAAA,IAAI,YAAA,GAAe,wBAAA,CAAyB,GAAA,CAAI,MAAM,CAAA;AACtD,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAIC,iBAAA,CAAa,IAAI,CAAA,IAAK,OAAO,IAAA,CAAK,SAAS,QAAA,IAAY,OAAO,IAAA,CAAK,WAAA,KAAgB,QAAA,EAAU;AAC/F,MAAA,YAAA,GAAe,YAAA,wBAAoB,GAAA,EAAI;AACvC,MAAA,IAAI,CAAC,YAAA,CAAa,GAAA,CAAI,IAAA,CAAK,IAAI,CAAA,EAAG;AAChC,QAAA,YAAA,CAAa,GAAA,CAAI,IAAA,CAAK,IAAA,EAAM,IAAA,CAAK,WAAW,CAAA;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AACA,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,wBAAA,CAAyB,GAAA,CAAI,QAAQ,YAAY,CAAA;AAAA,EACnD;AACF;AAOA,SAAS,sBAAA,CAAuB,MAAA,EAA4B,QAAA,EAAkB,KAAA,EAAoC;AAChH,EAAA,MAAM,OAAA,GAAU,SAAS,wBAAA,CAAyB,GAAA,CAAI,MAAM,CAAA,EAAG,GAAA,CAAI,QAAQ,CAAA,GAAI,MAAA;AAC/E,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,OAAO,OAAA;AAAA,EACT;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,CAAA,IAAA,KAAQA,kBAAa,IAAI,CAAA,IAAK,IAAA,CAAK,IAAA,KAAS,QAAQ,CAAA;AAC7E,IAAA,OAAOA,kBAAa,KAAK,CAAA,GAAID,aAAA,CAAS,KAAA,CAAM,WAAW,CAAA,GAAI,MAAA;AAAA,EAC7D;AACA,EAAA,IAAIC,iBAAA,CAAa,KAAK,CAAA,EAAG;AACvB,IAAA,MAAM,IAAA,GAAO,MAAM,QAAQ,CAAA;AAC3B,IAAA,OAAOA,kBAAa,IAAI,CAAA,GAAID,aAAA,CAAS,IAAA,CAAK,WAAW,CAAA,GAAI,MAAA;AAAA,EAC3D;AACA,EAAA,OAAO,MAAA;AACT;AAyDO,SAAS,+BAAA,CACdE,gBAAA,EACA,OAAA,GAAkC,EAAC,EAC7B;AACN,EAAAC,uCAAA;AAAA,IACED,iBAAuC,gCAAgC,CAAA;AAAA,IACvE,CAAA,IAAA,KAAQ,qBAAA,CAAsB,IAAA,EAAM,OAAO,CAAA;AAAA,IAC3C;AAAA;AAAA;AAAA,MAGE,aAAA,EAAe,CAAC,IAAA,EAAM,IAAA,KAAS;AAC7B,QAAA,eAAA,CAAgB,IAAA,EAAM,MAAM,OAAO,CAAA;AACnC,QAAA,gBAAA,CAAiB,IAAI,CAAA;AAAA,MACvB,CAAA;AAAA;AAAA;AAAA,MAGA,YAAA,EAAc,CAAC,EAAE,IAAA,EAAM,KAAI,KAAM,yBAAA,CAA0B,IAAA,EAAM,OAAA,EAAS,GAAG;AAAA;AAC/E,GACF;AACF;AAQA,SAAS,yBAAA,CACP,IAAA,EACA,OAAA,EACA,GAAA,EACS;AACT,EAAA,IAAI,KAAK,IAAA,KAAS,mBAAA,IAAuB,CAACD,iBAAA,CAAa,IAAA,CAAK,MAAM,CAAA,EAAG;AACnE,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,EAAA,MAAM,SAAS,MAAA,CAAO,MAAA;AACtB,EAAA,IAAI,CAACG,qBAAA,CAAiB,MAAM,CAAA,EAAG;AAC7B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,MAAA,GAASJ,aAAA,CAAS,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AACzC,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,mBAAA,CAAoB,IAAA,CAAK,OAAO,OAAO,CAAA;AACjE,EAAA,MAAA,CAAO,MAAA,GAASK,uBAAA;AAAA,IACd,MAAA;AAAA,IACA,CAAA,KAAA,KAAS;AAIP,MAAA,IAAA,CAAK,SAAS,EAAE,GAAG,QAAQ,GAAG,6BAAA,CAA8B,KAAK,CAAA,EAAE;AACnE,MAAA,GAAA,EAAI;AACJ,MAAA,2BAAA,CAA4B,MAAA,EAAQ,OAAO,aAAa,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,CAAA,KAAA,KAAS,IAAI,KAAK;AAAA,GACpB;AAEA,EAAA,OAAO,IAAA;AACT;AAGO,SAAS,8BAA8B,KAAA,EAAyD;AACrG,EAAA,MAAM,UAA0C,EAAC;AACjD,EAAA,IAAI,MAAM,IAAA,EAAM;AACd,IAAA,OAAA,CAAQ,KAAK,EAAE,IAAA,EAAM,QAAQ,IAAA,EAAM,KAAA,CAAM,MAAM,CAAA;AAAA,EACjD;AACA,EAAA,KAAA,MAAW,QAAA,IAAY,MAAM,SAAA,EAAW;AACtC,IAAA,OAAA,CAAQ,KAAK,EAAE,IAAA,EAAM,WAAA,EAAa,GAAG,UAAU,CAAA;AAAA,EACjD;AAEA,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,GAAI,MAAM,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,KAAA,CAAM,KAAA,EAAM,GAAI,EAAC;AAAA,IAC1D,GAAI,MAAM,YAAA,KAAiB,MAAA,GAAY,EAAE,YAAA,EAAc,KAAA,CAAM,YAAA,EAAa,GAAI,EAAC;AAAA,IAC/E,GAAI,MAAM,gBAAA,KAAqB,MAAA,GAAY,EAAE,gBAAA,EAAkB,KAAA,CAAM,gBAAA,EAAiB,GAAI,EAAC;AAAA,IAC3F,GAAI,KAAA,CAAM,UAAA,IAAc,KAAA,CAAM,aAAA,GAC1B;AAAA,MACE,QAAA,EAAU;AAAA,QACR,GAAI,MAAM,UAAA,GAAa,EAAE,IAAI,KAAA,CAAM,UAAA,KAAe,EAAC;AAAA,QACnD,GAAI,MAAM,aAAA,GAAgB,EAAE,SAAS,KAAA,CAAM,aAAA,KAAkB;AAAC;AAChE,QAEF;AAAC,GACP;AACF;AAOA,SAAS,2BAAA,CACP,MAAA,EACA,KAAA,EACA,aAAA,EACM;AACN,EAAA,MAAM,IAAA,GAAO,MAAA,GAAS,uBAAA,CAAwB,GAAA,CAAI,MAAM,CAAA,GAAI,MAAA;AAC5D,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,QAAQJ,iBAAA,CAAa,KAAA,CAAM,KAAK,CAAA,GAAI,MAAM,KAAA,GAAQ,MAAA;AACxD,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,IAAK,UAAA,CAAW,KAAA,CAAM,YAAY,CAAA,IAAK,UAAA,CAAW,KAAA,CAAM,MAAM,CAAA;AACxG,IAAA,MAAM,SAAS,UAAA,CAAW,KAAA,CAAM,YAAY,CAAA,IAAK,UAAA,CAAW,MAAM,gBAAgB,CAAA;AAClF,IAAA,eAAA,CAAgB,IAAA,EAAMK,sCAA2B,KAAK,CAAA;AACtD,IAAA,eAAA,CAAgB,IAAA,EAAMC,uCAA4B,MAAM,CAAA;AACxD,IAAA,eAAA,CAAgB,IAAA,EAAMC,sCAA2B,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,IAAKC,QAAA,CAAI,KAAA,EAAO,MAAM,CAAC,CAAA;AAAA,EACtG;AAEA,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,MAAM,KAAA,GAAQ,yBAAA,CAA0B,KAAA,CAAM,IAAA,EAAM,MAAM,SAAS,CAAA;AACnE,IAAA,MAAM,cAAA,GAAiB,oBAAoB,KAAA,EAAO,eAAA,CAAgB,EAAE,YAAA,EAAc,KAAA,CAAM,YAAA,EAAc,CAAC,CAAA;AACvG,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,IAAA,CAAK,YAAA,CAAaC,mCAAwB,cAAc,CAAA;AAAA,IAC1D;AAAA,EACF;AACF;AAGA,SAAS,eAAA,CAAgB,IAAA,EAAY,SAAA,EAAmB,KAAA,EAAiC;AACvF,EAAA,IAAI,UAAU,MAAA,EAAW;AACvB,IAAA;AAAA,EACF;AACA,EAAA,MAAM,OAAA,GAAUC,eAAA,CAAW,IAAI,CAAA,CAAE,KAAK,SAAS,CAAA;AAC/C,EAAA,IAAA,CAAK,aAAa,SAAA,EAAA,CAAY,OAAO,YAAY,QAAA,GAAW,OAAA,GAAU,KAAK,KAAK,CAAA;AAClF;AAQO,SAAS,qBAAA,CACd,MACA,cAAA,EACkB;AAClB,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,IAAA;AAExB,EAAA,IAAI,SAAS,MAAA,IAAU,CAAC,KAAA,IAAS,OAAO,UAAU,QAAA,EAAU;AAG1D,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAE,YAAA,EAAc,gBAAA,EAAiB,GAAI,mBAAA,CAAoB,OAAO,cAAc,CAAA;AACpF,EAAA,MAAM,QAAA,GAAWX,aAAA,CAAS,KAAA,CAAM,QAAQ,CAAA;AACxC,EAAA,MAAM,OAAA,GAAUA,aAAA,CAAS,KAAA,CAAM,OAAO,CAAA;AACtC,EAAA,MAAM,MAAA,GAASA,aAAA,CAAS,KAAA,CAAM,MAAM,CAAA;AACpC,EAAA,MAAM,UAAA,GAAaY,aAAA,CAAS,KAAA,CAAM,UAAU,CAAA;AAI5C,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,sBAAA,CAAuB,MAAA,EAAQ,MAAM,KAAK,CAAA;AAAA,EAC5C;AAEA,EAAA,MAAM,cAAA,GAA4D;AAAA,IAChE,CAACC,qCAAgC,GAAG,MAAA;AAAA,IACpC,GAAI,QAAA,GAAW,EAAE,CAACC,wBAAa,GAAG,QAAA,EAAU,CAAC,kCAAkC,GAAG,QAAA,EAAS,GAAI,EAAC;AAAA,IAChG,GAAI,UAAU,EAAE,CAACC,+BAAoB,GAAG,OAAA,KAAY,EAAC;AAAA,IACrD,GAAI,eAAe,MAAA,GAAY,EAAE,CAAC,wCAAwC,GAAG,UAAA,EAAW,GAAI;AAAC,GAC/F;AAEA,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,cAAA;AAAA,IACL,KAAK,YAAA;AAAA,IACL,KAAK,gBAAA;AAIH,MAAA,OAAO,qBAAqB,KAAA,EAAO,cAAA,EAAgB,cAAc,gBAAA,EAAkB,MAAA,EAAQ,SAAS,YAAY,CAAA;AAAA,IAClH,KAAK,mBAAA;AACH,MAAA,OAAO,mBAAmB,KAAA,EAAO,cAAA,EAAgB,YAAA,EAAc,gBAAA,EAAkB,QAAQ,OAAO,CAAA;AAAA,IAClG,KAAK,aAAA;AACH,MAAA,OAAO,aAAA,CAAc,OAAO,YAAY,CAAA;AAAA,IAC1C,KAAK,OAAA;AAAA,IACL,KAAK,WAAA,EAAa;AAEhB,MAAA,MAAM,KAAA,GAAQ,IAAA,KAAS,WAAA,GAAc,KAAA,CAAM,SAAS,KAAA,CAAM,KAAA;AAC1D,MAAA,OAAO,cAAA,CAAe,6BAA6B,OAAA,EAAS;AAAA,QAC1D,GAAG,cAAA;AAAA,QACH,GAAI,YAAA,IAAgB,KAAA,KAAU,MAAA,GAAY,EAAE,CAACC,kCAAuB,GAAGC,kBAAA,CAAc,KAAK,CAAA,EAAE,GAAI;AAAC,OAClG,CAAA;AAAA,IACH;AAAA,IACA,KAAK,QAAA;AACH,MAAA,OAAO,cAAA,CAAe,uBAAA,EAAyB,OAAA,EAAS,cAAc,CAAA;AAAA,IACxE;AAEE,MAAA,OAAO,MAAA;AAAA;AAEb;AAKA,SAAS,cAAA,CAAe,SAAA,EAAmB,MAAA,EAA4BC,YAAA,EAA8B;AACnG,EAAA,OAAOC,sBAAA,CAAkB;AAAA,IACvB,MAAM,MAAA,GAAS,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,GAAK,SAAA;AAAA,IAC1C,EAAA,EAAI,UAAU,SAAS,CAAA,CAAA;AAAA,IACvB,YAAY,EAAE,CAACC,gCAAqB,GAAG,SAAA,EAAW,GAAGF,YAAA;AAAW,GACjE,CAAA;AACH;AAEA,SAAS,qBACP,KAAA,EACA,cAAA,EACA,YAAA,EACA,gBAAA,EACA,QACA,QAAA,EACM;AACN,EAAA,MAAM,UAAA,GAAalB,aAAA,CAAS,KAAA,CAAM,UAAU,CAAA;AAC5C,EAAA,MAAM,cAAcA,aAAA,CAAS,KAAA,CAAM,WAAW,CAAA,KAAM,WAAW,eAAA,GAAkB,iBAAA,CAAA;AACjF,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,mBAAA,CAAoB,GAAA,CAAI,MAAA,EAAQ,EAAE,WAAA,EAAa,UAAU,CAAA;AAAA,EAC3D;AACA,EAAA,MAAM,IAAA,GAAO,cAAA,CAAeqB,8BAAA,EAA6B,UAAA,EAAY;AAAA,IACnE,GAAG,cAAA;AAAA,IACH,CAAC,gCAAgC,GAAG,WAAA;AAAA,IACpC,CAACC,oCAAyB,GAAG,QAAA;AAAA,IAC7B,GAAI,aAAa,EAAE,CAACC,6BAAkB,GAAG,UAAA,KAAe,EAAC;AAAA,IACzD,GAAI,YAAA,GAAe,2BAAA,CAA4B,KAAA,EAAO,gBAAgB,IAAI;AAAC,GAC5E,CAAA;AACD,EAAA,IAAI,YAAY,MAAA,EAAQ;AACtB,IAAA,uBAAA,CAAwB,GAAA,CAAI,QAAQ,IAAI,CAAA;AAAA,EAC1C;AAEA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,mBACP,KAAA,EACA,cAAA,EACA,YAAA,EACA,gBAAA,EACA,QACA,OAAA,EACM;AACN,EAAA,MAAM,MAAA,GAAS,MAAA,GAAS,mBAAA,CAAoB,GAAA,CAAI,MAAM,CAAA,GAAI,MAAA;AAC1D,EAAA,MAAM,WAAA,GAAc,MAAA,GAChB,CAAA,EAAG,MAAA,CAAO,WAAW,IAAI,MAAA,CAAO,QAAA,GAAW,UAAA,GAAa,YAAY,CAAA,CAAA,GACpE,4BAAA;AACJ,EAAA,OAAO,cAAA,CAAe,mCAAmC,OAAA,EAAS;AAAA,IAChE,GAAG,cAAA;AAAA,IACH,CAAC,gCAAgC,GAAG,WAAA;AAAA,IACpC,GAAI,YAAA,GAAe,2BAAA,CAA4B,KAAA,EAAO,gBAAgB,IAAI,EAAC;AAAA,IAC3E,GAAI,YAAA,IAAgB,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,KAAK,CAAA,GACzC,EAAE,CAACC,yCAA8B,GAAGP,kBAAA,CAAc,KAAA,CAAM,KAAK,CAAA,KAC7D;AAAC,GACN,CAAA;AACH;AAEA,SAAS,aAAA,CAAc,OAAgC,YAAA,EAA6B;AAClF,EAAA,MAAM,WAAWhB,iBAAA,CAAa,KAAA,CAAM,QAAQ,CAAA,GAAI,KAAA,CAAM,WAAW,EAAC;AAClE,EAAA,MAAM,QAAA,GAAWD,aAAA,CAAS,QAAA,CAAS,QAAQ,CAAA;AAC3C,EAAA,MAAM,aAAaA,aAAA,CAAS,KAAA,CAAM,UAAU,CAAA,IAAKA,aAAA,CAAS,SAAS,UAAU,CAAA;AAC7E,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,KAAA,IAAS,QAAA,CAAS,IAAA;AAG7C,EAAA,MAAM,WAAA,GACJ,YAAA,IAAgB,QAAA,GAAW,sBAAA,CAAuBA,aAAA,CAAS,KAAA,CAAM,MAAM,CAAA,EAAG,QAAA,EAAU,KAAA,CAAM,KAAK,CAAA,GAAI,MAAA;AACrG,EAAA,OAAO,cAAA,CAAeyB,gCAA6B,QAAA,EAAU;AAAA,IAC3D,CAACZ,qCAAgC,GAAG,MAAA;AAAA,IACpC,CAACa,2BAAgB,GAAG,UAAA;AAAA,IACpB,GAAI,WAAW,EAAE,CAACC,2BAAgB,GAAG,QAAA,KAAa,EAAC;AAAA,IACnD,GAAI,aAAa,EAAE,CAAC,6BAA6B,GAAG,UAAA,KAAe,EAAC;AAAA,IACpE,GAAI,cAAc,EAAE,CAAC,iCAAiC,GAAG,WAAA,KAAgB,EAAC;AAAA,IAC1E,GAAI,YAAA,IAAgB,SAAA,KAAc,MAAA,GAAY,EAAE,CAACC,4BAAiB,GAAGX,kBAAA,CAAc,SAAS,CAAA,EAAE,GAAI;AAAC,GACpG,CAAA;AACH;AAOO,SAAS,eAAA,CACd,IAAA,EACA,IAAA,EACA,cAAA,EACM;AACN,EAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAO,GAAI,IAAA;AACzB,EAAA,IAAI,CAAChB,iBAAA,CAAa,MAAM,CAAA,EAAG;AACzB,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,mBAAA,CAAoB,IAAA,CAAK,OAAO,cAAc,CAAA;AAExE,EAAA,IAAI,SAAS,aAAA,EAAe;AAC1B,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,IAAA,CAAK,aAAa4B,6BAAA,EAAoBZ,kBAAA,CAAc,MAAA,CAAO,MAAA,IAAU,MAAM,CAAC,CAAA;AAAA,IAC9E;AAIA,IAAA,MAAM,SAAShB,iBAAA,CAAa,MAAA,CAAO,MAAM,CAAA,GAAI,OAAO,MAAA,GAAS,MAAA;AAC7D,IAAA,IAAI,MAAA,EAAQ,SAAS,YAAA,EAAc;AACjC,MAAA,gBAAA,CAAiB,IAAA,EAAM,IAAA,EAAM,MAAA,CAAO,KAAK,CAAA;AAAA,IAC3C;AACA,IAAA;AAAA,EACF;AAKA,EAAA,MAAM,QAAQA,iBAAA,CAAa,MAAA,CAAO,KAAK,CAAA,GAAI,OAAO,KAAA,GAAQ,MAAA;AAC1D,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAM,WAAA,GAAc,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,IAAK,UAAA,CAAW,KAAA,CAAM,YAAY,CAAA,IAAK,UAAA,CAAW,KAAA,CAAM,MAAM,CAAA;AAC9G,IAAA,MAAM,eAAe,UAAA,CAAW,KAAA,CAAM,YAAY,CAAA,IAAK,UAAA,CAAW,MAAM,gBAAgB,CAAA;AACxF,IAAA,MAAM,cAAc,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,IAAKQ,QAAA,CAAI,aAAa,YAAY,CAAA;AAClF,IAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,MAAA,IAAA,CAAK,YAAA,CAAaH,sCAA2B,WAAW,CAAA;AAAA,IAC1D;AACA,IAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,MAAA,IAAA,CAAK,YAAA,CAAaC,uCAA4B,YAAY,CAAA;AAAA,IAC5D;AACA,IAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,MAAA,IAAA,CAAK,YAAA,CAAaC,sCAA2B,WAAW,CAAA;AAAA,IAC1D;AAAA,EACF;AAIA,EAAA,MAAM,YAAA,GAAe,gBAAgB,MAAM,CAAA;AAC3C,EAAA,IAAI,YAAA,IAAgB,SAAS,mBAAA,EAAqB;AAChD,IAAA,IAAA,CAAK,aAAasB,yCAAA,EAAgCb,kBAAA,CAAc,CAAC,YAAY,CAAC,CAAC,CAAA;AAAA,EACjF;AAEA,EAAA,MAAM,WAAWhB,iBAAA,CAAa,MAAA,CAAO,QAAQ,CAAA,GAAI,OAAO,QAAA,GAAW,MAAA;AACnE,EAAA,MAAM,aAAaD,aAAA,CAAS,QAAA,EAAU,EAAE,CAAA,IAAKA,aAAA,CAAS,OAAO,UAAU,CAAA;AACvE,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,IAAA,CAAK,YAAA,CAAa+B,+BAAoB,UAAU,CAAA;AAAA,EAClD;AACA,EAAA,MAAM,aAAA,GAAgB/B,cAAS,QAAA,EAAU,OAAO,KAAKA,aAAA,CAAS,IAAA,CAAK,MAAM,OAAO,CAAA;AAChF,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,IAAA,CAAK,YAAA,CAAagC,kCAAuB,aAAa,CAAA;AAAA,EACxD;AAKA,EAAA,MAAM,mBAAoB,MAAA,CAA0C,gBAAA;AACpE,EAAA,MAAM,kBAAA,GAAqBC,mCAA8B,gBAAgB,CAAA;AAGzE,EAAA,IACEC,yCAAoC,kBAAA,IACpCvB,eAAA,CAAW,IAAI,CAAA,CAAE,IAAA,CAAKuB,qCAAgC,CAAA,EACtD;AAEA,IAAA,OAAO,mBAAmBA,qCAAgC,CAAA;AAAA,EAC5D;AACA,EAAA,IAAA,CAAK,cAAc,kBAAkB,CAAA;AAErC,EAAA,IAAI,aAAA,EAAe;AAGjB,IAAA,MAAM,QACJ,IAAA,KAAS,mBAAA,IAAuB,KAAA,CAAM,OAAA,CAAQ,OAAO,OAAO,CAAA,GACxD,gBAAA,CAAiB,MAAA,CAAO,OAAO,CAAA,GAC/B,yBAAA,CAA0B,MAAA,CAAO,IAAA,EAAM,OAAO,SAAS,CAAA;AAC7D,IAAA,MAAM,cAAA,GAAiB,mBAAA,CAAoB,KAAA,EAAO,YAAY,CAAA;AAC9D,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,IAAA,CAAK,YAAA,CAAaxB,mCAAwB,cAAc,CAAA;AAAA,IAC1D;AAAA,EACF;AACF;AAGA,SAAS,sBAAsB,YAAA,EAA0C;AACvE,EAAA,OAAO,YAAA,KAAiB,YAAA,GAAe,WAAA,GAAe,YAAA,IAAgB,MAAA;AACxE;AAGA,SAAS,gBAAgB,MAAA,EAAqD;AAC5E,EAAA,MAAM,eAAe,MAAA,CAAO,YAAA;AAC5B,EAAA,IAAI,OAAO,iBAAiB,QAAA,EAAU;AACpC,IAAA,OAAO,YAAA;AAAA,EACT;AACA,EAAA,OAAOT,kBAAa,YAAY,CAAA,GAAID,aAAA,CAAS,YAAA,CAAa,OAAO,CAAA,GAAI,MAAA;AACvE;AAGA,SAAS,WAAW,KAAA,EAAoC;AACtD,EAAA,OAAOY,aAAA,CAAS,KAAK,CAAA,KAAMX,iBAAA,CAAa,KAAK,CAAA,GAAIW,aAAA,CAAS,KAAA,CAAM,KAAK,CAAA,GAAI,MAAA,CAAA;AAC3E;AAEA,SAAS,mBAAA,CACP,OACA,YAAA,EACoB;AACpB,EAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACjB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAOK,kBAAA,CAAc,CAAC,EAAE,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,aAAA,EAAe,qBAAA,CAAsB,YAAY,CAAA,EAAG,CAAC,CAAA;AACzG;AAEA,SAAS,aAAa,QAAA,EAA4D;AAChF,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,KAAA,IAAS,QAAA,CAAS,IAAA;AACxC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA,IACN,EAAA,EAAIjB,aAAA,CAAS,QAAA,CAAS,UAAU,CAAA;AAAA,IAChC,IAAA,EAAMA,aAAA,CAAS,QAAA,CAAS,QAAQ,CAAA;AAAA,IAChC,SAAA,EAAW,OAAO,IAAA,KAAS,QAAA,GAAW,OAAOiB,kBAAA,CAAc,IAAA,IAAQ,EAAE;AAAA,GACvE;AACF;AAEA,SAAS,iBAAiB,OAAA,EAAoD;AAC5E,EAAA,MAAM,QAAwC,EAAC;AAC/C,EAAA,KAAA,MAAW,QAAQ,OAAA,EAAS;AAC1B,IAAA,IAAI,CAAChB,iBAAA,CAAa,IAAI,CAAA,EAAG;AACvB,MAAA;AAAA,IACF;AACA,IAAA,IAAI,KAAK,IAAA,KAAS,MAAA,IAAU,OAAO,IAAA,CAAK,SAAS,QAAA,EAAU;AACzD,MAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,QAAQ,OAAA,EAAS,IAAA,CAAK,MAAM,CAAA;AAAA,IACjD,CAAA,MAAA,IAAW,IAAA,CAAK,IAAA,KAAS,WAAA,EAAa;AACpC,MAAA,KAAA,CAAM,IAAA,CAAK,YAAA,CAAa,IAAI,CAAC,CAAA;AAAA,IAC/B;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,yBAAA,CAA0B,MAAe,SAAA,EAAoD;AACpG,EAAA,MAAM,QAAwC,EAAC;AAC/C,EAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,CAAK,MAAA,EAAQ;AAC3C,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,MAAM,CAAA;AAAA,EAC5C;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,SAAS,CAAA,EAAG;AAC5B,IAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,MAAA,IAAIA,iBAAA,CAAa,QAAQ,CAAA,EAAG;AAC1B,QAAA,KAAA,CAAM,IAAA,CAAK,YAAA,CAAa,QAAQ,CAAC,CAAA;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,gBAAA,CAAiB,IAAA,EAAY,IAAA,EAA8B,KAAA,EAAsB;AAC/F,EAAA,IAAA,CAAK,SAAA,CAAU;AAAA,IACb,IAAA,EAAMkC,sBAAA;AAAA,IACN,OAAA,EAAS,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU;AAAA,GACnD,CAAA;AAED,EAAA,MAAM,QAAA,GAAWlC,kBAAa,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,QAAA,GAAW,EAAC;AAC5E,EAAA,MAAM,QAAA,GAAWD,aAAA,CAAS,QAAA,CAAS,QAAQ,CAAA;AAC3C,EAAA,MAAM,UAAA,GAAaA,cAAS,IAAA,CAAK,KAAA,CAAM,UAAU,CAAA,IAAKA,aAAA,CAAS,SAAS,UAAU,CAAA;AAElF,EAAAoC,cAAA,CAAU,CAAA,KAAA,KAAS;AACjB,IAAA,KAAA,CAAM,UAAA,CAAW,OAAA,EAASC,uBAAA,CAAmB,IAAI,CAAC,CAAA;AAClD,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,KAAA,CAAM,MAAA,CAAO,uBAAuB,QAAQ,CAAA;AAAA,IAC9C;AACA,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,KAAA,CAAM,MAAA,CAAO,yBAAyB,UAAU,CAAA;AAAA,IAClD;AACA,IAAA,KAAA,CAAM,SAAS,OAAO,CAAA;AACtB,IAAAC,qBAAA;AAAA,MACE,KAAA,YAAiB,QAAQ,KAAA,GAAQ,IAAI,MAAM,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,uBAAuB,CAAA;AAAA,MACtG;AAAA,QACE,SAAA,EAAW,EAAE,IAAA,EAAM,uBAAA,EAAyB,SAAS,KAAA;AAAM;AAC7D,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAEA,SAAS,mBAAA,CACP,OACA,cAAA,EAKA;AACA,EAAA,MAAM,KAAA,GAAQC,cAAA,EAAU,EAAG,wBAAA,EAAyB,CAAE,KAAA;AAEtD,EAAA,OAAO;AAAA,IACL,cAAc,gBAAA,CAAiB,cAAA,CAAe,cAAc,KAAA,CAAM,YAAA,EAAc,OAAO,MAAM,CAAA;AAAA,IAC7F,eAAe,gBAAA,CAAiB,cAAA,CAAe,eAAe,KAAA,CAAM,aAAA,EAAe,OAAO,OAAO,CAAA;AAAA,IACjG,gBAAA,EAAkBC,2BAAA,CAAuB,cAAA,CAAe,gBAAgB;AAAA,GAC1E;AACF;AAYA,SAAS,gBAAA,CAAiB,iBAAA,EAA4B,aAAA,EAAwB,aAAA,EAAiC;AAC7G,EAAA,IAAI,OAAO,sBAAsB,SAAA,EAAW;AAC1C,IAAA,OAAO,iBAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAO,kBAAkB,SAAA,EAAW;AACtC,IAAA,OAAO,aAAA;AAAA,EACT;AACA,EAAA,OAAO,aAAA,KAAkB,IAAA;AAC3B;AAEA,SAAS,2BAAA,CACP,OACA,gBAAA,EACiC;AACjC,EAAA,MAAMtB,eAA8C,EAAC;AAKrD,EAAA,MAAM,YAAA,GAAelB,aAAA,CAAS,KAAA,CAAM,YAAY,CAAA;AAChD,EAAA,IAAI,YAAA,EAAc;AAChB,IAAAkB,YAAA,CAAWuB,yCAAoC,CAAA,GAAIxB,kBAAA,CAAc,CAAC,EAAE,MAAM,MAAA,EAAQ,OAAA,EAAS,YAAA,EAAc,CAAC,CAAA;AAAA,EAC5G;AAIA,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,QAAA,IAAY,KAAA,CAAM,MAAA;AACzC,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAAC,YAAA,CAAWwB,gCAAqB,CAAA,GAAI,gBAAA,GAAmBC,4BAAuB,QAAQ,CAAA,GAAI1B,mBAAc,QAAQ,CAAA;AAEhH,IAAAC,YAAA,CAAW0B,oDAA+C,CAAA,GAAI,KAAA,CAAM,QAAQ,QAAQ,CAAA,GAAI,SAAS,MAAA,GAAS,CAAA;AAAA,EAC5G;AAEA,EAAA,OAAO1B,YAAA;AACT;;;;;;;;;;"} |
@@ -0,3 +1,5 @@ | ||
| export { graphqlIntegration } from './graphql/index.js'; | ||
| export { mongooseIntegration } from './mongoose/index.js'; | ||
| export { IOREDIS_DC_CHANNEL_COMMAND, IOREDIS_DC_CHANNEL_CONNECT, REDIS_DC_CHANNEL_BATCH, REDIS_DC_CHANNEL_COMMAND, REDIS_DC_CHANNEL_CONNECT, subscribeRedisDiagnosticChannels } from './redis/redis-dc-subscriber.js'; | ||
| export { mysql2Integration } from './mysql2/index.js'; | ||
| export { redisIntegration } from './redis/index.js'; | ||
| export { defaultDbStatementSerializer } from './redis/redis-statement-serializer.js'; | ||
@@ -4,0 +6,0 @@ export { bindTracingChannelToSpan } from './tracing-channel.js'; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"} | ||
| {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { defineIntegration, waitForTracingChannelBinding, debug, _INTERNAL_shouldSkipAiProviderWrapping, resolveAIRecordingOptions, shouldEnableTruncation, extractAnthropicRequestAttributes, GEN_AI_REQUEST_MODEL_ATTRIBUTE, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan, addAnthropicRequestAttributes, addAnthropicResponseAttributes, instrumentAsyncIterableStream, instrumentMessageStream } from '@sentry/core'; | ||
| import { defineIntegration, waitForTracingChannelBinding, debug, addAnthropicResponseAttributes, resolveAIRecordingOptions, instrumentAsyncIterableStream, instrumentMessageStream, _INTERNAL_shouldSkipAiProviderWrapping, shouldEnableTruncation, extractAnthropicRequestAttributes, GEN_AI_REQUEST_MODEL_ATTRIBUTE, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan, addAnthropicRequestAttributes } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
@@ -4,0 +4,0 @@ import { CHANNELS } from '../../orchestrion/channels.js'; |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { HTTP_ROUTE, URL_PATH, HTTP_REQUEST_METHOD, HTTP_RESPONSE_STATUS_CODE } from '@sentry/conventions/attributes'; | ||
| import { debug, getIsolationScope, getActiveSpan, getRootSpan, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_OP, startInactiveSpan, withActiveSpan, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_ERROR } from '@sentry/core'; | ||
| import { debug, getIsolationScope, getActiveSpan, getRootSpan, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_OP, startInactiveSpan, withActiveSpan, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, isObjectLike, SPAN_STATUS_ERROR } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../../debug-build.js'; | ||
@@ -39,3 +39,3 @@ | ||
| function isFastifyRequest(arg) { | ||
| return !!arg && typeof arg === "object" && !!arg.method && !!arg.url && (!!arg.routeOptions || "routerPath" in arg); | ||
| return isObjectLike(arg) && !!arg.method && !!arg.url && (!!arg.routeOptions || "routerPath" in arg); | ||
| } | ||
@@ -42,0 +42,0 @@ function fastifyOtelPlugin(instance, _opts, done) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing-channel/fastify/instrumentation.ts"],"sourcesContent":["/*\n * Copyright (c) 2024-present The Fastify team <https://github.com/fastify/fastify#team>\n * SPDX-License-Identifier: MIT\n *\n * NOTICE from the Sentry authors:\n * - Based on: https://github.com/fastify/otel/tree/bae80d6caef4287e7f01ff3c8dc753243706ea86 (@fastify/otel@0.18.1)\n * - Streamlined to the Sentry SDK's needs: dropped the `InstrumentationBase` wrapper, the unused\n * request/lifecycle hooks, `ignorePaths`/`minimatch` support and the OpenTelemetry tracer/context\n * APIs in favor of the Sentry span API.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-this-alias */\n/* eslint-disable max-lines */\n\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { HTTP_REQUEST_METHOD, HTTP_RESPONSE_STATUS_CODE, HTTP_ROUTE, URL_PATH } from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport {\n debug,\n getActiveSpan,\n getIsolationScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n spanToJSON,\n startInactiveSpan,\n startSpan,\n withActiveSpan,\n} from '@sentry/core';\nimport type { FastifyInstance, FastifyRequest } from './types';\nimport { DEBUG_BUILD } from '../../../debug-build';\n\nconst PACKAGE_NAME = '@sentry/instrumentation-fastify';\nconst SUPPORTED_VERSIONS = '>=3.21.0 <6';\n\nconst ORIGIN = 'auto.http.otel.fastify';\nconst HOOK_OP = 'hook.fastify';\nconst REQUEST_HANDLER_OP = 'request_handler.fastify';\n\nconst FASTIFY_HOOKS = [\n 'onRequest',\n 'preParsing',\n 'preValidation',\n 'preHandler',\n 'preSerialization',\n 'onSend',\n 'onResponse',\n 'onError',\n];\nconst ATTRIBUTE_HOOK_NAME = 'hook.name' as const;\nconst ATTRIBUTE_FASTIFY_TYPE = 'fastify.type' as const;\nconst ATTRIBUTE_HOOK_CALLBACK_NAME = 'hook.callback.name' as const;\nconst ATTRIBUTE_FASTIFY_ROOT = 'fastify.root' as const;\n\nconst HOOK_TYPE_ROUTE = 'route-hook' as const;\nconst HOOK_TYPE_INSTANCE = 'hook' as const;\nconst HOOK_TYPE_HANDLER = 'request-handler' as const;\nconst ANONYMOUS_FUNCTION_NAME = 'anonymous';\n\nconst kRequestSpan = Symbol('sentry fastify request span');\nconst kAddHookOriginal = Symbol('sentry fastify addHook original');\nconst kSetNotFoundOriginal = Symbol('sentry fastify setNotFoundHandler original');\n\ntype AnyFn = (...args: any[]) => any;\n\n/**\n * Read the matched route URL off a request. Fastify >=4 exposes it on `request.routeOptions.url`,\n * while v3 only has the (since-removed-in-v5) `request.routerPath`.\n */\nfunction getRequestRouteUrl(request: any): string | undefined {\n return request.routeOptions?.url ?? request.routerPath;\n}\n\n/**\n * Read the per-route config off a request. Fastify >=4 exposes it on `request.routeOptions.config`,\n * while v3 uses `request.routeConfig`. Used to honor the `{ config: { otel: false } }` opt-out.\n */\nfunction getRequestRouteConfig(request: any): { otel?: boolean } | undefined {\n return request.routeOptions?.config ?? request.routeConfig;\n}\n\n/**\n * Detect whether one of a wrapped handler's arguments is the Fastify request. We can't rely on a\n * single property since the route metadata moved from `routerPath` (v3) to `routeOptions` (>=4),\n * so we accept either shape.\n */\nfunction isFastifyRequest(arg: any): boolean {\n return !!arg && typeof arg === 'object' && !!arg.method && !!arg.url && (!!arg.routeOptions || 'routerPath' in arg);\n}\n\n/**\n * The Fastify plugin that wires up the request/hook/handler spans. It is registered on every Fastify\n * instance via the `fastify.initialization` diagnostics channel.\n */\nfunction fastifyOtelPlugin(this: unknown, instance: any, _opts: unknown, done: () => void): void {\n instance.decorate(kAddHookOriginal, instance.addHook);\n instance.decorate(kSetNotFoundOriginal, instance.setNotFoundHandler);\n instance.decorateRequest('opentelemetry', function opentelemetry(this: any) {\n return { span: this[kRequestSpan] as Span | null };\n });\n instance.decorateRequest(kRequestSpan, null);\n\n instance.addHook('onRoute', otelWireRoute);\n instance.addHook('onRequest', startRequestSpanHook);\n instance.addHook('onResponse', finalizeNotFoundSpanHook);\n\n instance.addHook = addHookPatched;\n instance.setNotFoundHandler = setNotFoundHandlerPatched;\n\n done();\n}\n\nconst pluginSymbols = fastifyOtelPlugin as unknown as Record<symbol, unknown>;\npluginSymbols[Symbol.for('skip-override')] = true;\npluginSymbols[Symbol.for('fastify.display-name')] = PACKAGE_NAME;\npluginSymbols[Symbol.for('plugin-meta')] = {\n fastify: SUPPORTED_VERSIONS,\n name: PACKAGE_NAME,\n};\n\nfunction otelWireRoute(this: any, routeOptions: any): void {\n if (routeOptions.config?.otel === false) {\n return;\n }\n\n for (const hook of FASTIFY_HOOKS) {\n const handlerLike = routeOptions[hook];\n\n if (typeof handlerLike === 'function') {\n routeOptions[hook] = handlerWrapper(\n handlerLike,\n hook,\n routeHookAttributes(this.pluginName, hook, handlerLike, routeOptions.url),\n );\n } else if (Array.isArray(handlerLike)) {\n routeOptions[hook] = handlerLike.map((handler: AnyFn) =>\n handlerWrapper(handler, hook, routeHookAttributes(this.pluginName, hook, handler, routeOptions.url)),\n );\n }\n }\n\n routeOptions.onSend = appendRouteHook(routeOptions.onSend, finalizeResponseSpanHook);\n routeOptions.onError = appendRouteHook(routeOptions.onError, recordErrorInSpanHook);\n\n routeOptions.handler = handlerWrapper(routeOptions.handler, 'handler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - route-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_HANDLER,\n [HTTP_ROUTE]: routeOptions.url,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n routeOptions.handler.name.length > 0 ? routeOptions.handler.name : ANONYMOUS_FUNCTION_NAME,\n });\n}\n\nfunction routeHookAttributes(pluginName: string, hook: string, handler: AnyFn, url: string): Record<string, string> {\n return {\n [ATTRIBUTE_HOOK_NAME]: `${pluginName} - route -> ${hook}`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_ROUTE,\n [HTTP_ROUTE]: url,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: handler.name?.length > 0 ? handler.name : ANONYMOUS_FUNCTION_NAME,\n };\n}\n\nfunction appendRouteHook(existing: AnyFn | AnyFn[] | undefined, hook: AnyFn): AnyFn | AnyFn[] {\n if (existing == null) {\n return hook;\n }\n return Array.isArray(existing) ? [...existing, hook] : [existing, hook];\n}\n\nfunction startRequestSpanHook(this: any, request: any, _reply: any, hookDone: () => void): void {\n if (getRequestRouteConfig(request)?.otel === false) {\n return hookDone();\n }\n\n const attributes: Record<string, string> = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [ATTRIBUTE_FASTIFY_ROOT]: PACKAGE_NAME,\n [HTTP_REQUEST_METHOD]: request.method,\n [URL_PATH]: request.url,\n };\n\n const route = getRequestRouteUrl(request);\n if (route != null) {\n attributes[HTTP_ROUTE] = route;\n\n // Update the route of the request on the root span, if it is a http.server span\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n if (rootSpan && spanToJSON(rootSpan).data[SEMANTIC_ATTRIBUTE_SENTRY_OP] === 'http.server') {\n rootSpan.setAttribute(HTTP_ROUTE, route);\n }\n }\n\n const requestSpan = startInactiveSpan({ name: 'request', op: REQUEST_HANDLER_OP, attributes });\n request[kRequestSpan] = requestSpan;\n\n // Set the request span as the active span for the remainder of the request lifecycle, so that\n // downstream hooks/handlers as well as errors captured via the error diagnostics channel are\n // parented to it (otherwise they would attach to the root `http.server` span instead).\n withActiveSpan(requestSpan, () => {\n hookDone();\n });\n}\n\nfunction finalizeNotFoundSpanHook(request: any, reply: any, hookDone: () => void): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n span.setAttributes({ [HTTP_RESPONSE_STATUS_CODE]: reply.statusCode });\n span.end();\n }\n\n request[kRequestSpan] = null;\n\n hookDone();\n}\n\nfunction finalizeResponseSpanHook(\n request: any,\n reply: any,\n payload: any,\n hookDone: (err: null, payload: any) => void,\n): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n if (reply.statusCode >= 500) {\n span.setStatus({ code: SPAN_STATUS_ERROR });\n }\n span.setAttributes({ [HTTP_RESPONSE_STATUS_CODE]: reply.statusCode });\n span.end();\n }\n\n request[kRequestSpan] = null;\n\n hookDone(null, payload);\n}\n\nfunction recordErrorInSpanHook(request: any, _reply: any, error: any, hookDone: () => void): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: error.message });\n }\n\n hookDone();\n}\n\nfunction addHookPatched(this: any, name: string, hook: AnyFn): unknown {\n const addHookOriginal = this[kAddHookOriginal];\n\n if (FASTIFY_HOOKS.includes(name)) {\n return addHookOriginal.call(\n this,\n name,\n handlerWrapper(hook, name, {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - ${name}`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: hook.name?.length > 0 ? hook.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n }\n\n return addHookOriginal.call(this, name, hook);\n}\n\nfunction setNotFoundHandlerPatched(this: any, hooks: any, handler?: any): void {\n const setNotFoundHandlerOriginal = this[kSetNotFoundOriginal];\n\n if (typeof hooks === 'function') {\n setNotFoundHandlerOriginal.call(\n this,\n handlerWrapper(hooks, 'notFoundHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: hooks.name?.length > 0 ? hooks.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n return;\n }\n\n if (hooks.preValidation != null) {\n hooks.preValidation = handlerWrapper(hooks.preValidation, 'notFoundHandler - preValidation', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler - preValidation`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n hooks.preValidation.name?.length > 0 ? hooks.preValidation.name : ANONYMOUS_FUNCTION_NAME,\n });\n }\n\n if (hooks.preHandler != null) {\n hooks.preHandler = handlerWrapper(hooks.preHandler, 'notFoundHandler - preHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler - preHandler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n hooks.preHandler.name?.length > 0 ? hooks.preHandler.name : ANONYMOUS_FUNCTION_NAME,\n });\n }\n\n // Fastify allows `setNotFoundHandler(opts)` without a handler, falling back to its built-in 404\n // handler. Forward the (already-wrapped) hooks unchanged so that fallback still applies.\n if (handler == null) {\n setNotFoundHandlerOriginal.call(this, hooks);\n return;\n }\n\n setNotFoundHandlerOriginal.call(\n this,\n hooks,\n handlerWrapper(handler, 'notFoundHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: handler.name?.length > 0 ? handler.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n}\n\nfunction getRequestFromArgs(args: any[]): any | null {\n for (const arg of args) {\n if (isFastifyRequest(arg)) {\n return arg;\n }\n }\n return null;\n}\n\nfunction handlerWrapper(handler: AnyFn, hookName: string, spanAttributes: Record<string, string> = {}): AnyFn {\n return function handlerWrapped(this: any, ...args: any[]) {\n const request = getRequestFromArgs(args);\n\n if (request === null || getRequestRouteConfig(request)?.otel === false) {\n return handler.call(this, ...args);\n }\n\n const parentSpan = (request[kRequestSpan] as Span | null) ?? undefined;\n const handlerName = handler.name?.length > 0 ? handler.name : (this.pluginName ?? ANONYMOUS_FUNCTION_NAME);\n\n const hookType = spanAttributes[ATTRIBUTE_FASTIFY_TYPE];\n const op =\n hookType === HOOK_TYPE_INSTANCE ? HOOK_OP : hookType === HOOK_TYPE_HANDLER ? REQUEST_HANDLER_OP : undefined;\n\n const name = op ? stripFastifyPrefix(spanAttributes[ATTRIBUTE_HOOK_NAME]) : `${hookName} - ${handlerName}`;\n\n return startSpan(\n {\n name,\n op,\n attributes: {\n ...spanAttributes,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n },\n parentSpan,\n },\n () => handler.call(this, ...args),\n );\n };\n}\n\n/**\n * Strip the framework/plugin prefixes from a Fastify `hook.name` to derive a readable span name.\n * This is a bit of a hack and does not always work for all spans, but it's the best we can do without a proper API.\n */\nfunction stripFastifyPrefix(hookName = ''): string {\n return hookName\n .replace(/^fastify -> /, '')\n .replace(/^@fastify\\/otel -> /, '')\n .replace(/^@sentry\\/instrumentation-fastify -> /, '');\n}\n\nfunction instrumentOnRequest(fastify: FastifyInstance): void {\n fastify.addHook('onRequest', async (request: FastifyRequest, _reply) => {\n const routeName = getRequestRouteUrl(request);\n const method = request.method || 'GET';\n\n getIsolationScope().setTransactionName(`${method} ${routeName}`);\n });\n}\n\nlet _isInstrumented = false;\n\n/**\n * Set up the Fastify (>= 3.21.0 < 6) instrumentation by subscribing to the `fastify.initialization`\n * diagnostics channel and registering the span-creating plugin on every Fastify instance.\n *\n * Idempotent and exposes an `id` so it can participate in the OpenTelemetry preload list.\n */\nexport const instrumentFastify = Object.assign(\n function instrumentFastify(): void {\n if (_isInstrumented) {\n return;\n }\n _isInstrumented = true;\n\n diagnosticsChannel.subscribe('fastify.initialization', message => {\n const fastifyInstance = (message as { fastify?: FastifyInstance }).fastify;\n\n fastifyInstance?.register(fastifyOtelPlugin).after(err => {\n if (err) {\n DEBUG_BUILD && debug.error('Failed to setup Fastify instrumentation', err);\n } else if (fastifyInstance) {\n instrumentOnRequest(fastifyInstance);\n }\n });\n });\n },\n { id: 'Fastify.v5' },\n);\n"],"names":["instrumentFastify"],"mappings":";;;;;AAmCA,MAAM,YAAA,GAAe,iCAAA;AACrB,MAAM,kBAAA,GAAqB,aAAA;AAE3B,MAAM,MAAA,GAAS,wBAAA;AACf,MAAM,OAAA,GAAU,cAAA;AAChB,MAAM,kBAAA,GAAqB,yBAAA;AAE3B,MAAM,aAAA,GAAgB;AAAA,EACpB,WAAA;AAAA,EACA,YAAA;AAAA,EACA,eAAA;AAAA,EACA,YAAA;AAAA,EACA,kBAAA;AAAA,EACA,QAAA;AAAA,EACA,YAAA;AAAA,EACA;AACF,CAAA;AACA,MAAM,mBAAA,GAAsB,WAAA;AAC5B,MAAM,sBAAA,GAAyB,cAAA;AAC/B,MAAM,4BAAA,GAA+B,oBAAA;AACrC,MAAM,sBAAA,GAAyB,cAAA;AAE/B,MAAM,eAAA,GAAkB,YAAA;AACxB,MAAM,kBAAA,GAAqB,MAAA;AAC3B,MAAM,iBAAA,GAAoB,iBAAA;AAC1B,MAAM,uBAAA,GAA0B,WAAA;AAEhC,MAAM,YAAA,0BAAsB,6BAA6B,CAAA;AACzD,MAAM,gBAAA,0BAA0B,iCAAiC,CAAA;AACjE,MAAM,oBAAA,0BAA8B,4CAA4C,CAAA;AAQhF,SAAS,mBAAmB,OAAA,EAAkC;AAC5D,EAAA,OAAO,OAAA,CAAQ,YAAA,EAAc,GAAA,IAAO,OAAA,CAAQ,UAAA;AAC9C;AAMA,SAAS,sBAAsB,OAAA,EAA8C;AAC3E,EAAA,OAAO,OAAA,CAAQ,YAAA,EAAc,MAAA,IAAU,OAAA,CAAQ,WAAA;AACjD;AAOA,SAAS,iBAAiB,GAAA,EAAmB;AAC3C,EAAA,OAAO,CAAC,CAAC,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,IAAY,CAAC,CAAC,GAAA,CAAI,MAAA,IAAU,CAAC,CAAC,GAAA,CAAI,GAAA,KAAQ,CAAC,CAAC,GAAA,CAAI,gBAAgB,YAAA,IAAgB,GAAA,CAAA;AACjH;AAMA,SAAS,iBAAA,CAAiC,QAAA,EAAe,KAAA,EAAgB,IAAA,EAAwB;AAC/F,EAAA,QAAA,CAAS,QAAA,CAAS,gBAAA,EAAkB,QAAA,CAAS,OAAO,CAAA;AACpD,EAAA,QAAA,CAAS,QAAA,CAAS,oBAAA,EAAsB,QAAA,CAAS,kBAAkB,CAAA;AACnE,EAAA,QAAA,CAAS,eAAA,CAAgB,eAAA,EAAiB,SAAS,aAAA,GAAyB;AAC1E,IAAA,OAAO,EAAE,IAAA,EAAM,IAAA,CAAK,YAAY,CAAA,EAAiB;AAAA,EACnD,CAAC,CAAA;AACD,EAAA,QAAA,CAAS,eAAA,CAAgB,cAAc,IAAI,CAAA;AAE3C,EAAA,QAAA,CAAS,OAAA,CAAQ,WAAW,aAAa,CAAA;AACzC,EAAA,QAAA,CAAS,OAAA,CAAQ,aAAa,oBAAoB,CAAA;AAClD,EAAA,QAAA,CAAS,OAAA,CAAQ,cAAc,wBAAwB,CAAA;AAEvD,EAAA,QAAA,CAAS,OAAA,GAAU,cAAA;AACnB,EAAA,QAAA,CAAS,kBAAA,GAAqB,yBAAA;AAE9B,EAAA,IAAA,EAAK;AACP;AAEA,MAAM,aAAA,GAAgB,iBAAA;AACtB,aAAA,iBAAc,MAAA,CAAO,GAAA,CAAI,eAAe,CAAC,CAAA,GAAI,IAAA;AAC7C,aAAA,iBAAc,MAAA,CAAO,GAAA,CAAI,sBAAsB,CAAC,CAAA,GAAI,YAAA;AACpD,aAAA,iBAAc,MAAA,CAAO,GAAA,CAAI,aAAa,CAAC,CAAA,GAAI;AAAA,EACzC,OAAA,EAAS,kBAAA;AAAA,EACT,IAAA,EAAM;AACR,CAAA;AAEA,SAAS,cAAyB,YAAA,EAAyB;AACzD,EAAA,IAAI,YAAA,CAAa,MAAA,EAAQ,IAAA,KAAS,KAAA,EAAO;AACvC,IAAA;AAAA,EACF;AAEA,EAAA,KAAA,MAAW,QAAQ,aAAA,EAAe;AAChC,IAAA,MAAM,WAAA,GAAc,aAAa,IAAI,CAAA;AAErC,IAAA,IAAI,OAAO,gBAAgB,UAAA,EAAY;AACrC,MAAA,YAAA,CAAa,IAAI,CAAA,GAAI,cAAA;AAAA,QACnB,WAAA;AAAA,QACA,IAAA;AAAA,QACA,oBAAoB,IAAA,CAAK,UAAA,EAAY,IAAA,EAAM,WAAA,EAAa,aAAa,GAAG;AAAA,OAC1E;AAAA,IACF,CAAA,MAAA,IAAW,KAAA,CAAM,OAAA,CAAQ,WAAW,CAAA,EAAG;AACrC,MAAA,YAAA,CAAa,IAAI,IAAI,WAAA,CAAY,GAAA;AAAA,QAAI,CAAC,OAAA,KACpC,cAAA,CAAe,OAAA,EAAS,IAAA,EAAM,mBAAA,CAAoB,IAAA,CAAK,UAAA,EAAY,IAAA,EAAM,OAAA,EAAS,YAAA,CAAa,GAAG,CAAC;AAAA,OACrG;AAAA,IACF;AAAA,EACF;AAEA,EAAA,YAAA,CAAa,MAAA,GAAS,eAAA,CAAgB,YAAA,CAAa,MAAA,EAAQ,wBAAwB,CAAA;AACnF,EAAA,YAAA,CAAa,OAAA,GAAU,eAAA,CAAgB,YAAA,CAAa,OAAA,EAAS,qBAAqB,CAAA;AAElF,EAAA,YAAA,CAAa,OAAA,GAAU,cAAA,CAAe,YAAA,CAAa,OAAA,EAAS,SAAA,EAAW;AAAA,IACrE,CAAC,mBAAmB,GAAG,CAAA,EAAG,KAAK,UAAU,CAAA,gBAAA,CAAA;AAAA,IACzC,CAAC,sBAAsB,GAAG,iBAAA;AAAA,IAC1B,CAAC,UAAU,GAAG,YAAA,CAAa,GAAA;AAAA,IAC3B,CAAC,4BAA4B,GAC3B,YAAA,CAAa,OAAA,CAAQ,KAAK,MAAA,GAAS,CAAA,GAAI,YAAA,CAAa,OAAA,CAAQ,IAAA,GAAO;AAAA,GACtE,CAAA;AACH;AAEA,SAAS,mBAAA,CAAoB,UAAA,EAAoB,IAAA,EAAc,OAAA,EAAgB,GAAA,EAAqC;AAClH,EAAA,OAAO;AAAA,IACL,CAAC,mBAAmB,GAAG,CAAA,EAAG,UAAU,eAAe,IAAI,CAAA,CAAA;AAAA,IACvD,CAAC,sBAAsB,GAAG,eAAA;AAAA,IAC1B,CAAC,UAAU,GAAG,GAAA;AAAA,IACd,CAAC,4BAA4B,GAAG,OAAA,CAAQ,MAAM,MAAA,GAAS,CAAA,GAAI,QAAQ,IAAA,GAAO;AAAA,GAC5E;AACF;AAEA,SAAS,eAAA,CAAgB,UAAuC,IAAA,EAA8B;AAC5F,EAAA,IAAI,YAAY,IAAA,EAAM;AACpB,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,GAAI,CAAC,GAAG,QAAA,EAAU,IAAI,CAAA,GAAI,CAAC,QAAA,EAAU,IAAI,CAAA;AACxE;AAEA,SAAS,oBAAA,CAAgC,OAAA,EAAc,MAAA,EAAa,QAAA,EAA4B;AAC9F,EAAA,IAAI,qBAAA,CAAsB,OAAO,CAAA,EAAG,IAAA,KAAS,KAAA,EAAO;AAClD,IAAA,OAAO,QAAA,EAAS;AAAA,EAClB;AAEA,EAAA,MAAM,UAAA,GAAqC;AAAA,IACzC,CAAC,gCAAgC,GAAG,MAAA;AAAA,IACpC,CAAC,sBAAsB,GAAG,YAAA;AAAA,IAC1B,CAAC,mBAAmB,GAAG,OAAA,CAAQ,MAAA;AAAA,IAC/B,CAAC,QAAQ,GAAG,OAAA,CAAQ;AAAA,GACtB;AAEA,EAAA,MAAM,KAAA,GAAQ,mBAAmB,OAAO,CAAA;AACxC,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,UAAA,CAAW,UAAU,CAAA,GAAI,KAAA;AAGzB,IAAA,MAAM,aAAa,aAAA,EAAc;AACjC,IAAA,MAAM,QAAA,GAAW,UAAA,IAAc,WAAA,CAAY,UAAU,CAAA;AACrD,IAAA,IAAI,YAAY,UAAA,CAAW,QAAQ,EAAE,IAAA,CAAK,4BAA4B,MAAM,aAAA,EAAe;AACzF,MAAA,QAAA,CAAS,YAAA,CAAa,YAAY,KAAK,CAAA;AAAA,IACzC;AAAA,EACF;AAEA,EAAA,MAAM,WAAA,GAAc,kBAAkB,EAAE,IAAA,EAAM,WAAW,EAAA,EAAI,kBAAA,EAAoB,YAAY,CAAA;AAC7F,EAAA,OAAA,CAAQ,YAAY,CAAA,GAAI,WAAA;AAKxB,EAAA,cAAA,CAAe,aAAa,MAAM;AAChC,IAAA,QAAA,EAAS;AAAA,EACX,CAAC,CAAA;AACH;AAEA,SAAS,wBAAA,CAAyB,OAAA,EAAc,KAAA,EAAY,QAAA,EAA4B;AACtF,EAAA,MAAM,IAAA,GAAO,QAAQ,YAAY,CAAA;AAEjC,EAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,IAAA,IAAA,CAAK,cAAc,EAAE,CAAC,yBAAyB,GAAG,KAAA,CAAM,YAAY,CAAA;AACpE,IAAA,IAAA,CAAK,GAAA,EAAI;AAAA,EACX;AAEA,EAAA,OAAA,CAAQ,YAAY,CAAA,GAAI,IAAA;AAExB,EAAA,QAAA,EAAS;AACX;AAEA,SAAS,wBAAA,CACP,OAAA,EACA,KAAA,EACA,OAAA,EACA,QAAA,EACM;AACN,EAAA,MAAM,IAAA,GAAO,QAAQ,YAAY,CAAA;AAEjC,EAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,IAAA,IAAI,KAAA,CAAM,cAAc,GAAA,EAAK;AAC3B,MAAA,IAAA,CAAK,SAAA,CAAU,EAAE,IAAA,EAAM,iBAAA,EAAmB,CAAA;AAAA,IAC5C;AACA,IAAA,IAAA,CAAK,cAAc,EAAE,CAAC,yBAAyB,GAAG,KAAA,CAAM,YAAY,CAAA;AACpE,IAAA,IAAA,CAAK,GAAA,EAAI;AAAA,EACX;AAEA,EAAA,OAAA,CAAQ,YAAY,CAAA,GAAI,IAAA;AAExB,EAAA,QAAA,CAAS,MAAM,OAAO,CAAA;AACxB;AAEA,SAAS,qBAAA,CAAsB,OAAA,EAAc,MAAA,EAAa,KAAA,EAAY,QAAA,EAA4B;AAChG,EAAA,MAAM,IAAA,GAAO,QAAQ,YAAY,CAAA;AAEjC,EAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,IAAA,IAAA,CAAK,UAAU,EAAE,IAAA,EAAM,mBAAmB,OAAA,EAAS,KAAA,CAAM,SAAS,CAAA;AAAA,EACpE;AAEA,EAAA,QAAA,EAAS;AACX;AAEA,SAAS,cAAA,CAA0B,MAAc,IAAA,EAAsB;AACrE,EAAA,MAAM,eAAA,GAAkB,KAAK,gBAAgB,CAAA;AAE7C,EAAA,IAAI,aAAA,CAAc,QAAA,CAAS,IAAI,CAAA,EAAG;AAChC,IAAA,OAAO,eAAA,CAAgB,IAAA;AAAA,MACrB,IAAA;AAAA,MACA,IAAA;AAAA,MACA,cAAA,CAAe,MAAM,IAAA,EAAM;AAAA,QACzB,CAAC,mBAAmB,GAAG,GAAG,IAAA,CAAK,UAAU,MAAM,IAAI,CAAA,CAAA;AAAA,QACnD,CAAC,sBAAsB,GAAG,kBAAA;AAAA,QAC1B,CAAC,4BAA4B,GAAG,IAAA,CAAK,MAAM,MAAA,GAAS,CAAA,GAAI,KAAK,IAAA,GAAO;AAAA,OACrE;AAAA,KACH;AAAA,EACF;AAEA,EAAA,OAAO,eAAA,CAAgB,IAAA,CAAK,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA;AAC9C;AAEA,SAAS,yBAAA,CAAqC,OAAY,OAAA,EAAqB;AAC7E,EAAA,MAAM,0BAAA,GAA6B,KAAK,oBAAoB,CAAA;AAE5D,EAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC/B,IAAA,0BAAA,CAA2B,IAAA;AAAA,MACzB,IAAA;AAAA,MACA,cAAA,CAAe,OAAO,iBAAA,EAAmB;AAAA,QACvC,CAAC,mBAAmB,GAAG,CAAA,EAAG,KAAK,UAAU,CAAA,oBAAA,CAAA;AAAA,QACzC,CAAC,sBAAsB,GAAG,kBAAA;AAAA,QAC1B,CAAC,4BAA4B,GAAG,KAAA,CAAM,MAAM,MAAA,GAAS,CAAA,GAAI,MAAM,IAAA,GAAO;AAAA,OACvE;AAAA,KACH;AACA,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,KAAA,CAAM,iBAAiB,IAAA,EAAM;AAC/B,IAAA,KAAA,CAAM,aAAA,GAAgB,cAAA,CAAe,KAAA,CAAM,aAAA,EAAe,iCAAA,EAAmC;AAAA,MAC3F,CAAC,mBAAmB,GAAG,CAAA,EAAG,KAAK,UAAU,CAAA,oCAAA,CAAA;AAAA,MACzC,CAAC,sBAAsB,GAAG,kBAAA;AAAA,MAC1B,CAAC,4BAA4B,GAC3B,KAAA,CAAM,aAAA,CAAc,MAAM,MAAA,GAAS,CAAA,GAAI,KAAA,CAAM,aAAA,CAAc,IAAA,GAAO;AAAA,KACrE,CAAA;AAAA,EACH;AAEA,EAAA,IAAI,KAAA,CAAM,cAAc,IAAA,EAAM;AAC5B,IAAA,KAAA,CAAM,UAAA,GAAa,cAAA,CAAe,KAAA,CAAM,UAAA,EAAY,8BAAA,EAAgC;AAAA,MAClF,CAAC,mBAAmB,GAAG,CAAA,EAAG,KAAK,UAAU,CAAA,iCAAA,CAAA;AAAA,MACzC,CAAC,sBAAsB,GAAG,kBAAA;AAAA,MAC1B,CAAC,4BAA4B,GAC3B,KAAA,CAAM,UAAA,CAAW,MAAM,MAAA,GAAS,CAAA,GAAI,KAAA,CAAM,UAAA,CAAW,IAAA,GAAO;AAAA,KAC/D,CAAA;AAAA,EACH;AAIA,EAAA,IAAI,WAAW,IAAA,EAAM;AACnB,IAAA,0BAAA,CAA2B,IAAA,CAAK,MAAM,KAAK,CAAA;AAC3C,IAAA;AAAA,EACF;AAEA,EAAA,0BAAA,CAA2B,IAAA;AAAA,IACzB,IAAA;AAAA,IACA,KAAA;AAAA,IACA,cAAA,CAAe,SAAS,iBAAA,EAAmB;AAAA,MACzC,CAAC,mBAAmB,GAAG,CAAA,EAAG,KAAK,UAAU,CAAA,oBAAA,CAAA;AAAA,MACzC,CAAC,sBAAsB,GAAG,kBAAA;AAAA,MAC1B,CAAC,4BAA4B,GAAG,OAAA,CAAQ,MAAM,MAAA,GAAS,CAAA,GAAI,QAAQ,IAAA,GAAO;AAAA,KAC3E;AAAA,GACH;AACF;AAEA,SAAS,mBAAmB,IAAA,EAAyB;AACnD,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,gBAAA,CAAiB,GAAG,CAAA,EAAG;AACzB,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,cAAA,CAAe,OAAA,EAAgB,QAAA,EAAkB,cAAA,GAAyC,EAAC,EAAU;AAC5G,EAAA,OAAO,SAAS,kBAA6B,IAAA,EAAa;AACxD,IAAA,MAAM,OAAA,GAAU,mBAAmB,IAAI,CAAA;AAEvC,IAAA,IAAI,YAAY,IAAA,IAAQ,qBAAA,CAAsB,OAAO,CAAA,EAAG,SAAS,KAAA,EAAO;AACtE,MAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,IAAA,EAAM,GAAG,IAAI,CAAA;AAAA,IACnC;AAEA,IAAA,MAAM,UAAA,GAAc,OAAA,CAAQ,YAAY,CAAA,IAAqB,MAAA;AAC7D,IAAA,MAAM,WAAA,GAAc,QAAQ,IAAA,EAAM,MAAA,GAAS,IAAI,OAAA,CAAQ,IAAA,GAAQ,KAAK,UAAA,IAAc,uBAAA;AAElF,IAAA,MAAM,QAAA,GAAW,eAAe,sBAAsB,CAAA;AACtD,IAAA,MAAM,KACJ,QAAA,KAAa,kBAAA,GAAqB,OAAA,GAAU,QAAA,KAAa,oBAAoB,kBAAA,GAAqB,MAAA;AAEpG,IAAA,MAAM,IAAA,GAAO,EAAA,GAAK,kBAAA,CAAmB,cAAA,CAAe,mBAAmB,CAAC,CAAA,GAAI,CAAA,EAAG,QAAQ,CAAA,GAAA,EAAM,WAAW,CAAA,CAAA;AAExG,IAAA,OAAO,SAAA;AAAA,MACL;AAAA,QACE,IAAA;AAAA,QACA,EAAA;AAAA,QACA,UAAA,EAAY;AAAA,UACV,GAAG,cAAA;AAAA,UACH,CAAC,gCAAgC,GAAG;AAAA,SACtC;AAAA,QACA;AAAA,OACF;AAAA,MACA,MAAM,OAAA,CAAQ,IAAA,CAAK,IAAA,EAAM,GAAG,IAAI;AAAA,KAClC;AAAA,EACF,CAAA;AACF;AAMA,SAAS,kBAAA,CAAmB,WAAW,EAAA,EAAY;AACjD,EAAA,OAAO,QAAA,CACJ,OAAA,CAAQ,cAAA,EAAgB,EAAE,CAAA,CAC1B,OAAA,CAAQ,qBAAA,EAAuB,EAAE,CAAA,CACjC,OAAA,CAAQ,uCAAA,EAAyC,EAAE,CAAA;AACxD;AAEA,SAAS,oBAAoB,OAAA,EAAgC;AAC3D,EAAA,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,OAAO,OAAA,EAAyB,MAAA,KAAW;AACtE,IAAA,MAAM,SAAA,GAAY,mBAAmB,OAAO,CAAA;AAC5C,IAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,KAAA;AAEjC,IAAA,iBAAA,GAAoB,kBAAA,CAAmB,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,SAAS,CAAA,CAAE,CAAA;AAAA,EACjE,CAAC,CAAA;AACH;AAEA,IAAI,eAAA,GAAkB,KAAA;AAQf,MAAM,oBAAoB,MAAA,CAAO,MAAA;AAAA,EACtC,SAASA,kBAAAA,GAA0B;AACjC,IAAA,IAAI,eAAA,EAAiB;AACnB,MAAA;AAAA,IACF;AACA,IAAA,eAAA,GAAkB,IAAA;AAElB,IAAA,kBAAA,CAAmB,SAAA,CAAU,0BAA0B,CAAA,OAAA,KAAW;AAChE,MAAA,MAAM,kBAAmB,OAAA,CAA0C,OAAA;AAEnE,MAAA,eAAA,EAAiB,QAAA,CAAS,iBAAiB,CAAA,CAAE,KAAA,CAAM,CAAA,GAAA,KAAO;AACxD,QAAA,IAAI,GAAA,EAAK;AACP,UAAA,WAAA,IAAe,KAAA,CAAM,KAAA,CAAM,yCAAA,EAA2C,GAAG,CAAA;AAAA,QAC3E,WAAW,eAAA,EAAiB;AAC1B,UAAA,mBAAA,CAAoB,eAAe,CAAA;AAAA,QACrC;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH,CAAA;AAAA,EACA,EAAE,IAAI,YAAA;AACR;;;;"} | ||
| {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing-channel/fastify/instrumentation.ts"],"sourcesContent":["/*\n * Copyright (c) 2024-present The Fastify team <https://github.com/fastify/fastify#team>\n * SPDX-License-Identifier: MIT\n *\n * NOTICE from the Sentry authors:\n * - Based on: https://github.com/fastify/otel/tree/bae80d6caef4287e7f01ff3c8dc753243706ea86 (@fastify/otel@0.18.1)\n * - Streamlined to the Sentry SDK's needs: dropped the `InstrumentationBase` wrapper, the unused\n * request/lifecycle hooks, `ignorePaths`/`minimatch` support and the OpenTelemetry tracer/context\n * APIs in favor of the Sentry span API.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-this-alias */\n/* eslint-disable max-lines */\n\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { HTTP_REQUEST_METHOD, HTTP_RESPONSE_STATUS_CODE, HTTP_ROUTE, URL_PATH } from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport {\n isObjectLike,\n debug,\n getActiveSpan,\n getIsolationScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n spanToJSON,\n startInactiveSpan,\n startSpan,\n withActiveSpan,\n} from '@sentry/core';\nimport type { FastifyInstance, FastifyRequest } from './types';\nimport { DEBUG_BUILD } from '../../../debug-build';\n\nconst PACKAGE_NAME = '@sentry/instrumentation-fastify';\nconst SUPPORTED_VERSIONS = '>=3.21.0 <6';\n\nconst ORIGIN = 'auto.http.otel.fastify';\nconst HOOK_OP = 'hook.fastify';\nconst REQUEST_HANDLER_OP = 'request_handler.fastify';\n\nconst FASTIFY_HOOKS = [\n 'onRequest',\n 'preParsing',\n 'preValidation',\n 'preHandler',\n 'preSerialization',\n 'onSend',\n 'onResponse',\n 'onError',\n];\nconst ATTRIBUTE_HOOK_NAME = 'hook.name' as const;\nconst ATTRIBUTE_FASTIFY_TYPE = 'fastify.type' as const;\nconst ATTRIBUTE_HOOK_CALLBACK_NAME = 'hook.callback.name' as const;\nconst ATTRIBUTE_FASTIFY_ROOT = 'fastify.root' as const;\n\nconst HOOK_TYPE_ROUTE = 'route-hook' as const;\nconst HOOK_TYPE_INSTANCE = 'hook' as const;\nconst HOOK_TYPE_HANDLER = 'request-handler' as const;\nconst ANONYMOUS_FUNCTION_NAME = 'anonymous';\n\nconst kRequestSpan = Symbol('sentry fastify request span');\nconst kAddHookOriginal = Symbol('sentry fastify addHook original');\nconst kSetNotFoundOriginal = Symbol('sentry fastify setNotFoundHandler original');\n\ntype AnyFn = (...args: any[]) => any;\n\n/**\n * Read the matched route URL off a request. Fastify >=4 exposes it on `request.routeOptions.url`,\n * while v3 only has the (since-removed-in-v5) `request.routerPath`.\n */\nfunction getRequestRouteUrl(request: any): string | undefined {\n return request.routeOptions?.url ?? request.routerPath;\n}\n\n/**\n * Read the per-route config off a request. Fastify >=4 exposes it on `request.routeOptions.config`,\n * while v3 uses `request.routeConfig`. Used to honor the `{ config: { otel: false } }` opt-out.\n */\nfunction getRequestRouteConfig(request: any): { otel?: boolean } | undefined {\n return request.routeOptions?.config ?? request.routeConfig;\n}\n\n/**\n * Detect whether one of a wrapped handler's arguments is the Fastify request. We can't rely on a\n * single property since the route metadata moved from `routerPath` (v3) to `routeOptions` (>=4),\n * so we accept either shape.\n */\nfunction isFastifyRequest(arg: any): boolean {\n return isObjectLike(arg) && !!arg.method && !!arg.url && (!!arg.routeOptions || 'routerPath' in arg);\n}\n\n/**\n * The Fastify plugin that wires up the request/hook/handler spans. It is registered on every Fastify\n * instance via the `fastify.initialization` diagnostics channel.\n */\nfunction fastifyOtelPlugin(this: unknown, instance: any, _opts: unknown, done: () => void): void {\n instance.decorate(kAddHookOriginal, instance.addHook);\n instance.decorate(kSetNotFoundOriginal, instance.setNotFoundHandler);\n instance.decorateRequest('opentelemetry', function opentelemetry(this: any) {\n return { span: this[kRequestSpan] as Span | null };\n });\n instance.decorateRequest(kRequestSpan, null);\n\n instance.addHook('onRoute', otelWireRoute);\n instance.addHook('onRequest', startRequestSpanHook);\n instance.addHook('onResponse', finalizeNotFoundSpanHook);\n\n instance.addHook = addHookPatched;\n instance.setNotFoundHandler = setNotFoundHandlerPatched;\n\n done();\n}\n\nconst pluginSymbols = fastifyOtelPlugin as unknown as Record<symbol, unknown>;\npluginSymbols[Symbol.for('skip-override')] = true;\npluginSymbols[Symbol.for('fastify.display-name')] = PACKAGE_NAME;\npluginSymbols[Symbol.for('plugin-meta')] = {\n fastify: SUPPORTED_VERSIONS,\n name: PACKAGE_NAME,\n};\n\nfunction otelWireRoute(this: any, routeOptions: any): void {\n if (routeOptions.config?.otel === false) {\n return;\n }\n\n for (const hook of FASTIFY_HOOKS) {\n const handlerLike = routeOptions[hook];\n\n if (typeof handlerLike === 'function') {\n routeOptions[hook] = handlerWrapper(\n handlerLike,\n hook,\n routeHookAttributes(this.pluginName, hook, handlerLike, routeOptions.url),\n );\n } else if (Array.isArray(handlerLike)) {\n routeOptions[hook] = handlerLike.map((handler: AnyFn) =>\n handlerWrapper(handler, hook, routeHookAttributes(this.pluginName, hook, handler, routeOptions.url)),\n );\n }\n }\n\n routeOptions.onSend = appendRouteHook(routeOptions.onSend, finalizeResponseSpanHook);\n routeOptions.onError = appendRouteHook(routeOptions.onError, recordErrorInSpanHook);\n\n routeOptions.handler = handlerWrapper(routeOptions.handler, 'handler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - route-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_HANDLER,\n [HTTP_ROUTE]: routeOptions.url,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n routeOptions.handler.name.length > 0 ? routeOptions.handler.name : ANONYMOUS_FUNCTION_NAME,\n });\n}\n\nfunction routeHookAttributes(pluginName: string, hook: string, handler: AnyFn, url: string): Record<string, string> {\n return {\n [ATTRIBUTE_HOOK_NAME]: `${pluginName} - route -> ${hook}`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_ROUTE,\n [HTTP_ROUTE]: url,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: handler.name?.length > 0 ? handler.name : ANONYMOUS_FUNCTION_NAME,\n };\n}\n\nfunction appendRouteHook(existing: AnyFn | AnyFn[] | undefined, hook: AnyFn): AnyFn | AnyFn[] {\n if (existing == null) {\n return hook;\n }\n return Array.isArray(existing) ? [...existing, hook] : [existing, hook];\n}\n\nfunction startRequestSpanHook(this: any, request: any, _reply: any, hookDone: () => void): void {\n if (getRequestRouteConfig(request)?.otel === false) {\n return hookDone();\n }\n\n const attributes: Record<string, string> = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [ATTRIBUTE_FASTIFY_ROOT]: PACKAGE_NAME,\n [HTTP_REQUEST_METHOD]: request.method,\n [URL_PATH]: request.url,\n };\n\n const route = getRequestRouteUrl(request);\n if (route != null) {\n attributes[HTTP_ROUTE] = route;\n\n // Update the route of the request on the root span, if it is a http.server span\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n if (rootSpan && spanToJSON(rootSpan).data[SEMANTIC_ATTRIBUTE_SENTRY_OP] === 'http.server') {\n rootSpan.setAttribute(HTTP_ROUTE, route);\n }\n }\n\n const requestSpan = startInactiveSpan({ name: 'request', op: REQUEST_HANDLER_OP, attributes });\n request[kRequestSpan] = requestSpan;\n\n // Set the request span as the active span for the remainder of the request lifecycle, so that\n // downstream hooks/handlers as well as errors captured via the error diagnostics channel are\n // parented to it (otherwise they would attach to the root `http.server` span instead).\n withActiveSpan(requestSpan, () => {\n hookDone();\n });\n}\n\nfunction finalizeNotFoundSpanHook(request: any, reply: any, hookDone: () => void): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n span.setAttributes({ [HTTP_RESPONSE_STATUS_CODE]: reply.statusCode });\n span.end();\n }\n\n request[kRequestSpan] = null;\n\n hookDone();\n}\n\nfunction finalizeResponseSpanHook(\n request: any,\n reply: any,\n payload: any,\n hookDone: (err: null, payload: any) => void,\n): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n if (reply.statusCode >= 500) {\n span.setStatus({ code: SPAN_STATUS_ERROR });\n }\n span.setAttributes({ [HTTP_RESPONSE_STATUS_CODE]: reply.statusCode });\n span.end();\n }\n\n request[kRequestSpan] = null;\n\n hookDone(null, payload);\n}\n\nfunction recordErrorInSpanHook(request: any, _reply: any, error: any, hookDone: () => void): void {\n const span = request[kRequestSpan] as Span | null;\n\n if (span != null) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: error.message });\n }\n\n hookDone();\n}\n\nfunction addHookPatched(this: any, name: string, hook: AnyFn): unknown {\n const addHookOriginal = this[kAddHookOriginal];\n\n if (FASTIFY_HOOKS.includes(name)) {\n return addHookOriginal.call(\n this,\n name,\n handlerWrapper(hook, name, {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - ${name}`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: hook.name?.length > 0 ? hook.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n }\n\n return addHookOriginal.call(this, name, hook);\n}\n\nfunction setNotFoundHandlerPatched(this: any, hooks: any, handler?: any): void {\n const setNotFoundHandlerOriginal = this[kSetNotFoundOriginal];\n\n if (typeof hooks === 'function') {\n setNotFoundHandlerOriginal.call(\n this,\n handlerWrapper(hooks, 'notFoundHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: hooks.name?.length > 0 ? hooks.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n return;\n }\n\n if (hooks.preValidation != null) {\n hooks.preValidation = handlerWrapper(hooks.preValidation, 'notFoundHandler - preValidation', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler - preValidation`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n hooks.preValidation.name?.length > 0 ? hooks.preValidation.name : ANONYMOUS_FUNCTION_NAME,\n });\n }\n\n if (hooks.preHandler != null) {\n hooks.preHandler = handlerWrapper(hooks.preHandler, 'notFoundHandler - preHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler - preHandler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]:\n hooks.preHandler.name?.length > 0 ? hooks.preHandler.name : ANONYMOUS_FUNCTION_NAME,\n });\n }\n\n // Fastify allows `setNotFoundHandler(opts)` without a handler, falling back to its built-in 404\n // handler. Forward the (already-wrapped) hooks unchanged so that fallback still applies.\n if (handler == null) {\n setNotFoundHandlerOriginal.call(this, hooks);\n return;\n }\n\n setNotFoundHandlerOriginal.call(\n this,\n hooks,\n handlerWrapper(handler, 'notFoundHandler', {\n [ATTRIBUTE_HOOK_NAME]: `${this.pluginName} - not-found-handler`,\n [ATTRIBUTE_FASTIFY_TYPE]: HOOK_TYPE_INSTANCE,\n [ATTRIBUTE_HOOK_CALLBACK_NAME]: handler.name?.length > 0 ? handler.name : ANONYMOUS_FUNCTION_NAME,\n }),\n );\n}\n\nfunction getRequestFromArgs(args: any[]): any | null {\n for (const arg of args) {\n if (isFastifyRequest(arg)) {\n return arg;\n }\n }\n return null;\n}\n\nfunction handlerWrapper(handler: AnyFn, hookName: string, spanAttributes: Record<string, string> = {}): AnyFn {\n return function handlerWrapped(this: any, ...args: any[]) {\n const request = getRequestFromArgs(args);\n\n if (request === null || getRequestRouteConfig(request)?.otel === false) {\n return handler.call(this, ...args);\n }\n\n const parentSpan = (request[kRequestSpan] as Span | null) ?? undefined;\n const handlerName = handler.name?.length > 0 ? handler.name : (this.pluginName ?? ANONYMOUS_FUNCTION_NAME);\n\n const hookType = spanAttributes[ATTRIBUTE_FASTIFY_TYPE];\n const op =\n hookType === HOOK_TYPE_INSTANCE ? HOOK_OP : hookType === HOOK_TYPE_HANDLER ? REQUEST_HANDLER_OP : undefined;\n\n const name = op ? stripFastifyPrefix(spanAttributes[ATTRIBUTE_HOOK_NAME]) : `${hookName} - ${handlerName}`;\n\n return startSpan(\n {\n name,\n op,\n attributes: {\n ...spanAttributes,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n },\n parentSpan,\n },\n () => handler.call(this, ...args),\n );\n };\n}\n\n/**\n * Strip the framework/plugin prefixes from a Fastify `hook.name` to derive a readable span name.\n * This is a bit of a hack and does not always work for all spans, but it's the best we can do without a proper API.\n */\nfunction stripFastifyPrefix(hookName = ''): string {\n return hookName\n .replace(/^fastify -> /, '')\n .replace(/^@fastify\\/otel -> /, '')\n .replace(/^@sentry\\/instrumentation-fastify -> /, '');\n}\n\nfunction instrumentOnRequest(fastify: FastifyInstance): void {\n fastify.addHook('onRequest', async (request: FastifyRequest, _reply) => {\n const routeName = getRequestRouteUrl(request);\n const method = request.method || 'GET';\n\n getIsolationScope().setTransactionName(`${method} ${routeName}`);\n });\n}\n\nlet _isInstrumented = false;\n\n/**\n * Set up the Fastify (>= 3.21.0 < 6) instrumentation by subscribing to the `fastify.initialization`\n * diagnostics channel and registering the span-creating plugin on every Fastify instance.\n *\n * Idempotent and exposes an `id` so it can participate in the OpenTelemetry preload list.\n */\nexport const instrumentFastify = Object.assign(\n function instrumentFastify(): void {\n if (_isInstrumented) {\n return;\n }\n _isInstrumented = true;\n\n diagnosticsChannel.subscribe('fastify.initialization', message => {\n const fastifyInstance = (message as { fastify?: FastifyInstance }).fastify;\n\n fastifyInstance?.register(fastifyOtelPlugin).after(err => {\n if (err) {\n DEBUG_BUILD && debug.error('Failed to setup Fastify instrumentation', err);\n } else if (fastifyInstance) {\n instrumentOnRequest(fastifyInstance);\n }\n });\n });\n },\n { id: 'Fastify.v5' },\n);\n"],"names":["instrumentFastify"],"mappings":"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"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { DB_STATEMENT, NET_PEER_PORT, NET_PEER_NAME, DB_SYSTEM } from '@sentry/conventions/attributes'; | ||
| import { defineIntegration, debug, waitForTracingChannelBinding, getActiveSpan, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
| import { defineIntegration, debug, waitForTracingChannelBinding, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
@@ -43,5 +43,2 @@ import { CHANNELS } from '../../orchestrion/channels.js'; | ||
| (data) => { | ||
| if (!getActiveSpan()) { | ||
| return void 0; | ||
| } | ||
| const command = data.arguments?.[0]; | ||
@@ -60,2 +57,4 @@ if (!command || typeof command !== "object") { | ||
| { | ||
| // ioredis' `requireParentSpan` default: only create a span under an active span. | ||
| requiresParentSpan: true, | ||
| beforeSpanEnd(span, data) { | ||
@@ -72,13 +71,14 @@ if ("error" in data || !responseHook) { | ||
| ); | ||
| bindTracingChannelToSpan(connectChannel, (data) => { | ||
| if (!getActiveSpan()) { | ||
| return void 0; | ||
| } | ||
| const { host, port } = getConnectionOptions(data.self); | ||
| return startInactiveSpan({ | ||
| name: "connect", | ||
| op: "db", | ||
| attributes: { ...connectionAttributes(host, port), [DB_STATEMENT]: "connect" } | ||
| }); | ||
| }); | ||
| bindTracingChannelToSpan( | ||
| connectChannel, | ||
| (data) => { | ||
| const { host, port } = getConnectionOptions(data.self); | ||
| return startInactiveSpan({ | ||
| name: "connect", | ||
| op: "db", | ||
| attributes: { ...connectionAttributes(host, port), [DB_STATEMENT]: "connect" } | ||
| }); | ||
| }, | ||
| { requiresParentSpan: true } | ||
| ); | ||
| }); | ||
@@ -85,0 +85,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"ioredis.js","sources":["../../../../src/integrations/tracing-channel/ioredis.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-deprecated -- we intentionally emit the OLD db/net semconv\n to match `@opentelemetry/instrumentation-ioredis` (and Sentry's `inferDbSpanData`, which keys off\n `db.statement`). TODO(v11): switch to the non-deprecated `db.system.name`/`db.query.text`/\n `server.address`/`server.port` conventions and drop this disable. */\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { DB_STATEMENT, DB_SYSTEM, NET_PEER_NAME, NET_PEER_PORT } from '@sentry/conventions/attributes';\nimport type { IntegrationFn, Span } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n getActiveSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { defaultDbStatementSerializer } from '../../redis/redis-statement-serializer';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Distinct from the OTel `Redis` integration, which is composite (node-redis +\n// ioredis + the >=5.11.0 diagnostics_channel subscriber) and stays in the set;\n// only its ioredis monkey-patch is gated off in the node SDK when this is active.\nconst INTEGRATION_NAME = 'IORedis' as const;\n\nconst ORIGIN = 'auto.db.orchestrion.redis';\n\n// todo(v11): Let's drop this as this is already covered with host and port\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\n\n/** Mirrors `@opentelemetry/instrumentation-ioredis`' response hook. Not called for failed commands. */\nexport type IORedisResponseHook = (span: Span, command: string, args: Array<string | Buffer>, result: unknown) => void;\n\nexport interface IORedisChannelIntegrationOptions {\n responseHook?: IORedisResponseHook;\n}\n\n/** Structural type for the command object ioredis passes to `sendCommand`. */\ninterface RedisCommand {\n name: string;\n args: Array<string | Buffer>;\n}\n\ninterface RedisClientLike {\n options?: { host?: string; port?: number };\n}\n\ninterface IORedisCommandContext {\n arguments?: unknown[];\n self?: RedisClientLike;\n result?: unknown;\n error?: unknown;\n}\n\ntype IORedisConnectContext = Omit<IORedisCommandContext, 'arguments'>;\n\nfunction getConnectionOptions(self: RedisClientLike | undefined): { host?: string; port?: number } {\n return { host: self?.options?.host, port: self?.options?.port };\n}\n\nfunction connectionAttributes(host: string | undefined, port: number | undefined): Record<string, unknown> {\n return {\n [DB_SYSTEM]: 'redis',\n [ATTR_DB_CONNECTION_STRING]: `redis://${host}:${port}`,\n [NET_PEER_NAME]: host,\n [NET_PEER_PORT]: port,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n };\n}\n\nconst _ioredisChannelIntegration = ((options: IORedisChannelIntegrationOptions = {}) => {\n const responseHook = options.responseHook;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD &&\n debug.log(`[orchestrion:ioredis] subscribing to \"${CHANNELS.IOREDIS_COMMAND}\"/\"${CHANNELS.IOREDIS_CONNECT}\"`);\n\n const commandChannel = diagnosticsChannel.tracingChannel<IORedisCommandContext, IORedisCommandContext>(\n CHANNELS.IOREDIS_COMMAND,\n );\n const connectChannel = diagnosticsChannel.tracingChannel<IORedisConnectContext, IORedisConnectContext>(\n CHANNELS.IOREDIS_CONNECT,\n );\n\n // `bindTracingChannelToSpan` uses `bindStore`, which needs the async-context\n // binding that `initOpenTelemetry()` registers after integration `setupOnce` —\n // defer until it's available (matches the native redis diagnostics-channel subscriber).\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan(\n commandChannel,\n data => {\n // ioredis' `requireParentSpan` default: only create a span under an active span.\n if (!getActiveSpan()) {\n return undefined;\n }\n const command = data.arguments?.[0] as RedisCommand | undefined;\n if (!command || typeof command !== 'object') {\n return undefined;\n }\n const { host, port } = getConnectionOptions(data.self);\n const statement = defaultDbStatementSerializer(command.name, command.args ?? []);\n return startInactiveSpan({\n name: statement,\n op: 'db',\n attributes: { ...connectionAttributes(host, port), [DB_STATEMENT]: statement },\n });\n },\n {\n beforeSpanEnd(span, data) {\n if ('error' in data || !responseHook) {\n return;\n }\n const command = data.arguments?.[0] as RedisCommand | undefined;\n if (command) {\n runResponseHook(responseHook, span, command, data.result);\n }\n },\n },\n );\n\n bindTracingChannelToSpan(connectChannel, data => {\n if (!getActiveSpan()) {\n return undefined;\n }\n const { host, port } = getConnectionOptions(data.self);\n return startInactiveSpan({\n name: 'connect',\n op: 'db',\n attributes: { ...connectionAttributes(host, port), [DB_STATEMENT]: 'connect' },\n });\n });\n });\n },\n };\n}) satisfies IntegrationFn;\n\nfunction runResponseHook(hook: IORedisResponseHook, span: Span, command: RedisCommand, result: unknown): void {\n try {\n hook(span, command.name, command.args, result);\n } catch {\n // never let a user-provided response hook break instrumentation\n }\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven ioredis integration. Subscribes to\n * `orchestrion:ioredis:command` / `:connect` (injected into ioredis' `<5.11.0`\n * `sendCommand`/`connect`) and creates db spans matching\n * `@opentelemetry/instrumentation-ioredis`. Requires the orchestrion runtime hook\n * or bundler plugin.\n */\nexport const ioredisChannelIntegration = defineIntegration(_ioredisChannelIntegration);\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"ioredis.js","sources":["../../../../src/integrations/tracing-channel/ioredis.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-deprecated -- we intentionally emit the OLD db/net semconv\n to match `@opentelemetry/instrumentation-ioredis` (and Sentry's `inferDbSpanData`, which keys off\n `db.statement`). TODO(v11): switch to the non-deprecated `db.system.name`/`db.query.text`/\n `server.address`/`server.port` conventions and drop this disable. */\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport { DB_STATEMENT, DB_SYSTEM, NET_PEER_NAME, NET_PEER_PORT } from '@sentry/conventions/attributes';\nimport type { IntegrationFn, Span } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { defaultDbStatementSerializer } from '../../redis/redis-statement-serializer';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Distinct from the OTel `Redis` integration, which is composite (node-redis +\n// ioredis + the >=5.11.0 diagnostics_channel subscriber) and stays in the set;\n// only its ioredis monkey-patch is gated off in the node SDK when this is active.\nconst INTEGRATION_NAME = 'IORedis' as const;\n\nconst ORIGIN = 'auto.db.orchestrion.redis';\n\n// todo(v11): Let's drop this as this is already covered with host and port\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\n\n/** Mirrors `@opentelemetry/instrumentation-ioredis`' response hook. Not called for failed commands. */\nexport type IORedisResponseHook = (span: Span, command: string, args: Array<string | Buffer>, result: unknown) => void;\n\nexport interface IORedisChannelIntegrationOptions {\n responseHook?: IORedisResponseHook;\n}\n\n/** Structural type for the command object ioredis passes to `sendCommand`. */\ninterface RedisCommand {\n name: string;\n args: Array<string | Buffer>;\n}\n\ninterface RedisClientLike {\n options?: { host?: string; port?: number };\n}\n\ninterface IORedisCommandContext {\n arguments?: unknown[];\n self?: RedisClientLike;\n result?: unknown;\n error?: unknown;\n}\n\ntype IORedisConnectContext = Omit<IORedisCommandContext, 'arguments'>;\n\nfunction getConnectionOptions(self: RedisClientLike | undefined): { host?: string; port?: number } {\n return { host: self?.options?.host, port: self?.options?.port };\n}\n\nfunction connectionAttributes(host: string | undefined, port: number | undefined): Record<string, unknown> {\n return {\n [DB_SYSTEM]: 'redis',\n [ATTR_DB_CONNECTION_STRING]: `redis://${host}:${port}`,\n [NET_PEER_NAME]: host,\n [NET_PEER_PORT]: port,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n };\n}\n\nconst _ioredisChannelIntegration = ((options: IORedisChannelIntegrationOptions = {}) => {\n const responseHook = options.responseHook;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD &&\n debug.log(`[orchestrion:ioredis] subscribing to \"${CHANNELS.IOREDIS_COMMAND}\"/\"${CHANNELS.IOREDIS_CONNECT}\"`);\n\n const commandChannel = diagnosticsChannel.tracingChannel<IORedisCommandContext, IORedisCommandContext>(\n CHANNELS.IOREDIS_COMMAND,\n );\n const connectChannel = diagnosticsChannel.tracingChannel<IORedisConnectContext, IORedisConnectContext>(\n CHANNELS.IOREDIS_CONNECT,\n );\n\n // `bindTracingChannelToSpan` uses `bindStore`, which needs the async-context\n // binding that `initOpenTelemetry()` registers after integration `setupOnce` —\n // defer until it's available (matches the native redis diagnostics-channel subscriber).\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan(\n commandChannel,\n data => {\n const command = data.arguments?.[0] as RedisCommand | undefined;\n if (!command || typeof command !== 'object') {\n return undefined;\n }\n const { host, port } = getConnectionOptions(data.self);\n const statement = defaultDbStatementSerializer(command.name, command.args ?? []);\n return startInactiveSpan({\n name: statement,\n op: 'db',\n attributes: { ...connectionAttributes(host, port), [DB_STATEMENT]: statement },\n });\n },\n {\n // ioredis' `requireParentSpan` default: only create a span under an active span.\n requiresParentSpan: true,\n beforeSpanEnd(span, data) {\n if ('error' in data || !responseHook) {\n return;\n }\n const command = data.arguments?.[0] as RedisCommand | undefined;\n if (command) {\n runResponseHook(responseHook, span, command, data.result);\n }\n },\n },\n );\n\n bindTracingChannelToSpan(\n connectChannel,\n data => {\n const { host, port } = getConnectionOptions(data.self);\n return startInactiveSpan({\n name: 'connect',\n op: 'db',\n attributes: { ...connectionAttributes(host, port), [DB_STATEMENT]: 'connect' },\n });\n },\n { requiresParentSpan: true },\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\nfunction runResponseHook(hook: IORedisResponseHook, span: Span, command: RedisCommand, result: unknown): void {\n try {\n hook(span, command.name, command.args, result);\n } catch {\n // never let a user-provided response hook break instrumentation\n }\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven ioredis integration. Subscribes to\n * `orchestrion:ioredis:command` / `:connect` (injected into ioredis' `<5.11.0`\n * `sendCommand`/`connect`) and creates db spans matching\n * `@opentelemetry/instrumentation-ioredis`. Requires the orchestrion runtime hook\n * or bundler plugin.\n */\nexport const ioredisChannelIntegration = defineIntegration(_ioredisChannelIntegration);\n"],"names":[],"mappings":"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"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { defineIntegration, debug, waitForTracingChannelBinding, getCurrentScope, startInactiveSpan, SPAN_KIND, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, bindScopeToEmitter } from '@sentry/core'; | ||
| import { defineIntegration, debug, waitForTracingChannelBinding, bindScopeToEmitter, getCurrentScope, startInactiveSpan, SPAN_KIND, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, isObjectLike } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
@@ -77,3 +77,3 @@ import { CHANNELS } from '../../orchestrion/channels.js'; | ||
| } | ||
| if (firstArg && typeof firstArg === "object" && "sql" in firstArg) { | ||
| if (isObjectLike(firstArg) && "sql" in firstArg) { | ||
| const sql = firstArg.sql; | ||
@@ -80,0 +80,0 @@ return typeof sql === "string" ? sql : void 0; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"mysql.js","sources":["../../../../src/integrations/tracing-channel/mysql.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope } from '@sentry/core';\nimport {\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, OTel 'Mysql' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Mysql' as const;\n\n// OTel \"OLD\" db/net semantic-conventions, inlined to keep this integration free of OTel deps. Matches\n// `@opentelemetry/instrumentation-mysql`'s default and the SDK's `inferDbSpanData` (which renames spans\n// off `db.statement`).\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\n\n/**\n * The shape orchestrion's transform attaches to the tracing-channel `context` object. Documented here\n * rather than imported because orchestrion's runtime doesn't export it.\n */\ninterface MysqlQueryChannelContext {\n // The live args array passed to the wrapped `connection.query` call; `arguments[0]` is the SQL.\n arguments: unknown[];\n self?: MysqlConnection;\n moduleVersion?: string;\n result?: unknown;\n error?: unknown;\n // The caller's scope, captured at `start` and replayed onto the streamed `Query` emitter (see below).\n _sentryCallerScope?: Scope;\n}\n\ninterface MysqlConnectionConfig {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n // Pool connections nest the real config one level deeper.\n connectionConfig?: MysqlConnectionConfig;\n}\n\ninterface MysqlConnection {\n config?: MysqlConnectionConfig;\n}\n\nconst _mysqlChannelIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD && debug.log(`[orchestrion:mysql] subscribing to channel \"${CHANNELS.MYSQL_QUERY}\"`);\n\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<MysqlQueryChannelContext>(CHANNELS.MYSQL_QUERY),\n data => {\n const sql = extractSql(data.arguments[0]);\n const { host, port, database, user } = getConnectionConfig(data.self);\n const portNumber = typeof port === 'string' ? parseInt(port, 10) : port;\n const portIsNumber = typeof portNumber === 'number' && !isNaN(portNumber);\n\n // For the streamed path: mysql emits the `Query` emitter's events from its socket data\n // handler with the caller's context lost. `deferSpanEnd` replays this scope onto the emitter.\n data._sentryCallerScope = getCurrentScope();\n\n return startInactiveSpan({\n name: sql ?? 'mysql.query',\n kind: SPAN_KIND.CLIENT,\n op: 'db',\n attributes: {\n [ATTR_DB_SYSTEM]: 'mysql',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.orchestrion.mysql',\n [ATTR_DB_CONNECTION_STRING]: getJDBCString(host, portIsNumber ? portNumber : undefined, database),\n ...(database ? { [ATTR_DB_NAME]: database } : {}),\n ...(user ? { [ATTR_DB_USER]: user } : {}),\n ...(sql ? { [ATTR_DB_STATEMENT]: sql } : {}),\n ...(host ? { [ATTR_NET_PEER_NAME]: host } : {}),\n ...(portIsNumber ? { [ATTR_NET_PEER_PORT]: portNumber } : {}),\n },\n });\n },\n {\n // No-callback `query(sql)` returns a streamable `Query` emitter as `result`; it settles on the\n // emitter's `'end'`/`'error'`, not the channel, so defer ending to those.\n deferSpanEnd({ data, end }) {\n const result = data.result;\n if (!result || typeof result !== 'object' || !hasOnMethod(result)) {\n return false;\n }\n\n // Replay the caller's scope so user listeners on the emitter nest under it, not a new trace.\n const callerScope = data._sentryCallerScope;\n if (callerScope) {\n bindScopeToEmitter(result, callerScope);\n }\n\n result.on('error', err => end(err));\n result.on('end', () => end());\n\n return true;\n },\n },\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\nfunction extractSql(firstArg: unknown): string | undefined {\n if (typeof firstArg === 'string') {\n return firstArg;\n }\n if (firstArg && typeof firstArg === 'object' && 'sql' in firstArg) {\n const sql = (firstArg as { sql?: unknown }).sql;\n return typeof sql === 'string' ? sql : undefined;\n }\n return undefined;\n}\n\nfunction getConnectionConfig(connection: MysqlConnection | undefined): {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n} {\n // Pool connections nest the real config under `.connectionConfig`; single\n // connections expose it directly. Matches `@opentelemetry/instrumentation-mysql`.\n const config = connection?.config?.connectionConfig ?? connection?.config ?? {};\n return {\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n };\n}\n\nfunction getJDBCString(host: string | undefined, port: number | undefined, database: string | undefined): string {\n let s = `jdbc:mysql://${host || 'localhost'}`;\n if (typeof port === 'number') {\n s += `:${port}`;\n }\n if (database) {\n s += `/${database}`;\n }\n return s;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven mysql integration.\n *\n * Subscribes to the `orchestrion:mysql:query` diagnostics_channel that the\n * orchestrion code transform injects into `mysql/lib/Connection.js`'s\n * `Connection.prototype.query`. Requires the orchestrion runtime hook or\n * bundler plugin to be active — wire that up via `_experimentalSetupOrchestrion`.\n */\nexport const mysqlChannelIntegration = defineIntegration(_mysqlChannelIntegration);\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"mysql.js","sources":["../../../../src/integrations/tracing-channel/mysql.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope } from '@sentry/core';\nimport {\n isObjectLike,\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, OTel 'Mysql' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Mysql' as const;\n\n// OTel \"OLD\" db/net semantic-conventions, inlined to keep this integration free of OTel deps. Matches\n// `@opentelemetry/instrumentation-mysql`'s default and the SDK's `inferDbSpanData` (which renames spans\n// off `db.statement`).\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\n\n/**\n * The shape orchestrion's transform attaches to the tracing-channel `context` object. Documented here\n * rather than imported because orchestrion's runtime doesn't export it.\n */\ninterface MysqlQueryChannelContext {\n // The live args array passed to the wrapped `connection.query` call; `arguments[0]` is the SQL.\n arguments: unknown[];\n self?: MysqlConnection;\n moduleVersion?: string;\n result?: unknown;\n error?: unknown;\n // The caller's scope, captured at `start` and replayed onto the streamed `Query` emitter (see below).\n _sentryCallerScope?: Scope;\n}\n\ninterface MysqlConnectionConfig {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n // Pool connections nest the real config one level deeper.\n connectionConfig?: MysqlConnectionConfig;\n}\n\ninterface MysqlConnection {\n config?: MysqlConnectionConfig;\n}\n\nconst _mysqlChannelIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n DEBUG_BUILD && debug.log(`[orchestrion:mysql] subscribing to channel \"${CHANNELS.MYSQL_QUERY}\"`);\n\n waitForTracingChannelBinding(() => {\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<MysqlQueryChannelContext>(CHANNELS.MYSQL_QUERY),\n data => {\n const sql = extractSql(data.arguments[0]);\n const { host, port, database, user } = getConnectionConfig(data.self);\n const portNumber = typeof port === 'string' ? parseInt(port, 10) : port;\n const portIsNumber = typeof portNumber === 'number' && !isNaN(portNumber);\n\n // For the streamed path: mysql emits the `Query` emitter's events from its socket data\n // handler with the caller's context lost. `deferSpanEnd` replays this scope onto the emitter.\n data._sentryCallerScope = getCurrentScope();\n\n return startInactiveSpan({\n name: sql ?? 'mysql.query',\n kind: SPAN_KIND.CLIENT,\n op: 'db',\n attributes: {\n [ATTR_DB_SYSTEM]: 'mysql',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.orchestrion.mysql',\n [ATTR_DB_CONNECTION_STRING]: getJDBCString(host, portIsNumber ? portNumber : undefined, database),\n ...(database ? { [ATTR_DB_NAME]: database } : {}),\n ...(user ? { [ATTR_DB_USER]: user } : {}),\n ...(sql ? { [ATTR_DB_STATEMENT]: sql } : {}),\n ...(host ? { [ATTR_NET_PEER_NAME]: host } : {}),\n ...(portIsNumber ? { [ATTR_NET_PEER_PORT]: portNumber } : {}),\n },\n });\n },\n {\n // No-callback `query(sql)` returns a streamable `Query` emitter as `result`; it settles on the\n // emitter's `'end'`/`'error'`, not the channel, so defer ending to those.\n deferSpanEnd({ data, end }) {\n const result = data.result;\n if (!result || typeof result !== 'object' || !hasOnMethod(result)) {\n return false;\n }\n\n // Replay the caller's scope so user listeners on the emitter nest under it, not a new trace.\n const callerScope = data._sentryCallerScope;\n if (callerScope) {\n bindScopeToEmitter(result, callerScope);\n }\n\n result.on('error', err => end(err));\n result.on('end', () => end());\n\n return true;\n },\n },\n );\n });\n },\n };\n}) satisfies IntegrationFn;\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\nfunction extractSql(firstArg: unknown): string | undefined {\n if (typeof firstArg === 'string') {\n return firstArg;\n }\n if (isObjectLike(firstArg) && 'sql' in firstArg) {\n const sql = (firstArg as { sql?: unknown }).sql;\n return typeof sql === 'string' ? sql : undefined;\n }\n return undefined;\n}\n\nfunction getConnectionConfig(connection: MysqlConnection | undefined): {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n} {\n // Pool connections nest the real config under `.connectionConfig`; single\n // connections expose it directly. Matches `@opentelemetry/instrumentation-mysql`.\n const config = connection?.config?.connectionConfig ?? connection?.config ?? {};\n return {\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n };\n}\n\nfunction getJDBCString(host: string | undefined, port: number | undefined, database: string | undefined): string {\n let s = `jdbc:mysql://${host || 'localhost'}`;\n if (typeof port === 'number') {\n s += `:${port}`;\n }\n if (database) {\n s += `/${database}`;\n }\n return s;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven mysql integration.\n *\n * Subscribes to the `orchestrion:mysql:query` diagnostics_channel that the\n * orchestrion code transform injects into `mysql/lib/Connection.js`'s\n * `Connection.prototype.query`. Requires the orchestrion runtime hook or\n * bundler plugin to be active — wire that up via `_experimentalSetupOrchestrion`.\n */\nexport const mysqlChannelIntegration = defineIntegration(_mysqlChannelIntegration);\n"],"names":[],"mappings":"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"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { defineIntegration, waitForTracingChannelBinding, debug, _INTERNAL_shouldSkipAiProviderWrapping, resolveAIRecordingOptions, shouldEnableTruncation, extractOpenAiRequestAttributes, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan, addOpenAiRequestAttributes, addOpenAiResponseAttributes, instrumentOpenAiStream } from '@sentry/core'; | ||
| import { defineIntegration, waitForTracingChannelBinding, debug, addOpenAiResponseAttributes, resolveAIRecordingOptions, instrumentOpenAiStream, _INTERNAL_shouldSkipAiProviderWrapping, shouldEnableTruncation, extractOpenAiRequestAttributes, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan, addOpenAiRequestAttributes } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
@@ -4,0 +4,0 @@ import { CHANNELS } from '../../orchestrion/channels.js'; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"openai.js","sources":["../../../../src/integrations/tracing-channel/openai.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, OpenAiOptions, Span, SpanAttributeValue } from '@sentry/core';\nimport {\n _INTERNAL_shouldSkipAiProviderWrapping,\n addOpenAiRequestAttributes,\n addOpenAiResponseAttributes,\n debug,\n defineIntegration,\n extractOpenAiRequestAttributes,\n instrumentOpenAiStream,\n resolveAIRecordingOptions,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Same name as the OTel integration by design: when enabled, the OTel 'OpenAI'\n// integration is dropped from the default set (see the Node opt-in loader).\nconst INTEGRATION_NAME = 'OpenAI' as const;\n\n// Distinct from the proxy's `auto.ai.openai` so spans from the orchestrion path\n// are attributable separately from the OTel/proxy one.\nconst ORIGIN = 'auto.ai.orchestrion.openai';\n\n// Each instrumented `create` method maps to the gen_ai operation its span reports.\nconst INSTRUMENTED_CHANNELS = [\n { channel: CHANNELS.OPENAI_CHAT, operation: 'chat' },\n { channel: CHANNELS.OPENAI_RESPONSES, operation: 'chat' },\n { channel: CHANNELS.OPENAI_EMBEDDINGS, operation: 'embeddings' },\n { channel: CHANNELS.OPENAI_CONVERSATIONS, operation: 'chat' },\n] as const;\n\n/**\n * The context object orchestrion shares across the tracing-channel lifecycle hooks: `arguments` is the\n * live args array passed to `Completions.create(body, options)`, and Node's `tracingChannel` attaches\n * `result` when the returned promise settles.\n */\ninterface OpenAiChatChannelContext {\n arguments: unknown[];\n result?: unknown;\n}\n\nlet subscribed = false;\n\nconst _openaiChannelIntegration = ((options: OpenAiOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19, and a second `init()` would double-subscribe.\n if (!diagnosticsChannel.tracingChannel || subscribed) {\n return;\n }\n subscribed = true;\n\n // `bindTracingChannelToSpan` needs the async-context binding that `initOpenTelemetry()` registers\n // after `setupOnce` runs, so wait for it before subscribing.\n waitForTracingChannelBinding(() => {\n for (const { channel, operation } of INSTRUMENTED_CHANNELS) {\n DEBUG_BUILD && debug.log(`[orchestrion:openai] subscribing to channel \"${channel}\"`);\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<OpenAiChatChannelContext>(channel),\n data => createGenAiSpan(data, operation, options),\n {\n beforeSpanEnd: (span, data) => {\n addOpenAiResponseAttributes(span, data.result, resolveAIRecordingOptions(options).recordOutputs);\n },\n // Streaming: the result is a `Stream` consumed later, so instrument it and let it end the span.\n deferSpanEnd: ({ span, data }) => wrapStreamResult(span, data, options),\n },\n );\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Build the span for an instrumented `create` call.\n * Returning `undefined` opts the payload out so no span is opened.\n */\nfunction createGenAiSpan(data: OpenAiChatChannelContext, operation: string, options: OpenAiOptions): Span | undefined {\n // langchain drives the openai SDK itself and instruments at its own layer; skip here to avoid double spans.\n if (_INTERNAL_shouldSkipAiProviderWrapping(INTEGRATION_NAME)) {\n return undefined;\n }\n\n const args = data.arguments ?? [];\n const params = args[0] as Record<string, unknown> | undefined;\n\n const { recordInputs } = resolveAIRecordingOptions(options);\n const enableTruncation = shouldEnableTruncation(options.enableTruncation);\n\n const attributes = extractOpenAiRequestAttributes(args, operation);\n attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN;\n const model = (params?.model as string) || 'unknown';\n\n const span = startInactiveSpan({\n name: `${operation} ${model}`,\n op: `gen_ai.${operation}`,\n attributes: attributes as Record<string, SpanAttributeValue>,\n });\n\n if (recordInputs && params) {\n addOpenAiRequestAttributes(span, params, operation, enableTruncation);\n }\n\n return span;\n}\n\ntype AsyncIterableStream = { [Symbol.asyncIterator]: () => AsyncIterator<unknown> };\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterableStream {\n return !!value && typeof (value as AsyncIterableStream)[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * For a streaming `create({ stream: true })` the result is a `Stream` the caller consumes later. We can't\n * swap what `create` returns, but the `Stream` in `data.result` is the same instance the caller holds and\n * `asyncEnd` fires before the caller iterates — so we patch its async iterator in place to run through\n * `instrumentOpenAiStream`, which accumulates the streamed attributes and ends the span when iteration finishes.\n * Only a streaming call resolves to an async-iterable, so that check alone distinguishes it. Returns `true`\n * to hand span-ending ownership to `instrumentOpenAiStream`; `false` for non-streaming/errored results, which end\n * via the normal `beforeSpanEnd` path.\n */\nfunction wrapStreamResult(span: Span, data: OpenAiChatChannelContext, options: OpenAiOptions): boolean {\n const result = data.result;\n if (!isAsyncIterable(result)) {\n return false;\n }\n\n const { recordOutputs } = resolveAIRecordingOptions(options);\n const iterate = result[Symbol.asyncIterator].bind(result);\n const instrumented = instrumentOpenAiStream({ [Symbol.asyncIterator]: iterate }, span, recordOutputs ?? false);\n result[Symbol.asyncIterator] = () => instrumented;\n\n return true;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven OpenAI integration. Subscribes to the `orchestrion:openai:*`\n * diagnostics_channels injected into `openai`'s `create` methods (chat completions, responses, embeddings,\n * conversations), so it requires the orchestrion runtime hook or bundler plugin.\n */\nexport const openaiChannelIntegration = defineIntegration(_openaiChannelIntegration);\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"openai.js","sources":["../../../../src/integrations/tracing-channel/openai.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, OpenAiOptions, Span, SpanAttributeValue } from '@sentry/core';\nimport {\n _INTERNAL_shouldSkipAiProviderWrapping,\n addOpenAiRequestAttributes,\n addOpenAiResponseAttributes,\n debug,\n defineIntegration,\n extractOpenAiRequestAttributes,\n instrumentOpenAiStream,\n resolveAIRecordingOptions,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// Same name as the OTel integration by design: when enabled, the OTel 'OpenAI'\n// integration is dropped from the default set (see the Node opt-in loader).\nconst INTEGRATION_NAME = 'OpenAI' as const;\n\n// Distinct from the proxy's `auto.ai.openai` so spans from the orchestrion path\n// are attributable separately from the OTel/proxy one.\nconst ORIGIN = 'auto.ai.orchestrion.openai';\n\n// Each instrumented `create` method maps to the gen_ai operation its span reports.\nconst INSTRUMENTED_CHANNELS = [\n { channel: CHANNELS.OPENAI_CHAT, operation: 'chat' },\n { channel: CHANNELS.OPENAI_RESPONSES, operation: 'chat' },\n { channel: CHANNELS.OPENAI_EMBEDDINGS, operation: 'embeddings' },\n { channel: CHANNELS.OPENAI_CONVERSATIONS, operation: 'chat' },\n] as const;\n\n/**\n * The context object orchestrion shares across the tracing-channel lifecycle hooks: `arguments` is the\n * live args array passed to `Completions.create(body, options)`, and Node's `tracingChannel` attaches\n * `result` when the returned promise settles.\n */\ninterface OpenAiChatChannelContext {\n arguments: unknown[];\n result?: unknown;\n}\n\nlet subscribed = false;\n\nconst _openaiChannelIntegration = ((options: OpenAiOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19, and a second `init()` would double-subscribe.\n if (!diagnosticsChannel.tracingChannel || subscribed) {\n return;\n }\n subscribed = true;\n\n // `bindTracingChannelToSpan` needs the async-context binding that `initOpenTelemetry()` registers\n // after `setupOnce` runs, so wait for it before subscribing.\n waitForTracingChannelBinding(() => {\n for (const { channel, operation } of INSTRUMENTED_CHANNELS) {\n DEBUG_BUILD && debug.log(`[orchestrion:openai] subscribing to channel \"${channel}\"`);\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<OpenAiChatChannelContext>(channel),\n data => createGenAiSpan(data, operation, options),\n {\n beforeSpanEnd: (span, data) => {\n addOpenAiResponseAttributes(span, data.result, resolveAIRecordingOptions(options).recordOutputs);\n },\n // Streaming: the result is a `Stream` consumed later, so instrument it and let it end the span.\n deferSpanEnd: ({ span, data }) => wrapStreamResult(span, data, options),\n },\n );\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Build the span for an instrumented `create` call.\n * Returning `undefined` opts the payload out so no span is opened.\n */\nfunction createGenAiSpan(data: OpenAiChatChannelContext, operation: string, options: OpenAiOptions): Span | undefined {\n // When another provider (e.g. LangChain) is driving the SDK, it records the spans itself and marks this\n // provider as skipped; skip here to avoid double spans.\n if (_INTERNAL_shouldSkipAiProviderWrapping(INTEGRATION_NAME)) {\n return undefined;\n }\n\n const args = data.arguments ?? [];\n const params = args[0] as Record<string, unknown> | undefined;\n\n const { recordInputs } = resolveAIRecordingOptions(options);\n const enableTruncation = shouldEnableTruncation(options.enableTruncation);\n\n const attributes = extractOpenAiRequestAttributes(args, operation);\n attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN;\n const model = (params?.model as string) || 'unknown';\n\n const span = startInactiveSpan({\n name: `${operation} ${model}`,\n op: `gen_ai.${operation}`,\n attributes: attributes as Record<string, SpanAttributeValue>,\n });\n\n if (recordInputs && params) {\n addOpenAiRequestAttributes(span, params, operation, enableTruncation);\n }\n\n return span;\n}\n\ntype AsyncIterableStream = { [Symbol.asyncIterator]: () => AsyncIterator<unknown> };\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterableStream {\n return !!value && typeof (value as AsyncIterableStream)[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * For a streaming `create({ stream: true })` the result is a `Stream` the caller consumes later. We can't\n * swap what `create` returns, but the `Stream` in `data.result` is the same instance the caller holds and\n * `asyncEnd` fires before the caller iterates — so we patch its async iterator in place to run through\n * `instrumentOpenAiStream`, which accumulates the streamed attributes and ends the span when iteration finishes.\n * Only a streaming call resolves to an async-iterable, so that check alone distinguishes it. Returns `true`\n * to hand span-ending ownership to `instrumentOpenAiStream`; `false` for non-streaming/errored results, which end\n * via the normal `beforeSpanEnd` path.\n */\nfunction wrapStreamResult(span: Span, data: OpenAiChatChannelContext, options: OpenAiOptions): boolean {\n const result = data.result;\n if (!isAsyncIterable(result)) {\n return false;\n }\n\n const { recordOutputs } = resolveAIRecordingOptions(options);\n const iterate = result[Symbol.asyncIterator].bind(result);\n const instrumented = instrumentOpenAiStream({ [Symbol.asyncIterator]: iterate }, span, recordOutputs ?? false);\n result[Symbol.asyncIterator] = () => instrumented;\n\n return true;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven OpenAI integration. Subscribes to the `orchestrion:openai:*`\n * diagnostics_channels injected into `openai`'s `create` methods (chat completions, responses, embeddings,\n * conversations), so it requires the orchestrion runtime hook or bundler plugin.\n */\nexport const openaiChannelIntegration = defineIntegration(_openaiChannelIntegration);\n"],"names":[],"mappings":"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"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { defineIntegration, waitForTracingChannelBinding, debug, getActiveSpan, getCurrentScope, startInactiveSpan, SPAN_KIND, bindScopeToEmitter, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
| import { defineIntegration, waitForTracingChannelBinding, debug, bindScopeToEmitter, getCurrentScope, startInactiveSpan, SPAN_KIND, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, isObjectLike } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
@@ -45,5 +45,2 @@ import { CHANNELS } from '../../orchestrion/channels.js'; | ||
| (data) => { | ||
| if (!getActiveSpan()) { | ||
| return void 0; | ||
| } | ||
| data._sentryCallerScope = getCurrentScope(); | ||
@@ -58,2 +55,5 @@ return startInactiveSpan({ ...getSpanOptions(data), kind: SPAN_KIND.CLIENT }); | ||
| deferStreamedResult ? { | ||
| // Only instrument under an active span, leaving the context untouched otherwise | ||
| // (e.g. connects issued during app startup). | ||
| requiresParentSpan: true, | ||
| // Streamable `Submittable` (e.g. `client.query(new Query())`) | ||
@@ -78,3 +78,3 @@ // returns an emitter that orchestrion stores on `ctx.result` while | ||
| } | ||
| } : void 0 | ||
| } : { requiresParentSpan: true } | ||
| ); | ||
@@ -113,3 +113,3 @@ } | ||
| } | ||
| if (arg0 && typeof arg0 === "object" && typeof arg0.text === "string") { | ||
| if (isObjectLike(arg0) && typeof arg0.text === "string") { | ||
| const obj = arg0; | ||
@@ -116,0 +116,0 @@ return { text: obj.text, name: obj.name }; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"postgres.js","sources":["../../../../src/integrations/tracing-channel/postgres.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope, SpanAttributes } from '@sentry/core';\nimport {\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getActiveSpan,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, the OTel 'Postgres' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Postgres' as const;\n\n// Only the query span carries an origin (the connect/pool-connect spans don't,\n// so they default to 'manual').\nconst ORIGIN = 'auto.db.orchestrion.postgres';\n\n// OpenTelemetry \"OLD\" db/net semantic-conventions, inlined to keep this\n// integration free of `@opentelemetry/*` deps.\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\nconst ATTR_PG_PLAN = 'db.postgresql.plan';\nconst ATTR_PG_IDLE_TIMEOUT = 'db.postgresql.idle.timeout.millis';\nconst ATTR_PG_MAX_CLIENT = 'db.postgresql.max.client';\nconst DB_SYSTEM_POSTGRESQL = 'postgresql';\n\n// We set `op: 'db'` and the SQL description directly here (same as mysql\n// orchestrion) rather than relying on the OTel pipeline's `inferDbSpanData`\n// processor, which only runs in the node SDK, so setting them here is what\n// makes the spans correct on the other runtimes\n//\n// The user-visible span is identical to OTel: query spans are named after\n// `db.statement`; connect/pool-connect spans keep these names.\nconst SPAN_QUERY_FALLBACK = 'pg.query';\nconst SPAN_CONNECT = 'pg.connect';\nconst SPAN_POOL_CONNECT = 'pg-pool.connect';\n\n/**\n * The shape orchestrion's transform attaches to the tracing-channel `context`. Documented here rather\n * than imported because orchestrion's runtime doesn't export it.\n */\ninterface PgChannelContext {\n // The live args array passed to the wrapped `query`/`connect` call.\n arguments: unknown[];\n self?: unknown;\n result?: unknown;\n error?: unknown;\n // The caller's scope, captured at `start` and replayed onto a streamed `Submittable` emitter (see below).\n _sentryCallerScope?: Scope;\n}\n\ninterface PgConnectionParams {\n database?: string;\n host?: string;\n port?: number;\n user?: string;\n connectionString?: string;\n}\n\ninterface PgPoolOptions extends PgConnectionParams {\n idleTimeoutMillis?: number;\n // pg-pool stores the max pool size as `max` (defaulting it to 10 in its\n // constructor). The OTel pg instrumentation reads a `maxClient` field that\n // pg-pool never sets, so its `db.postgresql.max.client` attribute is always\n // dropped; we read the real `max` so the attribute is actually populated.\n max?: number;\n}\n\nconst _postgresChannelIntegration = ((options: { ignoreConnectSpans?: boolean } = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n // Query spans: `pg`/native `Client.prototype.query`. Only this channel can return a streamable\n // `Submittable` result, so it's the only one that defers span-ending to the emitter (see below).\n subscribeQueryLikeChannel(CHANNELS.PG_QUERY, querySpanOptions, { deferStreamedResult: true });\n\n // Connect spans, gated by `ignoreConnectSpans` (same as OTel pg).\n // `Client.prototype.connect` (pg + native)\n // and `Pool.prototype.connect` (pg-pool).\n if (!options.ignoreConnectSpans) {\n subscribeQueryLikeChannel(CHANNELS.PG_CONNECT, connectSpanOptions);\n subscribeQueryLikeChannel(CHANNELS.PGPOOL_CONNECT, poolConnectSpanOptions);\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Subscribe to a pg tracing-channel and manage a span across its lifecycle.\n * Shared by the query/connect/pool-connect channels. They differ only in how\n * the span's name + attributes are built (`getSpanOptions`), and whether the\n * result can be a streamable emitter (`deferStreamedResult`, query-only).\n */\nfunction subscribeQueryLikeChannel(\n channelName: string,\n getSpanOptions: (ctx: PgChannelContext) => { name: string; op: string; attributes: SpanAttributes },\n { deferStreamedResult = false }: { deferStreamedResult?: boolean } = {},\n): void {\n DEBUG_BUILD && debug.log(`[orchestrion:pg] subscribing to channel \"${channelName}\"`);\n\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<PgChannelContext>(channelName),\n data => {\n // Only instrument when there's an active span; returning `undefined` opts this call out entirely,\n // leaving the active context untouched (e.g. connects issued during app startup).\n if (!getActiveSpan()) {\n return undefined;\n }\n\n // Capture the caller's scope while still synchronously inside the call, for the streamed path:\n // pg dispatches a `Submittable` emitter's events outside the original async scope, so `deferSpanEnd`\n // replays this scope onto that emitter.\n data._sentryCallerScope = getCurrentScope();\n\n // `kind: CLIENT` mirrors the OTel pg instrumentation, so the emitted\n // `otel.kind` matches across the OTel and diagnostics-channel paths.\n return startInactiveSpan({ ...getSpanOptions(data), kind: SPAN_KIND.CLIENT });\n },\n // `connect`/`pool-connect` resolve with a persistent `Client` (itself an\n // `EventEmitter`), which is NOT a streamed result. Deferring their span\n // to that emitter's `'end'`/`'error'` would keep it open for the whole\n // connection lifetime, so it never ends in time and is dropped. Only\n // `query` can return a streamable `Submittable`, so only it defers.\n deferStreamedResult\n ? {\n // Streamable `Submittable` (e.g. `client.query(new Query())`)\n // returns an emitter that orchestrion stores on `ctx.result` while\n // firing no async events; the query isn't done until the emitter\n // emits `'end'`/`'error'`. Defer ending to those events for that\n // path; the callback, promise, and sync-throw paths carry no\n // emitter, so the helper ends the span as usual.\n deferSpanEnd({ data, end }) {\n const result = data.result;\n if (!result || typeof result !== 'object' || !hasOnMethod(result)) {\n return false;\n }\n\n // Replay the caller's scope onto the emitter so listeners theu\n // user attaches after the call returns (and any spans they start)\n // nest under the caller, not a fresh root trace.\n const callerScope = data._sentryCallerScope;\n if (callerScope) {\n bindScopeToEmitter(result, callerScope);\n }\n\n result.on('error', err => end(err));\n result.on('end', () => end());\n\n return true;\n },\n }\n : undefined,\n );\n}\n\nfunction querySpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const params = (ctx.self as { connectionParameters?: PgConnectionParams } | undefined)?.connectionParameters ?? {};\n const queryConfig = extractQueryConfig(ctx.arguments);\n return {\n // The description is the SQL statement\n name: queryConfig?.text ?? SPAN_QUERY_FALLBACK,\n op: 'db',\n attributes: {\n ...getConnectionAttributes(params),\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [ATTR_DB_STATEMENT]: queryConfig?.text || undefined,\n [ATTR_PG_PLAN]: typeof queryConfig?.name === 'string' ? queryConfig.name : undefined,\n },\n };\n}\n\nfunction connectSpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const params = (ctx.self as { connectionParameters?: PgConnectionParams } | undefined)?.connectionParameters ?? {};\n // No origin set -> defaults to 'manual'\n return { name: SPAN_CONNECT, op: 'db', attributes: getConnectionAttributes(params) };\n}\n\nfunction poolConnectSpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const opts = (ctx.self as { options?: PgPoolOptions } | undefined)?.options ?? {};\n return { name: SPAN_POOL_CONNECT, op: 'db', attributes: getPoolConnectionAttributes(opts) };\n}\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\n// `client.query(text, cb?)`, `client.query(text, values, cb?)`, and\n// `client.query(configObj, cb?)` are all valid; normalize to `{ text, name }`\n// (the only fields the span needs). Returns undefined for invalid args.\nfunction extractQueryConfig(args: unknown[]): { text: string; name?: unknown } | undefined {\n const arg0 = args[0];\n if (typeof arg0 === 'string') {\n return { text: arg0 };\n }\n if (arg0 && typeof arg0 === 'object' && typeof (arg0 as { text?: unknown }).text === 'string') {\n const obj = arg0 as { text: string; name?: unknown };\n return { text: obj.text, name: obj.name };\n }\n return undefined;\n}\n\nfunction getConnectionAttributes(params: PgConnectionParams): SpanAttributes {\n return {\n [ATTR_DB_SYSTEM]: DB_SYSTEM_POSTGRESQL,\n [ATTR_DB_CONNECTION_STRING]: getConnectionString(params),\n [ATTR_DB_NAME]: params.database,\n [ATTR_DB_USER]: params.user,\n [ATTR_NET_PEER_NAME]: params.host,\n [ATTR_NET_PEER_PORT]: Number.isInteger(params.port) ? params.port : undefined,\n };\n}\n\nfunction getPoolConnectionAttributes(opts: PgPoolOptions): SpanAttributes {\n let url: URL | undefined;\n try {\n url = opts.connectionString ? new URL(opts.connectionString) : undefined;\n } catch {\n url = undefined;\n }\n const database = url?.pathname.slice(1) || opts.database;\n const host = url?.hostname || opts.host;\n // Mirror OTel's `getSemanticAttributesFromPoolConnection`: prefer the URL's\n // port, but fall back to an explicit `opts.port` when the connection string\n // omits it (`Number('')` / `Number(undefined)` -> falsy).\n const port = Number(url?.port) || (Number.isInteger(opts.port) ? opts.port : undefined);\n const user = url?.username || opts.user;\n return {\n [ATTR_DB_SYSTEM]: DB_SYSTEM_POSTGRESQL,\n [ATTR_DB_CONNECTION_STRING]: getConnectionString(opts),\n [ATTR_PG_IDLE_TIMEOUT]: opts.idleTimeoutMillis,\n [ATTR_PG_MAX_CLIENT]: opts.max,\n [ATTR_DB_NAME]: database,\n [ATTR_NET_PEER_PORT]: port,\n // these two come from a url parse and slice, can be ''\n [ATTR_NET_PEER_NAME]: host || undefined,\n [ATTR_DB_USER]: user || undefined,\n };\n}\n\n// Builds `postgresql://host:port/database`, masking credentials when a raw\n// connection string was provided.\nfunction getConnectionString(params: PgConnectionParams): string {\n if (params.connectionString) {\n try {\n const url = new URL(params.connectionString);\n url.username = '';\n url.password = '';\n return url.toString();\n } catch {\n return 'postgresql://localhost:5432/';\n }\n }\n const host = params.host || 'localhost';\n const port = params.port || 5432;\n const database = params.database || '';\n return `postgresql://${host}:${port}/${database}`;\n}\n\n/**\n * EXPERIMENTAL: orchestrion-driven `pg` (node-postgres) integration.\n *\n * Subscribes to the `orchestrion:pg:query`/`:connect` and\n * `orchestrion:pg-pool:connect` diagnostics_channels that the orchestrion code\n * transform injects into `pg`'s `Client.prototype.query`/`connect`\n * and `pg-pool`'s `Pool.prototype.connect`. Requires the orchestrion runtime\n * hook or bundler plugin to be active.\n */\nexport const postgresChannelIntegration = defineIntegration(_postgresChannelIntegration);\n"],"names":[],"mappings":";;;;;;AAmBA,MAAM,gBAAA,GAAmB,UAAA;AAIzB,MAAM,MAAA,GAAS,8BAAA;AAIf,MAAM,cAAA,GAAiB,WAAA;AACvB,MAAM,YAAA,GAAe,SAAA;AACrB,MAAM,yBAAA,GAA4B,sBAAA;AAClC,MAAM,YAAA,GAAe,SAAA;AACrB,MAAM,iBAAA,GAAoB,cAAA;AAC1B,MAAM,kBAAA,GAAqB,eAAA;AAC3B,MAAM,kBAAA,GAAqB,eAAA;AAC3B,MAAM,YAAA,GAAe,oBAAA;AACrB,MAAM,oBAAA,GAAuB,mCAAA;AAC7B,MAAM,kBAAA,GAAqB,0BAAA;AAC3B,MAAM,oBAAA,GAAuB,YAAA;AAS7B,MAAM,mBAAA,GAAsB,UAAA;AAC5B,MAAM,YAAA,GAAe,YAAA;AACrB,MAAM,iBAAA,GAAoB,iBAAA;AAiC1B,MAAM,2BAAA,IAA+B,CAAC,OAAA,GAA4C,EAAC,KAAM;AACvF,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,mBAAmB,cAAA,EAAgB;AACtC,QAAA;AAAA,MACF;AAEA,MAAA,4BAAA,CAA6B,MAAM;AAGjC,QAAA,yBAAA,CAA0B,SAAS,QAAA,EAAU,gBAAA,EAAkB,EAAE,mBAAA,EAAqB,MAAM,CAAA;AAK5F,QAAA,IAAI,CAAC,QAAQ,kBAAA,EAAoB;AAC/B,UAAA,yBAAA,CAA0B,QAAA,CAAS,YAAY,kBAAkB,CAAA;AACjE,UAAA,yBAAA,CAA0B,QAAA,CAAS,gBAAgB,sBAAsB,CAAA;AAAA,QAC3E;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAQA,SAAS,yBAAA,CACP,aACA,cAAA,EACA,EAAE,sBAAsB,KAAA,EAAM,GAAuC,EAAC,EAChE;AACN,EAAA,WAAA,IAAe,KAAA,CAAM,GAAA,CAAI,CAAA,yCAAA,EAA4C,WAAW,CAAA,CAAA,CAAG,CAAA;AAEnF,EAAA,wBAAA;AAAA,IACE,kBAAA,CAAmB,eAAiC,WAAW,CAAA;AAAA,IAC/D,CAAA,IAAA,KAAQ;AAGN,MAAA,IAAI,CAAC,eAAc,EAAG;AACpB,QAAA,OAAO,MAAA;AAAA,MACT;AAKA,MAAA,IAAA,CAAK,qBAAqB,eAAA,EAAgB;AAI1C,MAAA,OAAO,iBAAA,CAAkB,EAAE,GAAG,cAAA,CAAe,IAAI,CAAA,EAAG,IAAA,EAAM,SAAA,CAAU,MAAA,EAAQ,CAAA;AAAA,IAC9E,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,mBAAA,GACI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOE,YAAA,CAAa,EAAE,IAAA,EAAM,GAAA,EAAI,EAAG;AAC1B,QAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,QAAA,IAAI,CAAC,UAAU,OAAO,MAAA,KAAW,YAAY,CAAC,WAAA,CAAY,MAAM,CAAA,EAAG;AACjE,UAAA,OAAO,KAAA;AAAA,QACT;AAKA,QAAA,MAAM,cAAc,IAAA,CAAK,kBAAA;AACzB,QAAA,IAAI,WAAA,EAAa;AACf,UAAA,kBAAA,CAAmB,QAAQ,WAAW,CAAA;AAAA,QACxC;AAEA,QAAA,MAAA,CAAO,EAAA,CAAG,OAAA,EAAS,CAAA,GAAA,KAAO,GAAA,CAAI,GAAG,CAAC,CAAA;AAClC,QAAA,MAAA,CAAO,EAAA,CAAG,KAAA,EAAO,MAAM,GAAA,EAAK,CAAA;AAE5B,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,KACF,GACA;AAAA,GACN;AACF;AAEA,SAAS,iBAAiB,GAAA,EAAiF;AACzG,EAAA,MAAM,MAAA,GAAU,GAAA,CAAI,IAAA,EAAoE,oBAAA,IAAwB,EAAC;AACjH,EAAA,MAAM,WAAA,GAAc,kBAAA,CAAmB,GAAA,CAAI,SAAS,CAAA;AACpD,EAAA,OAAO;AAAA;AAAA,IAEL,IAAA,EAAM,aAAa,IAAA,IAAQ,mBAAA;AAAA,IAC3B,EAAA,EAAI,IAAA;AAAA,IACJ,UAAA,EAAY;AAAA,MACV,GAAG,wBAAwB,MAAM,CAAA;AAAA,MACjC,CAAC,gCAAgC,GAAG,MAAA;AAAA,MACpC,CAAC,iBAAiB,GAAG,WAAA,EAAa,IAAA,IAAQ,MAAA;AAAA,MAC1C,CAAC,YAAY,GAAG,OAAO,aAAa,IAAA,KAAS,QAAA,GAAW,YAAY,IAAA,GAAO;AAAA;AAC7E,GACF;AACF;AAEA,SAAS,mBAAmB,GAAA,EAAiF;AAC3G,EAAA,MAAM,MAAA,GAAU,GAAA,CAAI,IAAA,EAAoE,oBAAA,IAAwB,EAAC;AAEjH,EAAA,OAAO,EAAE,MAAM,YAAA,EAAc,EAAA,EAAI,MAAM,UAAA,EAAY,uBAAA,CAAwB,MAAM,CAAA,EAAE;AACrF;AAEA,SAAS,uBAAuB,GAAA,EAAiF;AAC/G,EAAA,MAAM,IAAA,GAAQ,GAAA,CAAI,IAAA,EAAkD,OAAA,IAAW,EAAC;AAChF,EAAA,OAAO,EAAE,MAAM,iBAAA,EAAmB,EAAA,EAAI,MAAM,UAAA,EAAY,2BAAA,CAA4B,IAAI,CAAA,EAAE;AAC5F;AAEA,SAAS,YAAY,GAAA,EAA2F;AAC9G,EAAA,OAAO,IAAA,IAAQ,GAAA,IAAO,OAAQ,GAAA,CAAyB,EAAA,KAAO,UAAA;AAChE;AAKA,SAAS,mBAAmB,IAAA,EAA+D;AACzF,EAAA,MAAM,IAAA,GAAO,KAAK,CAAC,CAAA;AACnB,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC5B,IAAA,OAAO,EAAE,MAAM,IAAA,EAAK;AAAA,EACtB;AACA,EAAA,IAAI,QAAQ,OAAO,IAAA,KAAS,YAAY,OAAQ,IAAA,CAA4B,SAAS,QAAA,EAAU;AAC7F,IAAA,MAAM,GAAA,GAAM,IAAA;AACZ,IAAA,OAAO,EAAE,IAAA,EAAM,GAAA,CAAI,IAAA,EAAM,IAAA,EAAM,IAAI,IAAA,EAAK;AAAA,EAC1C;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,wBAAwB,MAAA,EAA4C;AAC3E,EAAA,OAAO;AAAA,IACL,CAAC,cAAc,GAAG,oBAAA;AAAA,IAClB,CAAC,yBAAyB,GAAG,mBAAA,CAAoB,MAAM,CAAA;AAAA,IACvD,CAAC,YAAY,GAAG,MAAA,CAAO,QAAA;AAAA,IACvB,CAAC,YAAY,GAAG,MAAA,CAAO,IAAA;AAAA,IACvB,CAAC,kBAAkB,GAAG,MAAA,CAAO,IAAA;AAAA,IAC7B,CAAC,kBAAkB,GAAG,MAAA,CAAO,UAAU,MAAA,CAAO,IAAI,CAAA,GAAI,MAAA,CAAO,IAAA,GAAO;AAAA,GACtE;AACF;AAEA,SAAS,4BAA4B,IAAA,EAAqC;AACxE,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAM,KAAK,gBAAA,GAAmB,IAAI,GAAA,CAAI,IAAA,CAAK,gBAAgB,CAAA,GAAI,KAAA,CAAA;AAAA,EACjE,CAAA,CAAA,MAAQ;AACN,IAAA,GAAA,GAAM,MAAA;AAAA,EACR;AACA,EAAA,MAAM,WAAW,GAAA,EAAK,QAAA,CAAS,KAAA,CAAM,CAAC,KAAK,IAAA,CAAK,QAAA;AAChD,EAAA,MAAM,IAAA,GAAO,GAAA,EAAK,QAAA,IAAY,IAAA,CAAK,IAAA;AAInC,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,GAAA,EAAK,IAAI,CAAA,KAAM,MAAA,CAAO,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GAAI,IAAA,CAAK,IAAA,GAAO,MAAA,CAAA;AAC7E,EAAA,MAAM,IAAA,GAAO,GAAA,EAAK,QAAA,IAAY,IAAA,CAAK,IAAA;AACnC,EAAA,OAAO;AAAA,IACL,CAAC,cAAc,GAAG,oBAAA;AAAA,IAClB,CAAC,yBAAyB,GAAG,mBAAA,CAAoB,IAAI,CAAA;AAAA,IACrD,CAAC,oBAAoB,GAAG,IAAA,CAAK,iBAAA;AAAA,IAC7B,CAAC,kBAAkB,GAAG,IAAA,CAAK,GAAA;AAAA,IAC3B,CAAC,YAAY,GAAG,QAAA;AAAA,IAChB,CAAC,kBAAkB,GAAG,IAAA;AAAA;AAAA,IAEtB,CAAC,kBAAkB,GAAG,IAAA,IAAQ,MAAA;AAAA,IAC9B,CAAC,YAAY,GAAG,IAAA,IAAQ;AAAA,GAC1B;AACF;AAIA,SAAS,oBAAoB,MAAA,EAAoC;AAC/D,EAAA,IAAI,OAAO,gBAAA,EAAkB;AAC3B,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,MAAA,CAAO,gBAAgB,CAAA;AAC3C,MAAA,GAAA,CAAI,QAAA,GAAW,EAAA;AACf,MAAA,GAAA,CAAI,QAAA,GAAW,EAAA;AACf,MAAA,OAAO,IAAI,QAAA,EAAS;AAAA,IACtB,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,8BAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,MAAM,IAAA,GAAO,OAAO,IAAA,IAAQ,WAAA;AAC5B,EAAA,MAAM,IAAA,GAAO,OAAO,IAAA,IAAQ,IAAA;AAC5B,EAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,EAAA;AACpC,EAAA,OAAO,CAAA,aAAA,EAAgB,IAAI,CAAA,CAAA,EAAI,IAAI,IAAI,QAAQ,CAAA,CAAA;AACjD;AAWO,MAAM,0BAAA,GAA6B,kBAAkB,2BAA2B;;;;"} | ||
| {"version":3,"file":"postgres.js","sources":["../../../../src/integrations/tracing-channel/postgres.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope, SpanAttributes } from '@sentry/core';\nimport {\n isObjectLike,\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_KIND,\n startInactiveSpan,\n waitForTracingChannelBinding,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\nimport { bindTracingChannelToSpan } from '../../tracing-channel';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, the OTel 'Postgres' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Postgres' as const;\n\n// Only the query span carries an origin (the connect/pool-connect spans don't,\n// so they default to 'manual').\nconst ORIGIN = 'auto.db.orchestrion.postgres';\n\n// OpenTelemetry \"OLD\" db/net semantic-conventions, inlined to keep this\n// integration free of `@opentelemetry/*` deps.\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\nconst ATTR_PG_PLAN = 'db.postgresql.plan';\nconst ATTR_PG_IDLE_TIMEOUT = 'db.postgresql.idle.timeout.millis';\nconst ATTR_PG_MAX_CLIENT = 'db.postgresql.max.client';\nconst DB_SYSTEM_POSTGRESQL = 'postgresql';\n\n// We set `op: 'db'` and the SQL description directly here (same as mysql\n// orchestrion) rather than relying on the OTel pipeline's `inferDbSpanData`\n// processor, which only runs in the node SDK, so setting them here is what\n// makes the spans correct on the other runtimes\n//\n// The user-visible span is identical to OTel: query spans are named after\n// `db.statement`; connect/pool-connect spans keep these names.\nconst SPAN_QUERY_FALLBACK = 'pg.query';\nconst SPAN_CONNECT = 'pg.connect';\nconst SPAN_POOL_CONNECT = 'pg-pool.connect';\n\n/**\n * The shape orchestrion's transform attaches to the tracing-channel `context`. Documented here rather\n * than imported because orchestrion's runtime doesn't export it.\n */\ninterface PgChannelContext {\n // The live args array passed to the wrapped `query`/`connect` call.\n arguments: unknown[];\n self?: unknown;\n result?: unknown;\n error?: unknown;\n // The caller's scope, captured at `start` and replayed onto a streamed `Submittable` emitter (see below).\n _sentryCallerScope?: Scope;\n}\n\ninterface PgConnectionParams {\n database?: string;\n host?: string;\n port?: number;\n user?: string;\n connectionString?: string;\n}\n\ninterface PgPoolOptions extends PgConnectionParams {\n idleTimeoutMillis?: number;\n // pg-pool stores the max pool size as `max` (defaulting it to 10 in its\n // constructor). The OTel pg instrumentation reads a `maxClient` field that\n // pg-pool never sets, so its `db.postgresql.max.client` attribute is always\n // dropped; we read the real `max` so the attribute is actually populated.\n max?: number;\n}\n\nconst _postgresChannelIntegration = ((options: { ignoreConnectSpans?: boolean } = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // `tracingChannel` is unavailable before Node 18.19 so do nothing in that case.\n if (!diagnosticsChannel.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n // Query spans: `pg`/native `Client.prototype.query`. Only this channel can return a streamable\n // `Submittable` result, so it's the only one that defers span-ending to the emitter (see below).\n subscribeQueryLikeChannel(CHANNELS.PG_QUERY, querySpanOptions, { deferStreamedResult: true });\n\n // Connect spans, gated by `ignoreConnectSpans` (same as OTel pg).\n // `Client.prototype.connect` (pg + native)\n // and `Pool.prototype.connect` (pg-pool).\n if (!options.ignoreConnectSpans) {\n subscribeQueryLikeChannel(CHANNELS.PG_CONNECT, connectSpanOptions);\n subscribeQueryLikeChannel(CHANNELS.PGPOOL_CONNECT, poolConnectSpanOptions);\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Subscribe to a pg tracing-channel and manage a span across its lifecycle.\n * Shared by the query/connect/pool-connect channels. They differ only in how\n * the span's name + attributes are built (`getSpanOptions`), and whether the\n * result can be a streamable emitter (`deferStreamedResult`, query-only).\n */\nfunction subscribeQueryLikeChannel(\n channelName: string,\n getSpanOptions: (ctx: PgChannelContext) => { name: string; op: string; attributes: SpanAttributes },\n { deferStreamedResult = false }: { deferStreamedResult?: boolean } = {},\n): void {\n DEBUG_BUILD && debug.log(`[orchestrion:pg] subscribing to channel \"${channelName}\"`);\n\n bindTracingChannelToSpan(\n diagnosticsChannel.tracingChannel<PgChannelContext>(channelName),\n data => {\n // Capture the caller's scope while still synchronously inside the call, for the streamed path:\n // pg dispatches a `Submittable` emitter's events outside the original async scope, so `deferSpanEnd`\n // replays this scope onto that emitter.\n data._sentryCallerScope = getCurrentScope();\n\n // `kind: CLIENT` mirrors the OTel pg instrumentation, so the emitted\n // `otel.kind` matches across the OTel and diagnostics-channel paths.\n return startInactiveSpan({ ...getSpanOptions(data), kind: SPAN_KIND.CLIENT });\n },\n // `connect`/`pool-connect` resolve with a persistent `Client` (itself an\n // `EventEmitter`), which is NOT a streamed result. Deferring their span\n // to that emitter's `'end'`/`'error'` would keep it open for the whole\n // connection lifetime, so it never ends in time and is dropped. Only\n // `query` can return a streamable `Submittable`, so only it defers.\n deferStreamedResult\n ? {\n // Only instrument under an active span, leaving the context untouched otherwise\n // (e.g. connects issued during app startup).\n requiresParentSpan: true,\n // Streamable `Submittable` (e.g. `client.query(new Query())`)\n // returns an emitter that orchestrion stores on `ctx.result` while\n // firing no async events; the query isn't done until the emitter\n // emits `'end'`/`'error'`. Defer ending to those events for that\n // path; the callback, promise, and sync-throw paths carry no\n // emitter, so the helper ends the span as usual.\n deferSpanEnd({ data, end }) {\n const result = data.result;\n if (!result || typeof result !== 'object' || !hasOnMethod(result)) {\n return false;\n }\n\n // Replay the caller's scope onto the emitter so listeners theu\n // user attaches after the call returns (and any spans they start)\n // nest under the caller, not a fresh root trace.\n const callerScope = data._sentryCallerScope;\n if (callerScope) {\n bindScopeToEmitter(result, callerScope);\n }\n\n result.on('error', err => end(err));\n result.on('end', () => end());\n\n return true;\n },\n }\n : { requiresParentSpan: true },\n );\n}\n\nfunction querySpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const params = (ctx.self as { connectionParameters?: PgConnectionParams } | undefined)?.connectionParameters ?? {};\n const queryConfig = extractQueryConfig(ctx.arguments);\n return {\n // The description is the SQL statement\n name: queryConfig?.text ?? SPAN_QUERY_FALLBACK,\n op: 'db',\n attributes: {\n ...getConnectionAttributes(params),\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [ATTR_DB_STATEMENT]: queryConfig?.text || undefined,\n [ATTR_PG_PLAN]: typeof queryConfig?.name === 'string' ? queryConfig.name : undefined,\n },\n };\n}\n\nfunction connectSpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const params = (ctx.self as { connectionParameters?: PgConnectionParams } | undefined)?.connectionParameters ?? {};\n // No origin set -> defaults to 'manual'\n return { name: SPAN_CONNECT, op: 'db', attributes: getConnectionAttributes(params) };\n}\n\nfunction poolConnectSpanOptions(ctx: PgChannelContext): { name: string; op: string; attributes: SpanAttributes } {\n const opts = (ctx.self as { options?: PgPoolOptions } | undefined)?.options ?? {};\n return { name: SPAN_POOL_CONNECT, op: 'db', attributes: getPoolConnectionAttributes(opts) };\n}\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\n// `client.query(text, cb?)`, `client.query(text, values, cb?)`, and\n// `client.query(configObj, cb?)` are all valid; normalize to `{ text, name }`\n// (the only fields the span needs). Returns undefined for invalid args.\nfunction extractQueryConfig(args: unknown[]): { text: string; name?: unknown } | undefined {\n const arg0 = args[0];\n if (typeof arg0 === 'string') {\n return { text: arg0 };\n }\n if (isObjectLike(arg0) && typeof (arg0 as { text?: unknown }).text === 'string') {\n const obj = arg0 as { text: string; name?: unknown };\n return { text: obj.text, name: obj.name };\n }\n return undefined;\n}\n\nfunction getConnectionAttributes(params: PgConnectionParams): SpanAttributes {\n return {\n [ATTR_DB_SYSTEM]: DB_SYSTEM_POSTGRESQL,\n [ATTR_DB_CONNECTION_STRING]: getConnectionString(params),\n [ATTR_DB_NAME]: params.database,\n [ATTR_DB_USER]: params.user,\n [ATTR_NET_PEER_NAME]: params.host,\n [ATTR_NET_PEER_PORT]: Number.isInteger(params.port) ? params.port : undefined,\n };\n}\n\nfunction getPoolConnectionAttributes(opts: PgPoolOptions): SpanAttributes {\n let url: URL | undefined;\n try {\n url = opts.connectionString ? new URL(opts.connectionString) : undefined;\n } catch {\n url = undefined;\n }\n const database = url?.pathname.slice(1) || opts.database;\n const host = url?.hostname || opts.host;\n // Mirror OTel's `getSemanticAttributesFromPoolConnection`: prefer the URL's\n // port, but fall back to an explicit `opts.port` when the connection string\n // omits it (`Number('')` / `Number(undefined)` -> falsy).\n const port = Number(url?.port) || (Number.isInteger(opts.port) ? opts.port : undefined);\n const user = url?.username || opts.user;\n return {\n [ATTR_DB_SYSTEM]: DB_SYSTEM_POSTGRESQL,\n [ATTR_DB_CONNECTION_STRING]: getConnectionString(opts),\n [ATTR_PG_IDLE_TIMEOUT]: opts.idleTimeoutMillis,\n [ATTR_PG_MAX_CLIENT]: opts.max,\n [ATTR_DB_NAME]: database,\n [ATTR_NET_PEER_PORT]: port,\n // these two come from a url parse and slice, can be ''\n [ATTR_NET_PEER_NAME]: host || undefined,\n [ATTR_DB_USER]: user || undefined,\n };\n}\n\n// Builds `postgresql://host:port/database`, masking credentials when a raw\n// connection string was provided.\nfunction getConnectionString(params: PgConnectionParams): string {\n if (params.connectionString) {\n try {\n const url = new URL(params.connectionString);\n url.username = '';\n url.password = '';\n return url.toString();\n } catch {\n return 'postgresql://localhost:5432/';\n }\n }\n const host = params.host || 'localhost';\n const port = params.port || 5432;\n const database = params.database || '';\n return `postgresql://${host}:${port}/${database}`;\n}\n\n/**\n * EXPERIMENTAL: orchestrion-driven `pg` (node-postgres) integration.\n *\n * Subscribes to the `orchestrion:pg:query`/`:connect` and\n * `orchestrion:pg-pool:connect` diagnostics_channels that the orchestrion code\n * transform injects into `pg`'s `Client.prototype.query`/`connect`\n * and `pg-pool`'s `Pool.prototype.connect`. Requires the orchestrion runtime\n * hook or bundler plugin to be active.\n */\nexport const postgresChannelIntegration = defineIntegration(_postgresChannelIntegration);\n"],"names":[],"mappings":"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"} |
| import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core'; | ||
| import { vercelAiIntegration } from '../../vercel-ai/index.js'; | ||
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { subscribeVercelAiOrchestrionChannels } from '../../vercel-ai/vercel-ai-orchestrion-v6-subscriber.js'; | ||
| import { subscribeVercelAiOrchestrionChannels } from '../../vercel-ai/vercel-ai-orchestrion-subscriber.js'; | ||
@@ -6,0 +6,0 @@ const _vercelAiChannelIntegration = ((options = {}) => { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"vercel-ai.js","sources":["../../../../src/integrations/tracing-channel/vercel-ai.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';\nimport { vercelAiIntegration as baseVercelAiIntegration } from '../../vercel-ai';\nimport * as dc from 'node:diagnostics_channel';\nimport { subscribeVercelAiOrchestrionChannels } from '../../vercel-ai/vercel-ai-orchestrion-v6-subscriber';\n\ntype VercelAiOptions = Parameters<typeof baseVercelAiIntegration>[0];\n\n// In channel-based (orchestrion) mode we emit our own `gen_ai.*` spans from the\n// diagnostics channels. The `ai` SDK would otherwise emit its own native\n// OpenTelemetry spans whenever the user enables `experimental_telemetry`, which\n// would be duplicates. Rather than dropping those after the fact, the v6\n// subscriber suppresses them at the source: it flips the wrapped call's\n// `experimental_telemetry.isEnabled` to `false`, so `ai` falls back to its\n// internal no-op tracer and never creates the native spans in the first place.\n// See `subscribeVercelAiOrchestrionChannels`.\nconst _vercelAiChannelIntegration = ((options: VercelAiOptions = {}) => {\n const parentIntegration = baseVercelAiIntegration(options);\n\n return extendIntegration(parentIntegration, {\n options,\n setupOnce() {\n // Bail if this is not available\n if (!dc.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n subscribeVercelAiOrchestrionChannels(dc.tracingChannel, options);\n });\n },\n });\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the `ai` SDK. Supported are:\n * - v7 via native `ai:telemetry` tracing channel\n * - v6 via orchestrion `orchestrion:ai:*` channels\n */\nexport const vercelAiChannelIntegration = defineIntegration(_vercelAiChannelIntegration);\n"],"names":["baseVercelAiIntegration","dc"],"mappings":";;;;;AAgBA,MAAM,2BAAA,IAA+B,CAAC,OAAA,GAA2B,EAAC,KAAM;AACtE,EAAA,MAAM,iBAAA,GAAoBA,oBAAwB,OAAO,CAAA;AAEzD,EAAA,OAAO,kBAAkB,iBAAA,EAAmB;AAAA,IAC1C,OAAA;AAAA,IACA,SAAA,GAAY;AAEV,MAAA,IAAI,CAACC,mBAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAEA,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,oCAAA,CAAqCA,kBAAA,CAAG,gBAAgB,OAAO,CAAA;AAAA,MACjE,CAAC,CAAA;AAAA,IACH;AAAA,GACD,CAAA;AACH,CAAA,CAAA;AAOO,MAAM,0BAAA,GAA6B,kBAAkB,2BAA2B;;;;"} | ||
| {"version":3,"file":"vercel-ai.js","sources":["../../../../src/integrations/tracing-channel/vercel-ai.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';\nimport { vercelAiIntegration as baseVercelAiIntegration } from '../../vercel-ai';\nimport * as dc from 'node:diagnostics_channel';\nimport { subscribeVercelAiOrchestrionChannels } from '../../vercel-ai/vercel-ai-orchestrion-subscriber';\n\ntype VercelAiOptions = Parameters<typeof baseVercelAiIntegration>[0];\n\n// In channel-based (orchestrion) mode we emit our own `gen_ai.*` spans from the\n// diagnostics channels. The `ai` SDK would otherwise emit its own native\n// OpenTelemetry spans whenever the user enables `experimental_telemetry`, which\n// would be duplicates. Rather than dropping those after the fact, the v6\n// subscriber suppresses them at the source: it flips the wrapped call's\n// `experimental_telemetry.isEnabled` to `false`, so `ai` falls back to its\n// internal no-op tracer and never creates the native spans in the first place.\n// See `subscribeVercelAiOrchestrionChannels`.\nconst _vercelAiChannelIntegration = ((options: VercelAiOptions = {}) => {\n const parentIntegration = baseVercelAiIntegration(options);\n\n return extendIntegration(parentIntegration, {\n options,\n setupOnce() {\n // Bail if this is not available\n if (!dc.tracingChannel) {\n return;\n }\n\n waitForTracingChannelBinding(() => {\n subscribeVercelAiOrchestrionChannels(dc.tracingChannel, options);\n });\n },\n });\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the `ai` SDK. Supported are:\n * - v7 via native `ai:telemetry` tracing channel\n * - v4, v5 & v6 via orchestrion `orchestrion:ai:*` channels\n */\nexport const vercelAiChannelIntegration = defineIntegration(_vercelAiChannelIntegration);\n"],"names":["baseVercelAiIntegration","dc"],"mappings":";;;;;AAgBA,MAAM,2BAAA,IAA+B,CAAC,OAAA,GAA2B,EAAC,KAAM;AACtE,EAAA,MAAM,iBAAA,GAAoBA,oBAAwB,OAAO,CAAA;AAEzD,EAAA,OAAO,kBAAkB,iBAAA,EAAmB;AAAA,IAC1C,OAAA;AAAA,IACA,SAAA,GAAY;AAEV,MAAA,IAAI,CAACC,mBAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAEA,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,oCAAA,CAAqCA,kBAAA,CAAG,gBAAgB,OAAO,CAAA;AAAA,MACjE,CAAC,CAAA;AAAA,IACH;AAAA,GACD,CAAA;AACH,CAAA,CAAA;AAOO,MAAM,0BAAA,GAA6B,kBAAkB,2BAA2B;;;;"} |
| import { SERVER_PORT, SERVER_ADDRESS, DB_OPERATION_BATCH_SIZE, DB_QUERY_TEXT, DB_NAMESPACE, DB_COLLECTION_NAME, DB_OPERATION_NAME, DB_SYSTEM_NAME } from '@sentry/conventions/attributes'; | ||
| import { debug, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
| import { debug, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, isObjectLike } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../debug-build.js'; | ||
@@ -78,3 +78,3 @@ import { bindTracingChannelToSpan } from '../tracing-channel.js'; | ||
| } | ||
| if (value && typeof value === "object") { | ||
| if (isObjectLike(value)) { | ||
| const out = {}; | ||
@@ -81,0 +81,0 @@ for (const key of Object.keys(value)) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"mongoose-dc-subscriber.js","sources":["../../../src/mongoose/mongoose-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport {\n DB_COLLECTION_NAME,\n DB_NAMESPACE,\n DB_OPERATION_BATCH_SIZE,\n DB_OPERATION_NAME,\n DB_QUERY_TEXT,\n DB_SYSTEM_NAME,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport { debug, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n// Channel names published by mongoose >= 9.7.0 (see mongoose `lib/tracing.js`,\n// `lib/query.js`, `lib/aggregate.js`, `lib/model.js` and `lib/cursor/*`).\n// Hardcoded so the subscriber does not have to import mongoose — the channels\n// just have to be subscribed to before the user's mongoose code publishes.\nexport const MONGOOSE_DC_CHANNEL_QUERY = 'mongoose:query';\nexport const MONGOOSE_DC_CHANNEL_AGGREGATE = 'mongoose:aggregate';\nexport const MONGOOSE_DC_CHANNEL_MODEL_SAVE = 'mongoose:model:save';\nexport const MONGOOSE_DC_CHANNEL_MODEL_INSERT_MANY = 'mongoose:model:insertMany';\nexport const MONGOOSE_DC_CHANNEL_MODEL_BULK_WRITE = 'mongoose:model:bulkWrite';\nexport const MONGOOSE_DC_CHANNEL_CURSOR_NEXT = 'mongoose:cursor:next';\n\nconst ORIGIN = 'auto.db.mongoose.diagnostic_channel';\nconst DB_SYSTEM_NAME_VALUE_MONGODB = 'mongodb';\n\n// Cap recursion so a self-referential or pathologically deep query can never\n// stall (or blow the stack in) instrumentation.\nconst MAX_REDACTION_DEPTH = 10;\n\n/**\n * Shape of the context object mongoose >= 9.7.0 publishes on its tracing\n * channels (mongoose's `TracingContext`, see `types/tracing.d.ts`).\n *\n * Node's `tracePromise` mutates this same object with `result`/`error` once the\n * operation settles, which `bindTracingChannelToSpan` reads in its lifecycle\n * handlers — hence both are declared optional here.\n *\n * Unlike redis/ioredis, mongoose does NOT pre-sanitize the payload, so `args`\n * carries the raw user query. We redact it before emitting `db.query.text`.\n */\nexport interface MongooseTracingData {\n operation: string;\n /** Absent for connection-level `aggregate()` calls, which have no model/collection. */\n collection?: string;\n database?: string;\n serverAddress?: string;\n serverPort?: number;\n /** Cursor channels only: the cursor's fetch batch size and tailable flag. */\n batchSize?: number;\n tailable?: boolean;\n /**\n * Operation-specific arguments. `filter` (queries/cursors) and `pipeline`\n * (aggregations) carry the query shape; `docs`/`ops` carry batch payloads.\n */\n args?: {\n filter?: unknown;\n pipeline?: unknown;\n docs?: unknown;\n ops?: unknown;\n [key: string]: unknown;\n };\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Node passes `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type MongooseTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\nlet subscribed = false;\n\n/**\n * Subscribe Sentry span handlers to mongoose's diagnostics-channel events\n * (`mongoose:query`, `:aggregate`, `:model:save`, `:model:insertMany`,\n * `:model:bulkWrite`, `:cursor:next`), published by mongoose >= 9.7.0.\n *\n * On older mongoose versions the channels are never published to, so the\n * subscribers are inert — there is no double-instrumentation against the\n * IITM-based vendored patcher, which is gated to `< 9.7.0`.\n *\n * Idempotent: subsequent calls are a no-op.\n */\nexport function subscribeMongooseDiagnosticChannels(tracingChannel: MongooseTracingChannelFactory): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n try {\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_QUERY);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_AGGREGATE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_SAVE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_INSERT_MANY);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_BULK_WRITE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_CURSOR_NEXT);\n } catch {\n // The factory relies on `node:diagnostics_channel`, which isn't always\n // available. Fail closed; the SDK simply won't emit mongoose spans here.\n DEBUG_BUILD && debug.log('Mongoose node:diagnostics_channel subscription failed.');\n }\n}\n\nfunction setupChannel(tracingChannel: MongooseTracingChannelFactory, channelName: string): void {\n bindTracingChannelToSpan(tracingChannel<MongooseTracingData>(channelName), data => {\n const collection = data.collection;\n const queryText = redactMongoQuery(data.args?.pipeline ?? data.args?.filter);\n const batchSize = getBatchSize(data);\n\n return startInactiveSpan({\n name: collection ? `mongoose.${collection}.${data.operation}` : `mongoose.${data.operation}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MONGODB,\n [DB_OPERATION_NAME]: data.operation,\n [DB_COLLECTION_NAME]: collection ?? undefined,\n [DB_NAMESPACE]: data.database ?? undefined,\n [DB_QUERY_TEXT]: queryText ?? undefined,\n [DB_OPERATION_BATCH_SIZE]: batchSize ?? undefined,\n [SERVER_ADDRESS]: data.serverAddress ?? undefined,\n [SERVER_PORT]: data.serverPort ?? undefined,\n },\n });\n });\n}\n\n/**\n * `db.operation.batch.size` is only meaningful for genuine batch operations.\n * Mongoose's cursor `batchSize` is a fetch-tuning option, not a batch operation\n * size, so it is intentionally excluded here.\n */\nfunction getBatchSize(data: MongooseTracingData): number | undefined {\n const args = data.args;\n const batch = data.operation === 'insertMany' ? args?.docs : data.operation === 'bulkWrite' ? args?.ops : undefined;\n\n return Array.isArray(batch) && batch.length > 1 ? batch.length : undefined;\n}\n\n/**\n * Serialize a mongoose filter/pipeline into `db.query.text` while stripping every\n * value: keys and Mongo operators (`$gt`, `$in`, …) are preserved, leaf values\n * become `'?'`. Mongoose does not sanitize its channel payload, so this prevents\n * raw user data (potential PII) from leaving the process. Returns `undefined`\n * (rather than throwing) on anything it cannot serialize.\n */\nfunction redactMongoQuery(value: unknown): string | undefined {\n if (value == null) {\n return undefined;\n }\n\n try {\n const redacted = redactValue(value, 0);\n const text = JSON.stringify(redacted);\n\n // Skip empty/uninformative shapes (e.g. a `findOne()` with no filter).\n return text == null || text === '{}' || text === '[]' ? undefined : text;\n } catch {\n return undefined;\n }\n}\n\nfunction redactValue(value: unknown, depth: number): unknown {\n if (depth > MAX_REDACTION_DEPTH) {\n return '?';\n }\n\n if (Array.isArray(value)) {\n return value.map(item => redactValue(item, depth + 1));\n }\n\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n out[key] = redactValue((value as Record<string, unknown>)[key], depth + 1);\n }\n\n return out;\n }\n\n return '?';\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"mongoose-dc-subscriber.js","sources":["../../../src/mongoose/mongoose-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport {\n DB_COLLECTION_NAME,\n DB_NAMESPACE,\n DB_OPERATION_BATCH_SIZE,\n DB_OPERATION_NAME,\n DB_QUERY_TEXT,\n DB_SYSTEM_NAME,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport {\n isObjectLike,\n debug,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startInactiveSpan,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n// Channel names published by mongoose >= 9.7.0 (see mongoose `lib/tracing.js`,\n// `lib/query.js`, `lib/aggregate.js`, `lib/model.js` and `lib/cursor/*`).\n// Hardcoded so the subscriber does not have to import mongoose — the channels\n// just have to be subscribed to before the user's mongoose code publishes.\nexport const MONGOOSE_DC_CHANNEL_QUERY = 'mongoose:query';\nexport const MONGOOSE_DC_CHANNEL_AGGREGATE = 'mongoose:aggregate';\nexport const MONGOOSE_DC_CHANNEL_MODEL_SAVE = 'mongoose:model:save';\nexport const MONGOOSE_DC_CHANNEL_MODEL_INSERT_MANY = 'mongoose:model:insertMany';\nexport const MONGOOSE_DC_CHANNEL_MODEL_BULK_WRITE = 'mongoose:model:bulkWrite';\nexport const MONGOOSE_DC_CHANNEL_CURSOR_NEXT = 'mongoose:cursor:next';\n\nconst ORIGIN = 'auto.db.mongoose.diagnostic_channel';\nconst DB_SYSTEM_NAME_VALUE_MONGODB = 'mongodb';\n\n// Cap recursion so a self-referential or pathologically deep query can never\n// stall (or blow the stack in) instrumentation.\nconst MAX_REDACTION_DEPTH = 10;\n\n/**\n * Shape of the context object mongoose >= 9.7.0 publishes on its tracing\n * channels (mongoose's `TracingContext`, see `types/tracing.d.ts`).\n *\n * Node's `tracePromise` mutates this same object with `result`/`error` once the\n * operation settles, which `bindTracingChannelToSpan` reads in its lifecycle\n * handlers — hence both are declared optional here.\n *\n * Unlike redis/ioredis, mongoose does NOT pre-sanitize the payload, so `args`\n * carries the raw user query. We redact it before emitting `db.query.text`.\n */\nexport interface MongooseTracingData {\n operation: string;\n /** Absent for connection-level `aggregate()` calls, which have no model/collection. */\n collection?: string;\n database?: string;\n serverAddress?: string;\n serverPort?: number;\n /** Cursor channels only: the cursor's fetch batch size and tailable flag. */\n batchSize?: number;\n tailable?: boolean;\n /**\n * Operation-specific arguments. `filter` (queries/cursors) and `pipeline`\n * (aggregations) carry the query shape; `docs`/`ops` carry batch payloads.\n */\n args?: {\n filter?: unknown;\n pipeline?: unknown;\n docs?: unknown;\n ops?: unknown;\n [key: string]: unknown;\n };\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Node passes `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type MongooseTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\nlet subscribed = false;\n\n/**\n * Subscribe Sentry span handlers to mongoose's diagnostics-channel events\n * (`mongoose:query`, `:aggregate`, `:model:save`, `:model:insertMany`,\n * `:model:bulkWrite`, `:cursor:next`), published by mongoose >= 9.7.0.\n *\n * On older mongoose versions the channels are never published to, so the\n * subscribers are inert — there is no double-instrumentation against the\n * IITM-based vendored patcher, which is gated to `< 9.7.0`.\n *\n * Idempotent: subsequent calls are a no-op.\n */\nexport function subscribeMongooseDiagnosticChannels(tracingChannel: MongooseTracingChannelFactory): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n try {\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_QUERY);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_AGGREGATE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_SAVE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_INSERT_MANY);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_MODEL_BULK_WRITE);\n setupChannel(tracingChannel, MONGOOSE_DC_CHANNEL_CURSOR_NEXT);\n } catch {\n // The factory relies on `node:diagnostics_channel`, which isn't always\n // available. Fail closed; the SDK simply won't emit mongoose spans here.\n DEBUG_BUILD && debug.log('Mongoose node:diagnostics_channel subscription failed.');\n }\n}\n\nfunction setupChannel(tracingChannel: MongooseTracingChannelFactory, channelName: string): void {\n bindTracingChannelToSpan(tracingChannel<MongooseTracingData>(channelName), data => {\n const collection = data.collection;\n const queryText = redactMongoQuery(data.args?.pipeline ?? data.args?.filter);\n const batchSize = getBatchSize(data);\n\n return startInactiveSpan({\n name: collection ? `mongoose.${collection}.${data.operation}` : `mongoose.${data.operation}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MONGODB,\n [DB_OPERATION_NAME]: data.operation,\n [DB_COLLECTION_NAME]: collection ?? undefined,\n [DB_NAMESPACE]: data.database ?? undefined,\n [DB_QUERY_TEXT]: queryText ?? undefined,\n [DB_OPERATION_BATCH_SIZE]: batchSize ?? undefined,\n [SERVER_ADDRESS]: data.serverAddress ?? undefined,\n [SERVER_PORT]: data.serverPort ?? undefined,\n },\n });\n });\n}\n\n/**\n * `db.operation.batch.size` is only meaningful for genuine batch operations.\n * Mongoose's cursor `batchSize` is a fetch-tuning option, not a batch operation\n * size, so it is intentionally excluded here.\n */\nfunction getBatchSize(data: MongooseTracingData): number | undefined {\n const args = data.args;\n const batch = data.operation === 'insertMany' ? args?.docs : data.operation === 'bulkWrite' ? args?.ops : undefined;\n\n return Array.isArray(batch) && batch.length > 1 ? batch.length : undefined;\n}\n\n/**\n * Serialize a mongoose filter/pipeline into `db.query.text` while stripping every\n * value: keys and Mongo operators (`$gt`, `$in`, …) are preserved, leaf values\n * become `'?'`. Mongoose does not sanitize its channel payload, so this prevents\n * raw user data (potential PII) from leaving the process. Returns `undefined`\n * (rather than throwing) on anything it cannot serialize.\n */\nfunction redactMongoQuery(value: unknown): string | undefined {\n if (value == null) {\n return undefined;\n }\n\n try {\n const redacted = redactValue(value, 0);\n const text = JSON.stringify(redacted);\n\n // Skip empty/uninformative shapes (e.g. a `findOne()` with no filter).\n return text == null || text === '{}' || text === '[]' ? undefined : text;\n } catch {\n return undefined;\n }\n}\n\nfunction redactValue(value: unknown, depth: number): unknown {\n if (depth > MAX_REDACTION_DEPTH) {\n return '?';\n }\n\n if (Array.isArray(value)) {\n return value.map(item => redactValue(item, depth + 1));\n }\n\n if (isObjectLike(value)) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n out[key] = redactValue((value as Record<string, unknown>)[key], depth + 1);\n }\n\n return out;\n }\n\n return '?';\n}\n"],"names":[],"mappings":"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"} |
@@ -5,6 +5,12 @@ import { mysqlChannels } from './config/mysql.js'; | ||
| import { pgChannels } from './config/pg.js'; | ||
| import { postgresJsChannels } from './config/postgres.js'; | ||
| import { openaiChannels } from './config/openai.js'; | ||
| import { anthropicAiChannels } from './config/anthropic-ai.js'; | ||
| import { googleGenAiChannels } from './config/google-genai.js'; | ||
| import { vercelAiChannels } from './config/vercel-ai.js'; | ||
| import { amqplibChannels } from './config/amqplib.js'; | ||
| import { hapiChannels } from './config/hapi.js'; | ||
| import { redisChannels } from './config/redis.js'; | ||
| import { expressChannels } from './config/express.js'; | ||
| import { graphqlChannels } from './config/graphql.js'; | ||
@@ -16,6 +22,12 @@ const CHANNELS = { | ||
| ...pgChannels, | ||
| ...postgresJsChannels, | ||
| ...openaiChannels, | ||
| ...anthropicAiChannels, | ||
| ...googleGenAiChannels, | ||
| ...vercelAiChannels, | ||
| ...hapiChannels | ||
| ...amqplibChannels, | ||
| ...hapiChannels, | ||
| ...redisChannels, | ||
| ...expressChannels, | ||
| ...graphqlChannels | ||
| }; | ||
@@ -22,0 +34,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"channels.js","sources":["../../../src/orchestrion/channels.ts"],"sourcesContent":["import { mysqlChannels } from './config/mysql';\nimport { lruMemoizerChannels } from './config/lru-memoizer';\nimport { ioredisChannels } from './config/ioredis';\nimport { pgChannels } from './config/pg';\nimport { openaiChannels } from './config/openai';\nimport { anthropicAiChannels } from './config/anthropic-ai';\nimport { vercelAiChannels } from './config/vercel-ai';\nimport { hapiChannels } from './config/hapi';\n\n/**\n * Fully-qualified `diagnostics_channel` names that orchestrion publishes to.\n *\n * Orchestrion's transform always prefixes the configured `channelName` with\n * `orchestrion:${module.name}:`. So a config of\n * `{ channelName: 'query', module: { name: 'mysql' } }`\n * publishes to `orchestrion:mysql:query`.\n *\n * Subscribers (`integrations/<lib>/tracing-channel.ts`) consume the full\n * prefixed string from this map; the config files set only the unprefixed\n * suffix in `channelName`. Keeping both pieces in one file is what guarantees\n * they don't drift apart and silently stop firing.\n */\nexport const CHANNELS = {\n ...mysqlChannels,\n ...lruMemoizerChannels,\n ...ioredisChannels,\n ...pgChannels,\n ...openaiChannels,\n ...anthropicAiChannels,\n ...vercelAiChannels,\n ...hapiChannels,\n} as const;\n\nexport type ChannelName = (typeof CHANNELS)[keyof typeof CHANNELS];\n"],"names":[],"mappings":";;;;;;;;;AAsBO,MAAM,QAAA,GAAW;AAAA,EACtB,GAAG,aAAA;AAAA,EACH,GAAG,mBAAA;AAAA,EACH,GAAG,eAAA;AAAA,EACH,GAAG,UAAA;AAAA,EACH,GAAG,cAAA;AAAA,EACH,GAAG,mBAAA;AAAA,EACH,GAAG,gBAAA;AAAA,EACH,GAAG;AACL;;;;"} | ||
| {"version":3,"file":"channels.js","sources":["../../../src/orchestrion/channels.ts"],"sourcesContent":["import { mysqlChannels } from './config/mysql';\nimport { lruMemoizerChannels } from './config/lru-memoizer';\nimport { ioredisChannels } from './config/ioredis';\nimport { pgChannels } from './config/pg';\nimport { postgresJsChannels } from './config/postgres';\nimport { openaiChannels } from './config/openai';\nimport { anthropicAiChannels } from './config/anthropic-ai';\nimport { googleGenAiChannels } from './config/google-genai';\nimport { vercelAiChannels } from './config/vercel-ai';\nimport { amqplibChannels } from './config/amqplib';\nimport { hapiChannels } from './config/hapi';\nimport { redisChannels } from './config/redis';\nimport { expressChannels } from './config/express';\nimport { graphqlChannels } from './config/graphql';\n\n/**\n * Fully-qualified `diagnostics_channel` names that orchestrion publishes to.\n *\n * Orchestrion's transform always prefixes the configured `channelName` with\n * `orchestrion:${module.name}:`. So a config of\n * `{ channelName: 'query', module: { name: 'mysql' } }`\n * publishes to `orchestrion:mysql:query`.\n *\n * Subscribers (`integrations/<lib>/tracing-channel.ts`) consume the full\n * prefixed string from this map; the config files set only the unprefixed\n * suffix in `channelName`. Keeping both pieces in one file is what guarantees\n * they don't drift apart and silently stop firing.\n */\nexport const CHANNELS = {\n ...mysqlChannels,\n ...lruMemoizerChannels,\n ...ioredisChannels,\n ...pgChannels,\n ...postgresJsChannels,\n ...openaiChannels,\n ...anthropicAiChannels,\n ...googleGenAiChannels,\n ...vercelAiChannels,\n ...amqplibChannels,\n ...hapiChannels,\n ...redisChannels,\n ...expressChannels,\n ...graphqlChannels,\n} as const;\n\nexport type ChannelName = (typeof CHANNELS)[keyof typeof CHANNELS];\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AA4BO,MAAM,QAAA,GAAW;AAAA,EACtB,GAAG,aAAA;AAAA,EACH,GAAG,mBAAA;AAAA,EACH,GAAG,eAAA;AAAA,EACH,GAAG,UAAA;AAAA,EACH,GAAG,kBAAA;AAAA,EACH,GAAG,cAAA;AAAA,EACH,GAAG,mBAAA;AAAA,EACH,GAAG,mBAAA;AAAA,EACH,GAAG,gBAAA;AAAA,EACH,GAAG,eAAA;AAAA,EACH,GAAG,YAAA;AAAA,EACH,GAAG,aAAA;AAAA,EACH,GAAG,eAAA;AAAA,EACH,GAAG;AACL;;;;"} |
@@ -0,1 +1,2 @@ | ||
| import { uniq } from '@sentry/core'; | ||
| import { mysqlConfig } from './mysql.js'; | ||
@@ -6,5 +7,11 @@ import { lruMemoizerConfig } from './lru-memoizer.js'; | ||
| import { pgConfig } from './pg.js'; | ||
| import { postgresJsConfig } from './postgres.js'; | ||
| import { anthropicAiConfig } from './anthropic-ai.js'; | ||
| import { googleGenAiConfig } from './google-genai.js'; | ||
| import { vercelAiConfig } from './vercel-ai.js'; | ||
| import { amqplibConfig } from './amqplib.js'; | ||
| import { hapiConfig } from './hapi.js'; | ||
| import { redisConfig } from './redis.js'; | ||
| import { expressConfig } from './express.js'; | ||
| import { graphqlConfig } from './graphql.js'; | ||
@@ -17,7 +24,13 @@ const SENTRY_INSTRUMENTATIONS = [ | ||
| ...pgConfig, | ||
| ...postgresJsConfig, | ||
| ...anthropicAiConfig, | ||
| ...googleGenAiConfig, | ||
| ...vercelAiConfig, | ||
| ...hapiConfig | ||
| ...hapiConfig, | ||
| ...amqplibConfig, | ||
| ...redisConfig, | ||
| ...expressConfig, | ||
| ...graphqlConfig | ||
| ]; | ||
| const INSTRUMENTED_MODULE_NAMES = Array.from(new Set(SENTRY_INSTRUMENTATIONS.map((i) => i.module.name))); | ||
| const INSTRUMENTED_MODULE_NAMES = uniq(SENTRY_INSTRUMENTATIONS.map((i) => i.module.name)); | ||
| function withoutInstrumentedExternals(external) { | ||
@@ -24,0 +37,0 @@ if (!external) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../../src/orchestrion/config/index.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\nimport { mysqlConfig } from './mysql';\nimport { lruMemoizerConfig } from './lru-memoizer';\nimport { ioredisConfig } from './ioredis';\nimport { openaiConfig } from './openai';\nimport { pgConfig } from './pg';\nimport { anthropicAiConfig } from './anthropic-ai';\nimport { vercelAiConfig } from './vercel-ai';\nimport { hapiConfig } from './hapi';\n\nexport const SENTRY_INSTRUMENTATIONS: InstrumentationConfig[] = [\n ...mysqlConfig,\n ...lruMemoizerConfig,\n ...ioredisConfig,\n ...openaiConfig,\n ...pgConfig,\n ...anthropicAiConfig,\n ...vercelAiConfig,\n ...hapiConfig,\n];\n\n/**\n * The unique set of package names instrumented by `SENTRY_INSTRUMENTATIONS`\n * (e.g. `['mysql']`).\n *\n * Bundler plugins MUST ensure these are actually bundled rather than\n * externalized: an externalized dependency is resolved from `node_modules` at\n * runtime and never passes through the code transform's `onLoad`, so its\n * diagnostics_channel calls are silently never injected.\n */\nexport const INSTRUMENTED_MODULE_NAMES: string[] = Array.from(new Set(SENTRY_INSTRUMENTATIONS.map(i => i.module.name)));\n\n/**\n * Returns `external` with any instrumented packages removed, so a bundler that\n * uses an \"external\" denylist (esbuild, Bun, Rollup) still bundles — and thus\n * transforms — them. Matches an exact package name (`'mysql'`) or a subpath\n * (`'mysql/lib/...'`); wildcard/other patterns are left untouched. `undefined`\n * is returned unchanged.\n *\n * (Vite uses an `ssr.noExternal` allowlist instead, so it consumes\n * `INSTRUMENTED_MODULE_NAMES` directly rather than this helper.)\n */\nexport function withoutInstrumentedExternals(external: readonly string[] | undefined): string[] | undefined {\n if (!external) {\n return undefined;\n }\n return external.filter(\n entry => !INSTRUMENTED_MODULE_NAMES.some(name => entry === name || entry.startsWith(`${name}/`)),\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAUO,MAAM,uBAAA,GAAmD;AAAA,EAC9D,GAAG,WAAA;AAAA,EACH,GAAG,iBAAA;AAAA,EACH,GAAG,aAAA;AAAA,EACH,GAAG,YAAA;AAAA,EACH,GAAG,QAAA;AAAA,EACH,GAAG,iBAAA;AAAA,EACH,GAAG,cAAA;AAAA,EACH,GAAG;AACL;AAWO,MAAM,yBAAA,GAAsC,KAAA,CAAM,IAAA,CAAK,IAAI,GAAA,CAAI,uBAAA,CAAwB,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,CAAO,IAAI,CAAC,CAAC;AAY/G,SAAS,6BAA6B,QAAA,EAA+D;AAC1G,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,QAAA,CAAS,MAAA;AAAA,IACd,CAAA,KAAA,KAAS,CAAC,yBAAA,CAA0B,IAAA,CAAK,CAAA,IAAA,KAAQ,KAAA,KAAU,IAAA,IAAQ,KAAA,CAAM,UAAA,CAAW,CAAA,EAAG,IAAI,CAAA,CAAA,CAAG,CAAC;AAAA,GACjG;AACF;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../../src/orchestrion/config/index.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\nimport { uniq } from '@sentry/core';\nimport { mysqlConfig } from './mysql';\nimport { lruMemoizerConfig } from './lru-memoizer';\nimport { ioredisConfig } from './ioredis';\nimport { openaiConfig } from './openai';\nimport { pgConfig } from './pg';\nimport { postgresJsConfig } from './postgres';\nimport { anthropicAiConfig } from './anthropic-ai';\nimport { googleGenAiConfig } from './google-genai';\nimport { vercelAiConfig } from './vercel-ai';\nimport { amqplibConfig } from './amqplib';\nimport { hapiConfig } from './hapi';\nimport { redisConfig } from './redis';\nimport { expressConfig } from './express';\nimport { graphqlConfig } from './graphql';\n\nexport const SENTRY_INSTRUMENTATIONS: InstrumentationConfig[] = [\n ...mysqlConfig,\n ...lruMemoizerConfig,\n ...ioredisConfig,\n ...openaiConfig,\n ...pgConfig,\n ...postgresJsConfig,\n ...anthropicAiConfig,\n ...googleGenAiConfig,\n ...vercelAiConfig,\n ...hapiConfig,\n ...amqplibConfig,\n ...redisConfig,\n ...expressConfig,\n ...graphqlConfig,\n];\n\n/**\n * The unique set of package names instrumented by `SENTRY_INSTRUMENTATIONS`\n * (e.g. `['mysql']`).\n *\n * Bundler plugins MUST ensure these are actually bundled rather than\n * externalized: an externalized dependency is resolved from `node_modules` at\n * runtime and never passes through the code transform's `onLoad`, so its\n * diagnostics_channel calls are silently never injected.\n */\nexport const INSTRUMENTED_MODULE_NAMES: string[] = uniq(SENTRY_INSTRUMENTATIONS.map(i => i.module.name));\n\n/**\n * Returns `external` with any instrumented packages removed, so a bundler that\n * uses an \"external\" denylist (esbuild, Bun, Rollup) still bundles — and thus\n * transforms — them. Matches an exact package name (`'mysql'`) or a subpath\n * (`'mysql/lib/...'`); wildcard/other patterns are left untouched. `undefined`\n * is returned unchanged.\n *\n * (Vite uses an `ssr.noExternal` allowlist instead, so it consumes\n * `INSTRUMENTED_MODULE_NAMES` directly rather than this helper.)\n */\nexport function withoutInstrumentedExternals(external: readonly string[] | undefined): string[] | undefined {\n if (!external) {\n return undefined;\n }\n return external.filter(\n entry => !INSTRUMENTED_MODULE_NAMES.some(name => entry === name || entry.startsWith(`${name}/`)),\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAiBO,MAAM,uBAAA,GAAmD;AAAA,EAC9D,GAAG,WAAA;AAAA,EACH,GAAG,iBAAA;AAAA,EACH,GAAG,aAAA;AAAA,EACH,GAAG,YAAA;AAAA,EACH,GAAG,QAAA;AAAA,EACH,GAAG,gBAAA;AAAA,EACH,GAAG,iBAAA;AAAA,EACH,GAAG,iBAAA;AAAA,EACH,GAAG,cAAA;AAAA,EACH,GAAG,UAAA;AAAA,EACH,GAAG,aAAA;AAAA,EACH,GAAG,WAAA;AAAA,EACH,GAAG,aAAA;AAAA,EACH,GAAG;AACL;AAWO,MAAM,yBAAA,GAAsC,KAAK,uBAAA,CAAwB,GAAA,CAAI,OAAK,CAAA,CAAE,MAAA,CAAO,IAAI,CAAC;AAYhG,SAAS,6BAA6B,QAAA,EAA+D;AAC1G,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,QAAA,CAAS,MAAA;AAAA,IACd,CAAA,KAAA,KAAS,CAAC,yBAAA,CAA0B,IAAA,CAAK,CAAA,IAAA,KAAQ,KAAA,KAAU,IAAA,IAAQ,KAAA,CAAM,UAAA,CAAW,CAAA,EAAG,IAAI,CAAA,CAAA,CAAG,CAAC;AAAA,GACjG;AACF;;;;"} |
@@ -10,28 +10,32 @@ const vercelAiConfig = [ | ||
| // ends it when the (synchronous) call returns. | ||
| ...vercelAiV6Entries("generateText", "generateText", "Async"), | ||
| ...vercelAiV6Entries("streamText", "streamText", "Sync"), | ||
| ...vercelAiV6Entries("embed", "embed", "Async"), | ||
| ...vercelAiV6Entries("embedMany", "embedMany", "Async"), | ||
| ...vercelAiV6Entries("executeToolCall", "executeToolCall", "Async"), | ||
| ...vercelAiV6Entries("resolveLanguageModel", "resolveLanguageModel", "Sync") | ||
| // The majority of entrypoints are present in all versions we support | ||
| ...vercelAiEntries(">=4.0.0 <7.0.0", "generateText", "generateText", "Async"), | ||
| ...vercelAiEntries(">=4.0.0 <7.0.0", "streamText", "streamText", "Sync"), | ||
| ...vercelAiEntries(">=4.0.0 <7.0.0", "generateObject", "generateObject", "Async"), | ||
| ...vercelAiEntries(">=4.0.0 <7.0.0", "embed", "embed", "Async"), | ||
| ...vercelAiEntries(">=4.0.0 <7.0.0", "embedMany", "embedMany", "Async"), | ||
| // The following entry is only present in v5 and later | ||
| ...vercelAiEntries(">=5.0.0 <7.0.0", "resolveLanguageModel", "resolveLanguageModel", "Sync"), | ||
| // The following entry is only present in v6 and later | ||
| ...vercelAiEntries(">=6.0.0 <7.0.0", "executeToolCall", "executeToolCall", "Async") | ||
| ]; | ||
| const vercelAiChannels = { | ||
| // Vercel AI (`ai`) v6: orchestrion injects these so the same channel-based | ||
| // Vercel AI (`ai`): orchestrion injects these so the same channel-based | ||
| // integration that consumes `ai`'s native `ai:telemetry` channel (v7) can | ||
| // also instrument v6. Each maps to a top-level function in `ai`'s bundle. | ||
| // also instrument v4/v5/v6. Each maps to a top-level function in `ai`'s bundle. | ||
| // All three versions share the same channel names (the subscriber is version-agnostic); | ||
| // `VERCEL_AI_EXECUTE_TOOL_CALL` is v6-only (v4/v5 have no `executeToolCall` export) and | ||
| // `VERCEL_AI_RESOLVE_LANGUAGE_MODEL` is v5/v6-only (v4 has no such chokepoint). | ||
| VERCEL_AI_GENERATE_TEXT: "orchestrion:ai:generateText", | ||
| VERCEL_AI_STREAM_TEXT: "orchestrion:ai:streamText", | ||
| VERCEL_AI_GENERATE_OBJECT: "orchestrion:ai:generateObject", | ||
| VERCEL_AI_EMBED: "orchestrion:ai:embed", | ||
| VERCEL_AI_EMBED_MANY: "orchestrion:ai:embedMany", | ||
| VERCEL_AI_EXECUTE_TOOL_CALL: "orchestrion:ai:executeToolCall", | ||
| // `resolveLanguageModel` is the single chokepoint every model call flows | ||
| // through; we wrap it to monkey-patch `doGenerate`/`doStream` on the returned | ||
| // model (the model-call site itself is an inline call with no injectable | ||
| // definition). | ||
| VERCEL_AI_RESOLVE_LANGUAGE_MODEL: "orchestrion:ai:resolveLanguageModel" | ||
| }; | ||
| function vercelAiV6Entries(channelName, functionName, kind) { | ||
| function vercelAiEntries(versionRange, channelName, functionName, kind) { | ||
| return ["dist/index.js", "dist/index.mjs"].map((filePath) => ({ | ||
| channelName, | ||
| module: { name: "ai", versionRange: ">=6.0.0 <7.0.0", filePath }, | ||
| module: { name: "ai", versionRange, filePath }, | ||
| functionQuery: { functionName, kind } | ||
@@ -38,0 +42,0 @@ })); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"vercel-ai.js","sources":["../../../../src/orchestrion/config/vercel-ai.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\nexport const vercelAiConfig = [\n // Vercel AI v6: mirror the v7 native `ai:telemetry` channel by injecting\n // channels into the top-level entry points. `resolveLanguageModel` is wrapped\n // not to span it, but so the subscriber can monkey-patch `doGenerate`/\n // `doStream` on the returned model (the only way to span the model call,\n // which is an inline call with no injectable definition in `ai`).\n // `streamText` returns its result synchronously (streaming is lazy), so it's\n // `Sync`; the subscriber binds the span via `bindTracingChannelToSpan`, which\n // ends it when the (synchronous) call returns.\n ...vercelAiV6Entries('generateText', 'generateText', 'Async'),\n ...vercelAiV6Entries('streamText', 'streamText', 'Sync'),\n ...vercelAiV6Entries('embed', 'embed', 'Async'),\n ...vercelAiV6Entries('embedMany', 'embedMany', 'Async'),\n ...vercelAiV6Entries('executeToolCall', 'executeToolCall', 'Async'),\n ...vercelAiV6Entries('resolveLanguageModel', 'resolveLanguageModel', 'Sync'),\n] satisfies InstrumentationConfig[];\n\nexport const vercelAiChannels = {\n // Vercel AI (`ai`) v6: orchestrion injects these so the same channel-based\n // integration that consumes `ai`'s native `ai:telemetry` channel (v7) can\n // also instrument v6. Each maps to a top-level function in `ai`'s bundle.\n VERCEL_AI_GENERATE_TEXT: 'orchestrion:ai:generateText',\n VERCEL_AI_STREAM_TEXT: 'orchestrion:ai:streamText',\n VERCEL_AI_EMBED: 'orchestrion:ai:embed',\n VERCEL_AI_EMBED_MANY: 'orchestrion:ai:embedMany',\n VERCEL_AI_EXECUTE_TOOL_CALL: 'orchestrion:ai:executeToolCall',\n // `resolveLanguageModel` is the single chokepoint every model call flows\n // through; we wrap it to monkey-patch `doGenerate`/`doStream` on the returned\n // model (the model-call site itself is an inline call with no injectable\n // definition).\n VERCEL_AI_RESOLVE_LANGUAGE_MODEL: 'orchestrion:ai:resolveLanguageModel',\n} as const;\n\n/**\n * The central list of channel injections orchestrion should perform.\n *\n * This module has NO side effects — it's the only thing both the runtime hook\n * (`runtime/import-hook.mjs`) and the bundler plugins (`bundler/vite.ts`, …)\n * import from. Adding a new instrumented method is one entry here plus one\n * subscriber in `integrations/<lib>/tracing-channel.ts`.\n *\n * `channelName` here is the unprefixed suffix; the actual diagnostics_channel\n * name is `orchestrion:${module.name}:${channelName}` (see `channels.ts`).\n */\n/**\n * `ai` ships a single bundled entry per module system, so each instrumented\n * function needs one config entry per file (the app loads whichever matches its\n * module system). This expands a single target into both.\n */\nfunction vercelAiV6Entries(channelName: string, functionName: string, kind: 'Async' | 'Sync'): InstrumentationConfig[] {\n return ['dist/index.js', 'dist/index.mjs'].map(filePath => ({\n channelName,\n module: { name: 'ai', versionRange: '>=6.0.0 <7.0.0', filePath },\n functionQuery: { functionName, kind },\n }));\n}\n"],"names":[],"mappings":"AAEO,MAAM,cAAA,GAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS5B,GAAG,iBAAA,CAAkB,cAAA,EAAgB,cAAA,EAAgB,OAAO,CAAA;AAAA,EAC5D,GAAG,iBAAA,CAAkB,YAAA,EAAc,YAAA,EAAc,MAAM,CAAA;AAAA,EACvD,GAAG,iBAAA,CAAkB,OAAA,EAAS,OAAA,EAAS,OAAO,CAAA;AAAA,EAC9C,GAAG,iBAAA,CAAkB,WAAA,EAAa,WAAA,EAAa,OAAO,CAAA;AAAA,EACtD,GAAG,iBAAA,CAAkB,iBAAA,EAAmB,iBAAA,EAAmB,OAAO,CAAA;AAAA,EAClE,GAAG,iBAAA,CAAkB,sBAAA,EAAwB,sBAAA,EAAwB,MAAM;AAC7E;AAEO,MAAM,gBAAA,GAAmB;AAAA;AAAA;AAAA;AAAA,EAI9B,uBAAA,EAAyB,6BAAA;AAAA,EACzB,qBAAA,EAAuB,2BAAA;AAAA,EACvB,eAAA,EAAiB,sBAAA;AAAA,EACjB,oBAAA,EAAsB,0BAAA;AAAA,EACtB,2BAAA,EAA6B,gCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAK7B,gCAAA,EAAkC;AACpC;AAkBA,SAAS,iBAAA,CAAkB,WAAA,EAAqB,YAAA,EAAsB,IAAA,EAAiD;AACrH,EAAA,OAAO,CAAC,eAAA,EAAiB,gBAAgB,CAAA,CAAE,IAAI,CAAA,QAAA,MAAa;AAAA,IAC1D,WAAA;AAAA,IACA,QAAQ,EAAE,IAAA,EAAM,IAAA,EAAM,YAAA,EAAc,kBAAkB,QAAA,EAAS;AAAA,IAC/D,aAAA,EAAe,EAAE,YAAA,EAAc,IAAA;AAAK,GACtC,CAAE,CAAA;AACJ;;;;"} | ||
| {"version":3,"file":"vercel-ai.js","sources":["../../../../src/orchestrion/config/vercel-ai.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';\n\nexport const vercelAiConfig = [\n // Vercel AI v6: mirror the v7 native `ai:telemetry` channel by injecting\n // channels into the top-level entry points. `resolveLanguageModel` is wrapped\n // not to span it, but so the subscriber can monkey-patch `doGenerate`/\n // `doStream` on the returned model (the only way to span the model call,\n // which is an inline call with no injectable definition in `ai`).\n // `streamText` returns its result synchronously (streaming is lazy), so it's\n // `Sync`; the subscriber binds the span via `bindTracingChannelToSpan`, which\n // ends it when the (synchronous) call returns.\n\n // The majority of entrypoints are present in all versions we support\n ...vercelAiEntries('>=4.0.0 <7.0.0', 'generateText', 'generateText', 'Async'),\n ...vercelAiEntries('>=4.0.0 <7.0.0', 'streamText', 'streamText', 'Sync'),\n ...vercelAiEntries('>=4.0.0 <7.0.0', 'generateObject', 'generateObject', 'Async'),\n ...vercelAiEntries('>=4.0.0 <7.0.0', 'embed', 'embed', 'Async'),\n ...vercelAiEntries('>=4.0.0 <7.0.0', 'embedMany', 'embedMany', 'Async'),\n\n // The following entry is only present in v5 and later\n ...vercelAiEntries('>=5.0.0 <7.0.0', 'resolveLanguageModel', 'resolveLanguageModel', 'Sync'),\n\n // The following entry is only present in v6 and later\n ...vercelAiEntries('>=6.0.0 <7.0.0', 'executeToolCall', 'executeToolCall', 'Async'),\n] satisfies InstrumentationConfig[];\n\nexport const vercelAiChannels = {\n // Vercel AI (`ai`): orchestrion injects these so the same channel-based\n // integration that consumes `ai`'s native `ai:telemetry` channel (v7) can\n // also instrument v4/v5/v6. Each maps to a top-level function in `ai`'s bundle.\n // All three versions share the same channel names (the subscriber is version-agnostic);\n // `VERCEL_AI_EXECUTE_TOOL_CALL` is v6-only (v4/v5 have no `executeToolCall` export) and\n // `VERCEL_AI_RESOLVE_LANGUAGE_MODEL` is v5/v6-only (v4 has no such chokepoint).\n VERCEL_AI_GENERATE_TEXT: 'orchestrion:ai:generateText',\n VERCEL_AI_STREAM_TEXT: 'orchestrion:ai:streamText',\n VERCEL_AI_GENERATE_OBJECT: 'orchestrion:ai:generateObject',\n VERCEL_AI_EMBED: 'orchestrion:ai:embed',\n VERCEL_AI_EMBED_MANY: 'orchestrion:ai:embedMany',\n VERCEL_AI_EXECUTE_TOOL_CALL: 'orchestrion:ai:executeToolCall',\n VERCEL_AI_RESOLVE_LANGUAGE_MODEL: 'orchestrion:ai:resolveLanguageModel',\n} as const;\n\n/**\n * The central list of channel injections orchestrion should perform.\n *\n * This module has NO side effects — it's the only thing both the runtime hook\n * (`runtime/import-hook.mjs`) and the bundler plugins (`bundler/vite.ts`, …)\n * import from. Adding a new instrumented method is one entry here plus one\n * subscriber in `integrations/<lib>/tracing-channel.ts`.\n *\n * `channelName` here is the unprefixed suffix; the actual diagnostics_channel\n * name is `orchestrion:${module.name}:${channelName}` (see `channels.ts`).\n */\n/**\n * `ai` ships a single bundled entry per module system, so each instrumented\n * function needs one config entry per file (the app loads whichever matches its\n * module system). This expands a single target into both.\n */\nfunction vercelAiEntries(\n versionRange: string,\n channelName: string,\n functionName: string,\n kind: 'Async' | 'Sync',\n): InstrumentationConfig[] {\n return ['dist/index.js', 'dist/index.mjs'].map(filePath => ({\n channelName,\n module: { name: 'ai', versionRange, filePath },\n functionQuery: { functionName, kind },\n }));\n}\n"],"names":[],"mappings":"AAEO,MAAM,cAAA,GAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW5B,GAAG,eAAA,CAAgB,gBAAA,EAAkB,cAAA,EAAgB,gBAAgB,OAAO,CAAA;AAAA,EAC5E,GAAG,eAAA,CAAgB,gBAAA,EAAkB,YAAA,EAAc,cAAc,MAAM,CAAA;AAAA,EACvE,GAAG,eAAA,CAAgB,gBAAA,EAAkB,gBAAA,EAAkB,kBAAkB,OAAO,CAAA;AAAA,EAChF,GAAG,eAAA,CAAgB,gBAAA,EAAkB,OAAA,EAAS,SAAS,OAAO,CAAA;AAAA,EAC9D,GAAG,eAAA,CAAgB,gBAAA,EAAkB,WAAA,EAAa,aAAa,OAAO,CAAA;AAAA;AAAA,EAGtE,GAAG,eAAA,CAAgB,gBAAA,EAAkB,sBAAA,EAAwB,wBAAwB,MAAM,CAAA;AAAA;AAAA,EAG3F,GAAG,eAAA,CAAgB,gBAAA,EAAkB,iBAAA,EAAmB,mBAAmB,OAAO;AACpF;AAEO,MAAM,gBAAA,GAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO9B,uBAAA,EAAyB,6BAAA;AAAA,EACzB,qBAAA,EAAuB,2BAAA;AAAA,EACvB,yBAAA,EAA2B,+BAAA;AAAA,EAC3B,eAAA,EAAiB,sBAAA;AAAA,EACjB,oBAAA,EAAsB,0BAAA;AAAA,EACtB,2BAAA,EAA6B,gCAAA;AAAA,EAC7B,gCAAA,EAAkC;AACpC;AAkBA,SAAS,eAAA,CACP,YAAA,EACA,WAAA,EACA,YAAA,EACA,IAAA,EACyB;AACzB,EAAA,OAAO,CAAC,eAAA,EAAiB,gBAAgB,CAAA,CAAE,IAAI,CAAA,QAAA,MAAa;AAAA,IAC1D,WAAA;AAAA,IACA,MAAA,EAAQ,EAAE,IAAA,EAAM,IAAA,EAAM,cAAc,QAAA,EAAS;AAAA,IAC7C,aAAA,EAAe,EAAE,YAAA,EAAc,IAAA;AAAK,GACtC,CAAE,CAAA;AACJ;;;;"} |
@@ -0,2 +1,6 @@ | ||
| import { amqplibChannelIntegration } from '../integrations/tracing-channel/amqplib.js'; | ||
| import { anthropicChannelIntegration } from '../integrations/tracing-channel/anthropic.js'; | ||
| import { googleGenAIChannelIntegration } from '../integrations/tracing-channel/google-genai.js'; | ||
| import { graphqlDiagnosticsChannelIntegration } from '../integrations/tracing-channel/graphql/index.js'; | ||
| export { graphqlChannelIntegration } from '../integrations/tracing-channel/graphql/index.js'; | ||
| import { hapiChannelIntegration } from '../integrations/tracing-channel/hapi.js'; | ||
@@ -8,7 +12,11 @@ export { ioredisChannelIntegration } from '../integrations/tracing-channel/ioredis.js'; | ||
| import { postgresChannelIntegration } from '../integrations/tracing-channel/postgres.js'; | ||
| import { postgresJsChannelIntegration } from '../integrations/tracing-channel/postgres-js.js'; | ||
| import { vercelAiChannelIntegration } from '../integrations/tracing-channel/vercel-ai.js'; | ||
| import { expressChannelIntegration } from '../integrations/tracing-channel/express/index.js'; | ||
| export { detectOrchestrionSetup, isOrchestrionInjected } from './detect.js'; | ||
| export { redisChannelIntegration } from '../integrations/tracing-channel/redis.js'; | ||
| const channelIntegrations = { | ||
| postgresIntegration: postgresChannelIntegration, | ||
| postgresJsIntegration: postgresJsChannelIntegration, | ||
| mysqlIntegration: mysqlChannelIntegration, | ||
@@ -18,7 +26,11 @@ lruMemoizerIntegration: lruMemoizerChannelIntegration, | ||
| anthropicIntegration: anthropicChannelIntegration, | ||
| googleGenAIIntegration: googleGenAIChannelIntegration, | ||
| vercelAiIntegration: vercelAiChannelIntegration, | ||
| hapiIntegration: hapiChannelIntegration | ||
| amqplibIntegration: amqplibChannelIntegration, | ||
| hapiIntegration: hapiChannelIntegration, | ||
| expressIntegration: expressChannelIntegration, | ||
| graphqlIntegration: graphqlDiagnosticsChannelIntegration | ||
| }; | ||
| export { anthropicChannelIntegration, channelIntegrations, hapiChannelIntegration, lruMemoizerChannelIntegration, mysqlChannelIntegration, openaiChannelIntegration, postgresChannelIntegration, vercelAiChannelIntegration }; | ||
| export { amqplibChannelIntegration, anthropicChannelIntegration, channelIntegrations, expressChannelIntegration, googleGenAIChannelIntegration, hapiChannelIntegration, lruMemoizerChannelIntegration, mysqlChannelIntegration, openaiChannelIntegration, postgresChannelIntegration, postgresJsChannelIntegration, vercelAiChannelIntegration }; | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../src/orchestrion/index.ts"],"sourcesContent":["import { anthropicChannelIntegration } from '../integrations/tracing-channel/anthropic';\nimport { hapiChannelIntegration } from '../integrations/tracing-channel/hapi';\nimport { ioredisChannelIntegration } from '../integrations/tracing-channel/ioredis';\nimport { lruMemoizerChannelIntegration } from '../integrations/tracing-channel/lru-memoizer';\nimport { mysqlChannelIntegration } from '../integrations/tracing-channel/mysql';\nimport { openaiChannelIntegration } from '../integrations/tracing-channel/openai';\nimport { postgresChannelIntegration } from '../integrations/tracing-channel/postgres';\nimport { vercelAiChannelIntegration } from '../integrations/tracing-channel/vercel-ai';\n\nexport { detectOrchestrionSetup, isOrchestrionInjected } from './detect';\nexport {\n anthropicChannelIntegration,\n hapiChannelIntegration,\n ioredisChannelIntegration,\n lruMemoizerChannelIntegration,\n mysqlChannelIntegration,\n openaiChannelIntegration,\n postgresChannelIntegration,\n vercelAiChannelIntegration,\n};\nexport type { IORedisChannelIntegrationOptions, IORedisResponseHook } from '../integrations/tracing-channel/ioredis';\n\n/**\n * The canonical set of orchestrion diagnostics-channel integrations, keyed by their public\n * (OTel-parity) factory name.\n *\n * Single source of truth: add a new channel integration here and every consumer — the `@sentry/node`\n * opt-in helper (`experimentalUseDiagnosticsChannelInjection`) and its public\n * `diagnosticsChannelInjectionIntegrations()` map — picks it up automatically, so there's no separate\n * list to keep in sync.\n *\n * NOTE: `ioredisChannelIntegration` is intentionally NOT here. It only partially replaces the\n * composite OTel `Redis` integration and needs the node SDK's redis cache `responseHook` (which\n * can't live in `server-utils`), so `@sentry/node` wires it up separately.\n */\nexport const channelIntegrations = {\n postgresIntegration: postgresChannelIntegration,\n mysqlIntegration: mysqlChannelIntegration,\n lruMemoizerIntegration: lruMemoizerChannelIntegration,\n openaiIntegration: openaiChannelIntegration,\n anthropicIntegration: anthropicChannelIntegration,\n vercelAiIntegration: vercelAiChannelIntegration,\n hapiIntegration: hapiChannelIntegration,\n} as const;\n"],"names":[],"mappings":";;;;;;;;;;AAmCO,MAAM,mBAAA,GAAsB;AAAA,EACjC,mBAAA,EAAqB,0BAAA;AAAA,EACrB,gBAAA,EAAkB,uBAAA;AAAA,EAClB,sBAAA,EAAwB,6BAAA;AAAA,EACxB,iBAAA,EAAmB,wBAAA;AAAA,EACnB,oBAAA,EAAsB,2BAAA;AAAA,EACtB,mBAAA,EAAqB,0BAAA;AAAA,EACrB,eAAA,EAAiB;AACnB;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../src/orchestrion/index.ts"],"sourcesContent":["import { amqplibChannelIntegration } from '../integrations/tracing-channel/amqplib';\nimport { anthropicChannelIntegration } from '../integrations/tracing-channel/anthropic';\nimport { googleGenAIChannelIntegration } from '../integrations/tracing-channel/google-genai';\nimport {\n graphqlChannelIntegration,\n graphqlDiagnosticsChannelIntegration,\n} from '../integrations/tracing-channel/graphql';\nimport { hapiChannelIntegration } from '../integrations/tracing-channel/hapi';\nimport { ioredisChannelIntegration } from '../integrations/tracing-channel/ioredis';\nimport { lruMemoizerChannelIntegration } from '../integrations/tracing-channel/lru-memoizer';\nimport { mysqlChannelIntegration } from '../integrations/tracing-channel/mysql';\nimport { openaiChannelIntegration } from '../integrations/tracing-channel/openai';\nimport { postgresChannelIntegration } from '../integrations/tracing-channel/postgres';\nimport { postgresJsChannelIntegration } from '../integrations/tracing-channel/postgres-js';\nimport { vercelAiChannelIntegration } from '../integrations/tracing-channel/vercel-ai';\nimport { expressChannelIntegration } from '../integrations/tracing-channel/express';\n\nexport { detectOrchestrionSetup, isOrchestrionInjected } from './detect';\nexport {\n amqplibChannelIntegration,\n anthropicChannelIntegration,\n googleGenAIChannelIntegration,\n graphqlChannelIntegration,\n hapiChannelIntegration,\n ioredisChannelIntegration,\n lruMemoizerChannelIntegration,\n mysqlChannelIntegration,\n openaiChannelIntegration,\n postgresChannelIntegration,\n postgresJsChannelIntegration,\n vercelAiChannelIntegration,\n expressChannelIntegration,\n};\nexport type { IORedisChannelIntegrationOptions, IORedisResponseHook } from '../integrations/tracing-channel/ioredis';\nexport type { PostgresJsChannelIntegrationOptions } from '../integrations/tracing-channel/postgres-js';\nexport { redisChannelIntegration } from '../integrations/tracing-channel/redis';\nexport type { RedisChannelIntegrationOptions, RedisResponseHook } from '../integrations/tracing-channel/redis';\n\n// The structural `graphql` package types are the single source of truth shared with `@sentry/node`'s\n// vendored OTel graphql instrumentation (re-exported from here so the two can't drift).\nexport type * from '../integrations/tracing-channel/graphql/graphql-types';\n\n/**\n * The canonical set of orchestrion diagnostics-channel integrations, keyed by their public\n * (OTel-parity) factory name.\n *\n * Single source of truth: add a new channel integration here and every consumer — the `@sentry/node`\n * opt-in helper (`experimentalUseDiagnosticsChannelInjection`) and its public\n * `diagnosticsChannelInjectionIntegrations()` map — picks it up automatically, so there's no separate\n * list to keep in sync.\n *\n * NOTE: `ioredisChannelIntegration` and `redisChannelIntegration` are intentionally NOT here. They\n * only partially replace the composite OTel `Redis` integration and need the node SDK's redis cache\n * `responseHook` (which can't live in `server-utils`), so `@sentry/node` wires them up separately.\n */\nexport const channelIntegrations = {\n postgresIntegration: postgresChannelIntegration,\n postgresJsIntegration: postgresJsChannelIntegration,\n mysqlIntegration: mysqlChannelIntegration,\n lruMemoizerIntegration: lruMemoizerChannelIntegration,\n openaiIntegration: openaiChannelIntegration,\n anthropicIntegration: anthropicChannelIntegration,\n googleGenAIIntegration: googleGenAIChannelIntegration,\n vercelAiIntegration: vercelAiChannelIntegration,\n amqplibIntegration: amqplibChannelIntegration,\n hapiIntegration: hapiChannelIntegration,\n expressIntegration: expressChannelIntegration,\n graphqlIntegration: graphqlDiagnosticsChannelIntegration,\n} as const;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAuDO,MAAM,mBAAA,GAAsB;AAAA,EACjC,mBAAA,EAAqB,0BAAA;AAAA,EACrB,qBAAA,EAAuB,4BAAA;AAAA,EACvB,gBAAA,EAAkB,uBAAA;AAAA,EAClB,sBAAA,EAAwB,6BAAA;AAAA,EACxB,iBAAA,EAAmB,wBAAA;AAAA,EACnB,oBAAA,EAAsB,2BAAA;AAAA,EACtB,sBAAA,EAAwB,6BAAA;AAAA,EACxB,mBAAA,EAAqB,0BAAA;AAAA,EACrB,kBAAA,EAAoB,yBAAA;AAAA,EACpB,eAAA,EAAiB,sBAAA;AAAA,EACjB,kBAAA,EAAoB,yBAAA;AAAA,EACpB,kBAAA,EAAoB;AACtB;;;;"} |
@@ -1,1 +0,1 @@ | ||
| {"type":"module","version":"10.64.0","sideEffects":false} | ||
| {"type":"module","version":"10.65.0","sideEffects":false} |
| import { SERVER_PORT, SERVER_ADDRESS, DB_QUERY_TEXT, DB_SYSTEM_NAME, DB_OPERATION_BATCH_SIZE } from '@sentry/conventions/attributes'; | ||
| import { debug, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../debug-build.js'; | ||
| import { startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
| import { bindTracingChannelToSpan } from '../tracing-channel.js'; | ||
@@ -13,30 +12,20 @@ | ||
| const DB_SYSTEM_NAME_VALUE_REDIS = "redis"; | ||
| let subscribed = false; | ||
| let currentResponseHook; | ||
| let activeUnbinds = []; | ||
| function subscribeRedisDiagnosticChannels(tracingChannel, responseHook) { | ||
| currentResponseHook = responseHook; | ||
| if (subscribed) return; | ||
| subscribed = true; | ||
| try { | ||
| activeUnbinds.push( | ||
| setupCommandChannel(tracingChannel, REDIS_DC_CHANNEL_COMMAND, (data) => data.args.slice(1)), | ||
| setupBatchChannel( | ||
| tracingChannel, | ||
| REDIS_DC_CHANNEL_BATCH, | ||
| (data) => data.batchMode === "PIPELINE" ? "PIPELINE" : "MULTI" | ||
| ), | ||
| setupConnectChannel(tracingChannel, REDIS_DC_CHANNEL_CONNECT), | ||
| // ioredis: args already exclude the command name; no slicing needed. And | ||
| // ioredis has no separate batch channel — pipeline/MULTI metadata rides | ||
| // on the per-command payload via `batchMode`/`batchSize`. | ||
| setupCommandChannel(tracingChannel, IOREDIS_DC_CHANNEL_COMMAND, (data) => data.args), | ||
| setupConnectChannel(tracingChannel, IOREDIS_DC_CHANNEL_CONNECT) | ||
| ); | ||
| } catch { | ||
| DEBUG_BUILD && debug.log("Redis node:diagnostics_channel subscription failed."); | ||
| } | ||
| setupCommandChannel( | ||
| tracingChannel, | ||
| REDIS_DC_CHANNEL_COMMAND, | ||
| (data) => data.args.slice(1), | ||
| responseHook | ||
| ); | ||
| setupBatchChannel( | ||
| tracingChannel, | ||
| REDIS_DC_CHANNEL_BATCH, | ||
| (data) => data.batchMode === "PIPELINE" ? "PIPELINE" : "MULTI" | ||
| ); | ||
| setupConnectChannel(tracingChannel, REDIS_DC_CHANNEL_CONNECT); | ||
| setupCommandChannel(tracingChannel, IOREDIS_DC_CHANNEL_COMMAND, (data) => data.args, responseHook); | ||
| setupConnectChannel(tracingChannel, IOREDIS_DC_CHANNEL_CONNECT); | ||
| } | ||
| function setupCommandChannel(tracingChannel, channelName, getCommandArgs) { | ||
| return bindTracingChannelToSpan( | ||
| function setupCommandChannel(tracingChannel, channelName, getCommandArgs, responseHook) { | ||
| bindTracingChannelToSpan( | ||
| tracingChannel(channelName), | ||
@@ -64,9 +53,9 @@ (data) => { | ||
| if ("error" in data) return; | ||
| runResponseHook(span, data.command, getCommandArgs(data), data.result); | ||
| runResponseHook(responseHook, span, data.command, getCommandArgs(data), data.result); | ||
| } | ||
| } | ||
| ).unbind; | ||
| ); | ||
| } | ||
| function setupBatchChannel(tracingChannel, channelName, getOperationName) { | ||
| return bindTracingChannelToSpan( | ||
| bindTracingChannelToSpan( | ||
| tracingChannel(channelName), | ||
@@ -89,6 +78,6 @@ (data) => { | ||
| { captureError: false } | ||
| ).unbind; | ||
| ); | ||
| } | ||
| function setupConnectChannel(tracingChannel, channelName) { | ||
| return bindTracingChannelToSpan( | ||
| bindTracingChannelToSpan( | ||
| tracingChannel(channelName), | ||
@@ -108,6 +97,5 @@ (data) => { | ||
| { captureError: false } | ||
| ).unbind; | ||
| ); | ||
| } | ||
| function runResponseHook(span, command, args, result) { | ||
| const hook = currentResponseHook; | ||
| function runResponseHook(hook, span, command, args, result) { | ||
| if (!hook) return; | ||
@@ -114,0 +102,0 @@ try { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"redis-dc-subscriber.js","sources":["../../../src/redis/redis-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport {\n DB_OPERATION_BATCH_SIZE,\n DB_QUERY_TEXT,\n DB_SYSTEM_NAME,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport { debug, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n// Channel names published by node-redis >= 5.12.0 and ioredis >= 5.11.0.\n// Hardcoded so the subscriber does not have to import either library — the\n// channels just have to be subscribed to before the user's redis code\n// publishes.\nexport const REDIS_DC_CHANNEL_COMMAND = 'node-redis:command';\nexport const REDIS_DC_CHANNEL_BATCH = 'node-redis:batch';\nexport const REDIS_DC_CHANNEL_CONNECT = 'node-redis:connect';\nexport const IOREDIS_DC_CHANNEL_COMMAND = 'ioredis:command';\nexport const IOREDIS_DC_CHANNEL_CONNECT = 'ioredis:connect';\n\nconst ORIGIN = 'auto.db.redis.diagnostic_channel';\nconst DB_SYSTEM_NAME_VALUE_REDIS = 'redis';\n\n/**\n * Shape of the `node-redis:command` channel payload published by node-redis.\n *\n * Both `command` and `args` are already redacted by node-redis itself (see\n * `sanitizeArgs` in @redis/client) using the OTel `redis-common` rules. The\n * arg array is `[<command>, <safe arg>, ..., '?', ...]` — `?` replaces any\n * value the library considers sensitive. Subscribers can emit `args` directly\n * as `db.query.text` without further serialization.\n */\nexport interface RedisCommandData {\n command: string;\n /** First arg is the command name itself; consumers should slice it off. */\n args: string[];\n database?: number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Shape of the `ioredis:command` channel payload published by ioredis >= 5.11.0.\n *\n * As with node-redis, args are already sanitized by ioredis (`sanitizeArgs` in\n * `lib/tracing.ts`) before publishing. Unlike node-redis, the command name is\n * NOT prepended to `args`.\n */\nexport interface IORedisCommandData {\n command: string;\n args: string[];\n batchMode?: 'MULTI';\n batchSize?: number;\n database?: number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\n/** Shape of the `node-redis:batch` channel payload published by node-redis. */\nexport interface RedisBatchData {\n batchMode?: 'MULTI' | 'PIPELINE';\n batchSize?: number;\n database?: number;\n clientId?: string | number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown[];\n error?: Error;\n}\n\n/** Shape of the `*:connect` channel payload published by node-redis and ioredis. */\nexport interface RedisConnectData {\n serverAddress?: string;\n serverPort?: number;\n url?: string;\n error?: Error;\n}\n\n/**\n * Optional callback invoked once the redis command response arrives. Useful\n * for attaching response-derived attributes (e.g. cache hit/miss, payload size).\n *\n * Mirrors `@opentelemetry/instrumentation-ioredis`' response hook so existing\n * Sentry node code (`cacheResponseHook`) can be reused unchanged.\n */\nexport type RedisDiagnosticChannelResponseHook = (\n span: Span,\n cmdName: string,\n cmdArgs: string[],\n result: unknown,\n) => void;\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Both Node and Deno pass `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type RedisTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\nlet subscribed = false;\nlet currentResponseHook: RedisDiagnosticChannelResponseHook | undefined;\nlet activeUnbinds: Array<() => void> = [];\n\n/**\n * Subscribe Sentry span handlers to node-redis and ioredis diagnostics-channel\n * events: `node-redis:command`/`:batch`/`:connect` (published by node-redis\n * >= 5.12.0) and `ioredis:command`/`:connect` (published by ioredis >= 5.11.0).\n *\n * On older client versions the channels are never published to, so subscribers\n * are inert — there is no double-instrumentation against any IITM-based\n * patcher gated to those older versions.\n *\n * Idempotent: subsequent calls update the response hook but do not\n * re-subscribe.\n */\nexport function subscribeRedisDiagnosticChannels(\n tracingChannel: RedisTracingChannelFactory,\n responseHook?: RedisDiagnosticChannelResponseHook,\n): void {\n currentResponseHook = responseHook;\n if (subscribed) return;\n subscribed = true;\n\n try {\n // node-redis: command name appears as args[0] in the channel payload, so\n // strip it before the statement and response hook see it.\n activeUnbinds.push(\n setupCommandChannel<RedisCommandData>(tracingChannel, REDIS_DC_CHANNEL_COMMAND, data => data.args.slice(1)),\n setupBatchChannel(tracingChannel, REDIS_DC_CHANNEL_BATCH, data =>\n data.batchMode === 'PIPELINE' ? 'PIPELINE' : 'MULTI',\n ),\n setupConnectChannel(tracingChannel, REDIS_DC_CHANNEL_CONNECT),\n // ioredis: args already exclude the command name; no slicing needed. And\n // ioredis has no separate batch channel — pipeline/MULTI metadata rides\n // on the per-command payload via `batchMode`/`batchSize`.\n setupCommandChannel<IORedisCommandData>(tracingChannel, IOREDIS_DC_CHANNEL_COMMAND, data => data.args),\n setupConnectChannel(tracingChannel, IOREDIS_DC_CHANNEL_CONNECT),\n );\n } catch {\n // The factory may rely on `node:diagnostics_channel`, which isn't always\n // available. Fail closed; the SDK simply won't emit redis spans here.\n DEBUG_BUILD && debug.log('Redis node:diagnostics_channel subscription failed.');\n }\n}\n\nfunction setupCommandChannel<T extends RedisCommandData | IORedisCommandData>(\n tracingChannel: RedisTracingChannelFactory,\n channelName: string,\n getCommandArgs: (data: T) => string[],\n): () => void {\n return bindTracingChannelToSpan(\n tracingChannel<T>(channelName),\n data => {\n // `args` is already sanitized by the publishing library (node-redis /\n // ioredis call their own `sanitizeArgs` before publishing). Join with\n // spaces to mirror the format the libraries themselves intend.\n const args = getCommandArgs(data);\n const statement = args.length ? `${data.command} ${args.join(' ')}` : data.command;\n return startInactiveSpan({\n name: `redis-${data.command}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n [DB_QUERY_TEXT]: statement,\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n {\n // Command failures are surfaced to (and usually handled by) the caller; only annotate the\n // span so we don't emit a duplicate error event for every failed command.\n captureError: false,\n beforeSpanEnd(span, data) {\n if ('error' in data) return;\n runResponseHook(span, data.command, getCommandArgs(data), data.result);\n },\n },\n ).unbind;\n}\n\nfunction setupBatchChannel(\n tracingChannel: RedisTracingChannelFactory,\n channelName: string,\n getOperationName: (data: RedisBatchData) => string,\n): () => void {\n return bindTracingChannelToSpan(\n tracingChannel<RedisBatchData>(channelName),\n data => {\n return startInactiveSpan({\n name: getOperationName(data),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n // should only include batch size greater than 1,\n // or else it isn't properly considered a \"batch\"\n ...(Number(data.batchSize) > 1 ? { [DB_OPERATION_BATCH_SIZE]: data.batchSize } : {}),\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n { captureError: false },\n ).unbind;\n}\n\nfunction setupConnectChannel(tracingChannel: RedisTracingChannelFactory, channelName: string): () => void {\n return bindTracingChannelToSpan(\n tracingChannel<RedisConnectData>(channelName),\n data => {\n return startInactiveSpan({\n name: 'redis-connect',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis.connect',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n { captureError: false },\n ).unbind;\n}\n\nfunction runResponseHook(span: Span, command: string, args: string[], result: unknown): void {\n const hook = currentResponseHook;\n if (!hook) return;\n try {\n hook(span, command, args, result);\n } catch {\n // never let user hooks break instrumentation\n }\n}\n\n/** Test-only: detach all channel bindings and reset module-local subscribe state. */\nexport function _resetRedisDiagnosticChannelsForTesting(): void {\n activeUnbinds.forEach(unbind => unbind());\n activeUnbinds = [];\n subscribed = false;\n currentResponseHook = undefined;\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"redis-dc-subscriber.js","sources":["../../../src/redis/redis-dc-subscriber.ts"],"sourcesContent":["import type { TracingChannel } from 'node:diagnostics_channel';\nimport {\n DB_OPERATION_BATCH_SIZE,\n DB_QUERY_TEXT,\n DB_SYSTEM_NAME,\n SERVER_ADDRESS,\n SERVER_PORT,\n} from '@sentry/conventions/attributes';\nimport type { Span } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan } from '@sentry/core';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n// Channel names published by node-redis >= 5.12.0 and ioredis >= 5.11.0.\n// Hardcoded so the subscriber does not have to import either library — the\n// channels just have to be subscribed to before the user's redis code\n// publishes.\nexport const REDIS_DC_CHANNEL_COMMAND = 'node-redis:command';\nexport const REDIS_DC_CHANNEL_BATCH = 'node-redis:batch';\nexport const REDIS_DC_CHANNEL_CONNECT = 'node-redis:connect';\nexport const IOREDIS_DC_CHANNEL_COMMAND = 'ioredis:command';\nexport const IOREDIS_DC_CHANNEL_CONNECT = 'ioredis:connect';\n\nconst ORIGIN = 'auto.db.redis.diagnostic_channel';\nconst DB_SYSTEM_NAME_VALUE_REDIS = 'redis';\n\n/**\n * Shape of the `node-redis:command` channel payload published by node-redis.\n *\n * Both `command` and `args` are already redacted by node-redis itself (see\n * `sanitizeArgs` in @redis/client) using the OTel `redis-common` rules. The\n * arg array is `[<command>, <safe arg>, ..., '?', ...]` — `?` replaces any\n * value the library considers sensitive. Subscribers can emit `args` directly\n * as `db.query.text` without further serialization.\n */\nexport interface RedisCommandData {\n command: string;\n /** First arg is the command name itself; consumers should slice it off. */\n args: string[];\n database?: number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\n/**\n * Shape of the `ioredis:command` channel payload published by ioredis >= 5.11.0.\n *\n * As with node-redis, args are already sanitized by ioredis (`sanitizeArgs` in\n * `lib/tracing.ts`) before publishing. Unlike node-redis, the command name is\n * NOT prepended to `args`.\n */\nexport interface IORedisCommandData {\n command: string;\n args: string[];\n batchMode?: 'MULTI';\n batchSize?: number;\n database?: number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\n/** Shape of the `node-redis:batch` channel payload published by node-redis. */\nexport interface RedisBatchData {\n batchMode?: 'MULTI' | 'PIPELINE';\n batchSize?: number;\n database?: number;\n clientId?: string | number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown[];\n error?: Error;\n}\n\n/** Shape of the `*:connect` channel payload published by node-redis and ioredis. */\nexport interface RedisConnectData {\n serverAddress?: string;\n serverPort?: number;\n url?: string;\n error?: Error;\n}\n\n/**\n * Optional callback invoked once the redis command response arrives. Useful\n * for attaching response-derived attributes (e.g. cache hit/miss, payload size).\n *\n * Mirrors `@opentelemetry/instrumentation-ioredis`' response hook so existing\n * Sentry node code (`cacheResponseHook`) can be reused unchanged.\n */\nexport type RedisDiagnosticChannelResponseHook = (\n span: Span,\n cmdName: string,\n cmdArgs: string[],\n result: unknown,\n) => void;\n\n/**\n * Platform-provided factory that creates a native tracing channel for the given name. The\n * subscriber binds the span and its lifecycle onto the channel via `bindTracingChannelToSpan`,\n * which propagates the active span through the runtime's async context.\n *\n * Both Node and Deno pass `node:diagnostics_channel`'s `tracingChannel` directly.\n */\nexport type RedisTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/**\n * Subscribe Sentry span handlers to node-redis and ioredis diagnostics-channel\n * events: `node-redis:command`/`:batch`/`:connect` (published by node-redis\n * >= 5.12.0) and `ioredis:command`/`:connect` (published by ioredis >= 5.11.0).\n *\n * On older client versions the channels are never published to, so subscribers\n * are inert — there is no double-instrumentation against any IITM-based\n * patcher gated to those older versions.\n */\nexport function subscribeRedisDiagnosticChannels(\n tracingChannel: RedisTracingChannelFactory,\n responseHook?: RedisDiagnosticChannelResponseHook,\n): void {\n // node-redis: command name appears as args[0] in the channel payload, so\n // strip it before the statement and response hook see it.\n setupCommandChannel<RedisCommandData>(\n tracingChannel,\n REDIS_DC_CHANNEL_COMMAND,\n data => data.args.slice(1),\n responseHook,\n );\n setupBatchChannel(tracingChannel, REDIS_DC_CHANNEL_BATCH, data =>\n data.batchMode === 'PIPELINE' ? 'PIPELINE' : 'MULTI',\n );\n setupConnectChannel(tracingChannel, REDIS_DC_CHANNEL_CONNECT);\n // ioredis: args already exclude the command name; no slicing needed. And\n // ioredis has no separate batch channel — pipeline/MULTI metadata rides\n // on the per-command payload via `batchMode`/`batchSize`.\n setupCommandChannel<IORedisCommandData>(tracingChannel, IOREDIS_DC_CHANNEL_COMMAND, data => data.args, responseHook);\n setupConnectChannel(tracingChannel, IOREDIS_DC_CHANNEL_CONNECT);\n}\n\nfunction setupCommandChannel<T extends RedisCommandData | IORedisCommandData>(\n tracingChannel: RedisTracingChannelFactory,\n channelName: string,\n getCommandArgs: (data: T) => string[],\n responseHook?: RedisDiagnosticChannelResponseHook,\n): void {\n bindTracingChannelToSpan(\n tracingChannel<T>(channelName),\n data => {\n // `args` is already sanitized by the publishing library (node-redis /\n // ioredis call their own `sanitizeArgs` before publishing). Join with\n // spaces to mirror the format the libraries themselves intend.\n const args = getCommandArgs(data);\n const statement = args.length ? `${data.command} ${args.join(' ')}` : data.command;\n return startInactiveSpan({\n name: `redis-${data.command}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n [DB_QUERY_TEXT]: statement,\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n {\n // Command failures are surfaced to (and usually handled by) the caller; only annotate the\n // span so we don't emit a duplicate error event for every failed command.\n captureError: false,\n beforeSpanEnd(span, data) {\n if ('error' in data) return;\n runResponseHook(responseHook, span, data.command, getCommandArgs(data), data.result);\n },\n },\n );\n}\n\nfunction setupBatchChannel(\n tracingChannel: RedisTracingChannelFactory,\n channelName: string,\n getOperationName: (data: RedisBatchData) => string,\n): void {\n bindTracingChannelToSpan(\n tracingChannel<RedisBatchData>(channelName),\n data => {\n return startInactiveSpan({\n name: getOperationName(data),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n // should only include batch size greater than 1,\n // or else it isn't properly considered a \"batch\"\n ...(Number(data.batchSize) > 1 ? { [DB_OPERATION_BATCH_SIZE]: data.batchSize } : {}),\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n { captureError: false },\n );\n}\n\nfunction setupConnectChannel(tracingChannel: RedisTracingChannelFactory, channelName: string): void {\n bindTracingChannelToSpan(\n tracingChannel<RedisConnectData>(channelName),\n data => {\n return startInactiveSpan({\n name: 'redis-connect',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis.connect',\n [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS,\n ...(data.serverAddress != null ? { [SERVER_ADDRESS]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [SERVER_PORT]: data.serverPort } : {}),\n },\n });\n },\n { captureError: false },\n );\n}\n\nfunction runResponseHook(\n hook: RedisDiagnosticChannelResponseHook | undefined,\n span: Span,\n command: string,\n args: string[],\n result: unknown,\n): void {\n if (!hook) return;\n try {\n hook(span, command, args, result);\n } catch {\n // never let user hooks break instrumentation\n }\n}\n"],"names":[],"mappings":"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"} |
@@ -1,2 +0,2 @@ | ||
| import { getAsyncContextStrategy, getMainCarrier, debug, SPAN_STATUS_ERROR, captureException } from '@sentry/core'; | ||
| import { getAsyncContextStrategy, getMainCarrier, debug, getActiveSpan, captureException, SPAN_STATUS_ERROR, isObjectLike } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from './debug-build.js'; | ||
@@ -8,3 +8,3 @@ import { ERROR_TYPE } from '@sentry/conventions/attributes'; | ||
| function bindTracingChannelToSpan(channel, getSpan, opts) { | ||
| const handle = bindSpanToChannelStore(channel, getSpan); | ||
| const handle = bindSpanToChannelStore(channel, getSpan, opts); | ||
| const beforeSpanEnd = opts?.beforeSpanEnd; | ||
@@ -80,3 +80,3 @@ const deferSpanEnd = opts?.deferSpanEnd; | ||
| } | ||
| function bindSpanToChannelStore(channel, getSpan) { | ||
| function bindSpanToChannelStore(channel, getSpan, opts) { | ||
| const binding = getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.(); | ||
@@ -93,3 +93,4 @@ if (!binding) { | ||
| data._sentryCallerStore = asyncLocalStorage.getStore(); | ||
| const span = getSpan(data); | ||
| const shouldGetSpan = !opts?.requiresParentSpan || getActiveSpan(); | ||
| const span = shouldGetSpan ? getSpan(data) : void 0; | ||
| if (!span) { | ||
@@ -121,6 +122,6 @@ return data._sentryCallerStore; | ||
| function getErrorInfo(error) { | ||
| const isObject = !!error && typeof error === "object"; | ||
| const raw = isObject ? "message" in error ? error.message : void 0 : error; | ||
| const errorIsObject = isObjectLike(error); | ||
| const raw = errorIsObject ? "message" in error ? error.message : void 0 : error; | ||
| const message = raw ? String(raw) : void 0; | ||
| const type = isObject && "name" in error ? String(error.name) : "unknown"; | ||
| const type = errorIsObject && "name" in error ? String(error.name) : "unknown"; | ||
| return { | ||
@@ -127,0 +128,0 @@ message, |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"tracing-channel.js","sources":["../../src/tracing-channel.ts"],"sourcesContent":["import type { TracingChannel, TracingChannelSubscribers } from 'node:diagnostics_channel';\nimport type { AsyncLocalStorage } from 'node:async_hooks';\nimport type { ExclusiveEventHintOrCaptureContext, Span } from '@sentry/core';\nimport { debug, captureException, SPAN_STATUS_ERROR, getAsyncContextStrategy, getMainCarrier } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { ERROR_TYPE } from '@sentry/conventions/attributes';\n\nexport type TracingChannelPayloadWithSpan<TData extends object> = TData & {\n /**\n * The current active span for the traced call.\n */\n _sentrySpan?: Span;\n\n /**\n * The context's active store value, used to restore the context for asyncStart continuations for callback-based tracing.\n */\n _sentryCallerStore?: unknown;\n};\n\n/*\n * A type patch so that we don't have to handle all subscription types.\n */\nexport interface SentryTracingChannel<TData extends object = object> extends Omit<\n TracingChannel<TData, TracingChannelPayloadWithSpan<TData>>,\n 'subscribe' | 'unsubscribe'\n> {\n subscribe(subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>>): void;\n unsubscribe(subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>>): void;\n}\n\nexport interface TracingChannelLifeCycleOptions<TData extends object = object> {\n /**\n * Invoked with the span and the channel context object once the traced operation completes\n * Use it to enrich the span from the result/error (branch on `'error' in data` / `'result' in data`) or to run cleanup.\n */\n beforeSpanEnd?: (span: Span, data: TracingChannelPayloadWithSpan<TData>) => void;\n\n /**\n * Whether a thrown error is captured as a Sentry event. The span is always marked with error status regardless. Defaults to `false`.\n * You can alternatively pass a function that sets the ExclusiveEventHintOrCaptureContext on the captured error.\n * Set `true` for instrumentations that own the error boundary, (e.g: route handlers)\n * For database drivers, it is not recommended to set this at all.\n */\n captureError?: boolean | ((e: unknown) => ExclusiveEventHintOrCaptureContext);\n\n /**\n * Take ownership of *when* the span ends: return `true` and the helper won't end it on\n * `end`/`asyncEnd`. For results that settle out-of-band — e.g. a streamed `EventEmitter` that\n * completes via its own `'end'`/`'error'` events.\n *\n * Call `end` when it settles — `end()` on success, `end(error)` on failure. `end` owns *how* the span\n * ends (error status/attributes, `captureError`, `beforeSpanEnd`) and is idempotent. Default `false`\n * lets the helper end the span as usual.\n */\n deferSpanEnd?: (args: {\n span: Span;\n data: TracingChannelPayloadWithSpan<TData>;\n /** Ends the span: `end()` on success, `end(error)` on failure. Idempotent. */\n end: (error?: unknown) => void;\n }) => boolean;\n}\n\n/** Returned by {@link bindTracingChannelToSpan}: the bound channel plus a teardown handle. */\nexport interface TracingChannelBindingHandle<TData extends object = object> {\n /**\n * The tracing channel with the span bound into async context.\n */\n channel: SentryTracingChannel<TData>;\n\n /**\n * Tears down the binding: unsubscribes lifecycle handlers, when present, and unbinds the start store.\n * Idempotent, and a no-op when no async context binding was available.\n */\n unbind: () => void;\n}\n\nconst NOOP = (): void => {};\n\n/**\n * Bind a span and its lifecycle to a tracing channel so the span becomes the active async context\n * for the traced operation and is ended when the operation completes.\n *\n * `getSpan` may return `undefined` to opt a payload out entirely: nothing is bound, no span is\n * tracked, and the active context is left untouched. Use it for events that ride the same channel\n * but should reuse the enclosing span instead of opening (and ending) their own — e.g. an agent\n * loop's per-step events, where ending a freshly opened span would close the parent prematurely.\n */\nexport function bindTracingChannelToSpan<TData extends object>(\n channel: TracingChannel<TData, TData>,\n getSpan: (data: TracingChannelPayloadWithSpan<TData>) => Span | undefined,\n opts?: TracingChannelLifeCycleOptions<TData>,\n): TracingChannelBindingHandle<TData> {\n const handle = bindSpanToChannelStore(channel, getSpan);\n\n const beforeSpanEnd = opts?.beforeSpanEnd;\n const deferSpanEnd = opts?.deferSpanEnd;\n const getErrorHint = (e: unknown): ExclusiveEventHintOrCaptureContext => {\n if (typeof opts?.captureError === 'function') {\n return opts.captureError(e);\n }\n\n return {\n mechanism: {\n type: 'auto.diagnostic_channels.bind_span',\n handled: false,\n },\n };\n };\n\n // Apply Sentry error status + attributes (and capture, if configured) to a span. Shared by the\n // channel `error` lifecycle and the deferred `end` util so the two can't drift.\n const annotateSpanError = (span: Span, error: unknown): void => {\n if (opts?.captureError) {\n captureException(error, getErrorHint(error));\n }\n\n const { message, attributes } = getErrorInfo(error);\n span.setStatus({ code: SPAN_STATUS_ERROR, message });\n span.setAttributes(attributes);\n };\n\n // Creates an end fn for deferred handlers to use, ensures consistent span end behavior\n const makeDeferredEnd = (span: Span, data: TracingChannelPayloadWithSpan<TData>) => {\n let ended = false;\n\n return (error?: unknown): void => {\n if (ended) {\n return;\n }\n\n ended = true;\n if (error !== undefined) {\n annotateSpanError(span, error);\n }\n\n endBoundSpan(data, beforeSpanEnd);\n };\n };\n\n const subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>> = {\n start: NOOP,\n asyncStart: NOOP,\n end(data) {\n // The operation settled synchronously (returned or threw)\n // Presence checks because caller can return `undefined` result or throw a falsy value.\n if ('error' in data || 'result' in data) {\n const span = data._sentrySpan;\n if (span && deferSpanEnd?.({ span, data, end: makeDeferredEnd(span, data) })) {\n return;\n }\n endBoundSpan(data, beforeSpanEnd);\n }\n },\n error(data) {\n // No span was bound for this payload (`getSpan` returned undefined), so there is nothing to\n // annotate and no instrumentation that owns capturing this error.\n const span = data._sentrySpan;\n if (!span) {\n return;\n }\n\n annotateSpanError(span, data.error);\n },\n asyncEnd(data) {\n const span = data._sentrySpan;\n if (span && deferSpanEnd?.({ span, data, end: makeDeferredEnd(span, data) })) {\n return;\n }\n endBoundSpan(data, beforeSpanEnd);\n },\n };\n\n handle.channel.subscribe(subscribers);\n\n return {\n channel: handle.channel,\n unbind: () => {\n handle.channel.unsubscribe(subscribers);\n handle.unbind();\n },\n };\n}\n\n/**\n * Bind a span into the channel's async context so it becomes active for the traced operation,\n * without managing its lifecycle. The primitive behind {@link bindTracingChannelToSpan}, which\n * layers span-ending and error handling on top.\n *\n * `getSpan` may return `undefined` to leave the active context untouched for that payload.\n */\nfunction bindSpanToChannelStore<TData extends object>(\n channel: TracingChannel<TData, TData>,\n getSpan: (data: TracingChannelPayloadWithSpan<TData>) => Span | undefined,\n): TracingChannelBindingHandle<TData> {\n // Grabs the tracing channel binding defined by the AsyncContext strategy implementation\n const binding = getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.();\n\n // If no binding, then either the implementer doesn't support tracing channels or there is no active strategy\n // Failure mode here means we would still access the channel and potentially subscribe to it, but parenting will be off.\n if (!binding) {\n DEBUG_BUILD && debug.log('[TracingChannel] Could not access async context binding.');\n\n return {\n channel,\n unbind: NOOP,\n };\n }\n\n // Grab the ALS instance, we don't really care what is in it as long as the AsyncContext strategy can use its value to figure out parenting.\n const asyncLocalStorage = binding.asyncLocalStorage as AsyncLocalStorage<TData>;\n\n // bindStore activates the ALS for the traced call; any getStore() inside it returns the value bound for that context.\n // 1. Produce: getStoreWithActiveSpan(span) clones the current scope, plants the span via _INTERNAL_setSpanForScope, and returns { scope, isolationScope }, the active context carrying our span.\n // 2. Bind: the courier hands that opaque value to channel.start.bindStore(asyncLocalStorage, producer), which runs the traced op inside asyncLocalStorage.run(value, …); it never inspects the value.\n // 3. Read: inside the op, Sentry's scope machinery calls getScopes() → asyncStorage.getStore() on that same ALS, so getCurrentScope/getIsolationScope/getActiveSpan resolve to the scope carrying our span.\n // 4. Nest: any child span started in the traced op parents to that active span.\n channel.start.bindStore(asyncLocalStorage, (data: TracingChannelPayloadWithSpan<TData>) => {\n // Stash the caller's store before we swap in the span store, so `asyncStart` can restore it for\n // callback-style channels (see `_sentryCallerStore`).\n data._sentryCallerStore = asyncLocalStorage.getStore();\n\n const span = getSpan(data);\n if (!span) {\n // Leave the active context untouched so nested operations keep parenting to the enclosing span.\n return data._sentryCallerStore as TData;\n }\n data._sentrySpan = span;\n\n return binding.getStoreWithActiveSpan(span) as TData;\n });\n\n // Restore the caller's context for the async continuation. Only callback-style channels `runStores`\n // `asyncStart` (so the callback runs inside this store). promise channels `publish` it, leaving this\n // inert, their continuation already inherits the caller's context natively.\n channel.asyncStart.bindStore(asyncLocalStorage, (data: TracingChannelPayloadWithSpan<TData>) => {\n return data._sentryCallerStore as TData;\n });\n\n return {\n channel,\n unbind: () => {\n // Removes the stores\n channel.start.unbindStore(asyncLocalStorage);\n channel.asyncStart.unbindStore(asyncLocalStorage);\n },\n };\n}\n\nfunction endBoundSpan<TData extends object>(\n data: TracingChannelPayloadWithSpan<TData>,\n beforeSpanEnd: TracingChannelLifeCycleOptions<TData>['beforeSpanEnd'],\n): void {\n const span = data._sentrySpan;\n if (!span) {\n return;\n }\n beforeSpanEnd?.(span, data);\n span.end();\n}\n\ntype ErrorInfo = {\n message: string | undefined;\n attributes: Record<string, string>;\n};\n\n/**\n * Best-effort message and attribute extraction for thrown/rejected values.\n */\nfunction getErrorInfo(error: unknown): ErrorInfo {\n const isObject = !!error && typeof error === 'object';\n const raw = isObject ? ('message' in error ? error.message : undefined) : error;\n\n // Leave the status message unset if not set (e.g. an `AggregateError` from\n // ECONNREFUSED, whose `.message` is empty). Otherwise cast to string.\n const message = raw ? String(raw) : undefined;\n const type = isObject && 'name' in error ? String(error.name) : 'unknown';\n\n return {\n message,\n attributes: {\n [ERROR_TYPE]: type,\n },\n };\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"tracing-channel.js","sources":["../../src/tracing-channel.ts"],"sourcesContent":["import type { TracingChannel, TracingChannelSubscribers } from 'node:diagnostics_channel';\nimport type { AsyncLocalStorage } from 'node:async_hooks';\nimport type { ExclusiveEventHintOrCaptureContext, Span } from '@sentry/core';\nimport {\n debug,\n captureException,\n isObjectLike,\n SPAN_STATUS_ERROR,\n getAsyncContextStrategy,\n getMainCarrier,\n getActiveSpan,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { ERROR_TYPE } from '@sentry/conventions/attributes';\n\nexport type TracingChannelPayloadWithSpan<TData extends object> = TData & {\n /**\n * The current active span for the traced call.\n */\n _sentrySpan?: Span;\n\n /**\n * The context's active store value, used to restore the context for asyncStart continuations for callback-based tracing.\n */\n _sentryCallerStore?: unknown;\n};\n\n/*\n * A type patch so that we don't have to handle all subscription types.\n */\nexport interface SentryTracingChannel<TData extends object = object> extends Omit<\n TracingChannel<TData, TracingChannelPayloadWithSpan<TData>>,\n 'subscribe' | 'unsubscribe'\n> {\n subscribe(subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>>): void;\n unsubscribe(subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>>): void;\n}\n\nexport interface TracingChannelLifeCycleOptions<TData extends object = object> {\n /**\n * Invoked with the span and the channel context object once the traced operation completes\n * Use it to enrich the span from the result/error (branch on `'error' in data` / `'result' in data`) or to run cleanup.\n */\n beforeSpanEnd?: (span: Span, data: TracingChannelPayloadWithSpan<TData>) => void;\n\n /**\n * Whether a thrown error is captured as a Sentry event. The span is always marked with error status regardless. Defaults to `false`.\n * You can alternatively pass a function that sets the ExclusiveEventHintOrCaptureContext on the captured error.\n * Set `true` for instrumentations that own the error boundary, (e.g: route handlers)\n * For database drivers, it is not recommended to set this at all.\n */\n captureError?: boolean | ((e: unknown) => ExclusiveEventHintOrCaptureContext);\n\n /**\n * Take ownership of *when* the span ends: return `true` and the helper won't end it on\n * `end`/`asyncEnd`. For results that settle out-of-band — e.g. a streamed `EventEmitter` that\n * completes via its own `'end'`/`'error'` events.\n *\n * Call `end` when it settles — `end()` on success, `end(error)` on failure. `end` owns *how* the span\n * ends (error status/attributes, `captureError`, `beforeSpanEnd`) and is idempotent. Default `false`\n * lets the helper end the span as usual.\n */\n deferSpanEnd?: (args: {\n span: Span;\n data: TracingChannelPayloadWithSpan<TData>;\n /** Ends the span: `end()` on success, `end(error)` on failure. Idempotent. */\n end: (error?: unknown) => void;\n }) => boolean;\n\n /**\n * Apply span/scope binding only if there is a parent span, similar to returning `undefined` in the `getSpan` callback.\n * If you need to perform some conditional checking on the parent span before deciding, do it in the `getSpan` callback.\n */\n requiresParentSpan?: boolean;\n}\n\n/** Returned by {@link bindTracingChannelToSpan}: the bound channel plus a teardown handle. */\nexport interface TracingChannelBindingHandle<TData extends object = object> {\n /**\n * The tracing channel with the span bound into async context.\n */\n channel: SentryTracingChannel<TData>;\n\n /**\n * Tears down the binding: unsubscribes lifecycle handlers, when present, and unbinds the start store.\n * Idempotent, and a no-op when no async context binding was available.\n */\n unbind: () => void;\n}\n\nconst NOOP = (): void => {};\n\n/**\n * Bind a span and its lifecycle to a tracing channel so the span becomes the active async context\n * for the traced operation and is ended when the operation completes.\n *\n * `getSpan` may return `undefined` to opt a payload out entirely: nothing is bound, no span is\n * tracked, and the active context is left untouched. Use it for events that ride the same channel\n * but should reuse the enclosing span instead of opening (and ending) their own — e.g. an agent\n * loop's per-step events, where ending a freshly opened span would close the parent prematurely.\n */\nexport function bindTracingChannelToSpan<TData extends object>(\n channel: TracingChannel<TData, TData>,\n getSpan: (data: TracingChannelPayloadWithSpan<TData>) => Span | undefined,\n opts?: TracingChannelLifeCycleOptions<TData>,\n): TracingChannelBindingHandle<TData> {\n const handle = bindSpanToChannelStore(channel, getSpan, opts);\n\n const beforeSpanEnd = opts?.beforeSpanEnd;\n const deferSpanEnd = opts?.deferSpanEnd;\n const getErrorHint = (e: unknown): ExclusiveEventHintOrCaptureContext => {\n if (typeof opts?.captureError === 'function') {\n return opts.captureError(e);\n }\n\n return {\n mechanism: {\n type: 'auto.diagnostic_channels.bind_span',\n handled: false,\n },\n };\n };\n\n // Apply Sentry error status + attributes (and capture, if configured) to a span. Shared by the\n // channel `error` lifecycle and the deferred `end` util so the two can't drift.\n const annotateSpanError = (span: Span, error: unknown): void => {\n if (opts?.captureError) {\n captureException(error, getErrorHint(error));\n }\n\n const { message, attributes } = getErrorInfo(error);\n span.setStatus({ code: SPAN_STATUS_ERROR, message });\n span.setAttributes(attributes);\n };\n\n // Creates an end fn for deferred handlers to use, ensures consistent span end behavior\n const makeDeferredEnd = (span: Span, data: TracingChannelPayloadWithSpan<TData>) => {\n let ended = false;\n\n return (error?: unknown): void => {\n if (ended) {\n return;\n }\n\n ended = true;\n if (error !== undefined) {\n annotateSpanError(span, error);\n }\n\n endBoundSpan(data, beforeSpanEnd);\n };\n };\n\n const subscribers: Partial<TracingChannelSubscribers<TracingChannelPayloadWithSpan<TData>>> = {\n start: NOOP,\n asyncStart: NOOP,\n end(data) {\n // The operation settled synchronously (returned or threw)\n // Presence checks because caller can return `undefined` result or throw a falsy value.\n if ('error' in data || 'result' in data) {\n const span = data._sentrySpan;\n if (span && deferSpanEnd?.({ span, data, end: makeDeferredEnd(span, data) })) {\n return;\n }\n endBoundSpan(data, beforeSpanEnd);\n }\n },\n error(data) {\n // No span was bound for this payload (`getSpan` returned undefined), so there is nothing to\n // annotate and no instrumentation that owns capturing this error.\n const span = data._sentrySpan;\n if (!span) {\n return;\n }\n\n annotateSpanError(span, data.error);\n },\n asyncEnd(data) {\n const span = data._sentrySpan;\n if (span && deferSpanEnd?.({ span, data, end: makeDeferredEnd(span, data) })) {\n return;\n }\n endBoundSpan(data, beforeSpanEnd);\n },\n };\n\n handle.channel.subscribe(subscribers);\n\n return {\n channel: handle.channel,\n unbind: () => {\n handle.channel.unsubscribe(subscribers);\n handle.unbind();\n },\n };\n}\n\n/**\n * Bind a span into the channel's async context so it becomes active for the traced operation,\n * without managing its lifecycle. The primitive behind {@link bindTracingChannelToSpan}, which\n * layers span-ending and error handling on top.\n *\n * `getSpan` may return `undefined` to leave the active context untouched for that payload.\n */\nfunction bindSpanToChannelStore<TData extends object>(\n channel: TracingChannel<TData, TData>,\n getSpan: (data: TracingChannelPayloadWithSpan<TData>) => Span | undefined,\n opts?: Pick<TracingChannelLifeCycleOptions, 'requiresParentSpan'>,\n): TracingChannelBindingHandle<TData> {\n // Grabs the tracing channel binding defined by the AsyncContext strategy implementation\n const binding = getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.();\n\n // If no binding, then either the implementer doesn't support tracing channels or there is no active strategy\n // Failure mode here means we would still access the channel and potentially subscribe to it, but parenting will be off.\n if (!binding) {\n DEBUG_BUILD && debug.log('[TracingChannel] Could not access async context binding.');\n\n return {\n channel,\n unbind: NOOP,\n };\n }\n\n // Grab the ALS instance, we don't really care what is in it as long as the AsyncContext strategy can use its value to figure out parenting.\n const asyncLocalStorage = binding.asyncLocalStorage as AsyncLocalStorage<TData>;\n\n // bindStore activates the ALS for the traced call; any getStore() inside it returns the value bound for that context.\n // 1. Produce: getStoreWithActiveSpan(span) clones the current scope, plants the span via _INTERNAL_setSpanForScope, and returns { scope, isolationScope }, the active context carrying our span.\n // 2. Bind: the courier hands that opaque value to channel.start.bindStore(asyncLocalStorage, producer), which runs the traced op inside asyncLocalStorage.run(value, …); it never inspects the value.\n // 3. Read: inside the op, Sentry's scope machinery calls getScopes() → asyncStorage.getStore() on that same ALS, so getCurrentScope/getIsolationScope/getActiveSpan resolve to the scope carrying our span.\n // 4. Nest: any child span started in the traced op parents to that active span.\n channel.start.bindStore(asyncLocalStorage, (data: TracingChannelPayloadWithSpan<TData>) => {\n // Stash the caller's store before we swap in the span store, so `asyncStart` can restore it for\n // callback-style channels (see `_sentryCallerStore`).\n data._sentryCallerStore = asyncLocalStorage.getStore();\n\n const shouldGetSpan = !opts?.requiresParentSpan || getActiveSpan();\n const span = shouldGetSpan ? getSpan(data) : undefined;\n if (!span) {\n // Leave the active context untouched so nested operations keep parenting to the enclosing span.\n return data._sentryCallerStore as TData;\n }\n data._sentrySpan = span;\n\n return binding.getStoreWithActiveSpan(span) as TData;\n });\n\n // Restore the caller's context for the async continuation. Only callback-style channels `runStores`\n // `asyncStart` (so the callback runs inside this store). promise channels `publish` it, leaving this\n // inert, their continuation already inherits the caller's context natively.\n channel.asyncStart.bindStore(asyncLocalStorage, (data: TracingChannelPayloadWithSpan<TData>) => {\n return data._sentryCallerStore as TData;\n });\n\n return {\n channel,\n unbind: () => {\n // Removes the stores\n channel.start.unbindStore(asyncLocalStorage);\n channel.asyncStart.unbindStore(asyncLocalStorage);\n },\n };\n}\n\nfunction endBoundSpan<TData extends object>(\n data: TracingChannelPayloadWithSpan<TData>,\n beforeSpanEnd: TracingChannelLifeCycleOptions<TData>['beforeSpanEnd'],\n): void {\n const span = data._sentrySpan;\n if (!span) {\n return;\n }\n beforeSpanEnd?.(span, data);\n span.end();\n}\n\ntype ErrorInfo = {\n message: string | undefined;\n attributes: Record<string, string>;\n};\n\n/**\n * Best-effort message and attribute extraction for thrown/rejected values.\n */\nfunction getErrorInfo(error: unknown): ErrorInfo {\n const errorIsObject = isObjectLike(error);\n const raw = errorIsObject ? ('message' in error ? error.message : undefined) : error;\n\n // Leave the status message unset if not set (e.g. an `AggregateError` from\n // ECONNREFUSED, whose `.message` is empty). Otherwise cast to string.\n const message = raw ? String(raw) : undefined;\n const type = errorIsObject && 'name' in error ? String(error.name) : 'unknown';\n\n return {\n message,\n attributes: {\n [ERROR_TYPE]: type,\n },\n };\n}\n"],"names":[],"mappings":"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"} |
@@ -1,5 +0,6 @@ | ||
| import { GEN_AI_EMBEDDINGS_INPUT, GEN_AI_OPERATION_NAME, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, GEN_AI_TOOL_TYPE, GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_FUNCTION_ID, GEN_AI_RESPONSE_STREAMING, GEN_AI_TOOL_OUTPUT, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_TOTAL_TOKENS, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_RESPONSE_ID, GEN_AI_RESPONSE_MODEL, GEN_AI_OUTPUT_MESSAGES, GEN_AI_INPUT_MESSAGES, GEN_AI_REQUEST_MODEL, GEN_AI_SYSTEM } from '@sentry/conventions/attributes'; | ||
| import { GEN_AI_TOOL_OUTPUT, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_TOTAL_TOKENS, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_RESPONSE_ID, GEN_AI_RESPONSE_MODEL, GEN_AI_OUTPUT_MESSAGES, GEN_AI_EMBEDDINGS_INPUT, GEN_AI_OPERATION_NAME, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, GEN_AI_TOOL_TYPE, GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_FUNCTION_ID, GEN_AI_RESPONSE_STREAMING, GEN_AI_INPUT_MESSAGES, GEN_AI_REQUEST_MODEL, GEN_AI_SYSTEM } from '@sentry/conventions/attributes'; | ||
| import { GEN_AI_EXECUTE_TOOL_SPAN_OP, GEN_AI_INVOKE_AGENT_SPAN_OP } from '@sentry/conventions/op'; | ||
| import { getClient, shouldEnableTruncation, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getTruncatedJsonString, getProviderMetadataAttributes, GEN_AI_CONVERSATION_ID_ATTRIBUTE, spanToJSON, SPAN_STATUS_ERROR, withScope, spanToTraceContext, captureException, GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE } from '@sentry/core'; | ||
| import { isObjectLike, getProviderMetadataAttributes, GEN_AI_CONVERSATION_ID_ATTRIBUTE, spanToJSON, getClient, shouldEnableTruncation, SPAN_STATUS_ERROR, withScope, spanToTraceContext, captureException, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getTruncatedJsonString, GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE } from '@sentry/core'; | ||
| import { bindTracingChannelToSpan } from '../tracing-channel.js'; | ||
| import { isReadableStream, asString, tapModelCallStream, safeStringify, sum, asNumber } from './util.js'; | ||
@@ -18,3 +19,11 @@ const AI_SDK_TELEMETRY_TRACING_CHANNEL = "ai:telemetry"; | ||
| const toolDescriptionsByCallId = /* @__PURE__ */ new Map(); | ||
| const ROOT_OPERATION_TYPES = /* @__PURE__ */ new Set(["generateText", "streamText", "embed", "embedMany", "rerank"]); | ||
| const invokeAgentSpanByCallId = /* @__PURE__ */ new Map(); | ||
| const ROOT_OPERATION_TYPES = /* @__PURE__ */ new Set([ | ||
| "generateText", | ||
| "streamText", | ||
| "generateObject", | ||
| "embed", | ||
| "embedMany", | ||
| "rerank" | ||
| ]); | ||
| function clearOperationId(data) { | ||
@@ -32,2 +41,3 @@ if (!ROOT_OPERATION_TYPES.has(data.type)) { | ||
| toolDescriptionsByCallId.delete(callId); | ||
| invokeAgentSpanByCallId.delete(callId); | ||
| } | ||
@@ -40,3 +50,3 @@ function recordToolDescriptions(callId, tools) { | ||
| for (const tool of tools) { | ||
| if (isRecord(tool) && typeof tool.name === "string" && typeof tool.description === "string") { | ||
| if (isObjectLike(tool) && typeof tool.name === "string" && typeof tool.description === "string") { | ||
| descriptions = descriptions ?? /* @__PURE__ */ new Map(); | ||
@@ -58,17 +68,12 @@ if (!descriptions.has(tool.name)) { | ||
| if (Array.isArray(tools)) { | ||
| const match = tools.find((tool) => isRecord(tool) && tool.name === toolName); | ||
| return isRecord(match) ? asString(match.description) : void 0; | ||
| const match = tools.find((tool) => isObjectLike(tool) && tool.name === toolName); | ||
| return isObjectLike(match) ? asString(match.description) : void 0; | ||
| } | ||
| if (isRecord(tools)) { | ||
| if (isObjectLike(tools)) { | ||
| const tool = tools[toolName]; | ||
| return isRecord(tool) ? asString(tool.description) : void 0; | ||
| return isObjectLike(tool) ? asString(tool.description) : void 0; | ||
| } | ||
| return void 0; | ||
| } | ||
| let subscribed = false; | ||
| function subscribeVercelAiTracingChannel(tracingChannel, options = {}) { | ||
| if (subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| bindTracingChannelToSpan( | ||
@@ -83,6 +88,80 @@ tracingChannel(AI_SDK_TELEMETRY_TRACING_CHANNEL), | ||
| clearOperationId(data); | ||
| } | ||
| }, | ||
| // A streamed model call resolves before its stream is drained, so we tap the stream, keep the | ||
| // span open, and end it (via `end`) once the final usage/finish/output chunks arrive. | ||
| deferSpanEnd: ({ data, end }) => deferStreamedModelCallEnd(data, options, end) | ||
| } | ||
| ); | ||
| } | ||
| function deferStreamedModelCallEnd(data, options, end) { | ||
| if (data.type !== "languageModelCall" || !isObjectLike(data.result)) { | ||
| return false; | ||
| } | ||
| const result = data.result; | ||
| const stream = result.stream; | ||
| if (!isReadableStream(stream)) { | ||
| return false; | ||
| } | ||
| const callId = asString(data.event.callId); | ||
| const { recordOutputs } = getRecordingOptions(data.event, options); | ||
| result.stream = tapModelCallStream( | ||
| stream, | ||
| (final) => { | ||
| data.result = { ...result, ...streamedResultToChannelResult(final) }; | ||
| end(); | ||
| enrichInvokeAgentFromStream(callId, final, recordOutputs); | ||
| }, | ||
| (error) => end(error) | ||
| ); | ||
| return true; | ||
| } | ||
| function streamedResultToChannelResult(final) { | ||
| const content = []; | ||
| if (final.text) { | ||
| content.push({ type: "text", text: final.text }); | ||
| } | ||
| for (const toolCall of final.toolCalls) { | ||
| content.push({ type: "tool-call", ...toolCall }); | ||
| } | ||
| return { | ||
| content, | ||
| ...final.usage !== void 0 ? { usage: final.usage } : {}, | ||
| ...final.finishReason !== void 0 ? { finishReason: final.finishReason } : {}, | ||
| ...final.providerMetadata !== void 0 ? { providerMetadata: final.providerMetadata } : {}, | ||
| ...final.responseId || final.responseModel ? { | ||
| response: { | ||
| ...final.responseId ? { id: final.responseId } : {}, | ||
| ...final.responseModel ? { modelId: final.responseModel } : {} | ||
| } | ||
| } : {} | ||
| }; | ||
| } | ||
| function enrichInvokeAgentFromStream(callId, final, recordOutputs) { | ||
| const span = callId ? invokeAgentSpanByCallId.get(callId) : void 0; | ||
| if (!span) { | ||
| return; | ||
| } | ||
| const usage = isObjectLike(final.usage) ? final.usage : void 0; | ||
| if (usage) { | ||
| const input = tokenCount(usage.inputTokens) ?? tokenCount(usage.promptTokens) ?? tokenCount(usage.tokens); | ||
| const output = tokenCount(usage.outputTokens) ?? tokenCount(usage.completionTokens); | ||
| addTokensToSpan(span, GEN_AI_USAGE_INPUT_TOKENS, input); | ||
| addTokensToSpan(span, GEN_AI_USAGE_OUTPUT_TOKENS, output); | ||
| addTokensToSpan(span, GEN_AI_USAGE_TOTAL_TOKENS, tokenCount(usage.totalTokens) ?? sum(input, output)); | ||
| } | ||
| if (recordOutputs) { | ||
| const parts = partsFromTextAndToolCalls(final.text, final.toolCalls); | ||
| const outputMessages = buildOutputMessages(parts, getFinishReason({ finishReason: final.finishReason })); | ||
| if (outputMessages) { | ||
| span.setAttribute(GEN_AI_OUTPUT_MESSAGES, outputMessages); | ||
| } | ||
| } | ||
| } | ||
| function addTokensToSpan(span, attribute, value) { | ||
| if (value === void 0) { | ||
| return; | ||
| } | ||
| const current = spanToJSON(span).data[attribute]; | ||
| span.setAttribute(attribute, (typeof current === "number" ? current : 0) + value); | ||
| } | ||
| function createSpanFromMessage(data, channelOptions) { | ||
@@ -110,2 +189,3 @@ const { type, event } = data; | ||
| case "streamText": | ||
| case "generateObject": | ||
| return buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, type === "streamText"); | ||
@@ -143,3 +223,3 @@ case "languageModelCall": | ||
| } | ||
| return startGenAiSpan(GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, { | ||
| const span = startGenAiSpan(GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, { | ||
| ...baseAttributes, | ||
@@ -151,2 +231,6 @@ [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId, | ||
| }); | ||
| if (isStream && callId) { | ||
| invokeAgentSpanByCallId.set(callId, span); | ||
| } | ||
| return span; | ||
| } | ||
@@ -164,3 +248,3 @@ function buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId) { | ||
| function buildToolSpan(event, recordInputs) { | ||
| const toolCall = isRecord(event.toolCall) ? event.toolCall : {}; | ||
| const toolCall = isObjectLike(event.toolCall) ? event.toolCall : {}; | ||
| const toolName = asString(toolCall.toolName); | ||
@@ -181,3 +265,3 @@ const toolCallId = asString(event.toolCallId) ?? asString(toolCall.toolCallId); | ||
| const { type, result } = data; | ||
| if (!isRecord(result)) { | ||
| if (!isObjectLike(result)) { | ||
| return; | ||
@@ -190,3 +274,3 @@ } | ||
| } | ||
| const output = isRecord(result.output) ? result.output : void 0; | ||
| const output = isObjectLike(result.output) ? result.output : void 0; | ||
| if (output?.type === "tool-error") { | ||
@@ -197,6 +281,6 @@ captureToolError(span, data, output.error); | ||
| } | ||
| const usage = isRecord(result.usage) ? result.usage : void 0; | ||
| const usage = isObjectLike(result.usage) ? result.usage : void 0; | ||
| if (usage) { | ||
| const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.tokens); | ||
| const outputTokens = tokenCount(usage.outputTokens); | ||
| const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.promptTokens) ?? tokenCount(usage.tokens); | ||
| const outputTokens = tokenCount(usage.outputTokens) ?? tokenCount(usage.completionTokens); | ||
| const totalTokens = tokenCount(usage.totalTokens) ?? sum(inputTokens, outputTokens); | ||
@@ -217,3 +301,3 @@ if (inputTokens !== void 0) { | ||
| } | ||
| const response = isRecord(result.response) ? result.response : void 0; | ||
| const response = isObjectLike(result.response) ? result.response : void 0; | ||
| const responseId = asString(response?.id) ?? asString(result.responseId); | ||
@@ -249,6 +333,6 @@ if (responseId) { | ||
| } | ||
| return isRecord(finishReason) ? asString(finishReason.unified) : void 0; | ||
| return isObjectLike(finishReason) ? asString(finishReason.unified) : void 0; | ||
| } | ||
| function tokenCount(value) { | ||
| return asNumber(value) ?? (isRecord(value) ? asNumber(value.total) : void 0); | ||
| return asNumber(value) ?? (isObjectLike(value) ? asNumber(value.total) : void 0); | ||
| } | ||
@@ -273,3 +357,3 @@ function buildOutputMessages(parts, finishReason) { | ||
| for (const item of content) { | ||
| if (!isRecord(item)) { | ||
| if (!isObjectLike(item)) { | ||
| continue; | ||
@@ -292,3 +376,3 @@ } | ||
| for (const toolCall of toolCalls) { | ||
| if (isRecord(toolCall)) { | ||
| if (isObjectLike(toolCall)) { | ||
| parts.push(toolCallPart(toolCall)); | ||
@@ -305,3 +389,3 @@ } | ||
| }); | ||
| const toolCall = isRecord(data.event.toolCall) ? data.event.toolCall : {}; | ||
| const toolCall = isObjectLike(data.event.toolCall) ? data.event.toolCall : {}; | ||
| const toolName = asString(toolCall.toolName); | ||
@@ -356,26 +440,4 @@ const toolCallId = asString(data.event.toolCallId) ?? asString(toolCall.toolCallId); | ||
| } | ||
| function asString(value) { | ||
| return typeof value === "string" ? value : void 0; | ||
| } | ||
| function asNumber(value) { | ||
| return typeof value === "number" && !isNaN(value) ? value : void 0; | ||
| } | ||
| function sum(a, b) { | ||
| return a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0); | ||
| } | ||
| function isRecord(value) { | ||
| return typeof value === "object" && value !== null; | ||
| } | ||
| function safeStringify(value) { | ||
| if (typeof value === "string") { | ||
| return value; | ||
| } | ||
| try { | ||
| return JSON.stringify(value); | ||
| } catch { | ||
| return "[unserializable]"; | ||
| } | ||
| } | ||
| export { clearOperationCallId, clearOperationId, createSpanFromMessage, enrichSpanOnEnd, subscribeVercelAiTracingChannel }; | ||
| export { captureToolError, clearOperationCallId, clearOperationId, createSpanFromMessage, enrichSpanOnEnd, streamedResultToChannelResult, subscribeVercelAiTracingChannel }; | ||
| //# sourceMappingURL=vercel-ai-dc-subscriber.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"vercel-ai-dc-subscriber.js","sources":["../../../src/vercel-ai/vercel-ai-dc-subscriber.ts"],"sourcesContent":["/* eslint-disable max-lines */\n// `@sentry/conventions` marks several gen_ai attributes (e.g. `GEN_AI_SYSTEM`, `GEN_AI_TOOL_*`,\n// `GEN_AI_REQUEST_AVAILABLE_TOOLS`) as deprecated in favour of newer semconv names. We intentionally\n// keep emitting the current names so these spans match the OTel-based (v6) integration and what the\n// Sentry product consumes today; migrating to the new names is a separate, coordinated change.\n/* eslint-disable typescript-eslint/no-deprecated */\nimport {\n GEN_AI_EMBEDDINGS_INPUT,\n GEN_AI_FUNCTION_ID,\n GEN_AI_INPUT_MESSAGES,\n GEN_AI_OPERATION_NAME,\n GEN_AI_OUTPUT_MESSAGES,\n GEN_AI_REQUEST_AVAILABLE_TOOLS,\n GEN_AI_REQUEST_MODEL,\n GEN_AI_RESPONSE_FINISH_REASONS,\n GEN_AI_RESPONSE_ID,\n GEN_AI_RESPONSE_MODEL,\n GEN_AI_RESPONSE_STREAMING,\n GEN_AI_SYSTEM,\n GEN_AI_TOOL_INPUT,\n GEN_AI_TOOL_NAME,\n GEN_AI_TOOL_OUTPUT,\n GEN_AI_TOOL_TYPE,\n GEN_AI_USAGE_INPUT_TOKENS,\n GEN_AI_USAGE_OUTPUT_TOKENS,\n GEN_AI_USAGE_TOTAL_TOKENS,\n} from '@sentry/conventions/attributes';\nimport { GEN_AI_EXECUTE_TOOL_SPAN_OP, GEN_AI_INVOKE_AGENT_SPAN_OP } from '@sentry/conventions/op';\nimport type { Span } from '@sentry/core';\nimport {\n captureException,\n GEN_AI_CONVERSATION_ID_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE,\n getClient,\n getProviderMetadataAttributes,\n getTruncatedJsonString,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n SPAN_STATUS_ERROR,\n spanToJSON,\n spanToTraceContext,\n startInactiveSpan,\n withScope,\n} from '@sentry/core';\nimport type { TracingChannel } from 'node:diagnostics_channel';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n/**\n * The single tracing channel the `ai` package (>= 7) publishes all telemetry lifecycle events to\n * via `node:diagnostics_channel`. Events are discriminated by their `type` field.\n * @see https://github.com/vercel/ai/pull/15660\n */\nconst AI_SDK_TELEMETRY_TRACING_CHANNEL = 'ai:telemetry';\n\nconst ORIGIN = 'auto.vercelai.channel';\n\n// `@sentry/conventions` does not expose these yet, so we keep the literals here.\nconst GEN_AI_TOOL_CALL_ID_ATTRIBUTE = 'gen_ai.tool.call.id';\nconst GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE = 'gen_ai.tool.description';\nconst GEN_AI_EMBEDDINGS_OPERATION = 'embeddings';\nconst GEN_AI_RERANK_OPERATION = 'rerank';\n// The model-call op matches the Vercel AI OTel integration (`gen_ai.generate_content`) rather than\n// the generic `gen_ai.chat`, so v6 (OTel) and v7 (channel) produce the same spans.\nconst GEN_AI_GENERATE_CONTENT_OPERATION = 'generate_content';\n\n// Subset of the `vercel.ai.*` passthrough attributes the OTel integration emits that we reproduce.\nconst VERCEL_AI_OPERATION_ID_ATTRIBUTE = 'vercel.ai.operationId';\nconst VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE = 'vercel.ai.model.provider';\nconst VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE = 'vercel.ai.settings.maxRetries';\n\n// Tracks the top-level operationId (and whether it streams) per `callId` so a model-call span can\n// name its `doGenerate`/`doStream` operation the same way the OTel integration does. `isStream` is\n// the authoritative event-type signal rather than a substring check on the (possibly custom)\n// operationId. Cleared when the top-level span ends.\nconst operationIdByCallId = new Map<string, { operationId: string; isStream: boolean }>();\n\n// Per-operation map of tool name → description, harvested from a model-call /\n// top-level event's `tools` (keyed by the shared `callId`). The AI SDK's\n// `executeTool` event doesn't carry the tool's description, so we backfill it\n// onto the tool span here — without relying on the OTel `vercelAiEventProcessor`\n// (which isn't registered in channel/orchestrion mode). Cleared with the\n// operation. Only populated when inputs are recorded, matching the OTel path\n// (which sources descriptions from the recorded `available_tools`).\nconst toolDescriptionsByCallId = new Map<string, Map<string, string>>();\n\n// Only top-level operations own the `callId` → operationId mapping; `step`/`languageModelCall`/\n// `executeTool` share the parent's `callId`, so they must not clear it.\nconst ROOT_OPERATION_TYPES = new Set<ChannelEventType>(['generateText', 'streamText', 'embed', 'embedMany', 'rerank']);\n\n/** Drop the per-operation `callId` maps once the owning top-level operation settles (success or error). */\nexport function clearOperationId(data: VercelAiChannelMessage): void {\n if (!ROOT_OPERATION_TYPES.has(data.type)) {\n return;\n }\n const callId = asString(data.event.callId);\n if (callId) {\n clearOperationCallId(callId);\n }\n}\n\n/**\n * Drop the per-operation `callId` maps for a single id. The v6 orchestrion adapter uses this to clear a\n * `streamText` operation only after its lazily-run model call settles — the operation's own span ends\n * synchronously (when `streamText` returns) but the model call runs later as the stream is consumed, and\n * it still needs the operation's `operationId`/`isStream` entry to name itself `ai.streamText.doStream`.\n */\nexport function clearOperationCallId(callId: string): void {\n operationIdByCallId.delete(callId);\n toolDescriptionsByCallId.delete(callId);\n}\n\n/** Record tool name → description from an event's `tools`, so tool spans can backfill the description. */\nfunction recordToolDescriptions(callId: string | undefined, tools: unknown): void {\n if (!callId || !Array.isArray(tools)) {\n return;\n }\n let descriptions = toolDescriptionsByCallId.get(callId);\n for (const tool of tools) {\n if (isRecord(tool) && typeof tool.name === 'string' && typeof tool.description === 'string') {\n descriptions = descriptions ?? new Map();\n if (!descriptions.has(tool.name)) {\n descriptions.set(tool.name, tool.description);\n }\n }\n }\n if (descriptions) {\n toolDescriptionsByCallId.set(callId, descriptions);\n }\n}\n\n/**\n * Resolve a tool's description, preferring the per-operation map (populated from the model-call /\n * top-level event's `tools`, v7) and falling back to a `tools` collection on the event itself —\n * which may be an array of `{ name, description }` or a record keyed by tool name (v6).\n */\nfunction resolveToolDescription(callId: string | undefined, toolName: string, tools: unknown): string | undefined {\n const fromMap = callId ? toolDescriptionsByCallId.get(callId)?.get(toolName) : undefined;\n if (fromMap) {\n return fromMap;\n }\n if (Array.isArray(tools)) {\n const match = tools.find(tool => isRecord(tool) && tool.name === toolName);\n return isRecord(match) ? asString(match.description) : undefined;\n }\n if (isRecord(tools)) {\n const tool = tools[toolName];\n return isRecord(tool) ? asString(tool.description) : undefined;\n }\n return undefined;\n}\n\n/** The lifecycle event types the `ai:telemetry` channel can carry. */\nexport type ChannelEventType =\n | 'generateText'\n | 'streamText'\n | 'step'\n | 'languageModelCall'\n | 'executeTool'\n | 'embed'\n | 'embedMany'\n | 'rerank';\n\n/**\n * The context object the AI SDK passes through one tracing-channel call. It is the same object\n * identity across `start`/`end`/`asyncEnd`/`error`, and Node's `tracingChannel` attaches\n * `result`/`error` to it as the traced promise settles.\n */\nexport interface VercelAiChannelMessage {\n type: ChannelEventType;\n event: Record<string, unknown>;\n result?: unknown;\n error?: unknown;\n}\n\n/**\n * Platform-provided factory that returns a tracing channel for the given channel name. The factory\n * is responsible for, when `start` fires, calling `transformStart(data)` and storing the returned\n * span on `data._sentrySpan` so the subscriber's `asyncEnd`/`error` handlers can read it.\n *\n * Node passes `@sentry/opentelemetry/tracing-channel`, which uses `bindStore` to additionally make\n * the span the active OTel context for the duration of the traced operation. That is what makes\n * nested AI SDK operations (model calls, tool calls) become children of the enclosing span without\n * any manual parent bookkeeping here.\n */\nexport type VercelAiTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/** Integration-level recording options, pinned at subscribe time so we never look the integration up per event. */\nexport interface VercelAiChannelOptions {\n recordInputs?: boolean;\n recordOutputs?: boolean;\n enableTruncation?: boolean;\n}\n\nlet subscribed = false;\n\n/**\n * Subscribe Sentry span handlers to the `ai` SDK's native telemetry tracing channel (`ai:telemetry`,\n * available in `ai` >= 7) and emit fully-formed `gen_ai.*` spans directly — no OpenTelemetry span\n * post-processing involved.\n *\n * The integration passes its options in directly (rather than us looking the integration up on every\n * event); the global `dataCollection.genAI` default is still read from the client per event.\n *\n * Safe to always call: on `ai` versions that don't publish to the channel (e.g. < 7) nothing is\n * ever emitted and this is inert, so there is no double-instrumentation against the OTel-based\n * patcher. Idempotent.\n */\nexport function subscribeVercelAiTracingChannel(\n tracingChannel: VercelAiTracingChannelFactory,\n options: VercelAiChannelOptions = {},\n): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n bindTracingChannelToSpan(\n tracingChannel<VercelAiChannelMessage>(AI_SDK_TELEMETRY_TRACING_CHANNEL),\n data => createSpanFromMessage(data, options),\n {\n // The helper ends the span; we enrich it from the settled result first (tokens, output messages,\n // finish reasons, response model/id, provider metadata) and drop the per-operation `callId` maps.\n beforeSpanEnd: (span, data) => {\n enrichSpanOnEnd(span, data, options);\n clearOperationId(data);\n },\n },\n );\n}\n\n/**\n * Transform a channel `start` payload into the span that should be active for the operation. For\n * `step` we deliberately don't open a span (model calls and tool calls are siblings under the\n * invoke_agent span, matching the OTel-based output), so we reuse the active span and mark the\n * payload to skip ending it.\n */\nexport function createSpanFromMessage(\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): Span | undefined {\n const { type, event } = data;\n\n if (type === 'step' || !event || typeof event !== 'object') {\n // Opt out: returning `undefined` leaves the enclosing `invoke_agent` span as the active context\n // (model-call and tool-call events nest under it) without opening — or ending — a span of its own.\n return undefined;\n }\n\n const { recordInputs, enableTruncation } = getRecordingOptions(event, channelOptions);\n const provider = asString(event.provider);\n const modelId = asString(event.modelId);\n const callId = asString(event.callId);\n const maxRetries = asNumber(event.maxRetries);\n\n // Harvest tool descriptions from the operation/model-call `tools` so tool spans can backfill them.\n // Gated on `recordInputs` to match the OTel path, which only records `available_tools` then.\n if (recordInputs) {\n recordToolDescriptions(callId, event.tools);\n }\n\n const baseAttributes: Record<string, string | number | boolean> = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n ...(provider ? { [GEN_AI_SYSTEM]: provider, [VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE]: provider } : {}),\n ...(modelId ? { [GEN_AI_REQUEST_MODEL]: modelId } : {}),\n ...(maxRetries !== undefined ? { [VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE]: maxRetries } : {}),\n };\n\n switch (type) {\n case 'generateText':\n case 'streamText':\n return buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, type === 'streamText');\n case 'languageModelCall':\n return buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId);\n case 'executeTool':\n return buildToolSpan(event, recordInputs);\n case 'embed':\n case 'embedMany': {\n // `embed` carries a single `value`; `embedMany` a `values` array — both map to the embeddings input.\n const input = type === 'embedMany' ? event.values : event.value;\n return startGenAiSpan(GEN_AI_EMBEDDINGS_OPERATION, modelId, {\n ...baseAttributes,\n ...(recordInputs && input !== undefined ? { [GEN_AI_EMBEDDINGS_INPUT]: safeStringify(input) } : {}),\n });\n }\n case 'rerank':\n return startGenAiSpan(GEN_AI_RERANK_OPERATION, modelId, baseAttributes);\n default:\n // Unknown event type: opt out rather than open a span we can't shape correctly.\n return undefined;\n }\n}\n\ntype Attributes = Record<string, string | number | boolean>;\n\n/** Start a `gen_ai.<operation>` span named `<operation> <suffix>` (or just `<operation>` when no suffix). */\nfunction startGenAiSpan(operation: string, suffix: string | undefined, attributes: Attributes): Span {\n return startInactiveSpan({\n name: suffix ? `${operation} ${suffix}` : operation,\n op: `gen_ai.${operation}`,\n attributes: { [GEN_AI_OPERATION_NAME]: operation, ...attributes },\n });\n}\n\nfunction buildInvokeAgentSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n isStream: boolean,\n): Span {\n const functionId = asString(event.functionId);\n const operationId = asString(event.operationId) ?? (isStream ? 'ai.streamText' : 'ai.generateText');\n if (callId) {\n operationIdByCallId.set(callId, { operationId, isStream });\n }\n return startGenAiSpan(GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n [GEN_AI_RESPONSE_STREAMING]: isStream,\n ...(functionId ? { [GEN_AI_FUNCTION_ID]: functionId } : {}),\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n });\n}\n\nfunction buildModelCallSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n modelId: string | undefined,\n): Span {\n const parent = callId ? operationIdByCallId.get(callId) : undefined;\n const operationId = parent\n ? `${parent.operationId}.${parent.isStream ? 'doStream' : 'doGenerate'}`\n : 'ai.generateText.doGenerate';\n return startGenAiSpan(GEN_AI_GENERATE_CONTENT_OPERATION, modelId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n ...(recordInputs && Array.isArray(event.tools)\n ? { [GEN_AI_REQUEST_AVAILABLE_TOOLS]: safeStringify(event.tools) }\n : {}),\n });\n}\n\nfunction buildToolSpan(event: Record<string, unknown>, recordInputs: boolean): Span {\n const toolCall = isRecord(event.toolCall) ? event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(event.toolCallId) ?? asString(toolCall.toolCallId);\n const toolInput = toolCall.input ?? toolCall.args;\n // The `executeTool` event has no description; backfill it from the operation's recorded tools.\n // Gated on `recordInputs` to match the OTel path (descriptions come from the recorded tools list).\n const description =\n recordInputs && toolName ? resolveToolDescription(asString(event.callId), toolName, event.tools) : undefined;\n return startGenAiSpan(GEN_AI_EXECUTE_TOOL_SPAN_OP, toolName, {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [GEN_AI_TOOL_TYPE]: 'function',\n ...(toolName ? { [GEN_AI_TOOL_NAME]: toolName } : {}),\n ...(toolCallId ? { [GEN_AI_TOOL_CALL_ID_ATTRIBUTE]: toolCallId } : {}),\n ...(description ? { [GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE]: description } : {}),\n ...(recordInputs && toolInput !== undefined ? { [GEN_AI_TOOL_INPUT]: safeStringify(toolInput) } : {}),\n });\n}\n\n/**\n * Best-effort enrichment from the resolved value the AI SDK attaches to the channel context.\n * Everything here is guarded: when a field is missing or the shape differs across `ai` versions,\n * we simply don't set the attribute rather than emit a malformed span.\n */\nexport function enrichSpanOnEnd(\n span: Span,\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): void {\n const { type, result } = data;\n if (!isRecord(result)) {\n return;\n }\n\n const { recordOutputs } = getRecordingOptions(data.event, channelOptions);\n\n if (type === 'executeTool') {\n if (recordOutputs) {\n span.setAttribute(GEN_AI_TOOL_OUTPUT, safeStringify(result.output ?? result));\n }\n // From V5 on, tool errors are not rejected (so the `error` channel verb never fires) — they\n // surface as `tool-error` content on the resolved result. Mirror the OTel path by marking the\n // span and capturing the error.\n const output = isRecord(result.output) ? result.output : undefined;\n if (output?.type === 'tool-error') {\n captureToolError(span, data, output.error);\n }\n return;\n }\n\n // `languageModelCall` results report usage as `{ total }` objects; top-level/step results report\n // flat numbers. `tokenCount` handles both.\n const usage = isRecord(result.usage) ? result.usage : undefined;\n if (usage) {\n const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.tokens);\n const outputTokens = tokenCount(usage.outputTokens);\n const totalTokens = tokenCount(usage.totalTokens) ?? sum(inputTokens, outputTokens);\n if (inputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_INPUT_TOKENS, inputTokens);\n }\n if (outputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens);\n }\n if (totalTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_TOTAL_TOKENS, totalTokens);\n }\n }\n\n // Match the OTel integration: finish reasons live on the model-call (`generate_content`) span, not\n // on the top-level `invoke_agent` span.\n const finishReason = getFinishReason(result);\n if (finishReason && type === 'languageModelCall') {\n span.setAttribute(GEN_AI_RESPONSE_FINISH_REASONS, safeStringify([finishReason]));\n }\n\n const response = isRecord(result.response) ? result.response : undefined;\n const responseId = asString(response?.id) ?? asString(result.responseId);\n if (responseId) {\n span.setAttribute(GEN_AI_RESPONSE_ID, responseId);\n }\n const responseModel = asString(response?.modelId) ?? asString(data.event.modelId);\n if (responseModel) {\n span.setAttribute(GEN_AI_RESPONSE_MODEL, responseModel);\n }\n\n // Provider-specific cache/reasoning/prediction token breakdowns and `gen_ai.conversation.id`.\n // The channel exposes `providerMetadata` as an object (the OTel path parses it from a string);\n // both share `getProviderMetadataAttributes` so the emitted shape is identical.\n const providerMetadata = (result as { providerMetadata?: unknown }).providerMetadata;\n const providerAttributes = getProviderMetadataAttributes(providerMetadata);\n // Don't overwrite a conversation id already set on span start (e.g. by `conversationIdIntegration`\n // from a user-set scope value); the provider-derived id is only a fallback. Matches the OTel path.\n if (\n GEN_AI_CONVERSATION_ID_ATTRIBUTE in providerAttributes &&\n spanToJSON(span).data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]\n ) {\n // oxlint-disable-next-line typescript/no-dynamic-delete\n delete providerAttributes[GEN_AI_CONVERSATION_ID_ATTRIBUTE];\n }\n span.setAttributes(providerAttributes);\n\n if (recordOutputs) {\n // `languageModelCall` exposes the response as a `content` parts array; top-level results expose\n // `text` + `toolCalls`. Both normalize into the OTel `gen_ai.output.messages` assistant message.\n const parts =\n type === 'languageModelCall' && Array.isArray(result.content)\n ? partsFromContent(result.content)\n : partsFromTextAndToolCalls(result.text, result.toolCalls);\n const outputMessages = buildOutputMessages(parts, finishReason);\n if (outputMessages) {\n span.setAttribute(GEN_AI_OUTPUT_MESSAGES, outputMessages);\n }\n }\n}\n\n/** Maps a Vercel AI finish reason to the OTel `gen_ai.output.messages` form (`tool-calls` → `tool_call`). */\nfunction normalizeFinishReason(finishReason: string | undefined): string {\n return finishReason === 'tool-calls' ? 'tool_call' : (finishReason ?? 'stop');\n}\n\n/** Reads the finish reason from a result — a string on top-level results, `{ unified }` on model calls. */\nfunction getFinishReason(result: Record<string, unknown>): string | undefined {\n const finishReason = result.finishReason;\n if (typeof finishReason === 'string') {\n return finishReason;\n }\n return isRecord(finishReason) ? asString(finishReason.unified) : undefined;\n}\n\n/** Reads a token count that may be a plain number or a `{ total }` object (model-call usage). */\nfunction tokenCount(value: unknown): number | undefined {\n return asNumber(value) ?? (isRecord(value) ? asNumber(value.total) : undefined);\n}\n\nfunction buildOutputMessages(\n parts: Array<Record<string, unknown>>,\n finishReason: string | undefined,\n): string | undefined {\n if (!parts.length) {\n return undefined;\n }\n return safeStringify([{ role: 'assistant', parts, finish_reason: normalizeFinishReason(finishReason) }]);\n}\n\nfunction toolCallPart(toolCall: Record<string, unknown>): Record<string, unknown> {\n const args = toolCall.input ?? toolCall.args;\n return {\n type: 'tool_call',\n id: asString(toolCall.toolCallId),\n name: asString(toolCall.toolName),\n arguments: typeof args === 'string' ? args : safeStringify(args ?? {}),\n };\n}\n\nfunction partsFromContent(content: unknown[]): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n for (const item of content) {\n if (!isRecord(item)) {\n continue;\n }\n if (item.type === 'text' && typeof item.text === 'string') {\n parts.push({ type: 'text', content: item.text });\n } else if (item.type === 'tool-call') {\n parts.push(toolCallPart(item));\n }\n }\n return parts;\n}\n\nfunction partsFromTextAndToolCalls(text: unknown, toolCalls: unknown): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n if (typeof text === 'string' && text.length) {\n parts.push({ type: 'text', content: text });\n }\n if (Array.isArray(toolCalls)) {\n for (const toolCall of toolCalls) {\n if (isRecord(toolCall)) {\n parts.push(toolCallPart(toolCall));\n }\n }\n }\n return parts;\n}\n\nfunction captureToolError(span: Span, data: VercelAiChannelMessage, error: unknown): void {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: error instanceof Error ? error.message : 'tool_error',\n });\n\n const toolCall = isRecord(data.event.toolCall) ? data.event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(data.event.toolCallId) ?? asString(toolCall.toolCallId);\n\n withScope(scope => {\n scope.setContext('trace', spanToTraceContext(span));\n if (toolName) {\n scope.setTag('vercel.ai.tool.name', toolName);\n }\n if (toolCallId) {\n scope.setTag('vercel.ai.tool.callId', toolCallId);\n }\n scope.setLevel('error');\n captureException(\n error instanceof Error ? error : new Error(typeof error === 'string' ? error : 'Tool execution failed'),\n {\n mechanism: { type: 'auto.vercelai.channel', handled: false },\n },\n );\n });\n}\n\nfunction getRecordingOptions(\n event: Record<string, unknown>,\n channelOptions: VercelAiChannelOptions,\n): {\n recordInputs: boolean;\n recordOutputs: boolean;\n enableTruncation: boolean;\n} {\n const genAI = getClient()?.getDataCollectionOptions().genAI;\n\n return {\n recordInputs: resolveRecording(channelOptions.recordInputs, event.recordInputs, genAI?.inputs),\n recordOutputs: resolveRecording(channelOptions.recordOutputs, event.recordOutputs, genAI?.outputs),\n enableTruncation: shouldEnableTruncation(channelOptions.enableTruncation),\n };\n}\n\n/**\n * Mirrors the OTel integration's `determineRecordingSettings` precedence: an integration-level option\n * wins, then the per-call `experimental_telemetry.recordInputs/recordOutputs` flag the AI SDK forwards\n * on the channel event, then the global `dataCollection.genAI` default.\n *\n * NOTE: the OTel integration also defaults recording to `true` for a call with\n * `experimental_telemetry: { isEnabled: true }`. The `ai:telemetry` channel does not expose `isEnabled`\n * (nor a resolved recording flag), so that per-call default cannot be reproduced here — v7 users who\n * want inputs/outputs recorded must enable `dataCollection.genAI` or set `recordInputs`/`recordOutputs`.\n */\nfunction resolveRecording(integrationOption: unknown, perCallOption: unknown, globalDefault: unknown): boolean {\n if (typeof integrationOption === 'boolean') {\n return integrationOption;\n }\n if (typeof perCallOption === 'boolean') {\n return perCallOption;\n }\n return globalDefault === true;\n}\n\nfunction buildInputMessageAttributes(\n event: Record<string, unknown>,\n enableTruncation: boolean,\n): Record<string, string | number> {\n const attributes: Record<string, string | number> = {};\n\n // `ai` >= 7 forbids system messages in `messages`/`prompt` and exposes the system prompt as a\n // separate `instructions` field. The OTel path lifts the system message out of the prompt into\n // `gen_ai.system_instructions` as `[{ type: 'text', content }]`; mirror that shape here.\n const instructions = asString(event.instructions);\n if (instructions) {\n attributes[GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE] = safeStringify([{ type: 'text', content: instructions }]);\n }\n\n // The AI SDK start events extend `StandardizedPrompt`; messages live on `messages`, otherwise the\n // simpler `prompt` field is used.\n const messages = event.messages ?? event.prompt;\n if (messages !== undefined) {\n attributes[GEN_AI_INPUT_MESSAGES] = enableTruncation ? getTruncatedJsonString(messages) : safeStringify(messages);\n // The original (pre-truncation) message count, so the product can show how many were dropped.\n attributes[GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE] = Array.isArray(messages) ? messages.length : 1;\n }\n\n return attributes;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && !isNaN(value) ? value : undefined;\n}\n\nfunction sum(a: number | undefined, b: number | undefined): number | undefined {\n return a === undefined && b === undefined ? undefined : (a ?? 0) + (b ?? 0);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '[unserializable]';\n }\n}\n"],"names":[],"mappings":";;;;;AAqDA,MAAM,gCAAA,GAAmC,cAAA;AAEzC,MAAM,MAAA,GAAS,uBAAA;AAGf,MAAM,6BAAA,GAAgC,qBAAA;AACtC,MAAM,iCAAA,GAAoC,yBAAA;AAC1C,MAAM,2BAAA,GAA8B,YAAA;AACpC,MAAM,uBAAA,GAA0B,QAAA;AAGhC,MAAM,iCAAA,GAAoC,kBAAA;AAG1C,MAAM,gCAAA,GAAmC,uBAAA;AACzC,MAAM,kCAAA,GAAqC,0BAAA;AAC3C,MAAM,wCAAA,GAA2C,+BAAA;AAMjD,MAAM,mBAAA,uBAA0B,GAAA,EAAwD;AASxF,MAAM,wBAAA,uBAA+B,GAAA,EAAiC;AAItE,MAAM,oBAAA,uBAA2B,GAAA,CAAsB,CAAC,gBAAgB,YAAA,EAAc,OAAA,EAAS,WAAA,EAAa,QAAQ,CAAC,CAAA;AAG9G,SAAS,iBAAiB,IAAA,EAAoC;AACnE,EAAA,IAAI,CAAC,oBAAA,CAAqB,GAAA,CAAI,IAAA,CAAK,IAAI,CAAA,EAAG;AACxC,IAAA;AAAA,EACF;AACA,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AACzC,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,oBAAA,CAAqB,MAAM,CAAA;AAAA,EAC7B;AACF;AAQO,SAAS,qBAAqB,MAAA,EAAsB;AACzD,EAAA,mBAAA,CAAoB,OAAO,MAAM,CAAA;AACjC,EAAA,wBAAA,CAAyB,OAAO,MAAM,CAAA;AACxC;AAGA,SAAS,sBAAA,CAAuB,QAA4B,KAAA,EAAsB;AAChF,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACpC,IAAA;AAAA,EACF;AACA,EAAA,IAAI,YAAA,GAAe,wBAAA,CAAyB,GAAA,CAAI,MAAM,CAAA;AACtD,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,QAAA,CAAS,IAAI,CAAA,IAAK,OAAO,IAAA,CAAK,SAAS,QAAA,IAAY,OAAO,IAAA,CAAK,WAAA,KAAgB,QAAA,EAAU;AAC3F,MAAA,YAAA,GAAe,YAAA,wBAAoB,GAAA,EAAI;AACvC,MAAA,IAAI,CAAC,YAAA,CAAa,GAAA,CAAI,IAAA,CAAK,IAAI,CAAA,EAAG;AAChC,QAAA,YAAA,CAAa,GAAA,CAAI,IAAA,CAAK,IAAA,EAAM,IAAA,CAAK,WAAW,CAAA;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AACA,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,wBAAA,CAAyB,GAAA,CAAI,QAAQ,YAAY,CAAA;AAAA,EACnD;AACF;AAOA,SAAS,sBAAA,CAAuB,MAAA,EAA4B,QAAA,EAAkB,KAAA,EAAoC;AAChH,EAAA,MAAM,OAAA,GAAU,SAAS,wBAAA,CAAyB,GAAA,CAAI,MAAM,CAAA,EAAG,GAAA,CAAI,QAAQ,CAAA,GAAI,MAAA;AAC/E,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,OAAO,OAAA;AAAA,EACT;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,CAAA,IAAA,KAAQ,SAAS,IAAI,CAAA,IAAK,IAAA,CAAK,IAAA,KAAS,QAAQ,CAAA;AACzE,IAAA,OAAO,SAAS,KAAK,CAAA,GAAI,QAAA,CAAS,KAAA,CAAM,WAAW,CAAA,GAAI,MAAA;AAAA,EACzD;AACA,EAAA,IAAI,QAAA,CAAS,KAAK,CAAA,EAAG;AACnB,IAAA,MAAM,IAAA,GAAO,MAAM,QAAQ,CAAA;AAC3B,IAAA,OAAO,SAAS,IAAI,CAAA,GAAI,QAAA,CAAS,IAAA,CAAK,WAAW,CAAA,GAAI,MAAA;AAAA,EACvD;AACA,EAAA,OAAO,MAAA;AACT;AA4CA,IAAI,UAAA,GAAa,KAAA;AAcV,SAAS,+BAAA,CACd,cAAA,EACA,OAAA,GAAkC,EAAC,EAC7B;AACN,EAAA,IAAI,UAAA,EAAY;AACd,IAAA;AAAA,EACF;AACA,EAAA,UAAA,GAAa,IAAA;AAEb,EAAA,wBAAA;AAAA,IACE,eAAuC,gCAAgC,CAAA;AAAA,IACvE,CAAA,IAAA,KAAQ,qBAAA,CAAsB,IAAA,EAAM,OAAO,CAAA;AAAA,IAC3C;AAAA;AAAA;AAAA,MAGE,aAAA,EAAe,CAAC,IAAA,EAAM,IAAA,KAAS;AAC7B,QAAA,eAAA,CAAgB,IAAA,EAAM,MAAM,OAAO,CAAA;AACnC,QAAA,gBAAA,CAAiB,IAAI,CAAA;AAAA,MACvB;AAAA;AACF,GACF;AACF;AAQO,SAAS,qBAAA,CACd,MACA,cAAA,EACkB;AAClB,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,IAAA;AAExB,EAAA,IAAI,SAAS,MAAA,IAAU,CAAC,KAAA,IAAS,OAAO,UAAU,QAAA,EAAU;AAG1D,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAE,YAAA,EAAc,gBAAA,EAAiB,GAAI,mBAAA,CAAoB,OAAO,cAAc,CAAA;AACpF,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,KAAA,CAAM,QAAQ,CAAA;AACxC,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,KAAA,CAAM,OAAO,CAAA;AACtC,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,KAAA,CAAM,MAAM,CAAA;AACpC,EAAA,MAAM,UAAA,GAAa,QAAA,CAAS,KAAA,CAAM,UAAU,CAAA;AAI5C,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,sBAAA,CAAuB,MAAA,EAAQ,MAAM,KAAK,CAAA;AAAA,EAC5C;AAEA,EAAA,MAAM,cAAA,GAA4D;AAAA,IAChE,CAAC,gCAAgC,GAAG,MAAA;AAAA,IACpC,GAAI,QAAA,GAAW,EAAE,CAAC,aAAa,GAAG,QAAA,EAAU,CAAC,kCAAkC,GAAG,QAAA,EAAS,GAAI,EAAC;AAAA,IAChG,GAAI,UAAU,EAAE,CAAC,oBAAoB,GAAG,OAAA,KAAY,EAAC;AAAA,IACrD,GAAI,eAAe,MAAA,GAAY,EAAE,CAAC,wCAAwC,GAAG,UAAA,EAAW,GAAI;AAAC,GAC/F;AAEA,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,cAAA;AAAA,IACL,KAAK,YAAA;AACH,MAAA,OAAO,qBAAqB,KAAA,EAAO,cAAA,EAAgB,cAAc,gBAAA,EAAkB,MAAA,EAAQ,SAAS,YAAY,CAAA;AAAA,IAClH,KAAK,mBAAA;AACH,MAAA,OAAO,mBAAmB,KAAA,EAAO,cAAA,EAAgB,YAAA,EAAc,gBAAA,EAAkB,QAAQ,OAAO,CAAA;AAAA,IAClG,KAAK,aAAA;AACH,MAAA,OAAO,aAAA,CAAc,OAAO,YAAY,CAAA;AAAA,IAC1C,KAAK,OAAA;AAAA,IACL,KAAK,WAAA,EAAa;AAEhB,MAAA,MAAM,KAAA,GAAQ,IAAA,KAAS,WAAA,GAAc,KAAA,CAAM,SAAS,KAAA,CAAM,KAAA;AAC1D,MAAA,OAAO,cAAA,CAAe,6BAA6B,OAAA,EAAS;AAAA,QAC1D,GAAG,cAAA;AAAA,QACH,GAAI,YAAA,IAAgB,KAAA,KAAU,MAAA,GAAY,EAAE,CAAC,uBAAuB,GAAG,aAAA,CAAc,KAAK,CAAA,EAAE,GAAI;AAAC,OAClG,CAAA;AAAA,IACH;AAAA,IACA,KAAK,QAAA;AACH,MAAA,OAAO,cAAA,CAAe,uBAAA,EAAyB,OAAA,EAAS,cAAc,CAAA;AAAA,IACxE;AAEE,MAAA,OAAO,MAAA;AAAA;AAEb;AAKA,SAAS,cAAA,CAAe,SAAA,EAAmB,MAAA,EAA4B,UAAA,EAA8B;AACnG,EAAA,OAAO,iBAAA,CAAkB;AAAA,IACvB,MAAM,MAAA,GAAS,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,GAAK,SAAA;AAAA,IAC1C,EAAA,EAAI,UAAU,SAAS,CAAA,CAAA;AAAA,IACvB,YAAY,EAAE,CAAC,qBAAqB,GAAG,SAAA,EAAW,GAAG,UAAA;AAAW,GACjE,CAAA;AACH;AAEA,SAAS,qBACP,KAAA,EACA,cAAA,EACA,YAAA,EACA,gBAAA,EACA,QACA,QAAA,EACM;AACN,EAAA,MAAM,UAAA,GAAa,QAAA,CAAS,KAAA,CAAM,UAAU,CAAA;AAC5C,EAAA,MAAM,cAAc,QAAA,CAAS,KAAA,CAAM,WAAW,CAAA,KAAM,WAAW,eAAA,GAAkB,iBAAA,CAAA;AACjF,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,mBAAA,CAAoB,GAAA,CAAI,MAAA,EAAQ,EAAE,WAAA,EAAa,UAAU,CAAA;AAAA,EAC3D;AACA,EAAA,OAAO,cAAA,CAAe,6BAA6B,UAAA,EAAY;AAAA,IAC7D,GAAG,cAAA;AAAA,IACH,CAAC,gCAAgC,GAAG,WAAA;AAAA,IACpC,CAAC,yBAAyB,GAAG,QAAA;AAAA,IAC7B,GAAI,aAAa,EAAE,CAAC,kBAAkB,GAAG,UAAA,KAAe,EAAC;AAAA,IACzD,GAAI,YAAA,GAAe,2BAAA,CAA4B,KAAA,EAAO,gBAAgB,IAAI;AAAC,GAC5E,CAAA;AACH;AAEA,SAAS,mBACP,KAAA,EACA,cAAA,EACA,YAAA,EACA,gBAAA,EACA,QACA,OAAA,EACM;AACN,EAAA,MAAM,MAAA,GAAS,MAAA,GAAS,mBAAA,CAAoB,GAAA,CAAI,MAAM,CAAA,GAAI,MAAA;AAC1D,EAAA,MAAM,WAAA,GAAc,MAAA,GAChB,CAAA,EAAG,MAAA,CAAO,WAAW,IAAI,MAAA,CAAO,QAAA,GAAW,UAAA,GAAa,YAAY,CAAA,CAAA,GACpE,4BAAA;AACJ,EAAA,OAAO,cAAA,CAAe,mCAAmC,OAAA,EAAS;AAAA,IAChE,GAAG,cAAA;AAAA,IACH,CAAC,gCAAgC,GAAG,WAAA;AAAA,IACpC,GAAI,YAAA,GAAe,2BAAA,CAA4B,KAAA,EAAO,gBAAgB,IAAI,EAAC;AAAA,IAC3E,GAAI,YAAA,IAAgB,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,KAAK,CAAA,GACzC,EAAE,CAAC,8BAA8B,GAAG,aAAA,CAAc,KAAA,CAAM,KAAK,CAAA,KAC7D;AAAC,GACN,CAAA;AACH;AAEA,SAAS,aAAA,CAAc,OAAgC,YAAA,EAA6B;AAClF,EAAA,MAAM,WAAW,QAAA,CAAS,KAAA,CAAM,QAAQ,CAAA,GAAI,KAAA,CAAM,WAAW,EAAC;AAC9D,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,QAAA,CAAS,QAAQ,CAAA;AAC3C,EAAA,MAAM,aAAa,QAAA,CAAS,KAAA,CAAM,UAAU,CAAA,IAAK,QAAA,CAAS,SAAS,UAAU,CAAA;AAC7E,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,KAAA,IAAS,QAAA,CAAS,IAAA;AAG7C,EAAA,MAAM,WAAA,GACJ,YAAA,IAAgB,QAAA,GAAW,sBAAA,CAAuB,QAAA,CAAS,KAAA,CAAM,MAAM,CAAA,EAAG,QAAA,EAAU,KAAA,CAAM,KAAK,CAAA,GAAI,MAAA;AACrG,EAAA,OAAO,cAAA,CAAe,6BAA6B,QAAA,EAAU;AAAA,IAC3D,CAAC,gCAAgC,GAAG,MAAA;AAAA,IACpC,CAAC,gBAAgB,GAAG,UAAA;AAAA,IACpB,GAAI,WAAW,EAAE,CAAC,gBAAgB,GAAG,QAAA,KAAa,EAAC;AAAA,IACnD,GAAI,aAAa,EAAE,CAAC,6BAA6B,GAAG,UAAA,KAAe,EAAC;AAAA,IACpE,GAAI,cAAc,EAAE,CAAC,iCAAiC,GAAG,WAAA,KAAgB,EAAC;AAAA,IAC1E,GAAI,YAAA,IAAgB,SAAA,KAAc,MAAA,GAAY,EAAE,CAAC,iBAAiB,GAAG,aAAA,CAAc,SAAS,CAAA,EAAE,GAAI;AAAC,GACpG,CAAA;AACH;AAOO,SAAS,eAAA,CACd,IAAA,EACA,IAAA,EACA,cAAA,EACM;AACN,EAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAO,GAAI,IAAA;AACzB,EAAA,IAAI,CAAC,QAAA,CAAS,MAAM,CAAA,EAAG;AACrB,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,mBAAA,CAAoB,IAAA,CAAK,OAAO,cAAc,CAAA;AAExE,EAAA,IAAI,SAAS,aAAA,EAAe;AAC1B,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,IAAA,CAAK,aAAa,kBAAA,EAAoB,aAAA,CAAc,MAAA,CAAO,MAAA,IAAU,MAAM,CAAC,CAAA;AAAA,IAC9E;AAIA,IAAA,MAAM,SAAS,QAAA,CAAS,MAAA,CAAO,MAAM,CAAA,GAAI,OAAO,MAAA,GAAS,MAAA;AACzD,IAAA,IAAI,MAAA,EAAQ,SAAS,YAAA,EAAc;AACjC,MAAA,gBAAA,CAAiB,IAAA,EAAM,IAAA,EAAM,MAAA,CAAO,KAAK,CAAA;AAAA,IAC3C;AACA,IAAA;AAAA,EACF;AAIA,EAAA,MAAM,QAAQ,QAAA,CAAS,MAAA,CAAO,KAAK,CAAA,GAAI,OAAO,KAAA,GAAQ,MAAA;AACtD,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAM,cAAc,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,IAAK,UAAA,CAAW,MAAM,MAAM,CAAA;AAC5E,IAAA,MAAM,YAAA,GAAe,UAAA,CAAW,KAAA,CAAM,YAAY,CAAA;AAClD,IAAA,MAAM,cAAc,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,IAAK,GAAA,CAAI,aAAa,YAAY,CAAA;AAClF,IAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,MAAA,IAAA,CAAK,YAAA,CAAa,2BAA2B,WAAW,CAAA;AAAA,IAC1D;AACA,IAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,MAAA,IAAA,CAAK,YAAA,CAAa,4BAA4B,YAAY,CAAA;AAAA,IAC5D;AACA,IAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,MAAA,IAAA,CAAK,YAAA,CAAa,2BAA2B,WAAW,CAAA;AAAA,IAC1D;AAAA,EACF;AAIA,EAAA,MAAM,YAAA,GAAe,gBAAgB,MAAM,CAAA;AAC3C,EAAA,IAAI,YAAA,IAAgB,SAAS,mBAAA,EAAqB;AAChD,IAAA,IAAA,CAAK,aAAa,8BAAA,EAAgC,aAAA,CAAc,CAAC,YAAY,CAAC,CAAC,CAAA;AAAA,EACjF;AAEA,EAAA,MAAM,WAAW,QAAA,CAAS,MAAA,CAAO,QAAQ,CAAA,GAAI,OAAO,QAAA,GAAW,MAAA;AAC/D,EAAA,MAAM,aAAa,QAAA,CAAS,QAAA,EAAU,EAAE,CAAA,IAAK,QAAA,CAAS,OAAO,UAAU,CAAA;AACvE,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,IAAA,CAAK,YAAA,CAAa,oBAAoB,UAAU,CAAA;AAAA,EAClD;AACA,EAAA,MAAM,aAAA,GAAgB,SAAS,QAAA,EAAU,OAAO,KAAK,QAAA,CAAS,IAAA,CAAK,MAAM,OAAO,CAAA;AAChF,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,IAAA,CAAK,YAAA,CAAa,uBAAuB,aAAa,CAAA;AAAA,EACxD;AAKA,EAAA,MAAM,mBAAoB,MAAA,CAA0C,gBAAA;AACpE,EAAA,MAAM,kBAAA,GAAqB,8BAA8B,gBAAgB,CAAA;AAGzE,EAAA,IACE,oCAAoC,kBAAA,IACpC,UAAA,CAAW,IAAI,CAAA,CAAE,IAAA,CAAK,gCAAgC,CAAA,EACtD;AAEA,IAAA,OAAO,mBAAmB,gCAAgC,CAAA;AAAA,EAC5D;AACA,EAAA,IAAA,CAAK,cAAc,kBAAkB,CAAA;AAErC,EAAA,IAAI,aAAA,EAAe;AAGjB,IAAA,MAAM,QACJ,IAAA,KAAS,mBAAA,IAAuB,KAAA,CAAM,OAAA,CAAQ,OAAO,OAAO,CAAA,GACxD,gBAAA,CAAiB,MAAA,CAAO,OAAO,CAAA,GAC/B,yBAAA,CAA0B,MAAA,CAAO,IAAA,EAAM,OAAO,SAAS,CAAA;AAC7D,IAAA,MAAM,cAAA,GAAiB,mBAAA,CAAoB,KAAA,EAAO,YAAY,CAAA;AAC9D,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,IAAA,CAAK,YAAA,CAAa,wBAAwB,cAAc,CAAA;AAAA,IAC1D;AAAA,EACF;AACF;AAGA,SAAS,sBAAsB,YAAA,EAA0C;AACvE,EAAA,OAAO,YAAA,KAAiB,YAAA,GAAe,WAAA,GAAe,YAAA,IAAgB,MAAA;AACxE;AAGA,SAAS,gBAAgB,MAAA,EAAqD;AAC5E,EAAA,MAAM,eAAe,MAAA,CAAO,YAAA;AAC5B,EAAA,IAAI,OAAO,iBAAiB,QAAA,EAAU;AACpC,IAAA,OAAO,YAAA;AAAA,EACT;AACA,EAAA,OAAO,SAAS,YAAY,CAAA,GAAI,QAAA,CAAS,YAAA,CAAa,OAAO,CAAA,GAAI,MAAA;AACnE;AAGA,SAAS,WAAW,KAAA,EAAoC;AACtD,EAAA,OAAO,QAAA,CAAS,KAAK,CAAA,KAAM,QAAA,CAAS,KAAK,CAAA,GAAI,QAAA,CAAS,KAAA,CAAM,KAAK,CAAA,GAAI,MAAA,CAAA;AACvE;AAEA,SAAS,mBAAA,CACP,OACA,YAAA,EACoB;AACpB,EAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACjB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,aAAA,CAAc,CAAC,EAAE,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,aAAA,EAAe,qBAAA,CAAsB,YAAY,CAAA,EAAG,CAAC,CAAA;AACzG;AAEA,SAAS,aAAa,QAAA,EAA4D;AAChF,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,KAAA,IAAS,QAAA,CAAS,IAAA;AACxC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA,IACN,EAAA,EAAI,QAAA,CAAS,QAAA,CAAS,UAAU,CAAA;AAAA,IAChC,IAAA,EAAM,QAAA,CAAS,QAAA,CAAS,QAAQ,CAAA;AAAA,IAChC,SAAA,EAAW,OAAO,IAAA,KAAS,QAAA,GAAW,OAAO,aAAA,CAAc,IAAA,IAAQ,EAAE;AAAA,GACvE;AACF;AAEA,SAAS,iBAAiB,OAAA,EAAoD;AAC5E,EAAA,MAAM,QAAwC,EAAC;AAC/C,EAAA,KAAA,MAAW,QAAQ,OAAA,EAAS;AAC1B,IAAA,IAAI,CAAC,QAAA,CAAS,IAAI,CAAA,EAAG;AACnB,MAAA;AAAA,IACF;AACA,IAAA,IAAI,KAAK,IAAA,KAAS,MAAA,IAAU,OAAO,IAAA,CAAK,SAAS,QAAA,EAAU;AACzD,MAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,QAAQ,OAAA,EAAS,IAAA,CAAK,MAAM,CAAA;AAAA,IACjD,CAAA,MAAA,IAAW,IAAA,CAAK,IAAA,KAAS,WAAA,EAAa;AACpC,MAAA,KAAA,CAAM,IAAA,CAAK,YAAA,CAAa,IAAI,CAAC,CAAA;AAAA,IAC/B;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,yBAAA,CAA0B,MAAe,SAAA,EAAoD;AACpG,EAAA,MAAM,QAAwC,EAAC;AAC/C,EAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,CAAK,MAAA,EAAQ;AAC3C,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,MAAM,CAAA;AAAA,EAC5C;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,SAAS,CAAA,EAAG;AAC5B,IAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,MAAA,IAAI,QAAA,CAAS,QAAQ,CAAA,EAAG;AACtB,QAAA,KAAA,CAAM,IAAA,CAAK,YAAA,CAAa,QAAQ,CAAC,CAAA;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,gBAAA,CAAiB,IAAA,EAAY,IAAA,EAA8B,KAAA,EAAsB;AACxF,EAAA,IAAA,CAAK,SAAA,CAAU;AAAA,IACb,IAAA,EAAM,iBAAA;AAAA,IACN,OAAA,EAAS,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU;AAAA,GACnD,CAAA;AAED,EAAA,MAAM,QAAA,GAAW,SAAS,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,QAAA,GAAW,EAAC;AACxE,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,QAAA,CAAS,QAAQ,CAAA;AAC3C,EAAA,MAAM,UAAA,GAAa,SAAS,IAAA,CAAK,KAAA,CAAM,UAAU,CAAA,IAAK,QAAA,CAAS,SAAS,UAAU,CAAA;AAElF,EAAA,SAAA,CAAU,CAAA,KAAA,KAAS;AACjB,IAAA,KAAA,CAAM,UAAA,CAAW,OAAA,EAAS,kBAAA,CAAmB,IAAI,CAAC,CAAA;AAClD,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,KAAA,CAAM,MAAA,CAAO,uBAAuB,QAAQ,CAAA;AAAA,IAC9C;AACA,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,KAAA,CAAM,MAAA,CAAO,yBAAyB,UAAU,CAAA;AAAA,IAClD;AACA,IAAA,KAAA,CAAM,SAAS,OAAO,CAAA;AACtB,IAAA,gBAAA;AAAA,MACE,KAAA,YAAiB,QAAQ,KAAA,GAAQ,IAAI,MAAM,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,uBAAuB,CAAA;AAAA,MACtG;AAAA,QACE,SAAA,EAAW,EAAE,IAAA,EAAM,uBAAA,EAAyB,SAAS,KAAA;AAAM;AAC7D,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAEA,SAAS,mBAAA,CACP,OACA,cAAA,EAKA;AACA,EAAA,MAAM,KAAA,GAAQ,SAAA,EAAU,EAAG,wBAAA,EAAyB,CAAE,KAAA;AAEtD,EAAA,OAAO;AAAA,IACL,cAAc,gBAAA,CAAiB,cAAA,CAAe,cAAc,KAAA,CAAM,YAAA,EAAc,OAAO,MAAM,CAAA;AAAA,IAC7F,eAAe,gBAAA,CAAiB,cAAA,CAAe,eAAe,KAAA,CAAM,aAAA,EAAe,OAAO,OAAO,CAAA;AAAA,IACjG,gBAAA,EAAkB,sBAAA,CAAuB,cAAA,CAAe,gBAAgB;AAAA,GAC1E;AACF;AAYA,SAAS,gBAAA,CAAiB,iBAAA,EAA4B,aAAA,EAAwB,aAAA,EAAiC;AAC7G,EAAA,IAAI,OAAO,sBAAsB,SAAA,EAAW;AAC1C,IAAA,OAAO,iBAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAO,kBAAkB,SAAA,EAAW;AACtC,IAAA,OAAO,aAAA;AAAA,EACT;AACA,EAAA,OAAO,aAAA,KAAkB,IAAA;AAC3B;AAEA,SAAS,2BAAA,CACP,OACA,gBAAA,EACiC;AACjC,EAAA,MAAM,aAA8C,EAAC;AAKrD,EAAA,MAAM,YAAA,GAAe,QAAA,CAAS,KAAA,CAAM,YAAY,CAAA;AAChD,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,UAAA,CAAW,oCAAoC,CAAA,GAAI,aAAA,CAAc,CAAC,EAAE,MAAM,MAAA,EAAQ,OAAA,EAAS,YAAA,EAAc,CAAC,CAAA;AAAA,EAC5G;AAIA,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,QAAA,IAAY,KAAA,CAAM,MAAA;AACzC,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,UAAA,CAAW,qBAAqB,CAAA,GAAI,gBAAA,GAAmB,uBAAuB,QAAQ,CAAA,GAAI,cAAc,QAAQ,CAAA;AAEhH,IAAA,UAAA,CAAW,+CAA+C,CAAA,GAAI,KAAA,CAAM,QAAQ,QAAQ,CAAA,GAAI,SAAS,MAAA,GAAS,CAAA;AAAA,EAC5G;AAEA,EAAA,OAAO,UAAA;AACT;AAEA,SAAS,SAAS,KAAA,EAAoC;AACpD,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,MAAA;AAC7C;AAEA,SAAS,SAAS,KAAA,EAAoC;AACpD,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,KAAK,IAAI,KAAA,GAAQ,MAAA;AAC9D;AAEA,SAAS,GAAA,CAAI,GAAuB,CAAA,EAA2C;AAC7E,EAAA,OAAO,MAAM,MAAA,IAAa,CAAA,KAAM,SAAY,MAAA,GAAA,CAAa,CAAA,IAAK,MAAM,CAAA,IAAK,CAAA,CAAA;AAC3E;AAEA,SAAS,SAAS,KAAA,EAAkD;AAClE,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA;AAChD;AAEA,SAAS,cAAc,KAAA,EAAwB;AAC7C,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,CAAK,UAAU,KAAK,CAAA;AAAA,EAC7B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,kBAAA;AAAA,EACT;AACF;;;;"} | ||
| {"version":3,"file":"vercel-ai-dc-subscriber.js","sources":["../../../src/vercel-ai/vercel-ai-dc-subscriber.ts"],"sourcesContent":["/* eslint-disable max-lines */\n// `@sentry/conventions` marks several gen_ai attributes (e.g. `GEN_AI_SYSTEM`, `GEN_AI_TOOL_*`,\n// `GEN_AI_REQUEST_AVAILABLE_TOOLS`) as deprecated in favour of newer semconv names. We intentionally\n// keep emitting the current names so these spans match the OTel-based (v6) integration and what the\n// Sentry product consumes today; migrating to the new names is a separate, coordinated change.\n/* eslint-disable typescript-eslint/no-deprecated */\nimport {\n GEN_AI_EMBEDDINGS_INPUT,\n GEN_AI_FUNCTION_ID,\n GEN_AI_INPUT_MESSAGES,\n GEN_AI_OPERATION_NAME,\n GEN_AI_OUTPUT_MESSAGES,\n GEN_AI_REQUEST_AVAILABLE_TOOLS,\n GEN_AI_REQUEST_MODEL,\n GEN_AI_RESPONSE_FINISH_REASONS,\n GEN_AI_RESPONSE_ID,\n GEN_AI_RESPONSE_MODEL,\n GEN_AI_RESPONSE_STREAMING,\n GEN_AI_SYSTEM,\n GEN_AI_TOOL_INPUT,\n GEN_AI_TOOL_NAME,\n GEN_AI_TOOL_OUTPUT,\n GEN_AI_TOOL_TYPE,\n GEN_AI_USAGE_INPUT_TOKENS,\n GEN_AI_USAGE_OUTPUT_TOKENS,\n GEN_AI_USAGE_TOTAL_TOKENS,\n} from '@sentry/conventions/attributes';\nimport { GEN_AI_EXECUTE_TOOL_SPAN_OP, GEN_AI_INVOKE_AGENT_SPAN_OP } from '@sentry/conventions/op';\nimport type { Span } from '@sentry/core';\nimport {\n captureException,\n GEN_AI_CONVERSATION_ID_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE,\n getClient,\n getProviderMetadataAttributes,\n getTruncatedJsonString,\n isObjectLike,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n SPAN_STATUS_ERROR,\n spanToJSON,\n spanToTraceContext,\n startInactiveSpan,\n withScope,\n} from '@sentry/core';\nimport type { TracingChannel } from 'node:diagnostics_channel';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\nimport {\n asNumber,\n asString,\n isReadableStream,\n safeStringify,\n type StreamedModelCallResult,\n sum,\n tapModelCallStream,\n} from './util';\n\n/**\n * The single tracing channel the `ai` package (>= 7) publishes all telemetry lifecycle events to\n * via `node:diagnostics_channel`. Events are discriminated by their `type` field.\n * @see https://github.com/vercel/ai/pull/15660\n */\nconst AI_SDK_TELEMETRY_TRACING_CHANNEL = 'ai:telemetry';\n\nconst ORIGIN = 'auto.vercelai.channel';\n\n// `@sentry/conventions` does not expose these yet, so we keep the literals here.\nconst GEN_AI_TOOL_CALL_ID_ATTRIBUTE = 'gen_ai.tool.call.id';\nconst GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE = 'gen_ai.tool.description';\nconst GEN_AI_EMBEDDINGS_OPERATION = 'embeddings';\nconst GEN_AI_RERANK_OPERATION = 'rerank';\n// The model-call op matches the Vercel AI OTel integration (`gen_ai.generate_content`) rather than\n// the generic `gen_ai.chat`, so v6 (OTel) and v7 (channel) produce the same spans.\nconst GEN_AI_GENERATE_CONTENT_OPERATION = 'generate_content';\n\n// Subset of the `vercel.ai.*` passthrough attributes the OTel integration emits that we reproduce.\nconst VERCEL_AI_OPERATION_ID_ATTRIBUTE = 'vercel.ai.operationId';\nconst VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE = 'vercel.ai.model.provider';\nconst VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE = 'vercel.ai.settings.maxRetries';\n\n// Tracks the top-level operationId (and whether it streams) per `callId` so a model-call span can\n// name its `doGenerate`/`doStream` operation the same way the OTel integration does. `isStream` is\n// the authoritative event-type signal rather than a substring check on the (possibly custom)\n// operationId. Cleared when the top-level span ends.\nconst operationIdByCallId = new Map<string, { operationId: string; isStream: boolean }>();\n\n// Per-operation map of tool name → description, harvested from a model-call /\n// top-level event's `tools` (keyed by the shared `callId`). The AI SDK's\n// `executeTool` event doesn't carry the tool's description, so we backfill it\n// onto the tool span here — without relying on the OTel `vercelAiEventProcessor`\n// (which isn't registered in channel/orchestrion mode). Cleared with the\n// operation. Only populated when inputs are recorded, matching the OTel path\n// (which sources descriptions from the recorded `available_tools`).\nconst toolDescriptionsByCallId = new Map<string, Map<string, string>>();\n\n// A streamed `streamText` operation's own channel result is always `undefined` (the SDK exposes the\n// stream only on the model call), so its `invoke_agent` span can't be enriched from the channel. We\n// key the span by `callId` and enrich it as each streamed model call drains — the model calls flush\n// before the operation's own span ends. The running token sum lives on the span's own attributes.\nconst invokeAgentSpanByCallId = new Map<string, Span>();\n\n// Only top-level operations own the `callId` → operationId mapping; `step`/`languageModelCall`/\n// `executeTool` share the parent's `callId`, so they must not clear it.\nconst ROOT_OPERATION_TYPES = new Set<ChannelEventType>([\n 'generateText',\n 'streamText',\n 'generateObject',\n 'embed',\n 'embedMany',\n 'rerank',\n]);\n\n/** Drop the per-operation `callId` maps once the owning top-level operation settles (success or error). */\nexport function clearOperationId(data: VercelAiChannelMessage): void {\n if (!ROOT_OPERATION_TYPES.has(data.type)) {\n return;\n }\n const callId = asString(data.event.callId);\n if (callId) {\n clearOperationCallId(callId);\n }\n}\n\n/**\n * Drop the per-operation `callId` maps for a single id. The v6 orchestrion adapter uses this to clear a\n * `streamText` operation only after its lazily-run model call settles — the operation's own span ends\n * synchronously (when `streamText` returns) but the model call runs later as the stream is consumed, and\n * it still needs the operation's `operationId`/`isStream` entry to name itself `ai.streamText.doStream`.\n */\nexport function clearOperationCallId(callId: string): void {\n operationIdByCallId.delete(callId);\n toolDescriptionsByCallId.delete(callId);\n invokeAgentSpanByCallId.delete(callId);\n}\n\n/** Record tool name → description from an event's `tools`, so tool spans can backfill the description. */\nfunction recordToolDescriptions(callId: string | undefined, tools: unknown): void {\n if (!callId || !Array.isArray(tools)) {\n return;\n }\n let descriptions = toolDescriptionsByCallId.get(callId);\n for (const tool of tools) {\n if (isObjectLike(tool) && typeof tool.name === 'string' && typeof tool.description === 'string') {\n descriptions = descriptions ?? new Map();\n if (!descriptions.has(tool.name)) {\n descriptions.set(tool.name, tool.description);\n }\n }\n }\n if (descriptions) {\n toolDescriptionsByCallId.set(callId, descriptions);\n }\n}\n\n/**\n * Resolve a tool's description, preferring the per-operation map (populated from the model-call /\n * top-level event's `tools`, v7) and falling back to a `tools` collection on the event itself —\n * which may be an array of `{ name, description }` or a record keyed by tool name (v6).\n */\nfunction resolveToolDescription(callId: string | undefined, toolName: string, tools: unknown): string | undefined {\n const fromMap = callId ? toolDescriptionsByCallId.get(callId)?.get(toolName) : undefined;\n if (fromMap) {\n return fromMap;\n }\n if (Array.isArray(tools)) {\n const match = tools.find(tool => isObjectLike(tool) && tool.name === toolName);\n return isObjectLike(match) ? asString(match.description) : undefined;\n }\n if (isObjectLike(tools)) {\n const tool = tools[toolName];\n return isObjectLike(tool) ? asString(tool.description) : undefined;\n }\n return undefined;\n}\n\n/** The lifecycle event types the `ai:telemetry` channel can carry. */\nexport type ChannelEventType =\n | 'generateText'\n | 'streamText'\n | 'generateObject'\n | 'step'\n | 'languageModelCall'\n | 'executeTool'\n | 'embed'\n | 'embedMany'\n | 'rerank';\n\n/**\n * The context object the AI SDK passes through one tracing-channel call. It is the same object\n * identity across `start`/`end`/`asyncEnd`/`error`, and Node's `tracingChannel` attaches\n * `result`/`error` to it as the traced promise settles.\n */\nexport interface VercelAiChannelMessage {\n type: ChannelEventType;\n event: Record<string, unknown>;\n result?: unknown;\n error?: unknown;\n}\n\n/**\n * Platform-provided factory that returns a tracing channel for the given channel name. The factory\n * is responsible for, when `start` fires, calling `transformStart(data)` and storing the returned\n * span on `data._sentrySpan` so the subscriber's `asyncEnd`/`error` handlers can read it.\n *\n * Node passes `@sentry/opentelemetry/tracing-channel`, which uses `bindStore` to additionally make\n * the span the active OTel context for the duration of the traced operation. That is what makes\n * nested AI SDK operations (model calls, tool calls) become children of the enclosing span without\n * any manual parent bookkeeping here.\n */\nexport type VercelAiTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/** Integration-level recording options, pinned at subscribe time so we never look the integration up per event. */\nexport interface VercelAiChannelOptions {\n recordInputs?: boolean;\n recordOutputs?: boolean;\n enableTruncation?: boolean;\n}\n\n/**\n * Subscribe Sentry span handlers to the `ai` SDK's native telemetry tracing channel (`ai:telemetry`,\n * available in `ai` >= 7) and emit fully-formed `gen_ai.*` spans directly — no OpenTelemetry span\n * post-processing involved.\n *\n * The integration passes its options in directly (rather than us looking the integration up on every\n * event); the global `dataCollection.genAI` default is still read from the client per event.\n *\n * Safe to always call: on `ai` versions that don't publish to the channel (e.g. < 7) nothing is\n * ever emitted and this is inert, so there is no double-instrumentation against the OTel-based\n * patcher. The integration's `setupOnce` guarantees this runs a single time.\n */\nexport function subscribeVercelAiTracingChannel(\n tracingChannel: VercelAiTracingChannelFactory,\n options: VercelAiChannelOptions = {},\n): void {\n bindTracingChannelToSpan(\n tracingChannel<VercelAiChannelMessage>(AI_SDK_TELEMETRY_TRACING_CHANNEL),\n data => createSpanFromMessage(data, options),\n {\n // The helper ends the span; we enrich it from the settled result first (tokens, output messages,\n // finish reasons, response model/id, provider metadata) and drop the per-operation `callId` maps.\n beforeSpanEnd: (span, data) => {\n enrichSpanOnEnd(span, data, options);\n clearOperationId(data);\n },\n // A streamed model call resolves before its stream is drained, so we tap the stream, keep the\n // span open, and end it (via `end`) once the final usage/finish/output chunks arrive.\n deferSpanEnd: ({ data, end }) => deferStreamedModelCallEnd(data, options, end),\n },\n );\n}\n\n/**\n * When a `languageModelCall` resolves to a live `ReadableStream`, defer ending its span: swap in a\n * passthrough that forwards chunks to the SDK untouched while aggregating usage/finish/output, then\n * enrich and end the span once the stream settles. Returns `false` for anything that isn't a streamed\n * model call so the helper ends the span as usual.\n */\nfunction deferStreamedModelCallEnd(\n data: VercelAiChannelMessage,\n options: VercelAiChannelOptions,\n end: (error?: unknown) => void,\n): boolean {\n if (data.type !== 'languageModelCall' || !isObjectLike(data.result)) {\n return false;\n }\n const result = data.result;\n const stream = result.stream;\n if (!isReadableStream(stream)) {\n return false;\n }\n\n const callId = asString(data.event.callId);\n const { recordOutputs } = getRecordingOptions(data.event, options);\n result.stream = tapModelCallStream(\n stream,\n final => {\n // Reshape the aggregate into the result `enrichSpanOnEnd` expects, then let `end` run it (it calls\n // `beforeSpanEnd`). Enriching the model-call span and the parent from the same aggregate keeps\n // streamed and non-streamed spans identical.\n data.result = { ...result, ...streamedResultToChannelResult(final) };\n end();\n enrichInvokeAgentFromStream(callId, final, recordOutputs);\n },\n error => end(error),\n );\n\n return true;\n}\n\n/** Map the tapped stream aggregate onto the `languageModelCall` result shape `enrichSpanOnEnd` reads. */\nexport function streamedResultToChannelResult(final: StreamedModelCallResult): Record<string, unknown> {\n const content: Array<Record<string, unknown>> = [];\n if (final.text) {\n content.push({ type: 'text', text: final.text });\n }\n for (const toolCall of final.toolCalls) {\n content.push({ type: 'tool-call', ...toolCall });\n }\n\n return {\n content,\n ...(final.usage !== undefined ? { usage: final.usage } : {}),\n ...(final.finishReason !== undefined ? { finishReason: final.finishReason } : {}),\n ...(final.providerMetadata !== undefined ? { providerMetadata: final.providerMetadata } : {}),\n ...(final.responseId || final.responseModel\n ? {\n response: {\n ...(final.responseId ? { id: final.responseId } : {}),\n ...(final.responseModel ? { modelId: final.responseModel } : {}),\n },\n }\n : {}),\n };\n}\n\n/**\n * Propagate a streamed model call's usage (summed across the operation's model calls) and output onto\n * the enclosing `invoke_agent` span, which has no channel result of its own. Runs before that span ends\n * because the operation's completion promise settles only after every model-call stream has drained.\n */\nfunction enrichInvokeAgentFromStream(\n callId: string | undefined,\n final: StreamedModelCallResult,\n recordOutputs: boolean,\n): void {\n const span = callId ? invokeAgentSpanByCallId.get(callId) : undefined;\n if (!span) {\n return;\n }\n\n const usage = isObjectLike(final.usage) ? final.usage : undefined;\n if (usage) {\n const input = tokenCount(usage.inputTokens) ?? tokenCount(usage.promptTokens) ?? tokenCount(usage.tokens);\n const output = tokenCount(usage.outputTokens) ?? tokenCount(usage.completionTokens);\n addTokensToSpan(span, GEN_AI_USAGE_INPUT_TOKENS, input);\n addTokensToSpan(span, GEN_AI_USAGE_OUTPUT_TOKENS, output);\n addTokensToSpan(span, GEN_AI_USAGE_TOTAL_TOKENS, tokenCount(usage.totalTokens) ?? sum(input, output));\n }\n\n if (recordOutputs) {\n const parts = partsFromTextAndToolCalls(final.text, final.toolCalls);\n const outputMessages = buildOutputMessages(parts, getFinishReason({ finishReason: final.finishReason }));\n if (outputMessages) {\n span.setAttribute(GEN_AI_OUTPUT_MESSAGES, outputMessages);\n }\n }\n}\n\n/** Add `value` into a span's numeric token attribute, using the span itself as the running sum. */\nfunction addTokensToSpan(span: Span, attribute: string, value: number | undefined): void {\n if (value === undefined) {\n return;\n }\n const current = spanToJSON(span).data[attribute];\n span.setAttribute(attribute, (typeof current === 'number' ? current : 0) + value);\n}\n\n/**\n * Transform a channel `start` payload into the span that should be active for the operation. For\n * `step` we deliberately don't open a span (model calls and tool calls are siblings under the\n * invoke_agent span, matching the OTel-based output), so we reuse the active span and mark the\n * payload to skip ending it.\n */\nexport function createSpanFromMessage(\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): Span | undefined {\n const { type, event } = data;\n\n if (type === 'step' || !event || typeof event !== 'object') {\n // Opt out: returning `undefined` leaves the enclosing `invoke_agent` span as the active context\n // (model-call and tool-call events nest under it) without opening — or ending — a span of its own.\n return undefined;\n }\n\n const { recordInputs, enableTruncation } = getRecordingOptions(event, channelOptions);\n const provider = asString(event.provider);\n const modelId = asString(event.modelId);\n const callId = asString(event.callId);\n const maxRetries = asNumber(event.maxRetries);\n\n // Harvest tool descriptions from the operation/model-call `tools` so tool spans can backfill them.\n // Gated on `recordInputs` to match the OTel path, which only records `available_tools` then.\n if (recordInputs) {\n recordToolDescriptions(callId, event.tools);\n }\n\n const baseAttributes: Record<string, string | number | boolean> = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n ...(provider ? { [GEN_AI_SYSTEM]: provider, [VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE]: provider } : {}),\n ...(modelId ? { [GEN_AI_REQUEST_MODEL]: modelId } : {}),\n ...(maxRetries !== undefined ? { [VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE]: maxRetries } : {}),\n };\n\n switch (type) {\n case 'generateText':\n case 'streamText':\n case 'generateObject':\n // `generateObject` builds the same `invoke_agent` span as `generateText` (non-streaming); its\n // distinct `ai.generateObject` operationId rides on `event.operationId`. The JSON-schema attribute\n // the OTel path derives from the SDK's Zod schema is not reconstructed on the channel path.\n return buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, type === 'streamText');\n case 'languageModelCall':\n return buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId);\n case 'executeTool':\n return buildToolSpan(event, recordInputs);\n case 'embed':\n case 'embedMany': {\n // `embed` carries a single `value`; `embedMany` a `values` array — both map to the embeddings input.\n const input = type === 'embedMany' ? event.values : event.value;\n return startGenAiSpan(GEN_AI_EMBEDDINGS_OPERATION, modelId, {\n ...baseAttributes,\n ...(recordInputs && input !== undefined ? { [GEN_AI_EMBEDDINGS_INPUT]: safeStringify(input) } : {}),\n });\n }\n case 'rerank':\n return startGenAiSpan(GEN_AI_RERANK_OPERATION, modelId, baseAttributes);\n default:\n // Unknown event type: opt out rather than open a span we can't shape correctly.\n return undefined;\n }\n}\n\ntype Attributes = Record<string, string | number | boolean>;\n\n/** Start a `gen_ai.<operation>` span named `<operation> <suffix>` (or just `<operation>` when no suffix). */\nfunction startGenAiSpan(operation: string, suffix: string | undefined, attributes: Attributes): Span {\n return startInactiveSpan({\n name: suffix ? `${operation} ${suffix}` : operation,\n op: `gen_ai.${operation}`,\n attributes: { [GEN_AI_OPERATION_NAME]: operation, ...attributes },\n });\n}\n\nfunction buildInvokeAgentSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n isStream: boolean,\n): Span {\n const functionId = asString(event.functionId);\n const operationId = asString(event.operationId) ?? (isStream ? 'ai.streamText' : 'ai.generateText');\n if (callId) {\n operationIdByCallId.set(callId, { operationId, isStream });\n }\n const span = startGenAiSpan(GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n [GEN_AI_RESPONSE_STREAMING]: isStream,\n ...(functionId ? { [GEN_AI_FUNCTION_ID]: functionId } : {}),\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n });\n if (isStream && callId) {\n invokeAgentSpanByCallId.set(callId, span);\n }\n\n return span;\n}\n\nfunction buildModelCallSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n modelId: string | undefined,\n): Span {\n const parent = callId ? operationIdByCallId.get(callId) : undefined;\n const operationId = parent\n ? `${parent.operationId}.${parent.isStream ? 'doStream' : 'doGenerate'}`\n : 'ai.generateText.doGenerate';\n return startGenAiSpan(GEN_AI_GENERATE_CONTENT_OPERATION, modelId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n ...(recordInputs && Array.isArray(event.tools)\n ? { [GEN_AI_REQUEST_AVAILABLE_TOOLS]: safeStringify(event.tools) }\n : {}),\n });\n}\n\nfunction buildToolSpan(event: Record<string, unknown>, recordInputs: boolean): Span {\n const toolCall = isObjectLike(event.toolCall) ? event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(event.toolCallId) ?? asString(toolCall.toolCallId);\n const toolInput = toolCall.input ?? toolCall.args;\n // The `executeTool` event has no description; backfill it from the operation's recorded tools.\n // Gated on `recordInputs` to match the OTel path (descriptions come from the recorded tools list).\n const description =\n recordInputs && toolName ? resolveToolDescription(asString(event.callId), toolName, event.tools) : undefined;\n return startGenAiSpan(GEN_AI_EXECUTE_TOOL_SPAN_OP, toolName, {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [GEN_AI_TOOL_TYPE]: 'function',\n ...(toolName ? { [GEN_AI_TOOL_NAME]: toolName } : {}),\n ...(toolCallId ? { [GEN_AI_TOOL_CALL_ID_ATTRIBUTE]: toolCallId } : {}),\n ...(description ? { [GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE]: description } : {}),\n ...(recordInputs && toolInput !== undefined ? { [GEN_AI_TOOL_INPUT]: safeStringify(toolInput) } : {}),\n });\n}\n\n/**\n * Best-effort enrichment from the resolved value the AI SDK attaches to the channel context.\n * Everything here is guarded: when a field is missing or the shape differs across `ai` versions,\n * we simply don't set the attribute rather than emit a malformed span.\n */\nexport function enrichSpanOnEnd(\n span: Span,\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): void {\n const { type, result } = data;\n if (!isObjectLike(result)) {\n return;\n }\n\n const { recordOutputs } = getRecordingOptions(data.event, channelOptions);\n\n if (type === 'executeTool') {\n if (recordOutputs) {\n span.setAttribute(GEN_AI_TOOL_OUTPUT, safeStringify(result.output ?? result));\n }\n // From V5 on, tool errors are not rejected (so the `error` channel verb never fires) — they\n // surface as `tool-error` content on the resolved result. Mirror the OTel path by marking the\n // span and capturing the error.\n const output = isObjectLike(result.output) ? result.output : undefined;\n if (output?.type === 'tool-error') {\n captureToolError(span, data, output.error);\n }\n return;\n }\n\n // `languageModelCall` results report usage as `{ total }` objects; top-level/step results report\n // flat numbers. `tokenCount` handles both. v4 names tokens `promptTokens`/`completionTokens`\n // (v5+ uses `inputTokens`/`outputTokens`); the fallbacks are `undefined`-inert on v5+.\n const usage = isObjectLike(result.usage) ? result.usage : undefined;\n if (usage) {\n const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.promptTokens) ?? tokenCount(usage.tokens);\n const outputTokens = tokenCount(usage.outputTokens) ?? tokenCount(usage.completionTokens);\n const totalTokens = tokenCount(usage.totalTokens) ?? sum(inputTokens, outputTokens);\n if (inputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_INPUT_TOKENS, inputTokens);\n }\n if (outputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens);\n }\n if (totalTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_TOTAL_TOKENS, totalTokens);\n }\n }\n\n // Match the OTel integration: finish reasons live on the model-call (`generate_content`) span, not\n // on the top-level `invoke_agent` span.\n const finishReason = getFinishReason(result);\n if (finishReason && type === 'languageModelCall') {\n span.setAttribute(GEN_AI_RESPONSE_FINISH_REASONS, safeStringify([finishReason]));\n }\n\n const response = isObjectLike(result.response) ? result.response : undefined;\n const responseId = asString(response?.id) ?? asString(result.responseId);\n if (responseId) {\n span.setAttribute(GEN_AI_RESPONSE_ID, responseId);\n }\n const responseModel = asString(response?.modelId) ?? asString(data.event.modelId);\n if (responseModel) {\n span.setAttribute(GEN_AI_RESPONSE_MODEL, responseModel);\n }\n\n // Provider-specific cache/reasoning/prediction token breakdowns and `gen_ai.conversation.id`.\n // The channel exposes `providerMetadata` as an object (the OTel path parses it from a string);\n // both share `getProviderMetadataAttributes` so the emitted shape is identical.\n const providerMetadata = (result as { providerMetadata?: unknown }).providerMetadata;\n const providerAttributes = getProviderMetadataAttributes(providerMetadata);\n // Don't overwrite a conversation id already set on span start (e.g. by `conversationIdIntegration`\n // from a user-set scope value); the provider-derived id is only a fallback. Matches the OTel path.\n if (\n GEN_AI_CONVERSATION_ID_ATTRIBUTE in providerAttributes &&\n spanToJSON(span).data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]\n ) {\n // oxlint-disable-next-line typescript/no-dynamic-delete\n delete providerAttributes[GEN_AI_CONVERSATION_ID_ATTRIBUTE];\n }\n span.setAttributes(providerAttributes);\n\n if (recordOutputs) {\n // `languageModelCall` exposes the response as a `content` parts array; top-level results expose\n // `text` + `toolCalls`. Both normalize into the OTel `gen_ai.output.messages` assistant message.\n const parts =\n type === 'languageModelCall' && Array.isArray(result.content)\n ? partsFromContent(result.content)\n : partsFromTextAndToolCalls(result.text, result.toolCalls);\n const outputMessages = buildOutputMessages(parts, finishReason);\n if (outputMessages) {\n span.setAttribute(GEN_AI_OUTPUT_MESSAGES, outputMessages);\n }\n }\n}\n\n/** Maps a Vercel AI finish reason to the OTel `gen_ai.output.messages` form (`tool-calls` → `tool_call`). */\nfunction normalizeFinishReason(finishReason: string | undefined): string {\n return finishReason === 'tool-calls' ? 'tool_call' : (finishReason ?? 'stop');\n}\n\n/** Reads the finish reason from a result — a string on top-level results, `{ unified }` on model calls. */\nfunction getFinishReason(result: Record<string, unknown>): string | undefined {\n const finishReason = result.finishReason;\n if (typeof finishReason === 'string') {\n return finishReason;\n }\n return isObjectLike(finishReason) ? asString(finishReason.unified) : undefined;\n}\n\n/** Reads a token count that may be a plain number or a `{ total }` object (model-call usage). */\nfunction tokenCount(value: unknown): number | undefined {\n return asNumber(value) ?? (isObjectLike(value) ? asNumber(value.total) : undefined);\n}\n\nfunction buildOutputMessages(\n parts: Array<Record<string, unknown>>,\n finishReason: string | undefined,\n): string | undefined {\n if (!parts.length) {\n return undefined;\n }\n return safeStringify([{ role: 'assistant', parts, finish_reason: normalizeFinishReason(finishReason) }]);\n}\n\nfunction toolCallPart(toolCall: Record<string, unknown>): Record<string, unknown> {\n const args = toolCall.input ?? toolCall.args;\n return {\n type: 'tool_call',\n id: asString(toolCall.toolCallId),\n name: asString(toolCall.toolName),\n arguments: typeof args === 'string' ? args : safeStringify(args ?? {}),\n };\n}\n\nfunction partsFromContent(content: unknown[]): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n for (const item of content) {\n if (!isObjectLike(item)) {\n continue;\n }\n if (item.type === 'text' && typeof item.text === 'string') {\n parts.push({ type: 'text', content: item.text });\n } else if (item.type === 'tool-call') {\n parts.push(toolCallPart(item));\n }\n }\n return parts;\n}\n\nfunction partsFromTextAndToolCalls(text: unknown, toolCalls: unknown): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n if (typeof text === 'string' && text.length) {\n parts.push({ type: 'text', content: text });\n }\n if (Array.isArray(toolCalls)) {\n for (const toolCall of toolCalls) {\n if (isObjectLike(toolCall)) {\n parts.push(toolCallPart(toolCall));\n }\n }\n }\n return parts;\n}\n\nexport function captureToolError(span: Span, data: VercelAiChannelMessage, error: unknown): void {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: error instanceof Error ? error.message : 'tool_error',\n });\n\n const toolCall = isObjectLike(data.event.toolCall) ? data.event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(data.event.toolCallId) ?? asString(toolCall.toolCallId);\n\n withScope(scope => {\n scope.setContext('trace', spanToTraceContext(span));\n if (toolName) {\n scope.setTag('vercel.ai.tool.name', toolName);\n }\n if (toolCallId) {\n scope.setTag('vercel.ai.tool.callId', toolCallId);\n }\n scope.setLevel('error');\n captureException(\n error instanceof Error ? error : new Error(typeof error === 'string' ? error : 'Tool execution failed'),\n {\n mechanism: { type: 'auto.vercelai.channel', handled: false },\n },\n );\n });\n}\n\nfunction getRecordingOptions(\n event: Record<string, unknown>,\n channelOptions: VercelAiChannelOptions,\n): {\n recordInputs: boolean;\n recordOutputs: boolean;\n enableTruncation: boolean;\n} {\n const genAI = getClient()?.getDataCollectionOptions().genAI;\n\n return {\n recordInputs: resolveRecording(channelOptions.recordInputs, event.recordInputs, genAI?.inputs),\n recordOutputs: resolveRecording(channelOptions.recordOutputs, event.recordOutputs, genAI?.outputs),\n enableTruncation: shouldEnableTruncation(channelOptions.enableTruncation),\n };\n}\n\n/**\n * Mirrors the OTel integration's `determineRecordingSettings` precedence: an integration-level option\n * wins, then the per-call `experimental_telemetry.recordInputs/recordOutputs` flag the AI SDK forwards\n * on the channel event, then the global `dataCollection.genAI` default.\n *\n * NOTE: the OTel integration also defaults recording to `true` for a call with\n * `experimental_telemetry: { isEnabled: true }`. The `ai:telemetry` channel does not expose `isEnabled`\n * (nor a resolved recording flag), so that per-call default cannot be reproduced here — v7 users who\n * want inputs/outputs recorded must enable `dataCollection.genAI` or set `recordInputs`/`recordOutputs`.\n */\nfunction resolveRecording(integrationOption: unknown, perCallOption: unknown, globalDefault: unknown): boolean {\n if (typeof integrationOption === 'boolean') {\n return integrationOption;\n }\n if (typeof perCallOption === 'boolean') {\n return perCallOption;\n }\n return globalDefault === true;\n}\n\nfunction buildInputMessageAttributes(\n event: Record<string, unknown>,\n enableTruncation: boolean,\n): Record<string, string | number> {\n const attributes: Record<string, string | number> = {};\n\n // `ai` >= 7 forbids system messages in `messages`/`prompt` and exposes the system prompt as a\n // separate `instructions` field. The OTel path lifts the system message out of the prompt into\n // `gen_ai.system_instructions` as `[{ type: 'text', content }]`; mirror that shape here.\n const instructions = asString(event.instructions);\n if (instructions) {\n attributes[GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE] = safeStringify([{ type: 'text', content: instructions }]);\n }\n\n // The AI SDK start events extend `StandardizedPrompt`; messages live on `messages`, otherwise the\n // simpler `prompt` field is used.\n const messages = event.messages ?? event.prompt;\n if (messages !== undefined) {\n attributes[GEN_AI_INPUT_MESSAGES] = enableTruncation ? getTruncatedJsonString(messages) : safeStringify(messages);\n // The original (pre-truncation) message count, so the product can show how many were dropped.\n attributes[GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE] = Array.isArray(messages) ? messages.length : 1;\n }\n\n return attributes;\n}\n"],"names":[],"mappings":"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"} |
@@ -7,5 +7,7 @@ /// <reference path="./node-diagnostics-channel.d.ts" /> | ||
| */ | ||
| export { graphqlIntegration } from './graphql'; | ||
| export { mongooseIntegration } from './mongoose'; | ||
| export { IOREDIS_DC_CHANNEL_COMMAND, IOREDIS_DC_CHANNEL_CONNECT, REDIS_DC_CHANNEL_BATCH, REDIS_DC_CHANNEL_COMMAND, REDIS_DC_CHANNEL_CONNECT, subscribeRedisDiagnosticChannels, } from './redis/redis-dc-subscriber'; | ||
| export { IORedisCommandData, RedisBatchData, RedisCommandData, RedisConnectData, RedisDiagnosticChannelResponseHook, RedisTracingChannelFactory, } from './redis/redis-dc-subscriber'; | ||
| export { mysql2Integration } from './mysql2'; | ||
| export { redisIntegration, RedisDiagnosticChannelsOptions } from './redis'; | ||
| export { RedisDiagnosticChannelResponseHook } from './redis/redis-dc-subscriber'; | ||
| export { defaultDbStatementSerializer } from './redis/redis-statement-serializer'; | ||
@@ -12,0 +14,0 @@ export { bindTracingChannelToSpan } from './tracing-channel'; |
| /** | ||
| * Auto-instrument the `ai` SDK. Supported are: | ||
| * - v7 via native `ai:telemetry` tracing channel | ||
| * - v6 via orchestrion `orchestrion:ai:*` channels | ||
| * - v4, v5 & v6 via orchestrion `orchestrion:ai:*` channels | ||
| */ | ||
@@ -6,0 +6,0 @@ export declare const vercelAiChannelIntegration: (options?: { |
@@ -15,6 +15,33 @@ /** | ||
| export declare const CHANNELS: { | ||
| readonly GRAPHQL_PARSE: "orchestrion:graphql:parse"; | ||
| readonly GRAPHQL_VALIDATE: "orchestrion:graphql:validate"; | ||
| readonly GRAPHQL_EXECUTE: "orchestrion:graphql:execute"; | ||
| readonly EXPRESS_HANDLE: "orchestrion:express:handle"; | ||
| readonly ROUTER_HANDLE: "orchestrion:router:handle"; | ||
| readonly EXPRESS_ROUTE: "orchestrion:express:route"; | ||
| readonly EXPRESS_USE: "orchestrion:express:use"; | ||
| readonly ROUTER_ROUTE: "orchestrion:router:route"; | ||
| readonly ROUTER_USE: "orchestrion:router:use"; | ||
| readonly REDIS_COMMAND: "orchestrion:redis:command"; | ||
| readonly NODE_REDIS_COMMAND: "orchestrion:@redis/client:command"; | ||
| readonly NODE_REDIS_EXECUTOR: "orchestrion:@redis/client:executor"; | ||
| readonly NODE_REDIS_CONNECT: "orchestrion:@redis/client:connect"; | ||
| readonly NODE_REDIS_MULTI: "orchestrion:@redis/client:multi"; | ||
| readonly NODE_REDIS_PIPELINE: "orchestrion:@redis/client:pipeline"; | ||
| readonly NODE_REDIS_BATCH: "orchestrion:@redis/client:batch"; | ||
| readonly HAPI_ROUTE: "orchestrion:@hapi/hapi:route"; | ||
| readonly HAPI_EXT: "orchestrion:@hapi/hapi:ext"; | ||
| readonly AMQPLIB_PUBLISH: "orchestrion:amqplib:publish"; | ||
| readonly AMQPLIB_CONFIRM_PUBLISH: "orchestrion:amqplib:confirmPublish"; | ||
| readonly AMQPLIB_CONSUME: "orchestrion:amqplib:consume"; | ||
| readonly AMQPLIB_DISPATCH: "orchestrion:amqplib:dispatch"; | ||
| readonly AMQPLIB_ACK: "orchestrion:amqplib:ack"; | ||
| readonly AMQPLIB_NACK: "orchestrion:amqplib:nack"; | ||
| readonly AMQPLIB_REJECT: "orchestrion:amqplib:reject"; | ||
| readonly AMQPLIB_ACK_ALL: "orchestrion:amqplib:ackAll"; | ||
| readonly AMQPLIB_NACK_ALL: "orchestrion:amqplib:nackAll"; | ||
| readonly AMQPLIB_CONNECT: "orchestrion:amqplib:connect"; | ||
| readonly VERCEL_AI_GENERATE_TEXT: "orchestrion:ai:generateText"; | ||
| readonly VERCEL_AI_STREAM_TEXT: "orchestrion:ai:streamText"; | ||
| readonly VERCEL_AI_GENERATE_OBJECT: "orchestrion:ai:generateObject"; | ||
| readonly VERCEL_AI_EMBED: "orchestrion:ai:embed"; | ||
@@ -24,2 +51,5 @@ readonly VERCEL_AI_EMBED_MANY: "orchestrion:ai:embedMany"; | ||
| readonly VERCEL_AI_RESOLVE_LANGUAGE_MODEL: "orchestrion:ai:resolveLanguageModel"; | ||
| readonly GOOGLE_GENAI_GENERATE_CONTENT: "orchestrion:@google/genai:generate-content"; | ||
| readonly GOOGLE_GENAI_EMBED_CONTENT: "orchestrion:@google/genai:embed-content"; | ||
| readonly GOOGLE_GENAI_CHAT: "orchestrion:@google/genai:chat"; | ||
| readonly ANTHROPIC_CHAT: "orchestrion:@anthropic-ai/sdk:chat"; | ||
@@ -32,2 +62,6 @@ readonly ANTHROPIC_MODELS: "orchestrion:@anthropic-ai/sdk:models"; | ||
| readonly OPENAI_CONVERSATIONS: "orchestrion:openai:conversations"; | ||
| readonly POSTGRESJS_HANDLE: "orchestrion:postgres:handle"; | ||
| readonly POSTGRESJS_CONNECTION: "orchestrion:postgres:connection"; | ||
| readonly POSTGRESJS_EXECUTE: "orchestrion:postgres:execute"; | ||
| readonly POSTGRESJS_CONNECT: "orchestrion:postgres:connect"; | ||
| readonly PG_QUERY: "orchestrion:pg:query"; | ||
@@ -34,0 +68,0 @@ readonly PG_CONNECT: "orchestrion:pg:connect"; |
@@ -6,2 +6,3 @@ import { InstrumentationConfig } from '@apm-js-collab/code-transformer'; | ||
| readonly VERCEL_AI_STREAM_TEXT: "orchestrion:ai:streamText"; | ||
| readonly VERCEL_AI_GENERATE_OBJECT: "orchestrion:ai:generateObject"; | ||
| readonly VERCEL_AI_EMBED: "orchestrion:ai:embed"; | ||
@@ -8,0 +9,0 @@ readonly VERCEL_AI_EMBED_MANY: "orchestrion:ai:embedMany"; |
@@ -0,2 +1,5 @@ | ||
| import { amqplibChannelIntegration } from '../integrations/tracing-channel/amqplib'; | ||
| import { anthropicChannelIntegration } from '../integrations/tracing-channel/anthropic'; | ||
| import { googleGenAIChannelIntegration } from '../integrations/tracing-channel/google-genai'; | ||
| import { graphqlChannelIntegration } from '../integrations/tracing-channel/graphql'; | ||
| import { hapiChannelIntegration } from '../integrations/tracing-channel/hapi'; | ||
@@ -8,6 +11,12 @@ import { ioredisChannelIntegration } from '../integrations/tracing-channel/ioredis'; | ||
| import { postgresChannelIntegration } from '../integrations/tracing-channel/postgres'; | ||
| import { postgresJsChannelIntegration } from '../integrations/tracing-channel/postgres-js'; | ||
| import { vercelAiChannelIntegration } from '../integrations/tracing-channel/vercel-ai'; | ||
| import { expressChannelIntegration } from '../integrations/tracing-channel/express'; | ||
| export { detectOrchestrionSetup, isOrchestrionInjected } from './detect'; | ||
| export { anthropicChannelIntegration, hapiChannelIntegration, ioredisChannelIntegration, lruMemoizerChannelIntegration, mysqlChannelIntegration, openaiChannelIntegration, postgresChannelIntegration, vercelAiChannelIntegration, }; | ||
| export { amqplibChannelIntegration, anthropicChannelIntegration, googleGenAIChannelIntegration, graphqlChannelIntegration, hapiChannelIntegration, ioredisChannelIntegration, lruMemoizerChannelIntegration, mysqlChannelIntegration, openaiChannelIntegration, postgresChannelIntegration, postgresJsChannelIntegration, vercelAiChannelIntegration, expressChannelIntegration, }; | ||
| export { IORedisChannelIntegrationOptions, IORedisResponseHook } from '../integrations/tracing-channel/ioredis'; | ||
| export { PostgresJsChannelIntegrationOptions } from '../integrations/tracing-channel/postgres-js'; | ||
| export { redisChannelIntegration } from '../integrations/tracing-channel/redis'; | ||
| export { RedisChannelIntegrationOptions, RedisResponseHook } from '../integrations/tracing-channel/redis'; | ||
| export * from '../integrations/tracing-channel/graphql/graphql-types'; | ||
| /** | ||
@@ -22,5 +31,5 @@ * The canonical set of orchestrion diagnostics-channel integrations, keyed by their public | ||
| * | ||
| * NOTE: `ioredisChannelIntegration` is intentionally NOT here. It only partially replaces the | ||
| * composite OTel `Redis` integration and needs the node SDK's redis cache `responseHook` (which | ||
| * can't live in `server-utils`), so `@sentry/node` wires it up separately. | ||
| * NOTE: `ioredisChannelIntegration` and `redisChannelIntegration` are intentionally NOT here. They | ||
| * only partially replace the composite OTel `Redis` integration and need the node SDK's redis cache | ||
| * `responseHook` (which can't live in `server-utils`), so `@sentry/node` wires them up separately. | ||
| */ | ||
@@ -33,2 +42,5 @@ export declare const channelIntegrations: { | ||
| }; | ||
| readonly postgresJsIntegration: (options?: import(".").PostgresJsChannelIntegrationOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "PostgresJs"; | ||
| }; | ||
| readonly mysqlIntegration: () => import("@sentry/core").Integration & { | ||
@@ -46,2 +58,5 @@ name: "Mysql"; | ||
| }; | ||
| readonly googleGenAIIntegration: (options?: import("@sentry/core").GoogleGenAIOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Google_GenAI"; | ||
| }; | ||
| readonly vercelAiIntegration: (options?: { | ||
@@ -54,6 +69,20 @@ recordInputs?: boolean; | ||
| }; | ||
| readonly amqplibIntegration: () => import("@sentry/core").Integration & { | ||
| name: "Amqplib"; | ||
| }; | ||
| readonly hapiIntegration: () => import("@sentry/core").Integration & { | ||
| name: "Hapi"; | ||
| }; | ||
| readonly expressIntegration: (options?: import("../integrations/tracing-channel/express/types").ExpressIntegrationOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Express"; | ||
| }; | ||
| readonly graphqlIntegration: (options?: import("../graphql/graphql-dc-subscriber").GraphqlDiagnosticChannelsOptions) => Pick<import("@sentry/core").Integration & { | ||
| name: "Graphql"; | ||
| }, Exclude<keyof (import("@sentry/core").Integration & { | ||
| name: "Graphql"; | ||
| }), "name" | "setupOnce">> & { | ||
| name: "Graphql"; | ||
| setupOnce: () => void | undefined; | ||
| }; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
@@ -87,9 +87,4 @@ import { TracingChannel } from 'node:diagnostics_channel'; | ||
| * patcher gated to those older versions. | ||
| * | ||
| * Idempotent: subsequent calls update the response hook but do not | ||
| * re-subscribe. | ||
| */ | ||
| export declare function subscribeRedisDiagnosticChannels(tracingChannel: RedisTracingChannelFactory, responseHook?: RedisDiagnosticChannelResponseHook): void; | ||
| /** Test-only: detach all channel bindings and reset module-local subscribe state. */ | ||
| export declare function _resetRedisDiagnosticChannelsForTesting(): void; | ||
| //# sourceMappingURL=redis-dc-subscriber.d.ts.map |
@@ -45,2 +45,7 @@ import { TracingChannel, TracingChannelSubscribers } from 'node:diagnostics_channel'; | ||
| }) => boolean; | ||
| /** | ||
| * Apply span/scope binding only if there is a parent span, similar to returning `undefined` in the `getSpan` callback. | ||
| * If you need to perform some conditional checking on the parent span before deciding, do it in the `getSpan` callback. | ||
| */ | ||
| requiresParentSpan?: boolean; | ||
| } | ||
@@ -47,0 +52,0 @@ /** Returned by {@link bindTracingChannelToSpan}: the bound channel plus a teardown handle. */ |
| import { Span } from '@sentry/core'; | ||
| import { TracingChannel } from 'node:diagnostics_channel'; | ||
| import { StreamedModelCallResult } from './util'; | ||
| /** Drop the per-operation `callId` maps once the owning top-level operation settles (success or error). */ | ||
@@ -13,3 +14,3 @@ export declare function clearOperationId(data: VercelAiChannelMessage): void; | ||
| /** The lifecycle event types the `ai:telemetry` channel can carry. */ | ||
| export type ChannelEventType = 'generateText' | 'streamText' | 'step' | 'languageModelCall' | 'executeTool' | 'embed' | 'embedMany' | 'rerank'; | ||
| export type ChannelEventType = 'generateText' | 'streamText' | 'generateObject' | 'step' | 'languageModelCall' | 'executeTool' | 'embed' | 'embedMany' | 'rerank'; | ||
| /** | ||
@@ -53,5 +54,7 @@ * The context object the AI SDK passes through one tracing-channel call. It is the same object | ||
| * ever emitted and this is inert, so there is no double-instrumentation against the OTel-based | ||
| * patcher. Idempotent. | ||
| * patcher. The integration's `setupOnce` guarantees this runs a single time. | ||
| */ | ||
| export declare function subscribeVercelAiTracingChannel(tracingChannel: VercelAiTracingChannelFactory, options?: VercelAiChannelOptions): void; | ||
| /** Map the tapped stream aggregate onto the `languageModelCall` result shape `enrichSpanOnEnd` reads. */ | ||
| export declare function streamedResultToChannelResult(final: StreamedModelCallResult): Record<string, unknown>; | ||
| /** | ||
@@ -70,2 +73,3 @@ * Transform a channel `start` payload into the span that should be active for the operation. For | ||
| export declare function enrichSpanOnEnd(span: Span, data: VercelAiChannelMessage, channelOptions: VercelAiChannelOptions): void; | ||
| export declare function captureToolError(span: Span, data: VercelAiChannelMessage, error: unknown): void; | ||
| //# sourceMappingURL=vercel-ai-dc-subscriber.d.ts.map |
@@ -6,5 +6,7 @@ /** | ||
| */ | ||
| export { graphqlIntegration } from './graphql'; | ||
| export { mongooseIntegration } from './mongoose'; | ||
| export { IOREDIS_DC_CHANNEL_COMMAND, IOREDIS_DC_CHANNEL_CONNECT, REDIS_DC_CHANNEL_BATCH, REDIS_DC_CHANNEL_COMMAND, REDIS_DC_CHANNEL_CONNECT, subscribeRedisDiagnosticChannels, } from './redis/redis-dc-subscriber'; | ||
| export type { IORedisCommandData, RedisBatchData, RedisCommandData, RedisConnectData, RedisDiagnosticChannelResponseHook, RedisTracingChannelFactory, } from './redis/redis-dc-subscriber'; | ||
| export { mysql2Integration } from './mysql2'; | ||
| export { redisIntegration, type RedisDiagnosticChannelsOptions } from './redis'; | ||
| export type { RedisDiagnosticChannelResponseHook } from './redis/redis-dc-subscriber'; | ||
| export { defaultDbStatementSerializer } from './redis/redis-statement-serializer'; | ||
@@ -11,0 +13,0 @@ export { bindTracingChannelToSpan } from './tracing-channel'; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,gCAAgC,GACjC,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,kCAAkC,EAClC,0BAA0B,GAC3B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,2BAA2B,EAC3B,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EACL,kBAAkB,EAElB,kBAAkB,EAElB,iBAAiB,GAClB,MAAM,wCAAwC,CAAC"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,KAAK,8BAA8B,EAAE,MAAM,SAAS,CAAC;AAChF,YAAY,EAAE,kCAAkC,EAAE,MAAM,6BAA6B,CAAC;AACtF,OAAO,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,2BAA2B,EAC3B,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EACL,kBAAkB,EAElB,kBAAkB,EAElB,iBAAiB,GAClB,MAAM,wCAAwC,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/fastify/instrumentation.ts"],"names":[],"mappings":"AA+XA;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,SACE,IAAI;;CAmBnC,CAAC"} | ||
| {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing-channel/fastify/instrumentation.ts"],"names":[],"mappings":"AAgYA;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,SACE,IAAI;;CAmBnC,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"ioredis.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/ioredis.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAiB,IAAI,EAAE,MAAM,cAAc,CAAC;AAwBxD,uGAAuG;AACvG,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAEvH,MAAM,WAAW,gCAAgC;IAC/C,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAoHD;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB;;CAAgD,CAAC"} | ||
| {"version":3,"file":"ioredis.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/ioredis.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAiB,IAAI,EAAE,MAAM,cAAc,CAAC;AAuBxD,uGAAuG;AACvG,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAEvH,MAAM,WAAW,gCAAgC;IAC/C,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAmHD;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB;;CAAgD,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/mysql.ts"],"names":[],"mappings":"AAyKA;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB;;CAA8C,CAAC"} | ||
| {"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/mysql.ts"],"names":[],"mappings":"AA0KA;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB;;CAA8C,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/openai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,aAAa,EAA4B,MAAM,cAAc,CAAC;AA6I3F;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;CAA+C,CAAC"} | ||
| {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/openai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,aAAa,EAA4B,MAAM,cAAc,CAAC;AA8I3F;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;CAA+C,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/postgres.ts"],"names":[],"mappings":"AAsRA;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B;yBA9M+B,OAAO;;;CA8MW,CAAC"} | ||
| {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../../src/integrations/tracing-channel/postgres.ts"],"names":[],"mappings":"AAmRA;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B;yBA3M+B,OAAO;;;CA2MW,CAAC"} |
| /** | ||
| * Auto-instrument the `ai` SDK. Supported are: | ||
| * - v7 via native `ai:telemetry` tracing channel | ||
| * - v6 via orchestrion `orchestrion:ai:*` channels | ||
| * - v4, v5 & v6 via orchestrion `orchestrion:ai:*` channels | ||
| */ | ||
@@ -6,0 +6,0 @@ export declare const vercelAiChannelIntegration: (options?: { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"mongoose-dc-subscriber.d.ts","sourceRoot":"","sources":["../../../src/mongoose/mongoose-dc-subscriber.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAmB/D,eAAO,MAAM,yBAAyB,mBAAmB,CAAC;AAC1D,eAAO,MAAM,6BAA6B,uBAAuB,CAAC;AAClE,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AACpE,eAAO,MAAM,qCAAqC,8BAA8B,CAAC;AACjF,eAAO,MAAM,oCAAoC,6BAA6B,CAAC;AAC/E,eAAO,MAAM,+BAA+B,yBAAyB,CAAC;AAStE;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,GAAG,CAAC,EAAE,OAAO,CAAC;QACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAIrG;;;;;;;;;;GAUG;AACH,wBAAgB,mCAAmC,CAAC,cAAc,EAAE,6BAA6B,GAAG,IAAI,CAkBvG"} | ||
| {"version":3,"file":"mongoose-dc-subscriber.d.ts","sourceRoot":"","sources":["../../../src/mongoose/mongoose-dc-subscriber.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAyB/D,eAAO,MAAM,yBAAyB,mBAAmB,CAAC;AAC1D,eAAO,MAAM,6BAA6B,uBAAuB,CAAC;AAClE,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AACpE,eAAO,MAAM,qCAAqC,8BAA8B,CAAC;AACjF,eAAO,MAAM,oCAAoC,6BAA6B,CAAC;AAC/E,eAAO,MAAM,+BAA+B,yBAAyB,CAAC;AAStE;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,GAAG,CAAC,EAAE,OAAO,CAAC;QACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAIrG;;;;;;;;;;GAUG;AACH,wBAAgB,mCAAmC,CAAC,cAAc,EAAE,6BAA6B,GAAG,IAAI,CAkBvG"} |
@@ -15,6 +15,33 @@ /** | ||
| export declare const CHANNELS: { | ||
| readonly GRAPHQL_PARSE: "orchestrion:graphql:parse"; | ||
| readonly GRAPHQL_VALIDATE: "orchestrion:graphql:validate"; | ||
| readonly GRAPHQL_EXECUTE: "orchestrion:graphql:execute"; | ||
| readonly EXPRESS_HANDLE: "orchestrion:express:handle"; | ||
| readonly ROUTER_HANDLE: "orchestrion:router:handle"; | ||
| readonly EXPRESS_ROUTE: "orchestrion:express:route"; | ||
| readonly EXPRESS_USE: "orchestrion:express:use"; | ||
| readonly ROUTER_ROUTE: "orchestrion:router:route"; | ||
| readonly ROUTER_USE: "orchestrion:router:use"; | ||
| readonly REDIS_COMMAND: "orchestrion:redis:command"; | ||
| readonly NODE_REDIS_COMMAND: "orchestrion:@redis/client:command"; | ||
| readonly NODE_REDIS_EXECUTOR: "orchestrion:@redis/client:executor"; | ||
| readonly NODE_REDIS_CONNECT: "orchestrion:@redis/client:connect"; | ||
| readonly NODE_REDIS_MULTI: "orchestrion:@redis/client:multi"; | ||
| readonly NODE_REDIS_PIPELINE: "orchestrion:@redis/client:pipeline"; | ||
| readonly NODE_REDIS_BATCH: "orchestrion:@redis/client:batch"; | ||
| readonly HAPI_ROUTE: "orchestrion:@hapi/hapi:route"; | ||
| readonly HAPI_EXT: "orchestrion:@hapi/hapi:ext"; | ||
| readonly AMQPLIB_PUBLISH: "orchestrion:amqplib:publish"; | ||
| readonly AMQPLIB_CONFIRM_PUBLISH: "orchestrion:amqplib:confirmPublish"; | ||
| readonly AMQPLIB_CONSUME: "orchestrion:amqplib:consume"; | ||
| readonly AMQPLIB_DISPATCH: "orchestrion:amqplib:dispatch"; | ||
| readonly AMQPLIB_ACK: "orchestrion:amqplib:ack"; | ||
| readonly AMQPLIB_NACK: "orchestrion:amqplib:nack"; | ||
| readonly AMQPLIB_REJECT: "orchestrion:amqplib:reject"; | ||
| readonly AMQPLIB_ACK_ALL: "orchestrion:amqplib:ackAll"; | ||
| readonly AMQPLIB_NACK_ALL: "orchestrion:amqplib:nackAll"; | ||
| readonly AMQPLIB_CONNECT: "orchestrion:amqplib:connect"; | ||
| readonly VERCEL_AI_GENERATE_TEXT: "orchestrion:ai:generateText"; | ||
| readonly VERCEL_AI_STREAM_TEXT: "orchestrion:ai:streamText"; | ||
| readonly VERCEL_AI_GENERATE_OBJECT: "orchestrion:ai:generateObject"; | ||
| readonly VERCEL_AI_EMBED: "orchestrion:ai:embed"; | ||
@@ -24,2 +51,5 @@ readonly VERCEL_AI_EMBED_MANY: "orchestrion:ai:embedMany"; | ||
| readonly VERCEL_AI_RESOLVE_LANGUAGE_MODEL: "orchestrion:ai:resolveLanguageModel"; | ||
| readonly GOOGLE_GENAI_GENERATE_CONTENT: "orchestrion:@google/genai:generate-content"; | ||
| readonly GOOGLE_GENAI_EMBED_CONTENT: "orchestrion:@google/genai:embed-content"; | ||
| readonly GOOGLE_GENAI_CHAT: "orchestrion:@google/genai:chat"; | ||
| readonly ANTHROPIC_CHAT: "orchestrion:@anthropic-ai/sdk:chat"; | ||
@@ -32,2 +62,6 @@ readonly ANTHROPIC_MODELS: "orchestrion:@anthropic-ai/sdk:models"; | ||
| readonly OPENAI_CONVERSATIONS: "orchestrion:openai:conversations"; | ||
| readonly POSTGRESJS_HANDLE: "orchestrion:postgres:handle"; | ||
| readonly POSTGRESJS_CONNECTION: "orchestrion:postgres:connection"; | ||
| readonly POSTGRESJS_EXECUTE: "orchestrion:postgres:execute"; | ||
| readonly POSTGRESJS_CONNECT: "orchestrion:postgres:connect"; | ||
| readonly PG_QUERY: "orchestrion:pg:query"; | ||
@@ -34,0 +68,0 @@ readonly PG_CONNECT: "orchestrion:pg:connect"; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../../src/orchestrion/channels.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;CASX,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC"} | ||
| {"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../../src/orchestrion/channels.ts"],"names":[],"mappings":"AAeA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAeX,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/orchestrion/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAU7E,eAAO,MAAM,uBAAuB,EAAE,qBAAqB,EAS1D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,EAAyE,CAAC;AAExH;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,GAAG,SAAS,CAO1G"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/orchestrion/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAiB7E,eAAO,MAAM,uBAAuB,EAAE,qBAAqB,EAe1D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,EAA0D,CAAC;AAEzG;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,GAAG,SAAS,CAO1G"} |
@@ -6,2 +6,3 @@ import type { InstrumentationConfig } from '@apm-js-collab/code-transformer'; | ||
| readonly VERCEL_AI_STREAM_TEXT: "orchestrion:ai:streamText"; | ||
| readonly VERCEL_AI_GENERATE_OBJECT: "orchestrion:ai:generateObject"; | ||
| readonly VERCEL_AI_EMBED: "orchestrion:ai:embed"; | ||
@@ -8,0 +9,0 @@ readonly VERCEL_AI_EMBED_MANY: "orchestrion:ai:embedMany"; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"vercel-ai.d.ts","sourceRoot":"","sources":["../../../../src/orchestrion/config/vercel-ai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAE7E,eAAO,MAAM,cAAc,yBAeQ,CAAC;AAEpC,eAAO,MAAM,gBAAgB;;;;;;;CAcnB,CAAC"} | ||
| {"version":3,"file":"vercel-ai.d.ts","sourceRoot":"","sources":["../../../../src/orchestrion/config/vercel-ai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAE7E,eAAO,MAAM,cAAc,yBAsBQ,CAAC;AAEpC,eAAO,MAAM,gBAAgB;;;;;;;;CAcnB,CAAC"} |
@@ -0,2 +1,5 @@ | ||
| import { amqplibChannelIntegration } from '../integrations/tracing-channel/amqplib'; | ||
| import { anthropicChannelIntegration } from '../integrations/tracing-channel/anthropic'; | ||
| import { googleGenAIChannelIntegration } from '../integrations/tracing-channel/google-genai'; | ||
| import { graphqlChannelIntegration } from '../integrations/tracing-channel/graphql'; | ||
| import { hapiChannelIntegration } from '../integrations/tracing-channel/hapi'; | ||
@@ -8,6 +11,12 @@ import { ioredisChannelIntegration } from '../integrations/tracing-channel/ioredis'; | ||
| import { postgresChannelIntegration } from '../integrations/tracing-channel/postgres'; | ||
| import { postgresJsChannelIntegration } from '../integrations/tracing-channel/postgres-js'; | ||
| import { vercelAiChannelIntegration } from '../integrations/tracing-channel/vercel-ai'; | ||
| import { expressChannelIntegration } from '../integrations/tracing-channel/express'; | ||
| export { detectOrchestrionSetup, isOrchestrionInjected } from './detect'; | ||
| export { anthropicChannelIntegration, hapiChannelIntegration, ioredisChannelIntegration, lruMemoizerChannelIntegration, mysqlChannelIntegration, openaiChannelIntegration, postgresChannelIntegration, vercelAiChannelIntegration, }; | ||
| export { amqplibChannelIntegration, anthropicChannelIntegration, googleGenAIChannelIntegration, graphqlChannelIntegration, hapiChannelIntegration, ioredisChannelIntegration, lruMemoizerChannelIntegration, mysqlChannelIntegration, openaiChannelIntegration, postgresChannelIntegration, postgresJsChannelIntegration, vercelAiChannelIntegration, expressChannelIntegration, }; | ||
| export type { IORedisChannelIntegrationOptions, IORedisResponseHook } from '../integrations/tracing-channel/ioredis'; | ||
| export type { PostgresJsChannelIntegrationOptions } from '../integrations/tracing-channel/postgres-js'; | ||
| export { redisChannelIntegration } from '../integrations/tracing-channel/redis'; | ||
| export type { RedisChannelIntegrationOptions, RedisResponseHook } from '../integrations/tracing-channel/redis'; | ||
| export type * from '../integrations/tracing-channel/graphql/graphql-types'; | ||
| /** | ||
@@ -22,5 +31,5 @@ * The canonical set of orchestrion diagnostics-channel integrations, keyed by their public | ||
| * | ||
| * NOTE: `ioredisChannelIntegration` is intentionally NOT here. It only partially replaces the | ||
| * composite OTel `Redis` integration and needs the node SDK's redis cache `responseHook` (which | ||
| * can't live in `server-utils`), so `@sentry/node` wires it up separately. | ||
| * NOTE: `ioredisChannelIntegration` and `redisChannelIntegration` are intentionally NOT here. They | ||
| * only partially replace the composite OTel `Redis` integration and need the node SDK's redis cache | ||
| * `responseHook` (which can't live in `server-utils`), so `@sentry/node` wires them up separately. | ||
| */ | ||
@@ -33,2 +42,5 @@ export declare const channelIntegrations: { | ||
| }; | ||
| readonly postgresJsIntegration: (options?: import(".").PostgresJsChannelIntegrationOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "PostgresJs"; | ||
| }; | ||
| readonly mysqlIntegration: () => import("@sentry/core").Integration & { | ||
@@ -46,2 +58,5 @@ name: "Mysql"; | ||
| }; | ||
| readonly googleGenAIIntegration: (options?: import("@sentry/core").GoogleGenAIOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Google_GenAI"; | ||
| }; | ||
| readonly vercelAiIntegration: (options?: { | ||
@@ -54,6 +69,18 @@ recordInputs?: boolean; | ||
| }; | ||
| readonly amqplibIntegration: () => import("@sentry/core").Integration & { | ||
| name: "Amqplib"; | ||
| }; | ||
| readonly hapiIntegration: () => import("@sentry/core").Integration & { | ||
| name: "Hapi"; | ||
| }; | ||
| readonly expressIntegration: (options?: import("../integrations/tracing-channel/express/types").ExpressIntegrationOptions | undefined) => import("@sentry/core").Integration & { | ||
| name: "Express"; | ||
| }; | ||
| readonly graphqlIntegration: (options?: import("../graphql/graphql-dc-subscriber").GraphqlDiagnosticChannelsOptions) => Omit<import("@sentry/core").Integration & { | ||
| name: "Graphql"; | ||
| }, "name" | "setupOnce"> & { | ||
| name: "Graphql"; | ||
| setupOnce: () => void | undefined; | ||
| }; | ||
| }; | ||
| //# sourceMappingURL=index.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/orchestrion/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAC;AAC7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AACtF,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAEvF,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACzE,OAAO,EACL,2BAA2B,EAC3B,sBAAsB,EACtB,yBAAyB,EACzB,6BAA6B,EAC7B,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,EAC1B,0BAA0B,GAC3B,CAAC;AACF,YAAY,EAAE,gCAAgC,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAErH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAQtB,CAAC"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/orchestrion/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAC;AAC7F,OAAO,EACL,yBAAyB,EAE1B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAC;AAC7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AACtF,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAC;AAC3F,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AACvF,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AAEpF,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACzE,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,6BAA6B,EAC7B,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,EACzB,6BAA6B,EAC7B,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,EAC1B,4BAA4B,EAC5B,0BAA0B,EAC1B,yBAAyB,GAC1B,CAAC;AACF,YAAY,EAAE,gCAAgC,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AACrH,YAAY,EAAE,mCAAmC,EAAE,MAAM,6CAA6C,CAAC;AACvG,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,YAAY,EAAE,8BAA8B,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAI/G,mBAAmB,uDAAuD,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAatB,CAAC"} |
@@ -87,9 +87,4 @@ import type { TracingChannel } from 'node:diagnostics_channel'; | ||
| * patcher gated to those older versions. | ||
| * | ||
| * Idempotent: subsequent calls update the response hook but do not | ||
| * re-subscribe. | ||
| */ | ||
| export declare function subscribeRedisDiagnosticChannels(tracingChannel: RedisTracingChannelFactory, responseHook?: RedisDiagnosticChannelResponseHook): void; | ||
| /** Test-only: detach all channel bindings and reset module-local subscribe state. */ | ||
| export declare function _resetRedisDiagnosticChannelsForTesting(): void; | ||
| //# sourceMappingURL=redis-dc-subscriber.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"redis-dc-subscriber.d.ts","sourceRoot":"","sources":["../../../src/redis/redis-dc-subscriber.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAQ/D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AASzC,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAC7D,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAC7D,eAAO,MAAM,0BAA0B,oBAAoB,CAAC;AAC5D,eAAO,MAAM,0BAA0B,oBAAoB,CAAC;AAK5D;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,oFAAoF;AACpF,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,MAAM,kCAAkC,GAAG,CAC/C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,OAAO,KACZ,IAAI,CAAC;AAEV;;;;;;GAMG;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAMlG;;;;;;;;;;;GAWG;AACH,wBAAgB,gCAAgC,CAC9C,cAAc,EAAE,0BAA0B,EAC1C,YAAY,CAAC,EAAE,kCAAkC,GAChD,IAAI,CAyBN;AA8FD,qFAAqF;AACrF,wBAAgB,uCAAuC,IAAI,IAAI,CAK9D"} | ||
| {"version":3,"file":"redis-dc-subscriber.d.ts","sourceRoot":"","sources":["../../../src/redis/redis-dc-subscriber.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAQ/D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAQzC,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAC7D,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAC7D,eAAO,MAAM,0BAA0B,oBAAoB,CAAC;AAC5D,eAAO,MAAM,0BAA0B,oBAAoB,CAAC;AAK5D;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,oFAAoF;AACpF,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,MAAM,kCAAkC,GAAG,CAC/C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,OAAO,KACZ,IAAI,CAAC;AAEV;;;;;;GAMG;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAElG;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAC9C,cAAc,EAAE,0BAA0B,EAC1C,YAAY,CAAC,EAAE,kCAAkC,GAChD,IAAI,CAkBN"} |
@@ -45,2 +45,7 @@ import type { TracingChannel, TracingChannelSubscribers } from 'node:diagnostics_channel'; | ||
| }) => boolean; | ||
| /** | ||
| * Apply span/scope binding only if there is a parent span, similar to returning `undefined` in the `getSpan` callback. | ||
| * If you need to perform some conditional checking on the parent span before deciding, do it in the `getSpan` callback. | ||
| */ | ||
| requiresParentSpan?: boolean; | ||
| } | ||
@@ -47,0 +52,0 @@ /** Returned by {@link bindTracingChannelToSpan}: the bound channel plus a teardown handle. */ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"tracing-channel.d.ts","sourceRoot":"","sources":["../../src/tracing-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAE1F,OAAO,KAAK,EAAE,kCAAkC,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAK7E,MAAM,MAAM,6BAA6B,CAAC,KAAK,SAAS,MAAM,IAAI,KAAK,GAAG;IACxE;;OAEG;IACH,WAAW,CAAC,EAAE,IAAI,CAAC;IAEnB;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAKF,MAAM,WAAW,oBAAoB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,IAAI,CAC/E,cAAc,CAAC,KAAK,EAAE,6BAA6B,CAAC,KAAK,CAAC,CAAC,EAC3D,WAAW,GAAG,aAAa,CAC5B;IACC,SAAS,CAAC,WAAW,EAAE,OAAO,CAAC,yBAAyB,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACvG,WAAW,CAAC,WAAW,EAAE,OAAO,CAAC,yBAAyB,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAC1G;AAED,MAAM,WAAW,8BAA8B,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IAC3E;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,6BAA6B,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;IAEjF;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,KAAK,kCAAkC,CAAC,CAAC;IAE9E;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE;QACpB,IAAI,EAAE,IAAI,CAAC;QACX,IAAI,EAAE,6BAA6B,CAAC,KAAK,CAAC,CAAC;QAC3C,8EAA8E;QAC9E,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;KAChC,KAAK,OAAO,CAAC;CACf;AAED,8FAA8F;AAC9F,MAAM,WAAW,2BAA2B,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACxE;;OAEG;IACH,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAErC;;;OAGG;IACH,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAID;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,SAAS,MAAM,EAC3D,OAAO,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,EACrC,OAAO,EAAE,CAAC,IAAI,EAAE,6BAA6B,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,SAAS,EACzE,IAAI,CAAC,EAAE,8BAA8B,CAAC,KAAK,CAAC,GAC3C,2BAA2B,CAAC,KAAK,CAAC,CA0FpC"} | ||
| {"version":3,"file":"tracing-channel.d.ts","sourceRoot":"","sources":["../../src/tracing-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAE1F,OAAO,KAAK,EAAE,kCAAkC,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAa7E,MAAM,MAAM,6BAA6B,CAAC,KAAK,SAAS,MAAM,IAAI,KAAK,GAAG;IACxE;;OAEG;IACH,WAAW,CAAC,EAAE,IAAI,CAAC;IAEnB;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAKF,MAAM,WAAW,oBAAoB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,IAAI,CAC/E,cAAc,CAAC,KAAK,EAAE,6BAA6B,CAAC,KAAK,CAAC,CAAC,EAC3D,WAAW,GAAG,aAAa,CAC5B;IACC,SAAS,CAAC,WAAW,EAAE,OAAO,CAAC,yBAAyB,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACvG,WAAW,CAAC,WAAW,EAAE,OAAO,CAAC,yBAAyB,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAC1G;AAED,MAAM,WAAW,8BAA8B,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IAC3E;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,6BAA6B,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;IAEjF;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,KAAK,kCAAkC,CAAC,CAAC;IAE9E;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE;QACpB,IAAI,EAAE,IAAI,CAAC;QACX,IAAI,EAAE,6BAA6B,CAAC,KAAK,CAAC,CAAC;QAC3C,8EAA8E;QAC9E,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;KAChC,KAAK,OAAO,CAAC;IAEd;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,8FAA8F;AAC9F,MAAM,WAAW,2BAA2B,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACxE;;OAEG;IACH,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAErC;;;OAGG;IACH,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAID;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,SAAS,MAAM,EAC3D,OAAO,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,EACrC,OAAO,EAAE,CAAC,IAAI,EAAE,6BAA6B,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,SAAS,EACzE,IAAI,CAAC,EAAE,8BAA8B,CAAC,KAAK,CAAC,GAC3C,2BAA2B,CAAC,KAAK,CAAC,CA0FpC"} |
| import type { Span } from '@sentry/core'; | ||
| import type { TracingChannel } from 'node:diagnostics_channel'; | ||
| import { type StreamedModelCallResult } from './util'; | ||
| /** Drop the per-operation `callId` maps once the owning top-level operation settles (success or error). */ | ||
@@ -13,3 +14,3 @@ export declare function clearOperationId(data: VercelAiChannelMessage): void; | ||
| /** The lifecycle event types the `ai:telemetry` channel can carry. */ | ||
| export type ChannelEventType = 'generateText' | 'streamText' | 'step' | 'languageModelCall' | 'executeTool' | 'embed' | 'embedMany' | 'rerank'; | ||
| export type ChannelEventType = 'generateText' | 'streamText' | 'generateObject' | 'step' | 'languageModelCall' | 'executeTool' | 'embed' | 'embedMany' | 'rerank'; | ||
| /** | ||
@@ -53,5 +54,7 @@ * The context object the AI SDK passes through one tracing-channel call. It is the same object | ||
| * ever emitted and this is inert, so there is no double-instrumentation against the OTel-based | ||
| * patcher. Idempotent. | ||
| * patcher. The integration's `setupOnce` guarantees this runs a single time. | ||
| */ | ||
| export declare function subscribeVercelAiTracingChannel(tracingChannel: VercelAiTracingChannelFactory, options?: VercelAiChannelOptions): void; | ||
| /** Map the tapped stream aggregate onto the `languageModelCall` result shape `enrichSpanOnEnd` reads. */ | ||
| export declare function streamedResultToChannelResult(final: StreamedModelCallResult): Record<string, unknown>; | ||
| /** | ||
@@ -70,2 +73,3 @@ * Transform a channel `start` payload into the span that should be active for the operation. For | ||
| export declare function enrichSpanOnEnd(span: Span, data: VercelAiChannelMessage, channelOptions: VercelAiChannelOptions): void; | ||
| export declare function captureToolError(span: Span, data: VercelAiChannelMessage, error: unknown): void; | ||
| //# sourceMappingURL=vercel-ai-dc-subscriber.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"vercel-ai-dc-subscriber.d.ts","sourceRoot":"","sources":["../../../src/vercel-ai/vercel-ai-dc-subscriber.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAiBzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AA6C/D,2GAA2G;AAC3G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI,CAQnE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGzD;AA0CD,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,YAAY,GACZ,MAAM,GACN,mBAAmB,GACnB,aAAa,GACb,OAAO,GACP,WAAW,GACX,QAAQ,CAAC;AAEb;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAErG,mHAAmH;AACnH,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,+BAA+B,CAC7C,cAAc,EAAE,6BAA6B,EAC7C,OAAO,GAAE,sBAA2B,GACnC,IAAI,CAkBN;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,sBAAsB,EAC5B,cAAc,EAAE,sBAAsB,GACrC,IAAI,GAAG,SAAS,CAmDlB;AA4ED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,sBAAsB,EAC5B,cAAc,EAAE,sBAAsB,GACrC,IAAI,CAqFN"} | ||
| {"version":3,"file":"vercel-ai-dc-subscriber.d.ts","sourceRoot":"","sources":["../../../src/vercel-ai/vercel-ai-dc-subscriber.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAkBzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EAKL,KAAK,uBAAuB,EAG7B,MAAM,QAAQ,CAAC;AAyDhB,2GAA2G;AAC3G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI,CAQnE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAIzD;AA0CD,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,YAAY,GACZ,gBAAgB,GAChB,MAAM,GACN,mBAAmB,GACnB,aAAa,GACb,OAAO,GACP,WAAW,GACX,QAAQ,CAAC;AAEb;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAErG,mHAAmH;AACnH,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,+BAA+B,CAC7C,cAAc,EAAE,6BAA6B,EAC7C,OAAO,GAAE,sBAA2B,GACnC,IAAI,CAgBN;AAwCD,yGAAyG;AACzG,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuBrG;AA4CD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,sBAAsB,EAC5B,cAAc,EAAE,sBAAsB,GACrC,IAAI,GAAG,SAAS,CAuDlB;AAiFD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,sBAAsB,EAC5B,cAAc,EAAE,sBAAsB,GACrC,IAAI,CAsFN;AAuED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,sBAAsB,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CA0B/F"} |
+2
-2
| { | ||
| "name": "@sentry/server-utils", | ||
| "version": "10.64.0", | ||
| "version": "10.65.0", | ||
| "description": "Server Utilities for all Sentry JavaScript SDKs", | ||
@@ -89,3 +89,3 @@ "repository": "git://github.com/getsentry/sentry-javascript.git", | ||
| "@sentry/conventions": "^0.15.1", | ||
| "@sentry/core": "10.64.0", | ||
| "@sentry/core": "10.65.0", | ||
| "magic-string": "~0.30.0" | ||
@@ -92,0 +92,0 @@ }, |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| const debugBuild = require('../debug-build.js'); | ||
| const channels = require('../orchestrion/channels.js'); | ||
| const tracingChannel = require('../tracing-channel.js'); | ||
| const vercelAiDcSubscriber = require('./vercel-ai-dc-subscriber.js'); | ||
| const PATCHED = /* @__PURE__ */ Symbol("SentryVercelAiModelPatched"); | ||
| let callIdCounter = 0; | ||
| function nextCallId() { | ||
| return `v6-${++callIdCounter}`; | ||
| } | ||
| const messages = /* @__PURE__ */ new WeakMap(); | ||
| const operationSpans = /* @__PURE__ */ new WeakSet(); | ||
| const callIdBySpan = /* @__PURE__ */ new WeakMap(); | ||
| const recordingBySpan = /* @__PURE__ */ new WeakMap(); | ||
| const suppressedTelemetry = /* @__PURE__ */ new WeakSet(); | ||
| let subscribed = false; | ||
| function subscribeVercelAiOrchestrionChannels(tracingChannel, options = {}) { | ||
| if (subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| try { | ||
| bindOperation(tracingChannel, channels.CHANNELS.VERCEL_AI_GENERATE_TEXT, buildTextMessage("generateText"), options); | ||
| bindOperation(tracingChannel, channels.CHANNELS.VERCEL_AI_STREAM_TEXT, buildTextMessage("streamText"), options); | ||
| bindOperation( | ||
| tracingChannel, | ||
| channels.CHANNELS.VERCEL_AI_EMBED, | ||
| (callOptions, telemetry) => ({ | ||
| type: "embed", | ||
| event: { | ||
| callId: nextCallId(), | ||
| ...modelFields(callOptions.model), | ||
| maxRetries: callOptions.maxRetries, | ||
| value: callOptions.value, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| bindOperation( | ||
| tracingChannel, | ||
| channels.CHANNELS.VERCEL_AI_EMBED_MANY, | ||
| // `embedMany` takes a `values` array (vs `embed`'s single `value`); the shared core reads it as the | ||
| // embeddings input, matching the OTel path's batch `ai.embedMany` span. | ||
| (callOptions, telemetry) => ({ | ||
| type: "embedMany", | ||
| event: { | ||
| callId: nextCallId(), | ||
| ...modelFields(callOptions.model), | ||
| maxRetries: callOptions.maxRetries, | ||
| values: callOptions.values, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| bindOperation( | ||
| tracingChannel, | ||
| channels.CHANNELS.VERCEL_AI_EXECUTE_TOOL_CALL, | ||
| (callOptions, telemetry) => ({ | ||
| type: "executeTool", | ||
| // v6 carries the tool definitions on the executeToolCall args (a record keyed by name); | ||
| // the shared core reads the matching tool's `description` for the span. | ||
| event: { | ||
| callId: nextCallId(), | ||
| toolCall: callOptions.toolCall, | ||
| tools: callOptions.tools, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| subscribeResolveLanguageModel(tracingChannel, channels.CHANNELS.VERCEL_AI_RESOLVE_LANGUAGE_MODEL, options); | ||
| } catch { | ||
| debugBuild.DEBUG_BUILD && core.debug.log("Vercel AI orchestrion channel subscription failed."); | ||
| } | ||
| } | ||
| function bindOperation(tracingChannel$1, channelName, build, options) { | ||
| const channel = tracingChannel$1(channelName); | ||
| const buildOperationSpan = (data) => { | ||
| const callOptions = isRecord(data.arguments[0]) ? data.arguments[0] : {}; | ||
| const telemetry = isRecord(callOptions.experimental_telemetry) ? callOptions.experimental_telemetry : {}; | ||
| if (telemetry.isEnabled === false && !suppressedTelemetry.has(telemetry)) { | ||
| return void 0; | ||
| } | ||
| const message = build(callOptions, telemetry); | ||
| suppressNativeTelemetry(callOptions, telemetry); | ||
| const span = vercelAiDcSubscriber.createSpanFromMessage(message, options); | ||
| if (span) { | ||
| messages.set(data, message); | ||
| operationSpans.add(span); | ||
| const callId = asString(message.event.callId); | ||
| if (callId) { | ||
| callIdBySpan.set(span, callId); | ||
| } | ||
| recordingBySpan.set(span, recording(telemetry)); | ||
| } | ||
| return span; | ||
| }; | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| channel, | ||
| (data) => buildOperationSpan(data), | ||
| { | ||
| beforeSpanEnd: (span, data) => { | ||
| const message = messages.get(data); | ||
| if (!message) { | ||
| return; | ||
| } | ||
| if (!("error" in data)) { | ||
| message.result = message.type === "executeTool" ? { output: data.result } : data.result; | ||
| vercelAiDcSubscriber.enrichSpanOnEnd(span, message, options); | ||
| } | ||
| if (message.type !== "streamText") { | ||
| vercelAiDcSubscriber.clearOperationId(message); | ||
| } | ||
| messages.delete(data); | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function suppressNativeTelemetry(callOptions, telemetry) { | ||
| if (telemetry.isEnabled !== true) { | ||
| return; | ||
| } | ||
| const suppressed = { ...telemetry, isEnabled: false }; | ||
| suppressedTelemetry.add(suppressed); | ||
| callOptions.experimental_telemetry = suppressed; | ||
| } | ||
| function subscribeResolveLanguageModel(tracingChannel, channelName, options) { | ||
| tracingChannel(channelName).subscribe({ | ||
| end(rawCtx) { | ||
| const ctx = rawCtx; | ||
| if (!isRecord(ctx.result)) { | ||
| return; | ||
| } | ||
| const model = ctx.result; | ||
| if (!model[PATCHED]) { | ||
| model[PATCHED] = true; | ||
| patchModelMethod(model, "doGenerate", options); | ||
| patchModelMethod(model, "doStream", options); | ||
| } | ||
| }, | ||
| start() { | ||
| }, | ||
| asyncStart() { | ||
| }, | ||
| asyncEnd() { | ||
| }, | ||
| error() { | ||
| } | ||
| }); | ||
| } | ||
| function resolveModelCallParent() { | ||
| const active = core.getActiveSpan(); | ||
| return active && operationSpans.has(active) ? active : void 0; | ||
| } | ||
| function patchModelMethod(model, method, options) { | ||
| const original = model[method]; | ||
| if (typeof original !== "function") { | ||
| return; | ||
| } | ||
| model[method] = function(...args) { | ||
| const parent = resolveModelCallParent(); | ||
| if (!parent) { | ||
| return Promise.resolve(original.apply(this, args)); | ||
| } | ||
| const callArgs = isRecord(args[0]) ? args[0] : {}; | ||
| const callId = callIdBySpan.get(parent); | ||
| const message = { | ||
| type: "languageModelCall", | ||
| event: { | ||
| callId, | ||
| provider: model.provider, | ||
| modelId: model.modelId, | ||
| tools: callArgs.tools, | ||
| messages: callArgs.prompt, | ||
| // Inherit the enclosing operation's per-call recording flags so inputs/tools/outputs are recorded on | ||
| // the model-call span whenever they are on the parent `invoke_agent` span. | ||
| ...recordingBySpan.get(parent) | ||
| } | ||
| }; | ||
| const span = core.withActiveSpan(parent, () => vercelAiDcSubscriber.createSpanFromMessage(message, options)); | ||
| if (!span) { | ||
| return Promise.resolve(original.apply(this, args)); | ||
| } | ||
| const clearStreamCallId = () => { | ||
| if (method === "doStream" && callId) { | ||
| vercelAiDcSubscriber.clearOperationCallId(callId); | ||
| } | ||
| }; | ||
| const failSpan = (error) => { | ||
| span.setStatus({ code: core.SPAN_STATUS_ERROR, message: error instanceof Error ? error.message : "unknown_error" }); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| throw error; | ||
| }; | ||
| try { | ||
| const result = Promise.resolve(original.apply(this, args)); | ||
| return result.then((value) => { | ||
| message.result = value; | ||
| vercelAiDcSubscriber.enrichSpanOnEnd(span, message, options); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| return value; | ||
| }, failSpan); | ||
| } catch (error) { | ||
| return failSpan(error); | ||
| } | ||
| }; | ||
| } | ||
| function buildTextMessage(type) { | ||
| return (options, telemetry) => ({ | ||
| type, | ||
| event: { | ||
| callId: nextCallId(), | ||
| operationId: `ai.${type}`, | ||
| functionId: asString(telemetry.functionId), | ||
| ...modelFields(options.model), | ||
| maxRetries: options.maxRetries, | ||
| // Normalize to the message-array shape the shared core (and v7's channel) expects: a bare string | ||
| // `prompt` becomes a single user message, matching the SDK's own normalization. | ||
| messages: normalizePromptMessages(options), | ||
| ...recording(telemetry) | ||
| } | ||
| }); | ||
| } | ||
| function normalizePromptMessages(options) { | ||
| if (Array.isArray(options.messages)) { | ||
| return options.messages; | ||
| } | ||
| if (typeof options.prompt === "string") { | ||
| return [{ role: "user", content: options.prompt }]; | ||
| } | ||
| return options.messages ?? options.prompt; | ||
| } | ||
| function recording(telemetry) { | ||
| return { recordInputs: telemetry.recordInputs, recordOutputs: telemetry.recordOutputs }; | ||
| } | ||
| function modelFields(model) { | ||
| return { provider: modelField(model, "provider"), modelId: modelField(model, "modelId") }; | ||
| } | ||
| function modelField(model, field) { | ||
| return isRecord(model) ? asString(model[field]) : void 0; | ||
| } | ||
| function asString(value) { | ||
| return typeof value === "string" ? value : void 0; | ||
| } | ||
| function isRecord(value) { | ||
| return typeof value === "object" && value !== null; | ||
| } | ||
| exports.subscribeVercelAiOrchestrionChannels = subscribeVercelAiOrchestrionChannels; | ||
| //# sourceMappingURL=vercel-ai-orchestrion-v6-subscriber.js.map |
| {"version":3,"file":"vercel-ai-orchestrion-v6-subscriber.js","sources":["../../../src/vercel-ai/vercel-ai-orchestrion-v6-subscriber.ts"],"sourcesContent":["import type { Span } from '@sentry/core';\nimport { debug, getActiveSpan, SPAN_STATUS_ERROR, withActiveSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { CHANNELS } from '../orchestrion/channels';\nimport { bindTracingChannelToSpan, type TracingChannelPayloadWithSpan } from '../tracing-channel';\nimport {\n clearOperationCallId,\n clearOperationId,\n createSpanFromMessage,\n enrichSpanOnEnd,\n type VercelAiChannelMessage,\n type VercelAiChannelOptions,\n type VercelAiTracingChannelFactory,\n} from './vercel-ai-dc-subscriber';\n\n/**\n * v6 channel adapter for the Vercel AI (`ai`) SDK.\n *\n * `ai` >= 7 publishes a normalized `ai:telemetry` tracing channel natively\n * (consumed by `subscribeVercelAiTracingChannel`). v6 has no such channel, so\n * orchestrion injects `orchestrion:ai:*` channels around the top-level\n * functions (see `orchestrion/config/index.ts`). The injected channels carry only the\n * wrapped call's `{ arguments, result, error }` — NOT v7's normalized `event`\n * object — so this adapter reconstructs an equivalent {@link VercelAiChannelMessage}\n * from v6's argument/result shapes and delegates to the SAME span-building core\n * (`createSpanFromMessage` / `enrichSpanOnEnd`) the v7 subscriber uses, so the\n * emitted spans are identical between v6 and v7.\n *\n * The model call (`languageModelCall` / `generate_content` span) has no\n * injectable definition in `ai`, so we instead wrap `resolveLanguageModel` (the\n * single chokepoint every model call flows through) and monkey-patch\n * `doGenerate`/`doStream` on the returned model.\n */\n\n/** Shape orchestrion's transform attaches to the tracing-channel context. */\ninterface OrchestrionContext {\n arguments: unknown[];\n result?: unknown;\n error?: unknown;\n}\n\n/** Builds the normalized message for a channel from the wrapped call's first-arg options. */\ntype MessageBuilder = (options: Record<string, unknown>, telemetry: Record<string, unknown>) => VercelAiChannelMessage;\n\n/** A resolved `ai` language model — has `doGenerate`/`doStream` and identity fields. */\ninterface ResolvedModel {\n modelId?: string;\n provider?: string;\n doGenerate?: (...args: unknown[]) => Promise<unknown>;\n doStream?: (...args: unknown[]) => Promise<unknown>;\n}\n\nconst PATCHED = Symbol('SentryVercelAiModelPatched');\n\n/** A resolved model with our patch bookkeeping (idempotency flag). */\ntype PatchableModel = ResolvedModel & { [PATCHED]?: boolean };\n\n// Per-operation correlation id. No Date/random (unavailable / non-deterministic) — a counter is enough.\nlet callIdCounter = 0;\nfunction nextCallId(): string {\n return `v6-${++callIdCounter}`;\n}\n\n// The message built on `start` for each operation, keyed by the (stable-identity) channel context, so\n// the `beforeSpanEnd` handler can enrich the span from the settled result and clear the `callId` maps.\nconst messages = new WeakMap<object, VercelAiChannelMessage>();\n// The spans we opened for top-level operations, and each one's `callId`. A model call resolves its\n// parent against this set (so it never mis-attributes to the enclosing `main`/user span) and reads the\n// parent's `callId` so its span can be named after the operation (e.g. `ai.streamText.doStream`).\nconst operationSpans = new WeakSet<Span>();\nconst callIdBySpan = new WeakMap<Span, string>();\n// The operation's per-call recording flags (`experimental_telemetry.recordInputs/recordOutputs`), keyed by\n// its span. A model call carries no telemetry of its own, so it inherits the enclosing operation's flags —\n// otherwise a per-call `recordInputs: true` would record inputs on the `invoke_agent` span but not on the\n// child `generate_content` span (whose event would fall back to the global default). v7's channel forwards\n// these flags on every event, so this keeps v6 identical.\nconst recordingBySpan = new WeakMap<Span, ReturnType<typeof recording>>();\n\n// The `experimental_telemetry` objects we swapped in to suppress `ai`'s native OTel spans (see\n// `suppressNativeTelemetry`). Our skip logic treats `isEnabled === false` as \"user disabled telemetry,\n// emit no span\"; without this set, a call whose options object we already neutralized — or a user-shared\n// telemetry object we replaced on a prior call — would be misread as user-disabled and lose its span.\nconst suppressedTelemetry = new WeakSet<object>();\n\nlet subscribed = false;\n\n/**\n * Subscribe the v6 orchestrion channel adapter. Safe to always call: inert on\n * `ai` >= 7 (those channels are never published) and when orchestrion injection\n * isn't active. Idempotent.\n *\n * `tracingChannel` is the platform-provided factory (the same one passed to\n * `subscribeVercelAiTracingChannel`); `options` pins the recording settings at\n * subscribe time so we never look the integration up per event.\n */\nexport function subscribeVercelAiOrchestrionChannels(\n tracingChannel: VercelAiTracingChannelFactory,\n options: VercelAiChannelOptions = {},\n): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n try {\n bindOperation(tracingChannel, CHANNELS.VERCEL_AI_GENERATE_TEXT, buildTextMessage('generateText'), options);\n bindOperation(tracingChannel, CHANNELS.VERCEL_AI_STREAM_TEXT, buildTextMessage('streamText'), options);\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EMBED,\n (callOptions, telemetry) => ({\n type: 'embed',\n event: {\n callId: nextCallId(),\n ...modelFields(callOptions.model),\n maxRetries: callOptions.maxRetries,\n value: callOptions.value,\n ...recording(telemetry),\n },\n }),\n options,\n );\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EMBED_MANY,\n // `embedMany` takes a `values` array (vs `embed`'s single `value`); the shared core reads it as the\n // embeddings input, matching the OTel path's batch `ai.embedMany` span.\n (callOptions, telemetry) => ({\n type: 'embedMany',\n event: {\n callId: nextCallId(),\n ...modelFields(callOptions.model),\n maxRetries: callOptions.maxRetries,\n values: callOptions.values,\n ...recording(telemetry),\n },\n }),\n options,\n );\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EXECUTE_TOOL_CALL,\n (callOptions, telemetry) => ({\n type: 'executeTool',\n // v6 carries the tool definitions on the executeToolCall args (a record keyed by name);\n // the shared core reads the matching tool's `description` for the span.\n event: {\n callId: nextCallId(),\n toolCall: callOptions.toolCall,\n tools: callOptions.tools,\n ...recording(telemetry),\n },\n }),\n options,\n );\n subscribeResolveLanguageModel(tracingChannel, CHANNELS.VERCEL_AI_RESOLVE_LANGUAGE_MODEL, options);\n } catch {\n DEBUG_BUILD && debug.log('Vercel AI orchestrion channel subscription failed.');\n }\n}\n\n/**\n * Bind one operation channel: `getSpan` opens a span from the message reconstructed out of the wrapped\n * call's first argument; `beforeSpanEnd` enriches it from the settled result (tokens, output messages,\n * finish reasons, …) before the helper ends the span.\n *\n * An operation whose `experimental_telemetry.isEnabled` is explicitly `false` is skipped entirely (no\n * span): the orchestrion channel fires regardless of that flag, whereas v7's native `ai:telemetry`\n * channel is simply not published in that case — so we reproduce v7's \"no telemetry → no span\".\n */\nfunction bindOperation(\n tracingChannel: VercelAiTracingChannelFactory,\n channelName: string,\n build: MessageBuilder,\n options: VercelAiChannelOptions,\n): void {\n const channel = tracingChannel<OrchestrionContext>(channelName);\n\n // Build the operation span from the wrapped call's first argument and track it (so a model call can\n // resolve it as its parent). `bindTracingChannelToSpan` calls this once at channel `start` and makes\n // the returned span the active async context for the operation's duration — that active span is what\n // `resolveModelCallParent` reads. It also sets `data._sentrySpan`, so we don't here.\n const buildOperationSpan = (data: TracingChannelPayloadWithSpan<OrchestrionContext>): Span | undefined => {\n const callOptions = isRecord(data.arguments[0]) ? data.arguments[0] : {};\n const telemetry = isRecord(callOptions.experimental_telemetry) ? callOptions.experimental_telemetry : {};\n // `isEnabled === false` means the user opted out — emit no span. But `isEnabled` is also `false` on\n // the telemetry object we swap in to suppress native spans, so don't mistake our own object for a\n // user opt-out (which would drop the span on a call whose options we already neutralized).\n if (telemetry.isEnabled === false && !suppressedTelemetry.has(telemetry)) {\n return undefined;\n }\n const message = build(callOptions, telemetry);\n // Stop `ai` from emitting its own native OTel spans for this call — we build the equivalent spans\n // from the channels, so the SDK's would be duplicates. Reads above have already captured everything\n // we need off `telemetry`.\n suppressNativeTelemetry(callOptions, telemetry);\n const span = createSpanFromMessage(message, options);\n if (span) {\n messages.set(data, message);\n operationSpans.add(span);\n const callId = asString(message.event.callId);\n if (callId) {\n callIdBySpan.set(span, callId);\n }\n recordingBySpan.set(span, recording(telemetry));\n }\n return span;\n };\n\n bindTracingChannelToSpan(\n channel,\n (data: TracingChannelPayloadWithSpan<OrchestrionContext>) => buildOperationSpan(data),\n {\n beforeSpanEnd: (span, data) => {\n const message = messages.get(data);\n if (!message) {\n return;\n }\n // The helper's `error` handler already set the span status; only enrich from a successful result.\n if (!('error' in data)) {\n // v6's `executeToolCall` returns the tool result/error object directly, whereas the shared core\n // (matching v7) expects it nested under `output`; wrap it so tool-error detection works.\n message.result = message.type === 'executeTool' ? { output: data.result } : data.result;\n enrichSpanOnEnd(span, message, options);\n }\n // A `streamText` model call runs after this (synchronously-returning) operation's span has\n // already ended, so its `callId` entry must outlive the operation — it's cleared once the model\n // call settles (see `patchModelMethod`). Every other operation can clear here.\n if (message.type !== 'streamText') {\n clearOperationId(message);\n }\n messages.delete(data);\n },\n },\n );\n}\n\n/**\n * Neutralize `ai`'s native OpenTelemetry instrumentation for this call by pointing\n * `experimental_telemetry` at a copy with `isEnabled: false`. `ai`'s `getTracer` then returns its\n * internal no-op tracer, so it never creates (nor sets active) the duplicate `ai.*` spans we'd\n * otherwise have to drop.\n *\n * Only a call that explicitly enabled telemetry emits native spans — otherwise `ai` already uses its\n * no-op tracer, so there's nothing to suppress and we leave the user's options untouched. When\n * `isEnabled === true`, `telemetry` is `callOptions.experimental_telemetry` and `callOptions` is the\n * real first argument the SDK will read, so the reassignment takes effect for the wrapped call. We\n * replace rather than mutate in place, so a telemetry object the user shares across calls keeps its own\n * `isEnabled: true`; the replacement is tracked in `suppressedTelemetry` so our skip logic doesn't\n * later read it back as a user opt-out.\n */\nfunction suppressNativeTelemetry(callOptions: Record<string, unknown>, telemetry: Record<string, unknown>): void {\n if (telemetry.isEnabled !== true) {\n return;\n }\n const suppressed = { ...telemetry, isEnabled: false };\n suppressedTelemetry.add(suppressed);\n callOptions.experimental_telemetry = suppressed;\n}\n\n/**\n * `resolveLanguageModel` returns the model every call flows through. We don't span it — on `end` we\n * monkey-patch `doGenerate`/`doStream` on the returned model so each invocation produces a\n * `languageModelCall` span parented to the enclosing invoke_agent span.\n */\nfunction subscribeResolveLanguageModel(\n tracingChannel: VercelAiTracingChannelFactory,\n channelName: string,\n options: VercelAiChannelOptions,\n): void {\n tracingChannel<OrchestrionContext>(channelName).subscribe({\n end(rawCtx) {\n const ctx = rawCtx as OrchestrionContext;\n if (!isRecord(ctx.result)) {\n return;\n }\n const model = ctx.result as PatchableModel;\n // Patch the model's `doGenerate`/`doStream` once. The model call recovers its parent from the\n // active async context at call time (the operation span `bindTracingChannelToSpan` bound), which\n // propagates into the model call for `streamText` too, so there is nothing to capture on the model here.\n if (!model[PATCHED]) {\n model[PATCHED] = true;\n patchModelMethod(model, 'doGenerate', options);\n patchModelMethod(model, 'doStream', options);\n }\n },\n start() {\n /* no-op */\n },\n asyncStart() {\n /* no-op */\n },\n asyncEnd() {\n /* no-op */\n },\n error() {\n /* no-op */\n },\n });\n}\n\n/**\n * Pick the invoke_agent span a model call should hang under: the operation span that\n * `bindTracingChannelToSpan` planted as the active async context for the enclosing operation.\n *\n * Because we suppress `ai`'s native telemetry (so it never installs its own span as active), the active\n * span inside `doGenerate`/`doStream` is our operation span. The `operationSpans` gate makes this return\n * `undefined` when the active span isn't one of ours — e.g. telemetry was disabled for the call so we\n * opened no operation span and the active span is the user's enclosing span — so the model call is\n * skipped rather than mis-parented.\n *\n * This covers `generateText`/`embed` (whose model call is awaited inside the operation body) and\n * `streamText` alike — `ai` initiates the stream synchronously within the operation's bound context, so\n * the later `doStream` continuation restores the same active span even though the operation's span has\n * already ended. The per-operation binding also disambiguates concurrent calls that share one model\n * instance (a single mutable slot on the shared model could not — it would hold whichever operation\n * resolved the model last).\n */\nfunction resolveModelCallParent(): Span | undefined {\n const active = getActiveSpan();\n return active && operationSpans.has(active) ? active : undefined;\n}\n\nfunction patchModelMethod(\n model: PatchableModel,\n method: 'doGenerate' | 'doStream',\n options: VercelAiChannelOptions,\n): void {\n const original = model[method];\n if (typeof original !== 'function') {\n return;\n }\n model[method] = function (this: unknown, ...args: unknown[]): Promise<unknown> {\n const parent = resolveModelCallParent();\n // No enclosing operation span (e.g. telemetry disabled for the call) → don't open a model-call span.\n if (!parent) {\n return Promise.resolve(original.apply(this, args));\n }\n\n const callArgs = isRecord(args[0]) ? args[0] : {};\n // Carry the operation's `callId` so the shared core can name the span after it\n // (`ai.generateText.doGenerate` / `ai.streamText.doStream`).\n const callId = callIdBySpan.get(parent);\n const message: VercelAiChannelMessage = {\n type: 'languageModelCall',\n event: {\n callId,\n provider: model.provider,\n modelId: model.modelId,\n tools: callArgs.tools,\n messages: callArgs.prompt,\n // Inherit the enclosing operation's per-call recording flags so inputs/tools/outputs are recorded on\n // the model-call span whenever they are on the parent `invoke_agent` span.\n ...recordingBySpan.get(parent),\n },\n };\n const span = withActiveSpan(parent, () => createSpanFromMessage(message, options));\n // `languageModelCall` always opens a span; the guard just keeps the wrapper safe if that changes.\n if (!span) {\n return Promise.resolve(original.apply(this, args));\n }\n\n // `streamText` ends its operation span synchronously, so its `callId` entry was deliberately left in\n // place for this later model call; drop it now that we've used it.\n const clearStreamCallId = (): void => {\n if (method === 'doStream' && callId) {\n clearOperationCallId(callId);\n }\n };\n\n // Both the synchronous throw and the async rejection of the model call must end the span with an\n // error status (an `async` `doGenerate`/`doStream` that throws rejects rather than throwing here).\n const failSpan = (error: unknown): never => {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: error instanceof Error ? error.message : 'unknown_error' });\n span.end();\n clearStreamCallId();\n throw error;\n };\n\n try {\n const result = Promise.resolve(original.apply(this, args));\n // `doStream` resolves to `{ stream, ... }` before the stream is consumed; we end here (start/end\n // bracket the call) to match the channel timing.\n return result.then(value => {\n message.result = value;\n enrichSpanOnEnd(span, message, options);\n span.end();\n clearStreamCallId();\n return value;\n }, failSpan);\n } catch (error) {\n return failSpan(error);\n }\n };\n}\n\nfunction buildTextMessage(type: 'generateText' | 'streamText'): MessageBuilder {\n return (options, telemetry) => ({\n type,\n event: {\n callId: nextCallId(),\n operationId: `ai.${type}`,\n functionId: asString(telemetry.functionId),\n ...modelFields(options.model),\n maxRetries: options.maxRetries,\n // Normalize to the message-array shape the shared core (and v7's channel) expects: a bare string\n // `prompt` becomes a single user message, matching the SDK's own normalization.\n messages: normalizePromptMessages(options),\n ...recording(telemetry),\n },\n });\n}\n\nfunction normalizePromptMessages(options: Record<string, unknown>): unknown {\n if (Array.isArray(options.messages)) {\n return options.messages;\n }\n if (typeof options.prompt === 'string') {\n return [{ role: 'user', content: options.prompt }];\n }\n return options.messages ?? options.prompt;\n}\n\nfunction recording(telemetry: Record<string, unknown>): { recordInputs: unknown; recordOutputs: unknown } {\n return { recordInputs: telemetry.recordInputs, recordOutputs: telemetry.recordOutputs };\n}\n\nfunction modelFields(model: unknown): { provider?: string; modelId?: string } {\n return { provider: modelField(model, 'provider'), modelId: modelField(model, 'modelId') };\n}\n\nfunction modelField(model: unknown, field: 'modelId' | 'provider'): string | undefined {\n return isRecord(model) ? asString(model[field]) : undefined;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n"],"names":["CHANNELS","DEBUG_BUILD","debug","tracingChannel","createSpanFromMessage","bindTracingChannelToSpan","enrichSpanOnEnd","clearOperationId","getActiveSpan","withActiveSpan","clearOperationCallId","SPAN_STATUS_ERROR"],"mappings":"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"} |
| import { debug, withActiveSpan, getActiveSpan, SPAN_STATUS_ERROR } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../debug-build.js'; | ||
| import { CHANNELS } from '../orchestrion/channels.js'; | ||
| import { bindTracingChannelToSpan } from '../tracing-channel.js'; | ||
| import { createSpanFromMessage, enrichSpanOnEnd, clearOperationCallId, clearOperationId } from './vercel-ai-dc-subscriber.js'; | ||
| const PATCHED = /* @__PURE__ */ Symbol("SentryVercelAiModelPatched"); | ||
| let callIdCounter = 0; | ||
| function nextCallId() { | ||
| return `v6-${++callIdCounter}`; | ||
| } | ||
| const messages = /* @__PURE__ */ new WeakMap(); | ||
| const operationSpans = /* @__PURE__ */ new WeakSet(); | ||
| const callIdBySpan = /* @__PURE__ */ new WeakMap(); | ||
| const recordingBySpan = /* @__PURE__ */ new WeakMap(); | ||
| const suppressedTelemetry = /* @__PURE__ */ new WeakSet(); | ||
| let subscribed = false; | ||
| function subscribeVercelAiOrchestrionChannels(tracingChannel, options = {}) { | ||
| if (subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| try { | ||
| bindOperation(tracingChannel, CHANNELS.VERCEL_AI_GENERATE_TEXT, buildTextMessage("generateText"), options); | ||
| bindOperation(tracingChannel, CHANNELS.VERCEL_AI_STREAM_TEXT, buildTextMessage("streamText"), options); | ||
| bindOperation( | ||
| tracingChannel, | ||
| CHANNELS.VERCEL_AI_EMBED, | ||
| (callOptions, telemetry) => ({ | ||
| type: "embed", | ||
| event: { | ||
| callId: nextCallId(), | ||
| ...modelFields(callOptions.model), | ||
| maxRetries: callOptions.maxRetries, | ||
| value: callOptions.value, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| bindOperation( | ||
| tracingChannel, | ||
| CHANNELS.VERCEL_AI_EMBED_MANY, | ||
| // `embedMany` takes a `values` array (vs `embed`'s single `value`); the shared core reads it as the | ||
| // embeddings input, matching the OTel path's batch `ai.embedMany` span. | ||
| (callOptions, telemetry) => ({ | ||
| type: "embedMany", | ||
| event: { | ||
| callId: nextCallId(), | ||
| ...modelFields(callOptions.model), | ||
| maxRetries: callOptions.maxRetries, | ||
| values: callOptions.values, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| bindOperation( | ||
| tracingChannel, | ||
| CHANNELS.VERCEL_AI_EXECUTE_TOOL_CALL, | ||
| (callOptions, telemetry) => ({ | ||
| type: "executeTool", | ||
| // v6 carries the tool definitions on the executeToolCall args (a record keyed by name); | ||
| // the shared core reads the matching tool's `description` for the span. | ||
| event: { | ||
| callId: nextCallId(), | ||
| toolCall: callOptions.toolCall, | ||
| tools: callOptions.tools, | ||
| ...recording(telemetry) | ||
| } | ||
| }), | ||
| options | ||
| ); | ||
| subscribeResolveLanguageModel(tracingChannel, CHANNELS.VERCEL_AI_RESOLVE_LANGUAGE_MODEL, options); | ||
| } catch { | ||
| DEBUG_BUILD && debug.log("Vercel AI orchestrion channel subscription failed."); | ||
| } | ||
| } | ||
| function bindOperation(tracingChannel, channelName, build, options) { | ||
| const channel = tracingChannel(channelName); | ||
| const buildOperationSpan = (data) => { | ||
| const callOptions = isRecord(data.arguments[0]) ? data.arguments[0] : {}; | ||
| const telemetry = isRecord(callOptions.experimental_telemetry) ? callOptions.experimental_telemetry : {}; | ||
| if (telemetry.isEnabled === false && !suppressedTelemetry.has(telemetry)) { | ||
| return void 0; | ||
| } | ||
| const message = build(callOptions, telemetry); | ||
| suppressNativeTelemetry(callOptions, telemetry); | ||
| const span = createSpanFromMessage(message, options); | ||
| if (span) { | ||
| messages.set(data, message); | ||
| operationSpans.add(span); | ||
| const callId = asString(message.event.callId); | ||
| if (callId) { | ||
| callIdBySpan.set(span, callId); | ||
| } | ||
| recordingBySpan.set(span, recording(telemetry)); | ||
| } | ||
| return span; | ||
| }; | ||
| bindTracingChannelToSpan( | ||
| channel, | ||
| (data) => buildOperationSpan(data), | ||
| { | ||
| beforeSpanEnd: (span, data) => { | ||
| const message = messages.get(data); | ||
| if (!message) { | ||
| return; | ||
| } | ||
| if (!("error" in data)) { | ||
| message.result = message.type === "executeTool" ? { output: data.result } : data.result; | ||
| enrichSpanOnEnd(span, message, options); | ||
| } | ||
| if (message.type !== "streamText") { | ||
| clearOperationId(message); | ||
| } | ||
| messages.delete(data); | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function suppressNativeTelemetry(callOptions, telemetry) { | ||
| if (telemetry.isEnabled !== true) { | ||
| return; | ||
| } | ||
| const suppressed = { ...telemetry, isEnabled: false }; | ||
| suppressedTelemetry.add(suppressed); | ||
| callOptions.experimental_telemetry = suppressed; | ||
| } | ||
| function subscribeResolveLanguageModel(tracingChannel, channelName, options) { | ||
| tracingChannel(channelName).subscribe({ | ||
| end(rawCtx) { | ||
| const ctx = rawCtx; | ||
| if (!isRecord(ctx.result)) { | ||
| return; | ||
| } | ||
| const model = ctx.result; | ||
| if (!model[PATCHED]) { | ||
| model[PATCHED] = true; | ||
| patchModelMethod(model, "doGenerate", options); | ||
| patchModelMethod(model, "doStream", options); | ||
| } | ||
| }, | ||
| start() { | ||
| }, | ||
| asyncStart() { | ||
| }, | ||
| asyncEnd() { | ||
| }, | ||
| error() { | ||
| } | ||
| }); | ||
| } | ||
| function resolveModelCallParent() { | ||
| const active = getActiveSpan(); | ||
| return active && operationSpans.has(active) ? active : void 0; | ||
| } | ||
| function patchModelMethod(model, method, options) { | ||
| const original = model[method]; | ||
| if (typeof original !== "function") { | ||
| return; | ||
| } | ||
| model[method] = function(...args) { | ||
| const parent = resolveModelCallParent(); | ||
| if (!parent) { | ||
| return Promise.resolve(original.apply(this, args)); | ||
| } | ||
| const callArgs = isRecord(args[0]) ? args[0] : {}; | ||
| const callId = callIdBySpan.get(parent); | ||
| const message = { | ||
| type: "languageModelCall", | ||
| event: { | ||
| callId, | ||
| provider: model.provider, | ||
| modelId: model.modelId, | ||
| tools: callArgs.tools, | ||
| messages: callArgs.prompt, | ||
| // Inherit the enclosing operation's per-call recording flags so inputs/tools/outputs are recorded on | ||
| // the model-call span whenever they are on the parent `invoke_agent` span. | ||
| ...recordingBySpan.get(parent) | ||
| } | ||
| }; | ||
| const span = withActiveSpan(parent, () => createSpanFromMessage(message, options)); | ||
| if (!span) { | ||
| return Promise.resolve(original.apply(this, args)); | ||
| } | ||
| const clearStreamCallId = () => { | ||
| if (method === "doStream" && callId) { | ||
| clearOperationCallId(callId); | ||
| } | ||
| }; | ||
| const failSpan = (error) => { | ||
| span.setStatus({ code: SPAN_STATUS_ERROR, message: error instanceof Error ? error.message : "unknown_error" }); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| throw error; | ||
| }; | ||
| try { | ||
| const result = Promise.resolve(original.apply(this, args)); | ||
| return result.then((value) => { | ||
| message.result = value; | ||
| enrichSpanOnEnd(span, message, options); | ||
| span.end(); | ||
| clearStreamCallId(); | ||
| return value; | ||
| }, failSpan); | ||
| } catch (error) { | ||
| return failSpan(error); | ||
| } | ||
| }; | ||
| } | ||
| function buildTextMessage(type) { | ||
| return (options, telemetry) => ({ | ||
| type, | ||
| event: { | ||
| callId: nextCallId(), | ||
| operationId: `ai.${type}`, | ||
| functionId: asString(telemetry.functionId), | ||
| ...modelFields(options.model), | ||
| maxRetries: options.maxRetries, | ||
| // Normalize to the message-array shape the shared core (and v7's channel) expects: a bare string | ||
| // `prompt` becomes a single user message, matching the SDK's own normalization. | ||
| messages: normalizePromptMessages(options), | ||
| ...recording(telemetry) | ||
| } | ||
| }); | ||
| } | ||
| function normalizePromptMessages(options) { | ||
| if (Array.isArray(options.messages)) { | ||
| return options.messages; | ||
| } | ||
| if (typeof options.prompt === "string") { | ||
| return [{ role: "user", content: options.prompt }]; | ||
| } | ||
| return options.messages ?? options.prompt; | ||
| } | ||
| function recording(telemetry) { | ||
| return { recordInputs: telemetry.recordInputs, recordOutputs: telemetry.recordOutputs }; | ||
| } | ||
| function modelFields(model) { | ||
| return { provider: modelField(model, "provider"), modelId: modelField(model, "modelId") }; | ||
| } | ||
| function modelField(model, field) { | ||
| return isRecord(model) ? asString(model[field]) : void 0; | ||
| } | ||
| function asString(value) { | ||
| return typeof value === "string" ? value : void 0; | ||
| } | ||
| function isRecord(value) { | ||
| return typeof value === "object" && value !== null; | ||
| } | ||
| export { subscribeVercelAiOrchestrionChannels }; | ||
| //# sourceMappingURL=vercel-ai-orchestrion-v6-subscriber.js.map |
| {"version":3,"file":"vercel-ai-orchestrion-v6-subscriber.js","sources":["../../../src/vercel-ai/vercel-ai-orchestrion-v6-subscriber.ts"],"sourcesContent":["import type { Span } from '@sentry/core';\nimport { debug, getActiveSpan, SPAN_STATUS_ERROR, withActiveSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { CHANNELS } from '../orchestrion/channels';\nimport { bindTracingChannelToSpan, type TracingChannelPayloadWithSpan } from '../tracing-channel';\nimport {\n clearOperationCallId,\n clearOperationId,\n createSpanFromMessage,\n enrichSpanOnEnd,\n type VercelAiChannelMessage,\n type VercelAiChannelOptions,\n type VercelAiTracingChannelFactory,\n} from './vercel-ai-dc-subscriber';\n\n/**\n * v6 channel adapter for the Vercel AI (`ai`) SDK.\n *\n * `ai` >= 7 publishes a normalized `ai:telemetry` tracing channel natively\n * (consumed by `subscribeVercelAiTracingChannel`). v6 has no such channel, so\n * orchestrion injects `orchestrion:ai:*` channels around the top-level\n * functions (see `orchestrion/config/index.ts`). The injected channels carry only the\n * wrapped call's `{ arguments, result, error }` — NOT v7's normalized `event`\n * object — so this adapter reconstructs an equivalent {@link VercelAiChannelMessage}\n * from v6's argument/result shapes and delegates to the SAME span-building core\n * (`createSpanFromMessage` / `enrichSpanOnEnd`) the v7 subscriber uses, so the\n * emitted spans are identical between v6 and v7.\n *\n * The model call (`languageModelCall` / `generate_content` span) has no\n * injectable definition in `ai`, so we instead wrap `resolveLanguageModel` (the\n * single chokepoint every model call flows through) and monkey-patch\n * `doGenerate`/`doStream` on the returned model.\n */\n\n/** Shape orchestrion's transform attaches to the tracing-channel context. */\ninterface OrchestrionContext {\n arguments: unknown[];\n result?: unknown;\n error?: unknown;\n}\n\n/** Builds the normalized message for a channel from the wrapped call's first-arg options. */\ntype MessageBuilder = (options: Record<string, unknown>, telemetry: Record<string, unknown>) => VercelAiChannelMessage;\n\n/** A resolved `ai` language model — has `doGenerate`/`doStream` and identity fields. */\ninterface ResolvedModel {\n modelId?: string;\n provider?: string;\n doGenerate?: (...args: unknown[]) => Promise<unknown>;\n doStream?: (...args: unknown[]) => Promise<unknown>;\n}\n\nconst PATCHED = Symbol('SentryVercelAiModelPatched');\n\n/** A resolved model with our patch bookkeeping (idempotency flag). */\ntype PatchableModel = ResolvedModel & { [PATCHED]?: boolean };\n\n// Per-operation correlation id. No Date/random (unavailable / non-deterministic) — a counter is enough.\nlet callIdCounter = 0;\nfunction nextCallId(): string {\n return `v6-${++callIdCounter}`;\n}\n\n// The message built on `start` for each operation, keyed by the (stable-identity) channel context, so\n// the `beforeSpanEnd` handler can enrich the span from the settled result and clear the `callId` maps.\nconst messages = new WeakMap<object, VercelAiChannelMessage>();\n// The spans we opened for top-level operations, and each one's `callId`. A model call resolves its\n// parent against this set (so it never mis-attributes to the enclosing `main`/user span) and reads the\n// parent's `callId` so its span can be named after the operation (e.g. `ai.streamText.doStream`).\nconst operationSpans = new WeakSet<Span>();\nconst callIdBySpan = new WeakMap<Span, string>();\n// The operation's per-call recording flags (`experimental_telemetry.recordInputs/recordOutputs`), keyed by\n// its span. A model call carries no telemetry of its own, so it inherits the enclosing operation's flags —\n// otherwise a per-call `recordInputs: true` would record inputs on the `invoke_agent` span but not on the\n// child `generate_content` span (whose event would fall back to the global default). v7's channel forwards\n// these flags on every event, so this keeps v6 identical.\nconst recordingBySpan = new WeakMap<Span, ReturnType<typeof recording>>();\n\n// The `experimental_telemetry` objects we swapped in to suppress `ai`'s native OTel spans (see\n// `suppressNativeTelemetry`). Our skip logic treats `isEnabled === false` as \"user disabled telemetry,\n// emit no span\"; without this set, a call whose options object we already neutralized — or a user-shared\n// telemetry object we replaced on a prior call — would be misread as user-disabled and lose its span.\nconst suppressedTelemetry = new WeakSet<object>();\n\nlet subscribed = false;\n\n/**\n * Subscribe the v6 orchestrion channel adapter. Safe to always call: inert on\n * `ai` >= 7 (those channels are never published) and when orchestrion injection\n * isn't active. Idempotent.\n *\n * `tracingChannel` is the platform-provided factory (the same one passed to\n * `subscribeVercelAiTracingChannel`); `options` pins the recording settings at\n * subscribe time so we never look the integration up per event.\n */\nexport function subscribeVercelAiOrchestrionChannels(\n tracingChannel: VercelAiTracingChannelFactory,\n options: VercelAiChannelOptions = {},\n): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n try {\n bindOperation(tracingChannel, CHANNELS.VERCEL_AI_GENERATE_TEXT, buildTextMessage('generateText'), options);\n bindOperation(tracingChannel, CHANNELS.VERCEL_AI_STREAM_TEXT, buildTextMessage('streamText'), options);\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EMBED,\n (callOptions, telemetry) => ({\n type: 'embed',\n event: {\n callId: nextCallId(),\n ...modelFields(callOptions.model),\n maxRetries: callOptions.maxRetries,\n value: callOptions.value,\n ...recording(telemetry),\n },\n }),\n options,\n );\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EMBED_MANY,\n // `embedMany` takes a `values` array (vs `embed`'s single `value`); the shared core reads it as the\n // embeddings input, matching the OTel path's batch `ai.embedMany` span.\n (callOptions, telemetry) => ({\n type: 'embedMany',\n event: {\n callId: nextCallId(),\n ...modelFields(callOptions.model),\n maxRetries: callOptions.maxRetries,\n values: callOptions.values,\n ...recording(telemetry),\n },\n }),\n options,\n );\n bindOperation(\n tracingChannel,\n CHANNELS.VERCEL_AI_EXECUTE_TOOL_CALL,\n (callOptions, telemetry) => ({\n type: 'executeTool',\n // v6 carries the tool definitions on the executeToolCall args (a record keyed by name);\n // the shared core reads the matching tool's `description` for the span.\n event: {\n callId: nextCallId(),\n toolCall: callOptions.toolCall,\n tools: callOptions.tools,\n ...recording(telemetry),\n },\n }),\n options,\n );\n subscribeResolveLanguageModel(tracingChannel, CHANNELS.VERCEL_AI_RESOLVE_LANGUAGE_MODEL, options);\n } catch {\n DEBUG_BUILD && debug.log('Vercel AI orchestrion channel subscription failed.');\n }\n}\n\n/**\n * Bind one operation channel: `getSpan` opens a span from the message reconstructed out of the wrapped\n * call's first argument; `beforeSpanEnd` enriches it from the settled result (tokens, output messages,\n * finish reasons, …) before the helper ends the span.\n *\n * An operation whose `experimental_telemetry.isEnabled` is explicitly `false` is skipped entirely (no\n * span): the orchestrion channel fires regardless of that flag, whereas v7's native `ai:telemetry`\n * channel is simply not published in that case — so we reproduce v7's \"no telemetry → no span\".\n */\nfunction bindOperation(\n tracingChannel: VercelAiTracingChannelFactory,\n channelName: string,\n build: MessageBuilder,\n options: VercelAiChannelOptions,\n): void {\n const channel = tracingChannel<OrchestrionContext>(channelName);\n\n // Build the operation span from the wrapped call's first argument and track it (so a model call can\n // resolve it as its parent). `bindTracingChannelToSpan` calls this once at channel `start` and makes\n // the returned span the active async context for the operation's duration — that active span is what\n // `resolveModelCallParent` reads. It also sets `data._sentrySpan`, so we don't here.\n const buildOperationSpan = (data: TracingChannelPayloadWithSpan<OrchestrionContext>): Span | undefined => {\n const callOptions = isRecord(data.arguments[0]) ? data.arguments[0] : {};\n const telemetry = isRecord(callOptions.experimental_telemetry) ? callOptions.experimental_telemetry : {};\n // `isEnabled === false` means the user opted out — emit no span. But `isEnabled` is also `false` on\n // the telemetry object we swap in to suppress native spans, so don't mistake our own object for a\n // user opt-out (which would drop the span on a call whose options we already neutralized).\n if (telemetry.isEnabled === false && !suppressedTelemetry.has(telemetry)) {\n return undefined;\n }\n const message = build(callOptions, telemetry);\n // Stop `ai` from emitting its own native OTel spans for this call — we build the equivalent spans\n // from the channels, so the SDK's would be duplicates. Reads above have already captured everything\n // we need off `telemetry`.\n suppressNativeTelemetry(callOptions, telemetry);\n const span = createSpanFromMessage(message, options);\n if (span) {\n messages.set(data, message);\n operationSpans.add(span);\n const callId = asString(message.event.callId);\n if (callId) {\n callIdBySpan.set(span, callId);\n }\n recordingBySpan.set(span, recording(telemetry));\n }\n return span;\n };\n\n bindTracingChannelToSpan(\n channel,\n (data: TracingChannelPayloadWithSpan<OrchestrionContext>) => buildOperationSpan(data),\n {\n beforeSpanEnd: (span, data) => {\n const message = messages.get(data);\n if (!message) {\n return;\n }\n // The helper's `error` handler already set the span status; only enrich from a successful result.\n if (!('error' in data)) {\n // v6's `executeToolCall` returns the tool result/error object directly, whereas the shared core\n // (matching v7) expects it nested under `output`; wrap it so tool-error detection works.\n message.result = message.type === 'executeTool' ? { output: data.result } : data.result;\n enrichSpanOnEnd(span, message, options);\n }\n // A `streamText` model call runs after this (synchronously-returning) operation's span has\n // already ended, so its `callId` entry must outlive the operation — it's cleared once the model\n // call settles (see `patchModelMethod`). Every other operation can clear here.\n if (message.type !== 'streamText') {\n clearOperationId(message);\n }\n messages.delete(data);\n },\n },\n );\n}\n\n/**\n * Neutralize `ai`'s native OpenTelemetry instrumentation for this call by pointing\n * `experimental_telemetry` at a copy with `isEnabled: false`. `ai`'s `getTracer` then returns its\n * internal no-op tracer, so it never creates (nor sets active) the duplicate `ai.*` spans we'd\n * otherwise have to drop.\n *\n * Only a call that explicitly enabled telemetry emits native spans — otherwise `ai` already uses its\n * no-op tracer, so there's nothing to suppress and we leave the user's options untouched. When\n * `isEnabled === true`, `telemetry` is `callOptions.experimental_telemetry` and `callOptions` is the\n * real first argument the SDK will read, so the reassignment takes effect for the wrapped call. We\n * replace rather than mutate in place, so a telemetry object the user shares across calls keeps its own\n * `isEnabled: true`; the replacement is tracked in `suppressedTelemetry` so our skip logic doesn't\n * later read it back as a user opt-out.\n */\nfunction suppressNativeTelemetry(callOptions: Record<string, unknown>, telemetry: Record<string, unknown>): void {\n if (telemetry.isEnabled !== true) {\n return;\n }\n const suppressed = { ...telemetry, isEnabled: false };\n suppressedTelemetry.add(suppressed);\n callOptions.experimental_telemetry = suppressed;\n}\n\n/**\n * `resolveLanguageModel` returns the model every call flows through. We don't span it — on `end` we\n * monkey-patch `doGenerate`/`doStream` on the returned model so each invocation produces a\n * `languageModelCall` span parented to the enclosing invoke_agent span.\n */\nfunction subscribeResolveLanguageModel(\n tracingChannel: VercelAiTracingChannelFactory,\n channelName: string,\n options: VercelAiChannelOptions,\n): void {\n tracingChannel<OrchestrionContext>(channelName).subscribe({\n end(rawCtx) {\n const ctx = rawCtx as OrchestrionContext;\n if (!isRecord(ctx.result)) {\n return;\n }\n const model = ctx.result as PatchableModel;\n // Patch the model's `doGenerate`/`doStream` once. The model call recovers its parent from the\n // active async context at call time (the operation span `bindTracingChannelToSpan` bound), which\n // propagates into the model call for `streamText` too, so there is nothing to capture on the model here.\n if (!model[PATCHED]) {\n model[PATCHED] = true;\n patchModelMethod(model, 'doGenerate', options);\n patchModelMethod(model, 'doStream', options);\n }\n },\n start() {\n /* no-op */\n },\n asyncStart() {\n /* no-op */\n },\n asyncEnd() {\n /* no-op */\n },\n error() {\n /* no-op */\n },\n });\n}\n\n/**\n * Pick the invoke_agent span a model call should hang under: the operation span that\n * `bindTracingChannelToSpan` planted as the active async context for the enclosing operation.\n *\n * Because we suppress `ai`'s native telemetry (so it never installs its own span as active), the active\n * span inside `doGenerate`/`doStream` is our operation span. The `operationSpans` gate makes this return\n * `undefined` when the active span isn't one of ours — e.g. telemetry was disabled for the call so we\n * opened no operation span and the active span is the user's enclosing span — so the model call is\n * skipped rather than mis-parented.\n *\n * This covers `generateText`/`embed` (whose model call is awaited inside the operation body) and\n * `streamText` alike — `ai` initiates the stream synchronously within the operation's bound context, so\n * the later `doStream` continuation restores the same active span even though the operation's span has\n * already ended. The per-operation binding also disambiguates concurrent calls that share one model\n * instance (a single mutable slot on the shared model could not — it would hold whichever operation\n * resolved the model last).\n */\nfunction resolveModelCallParent(): Span | undefined {\n const active = getActiveSpan();\n return active && operationSpans.has(active) ? active : undefined;\n}\n\nfunction patchModelMethod(\n model: PatchableModel,\n method: 'doGenerate' | 'doStream',\n options: VercelAiChannelOptions,\n): void {\n const original = model[method];\n if (typeof original !== 'function') {\n return;\n }\n model[method] = function (this: unknown, ...args: unknown[]): Promise<unknown> {\n const parent = resolveModelCallParent();\n // No enclosing operation span (e.g. telemetry disabled for the call) → don't open a model-call span.\n if (!parent) {\n return Promise.resolve(original.apply(this, args));\n }\n\n const callArgs = isRecord(args[0]) ? args[0] : {};\n // Carry the operation's `callId` so the shared core can name the span after it\n // (`ai.generateText.doGenerate` / `ai.streamText.doStream`).\n const callId = callIdBySpan.get(parent);\n const message: VercelAiChannelMessage = {\n type: 'languageModelCall',\n event: {\n callId,\n provider: model.provider,\n modelId: model.modelId,\n tools: callArgs.tools,\n messages: callArgs.prompt,\n // Inherit the enclosing operation's per-call recording flags so inputs/tools/outputs are recorded on\n // the model-call span whenever they are on the parent `invoke_agent` span.\n ...recordingBySpan.get(parent),\n },\n };\n const span = withActiveSpan(parent, () => createSpanFromMessage(message, options));\n // `languageModelCall` always opens a span; the guard just keeps the wrapper safe if that changes.\n if (!span) {\n return Promise.resolve(original.apply(this, args));\n }\n\n // `streamText` ends its operation span synchronously, so its `callId` entry was deliberately left in\n // place for this later model call; drop it now that we've used it.\n const clearStreamCallId = (): void => {\n if (method === 'doStream' && callId) {\n clearOperationCallId(callId);\n }\n };\n\n // Both the synchronous throw and the async rejection of the model call must end the span with an\n // error status (an `async` `doGenerate`/`doStream` that throws rejects rather than throwing here).\n const failSpan = (error: unknown): never => {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: error instanceof Error ? error.message : 'unknown_error' });\n span.end();\n clearStreamCallId();\n throw error;\n };\n\n try {\n const result = Promise.resolve(original.apply(this, args));\n // `doStream` resolves to `{ stream, ... }` before the stream is consumed; we end here (start/end\n // bracket the call) to match the channel timing.\n return result.then(value => {\n message.result = value;\n enrichSpanOnEnd(span, message, options);\n span.end();\n clearStreamCallId();\n return value;\n }, failSpan);\n } catch (error) {\n return failSpan(error);\n }\n };\n}\n\nfunction buildTextMessage(type: 'generateText' | 'streamText'): MessageBuilder {\n return (options, telemetry) => ({\n type,\n event: {\n callId: nextCallId(),\n operationId: `ai.${type}`,\n functionId: asString(telemetry.functionId),\n ...modelFields(options.model),\n maxRetries: options.maxRetries,\n // Normalize to the message-array shape the shared core (and v7's channel) expects: a bare string\n // `prompt` becomes a single user message, matching the SDK's own normalization.\n messages: normalizePromptMessages(options),\n ...recording(telemetry),\n },\n });\n}\n\nfunction normalizePromptMessages(options: Record<string, unknown>): unknown {\n if (Array.isArray(options.messages)) {\n return options.messages;\n }\n if (typeof options.prompt === 'string') {\n return [{ role: 'user', content: options.prompt }];\n }\n return options.messages ?? options.prompt;\n}\n\nfunction recording(telemetry: Record<string, unknown>): { recordInputs: unknown; recordOutputs: unknown } {\n return { recordInputs: telemetry.recordInputs, recordOutputs: telemetry.recordOutputs };\n}\n\nfunction modelFields(model: unknown): { provider?: string; modelId?: string } {\n return { provider: modelField(model, 'provider'), modelId: modelField(model, 'modelId') };\n}\n\nfunction modelField(model: unknown, field: 'modelId' | 'provider'): string | undefined {\n return isRecord(model) ? asString(model[field]) : undefined;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n"],"names":[],"mappings":"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"} |
| import { VercelAiChannelOptions, VercelAiTracingChannelFactory } from './vercel-ai-dc-subscriber'; | ||
| /** | ||
| * Subscribe the v6 orchestrion channel adapter. Safe to always call: inert on | ||
| * `ai` >= 7 (those channels are never published) and when orchestrion injection | ||
| * isn't active. Idempotent. | ||
| * | ||
| * `tracingChannel` is the platform-provided factory (the same one passed to | ||
| * `subscribeVercelAiTracingChannel`); `options` pins the recording settings at | ||
| * subscribe time so we never look the integration up per event. | ||
| */ | ||
| export declare function subscribeVercelAiOrchestrionChannels(tracingChannel: VercelAiTracingChannelFactory, options?: VercelAiChannelOptions): void; | ||
| //# sourceMappingURL=vercel-ai-orchestrion-v6-subscriber.d.ts.map |
| import { type VercelAiChannelOptions, type VercelAiTracingChannelFactory } from './vercel-ai-dc-subscriber'; | ||
| /** | ||
| * Subscribe the v6 orchestrion channel adapter. Safe to always call: inert on | ||
| * `ai` >= 7 (those channels are never published) and when orchestrion injection | ||
| * isn't active. Idempotent. | ||
| * | ||
| * `tracingChannel` is the platform-provided factory (the same one passed to | ||
| * `subscribeVercelAiTracingChannel`); `options` pins the recording settings at | ||
| * subscribe time so we never look the integration up per event. | ||
| */ | ||
| export declare function subscribeVercelAiOrchestrionChannels(tracingChannel: VercelAiTracingChannelFactory, options?: VercelAiChannelOptions): void; | ||
| //# sourceMappingURL=vercel-ai-orchestrion-v6-subscriber.d.ts.map |
| {"version":3,"file":"vercel-ai-orchestrion-v6-subscriber.d.ts","sourceRoot":"","sources":["../../../src/vercel-ai/vercel-ai-orchestrion-v6-subscriber.ts"],"names":[],"mappings":"AAKA,OAAO,EAML,KAAK,sBAAsB,EAC3B,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;AAyEnC;;;;;;;;GAQG;AACH,wBAAgB,oCAAoC,CAClD,cAAc,EAAE,6BAA6B,EAC7C,OAAO,GAAE,sBAA2B,GACnC,IAAI,CA6DN"} |
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1705506
90.62%451
64.6%14282
93.63%54
63.64%+ Added
- Removed
Updated