🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@sentry/server-utils

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sentry/server-utils - npm Package Compare versions

Comparing version
10.64.0
to
10.65.0
+125
build/cjs/graphql/graphql-dc-subscriber.js
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":";;;;;;;;;AAmCA,MAAM,gBAAA,GAAmB,SAAA;AAEzB,MAAM,gBAAA,GAAmB,oCAAA;AACzB,MAAM,eAAA,GAAkB,mCAAA;AAMxB,MAAM,wBAAA,GAA2B,qBAAA;AACjC,MAAM,0BAAA,GAA6B,uBAAA;AACnC,MAAM,+BAAA,GAAkC,4BAAA;AACxC,MAAM,mCAAA,GAAsC,gCAAA;AAC5C,MAAM,uBAAA,GAA0B,oBAAA;AAChC,MAAM,sCAAA,GAAyC,4BAAA;AAC/C,MAAM,kBAAA,GAAqB,eAAA;AAC3B,MAAM,yBAAA,GAA4B,sBAAA;AAClC,MAAM,qCAAA,GAAwC,2BAAA;AAG9C,MAAM,+CAAA,GAAkD,4CAAA;AACxD,MAAM,8BAAA,GAAiC,mCAAA;AAEvC,MAAM,sCAAA,GAAyC,OAAA;AAC/C,MAAM,iCAAA,GAAoC,SAAA;AAC1C,MAAM,8BAAA,GAAiC,MAAA;AAIvC,MAAM,qBAAqB,GAAA,GAAO,EAAA;AAGlC,MAAM,MAAA,GAAS;AAAA,EACb,GAAA,EAAK,KAAA;AAAA,EACL,MAAA,EAAQ,QAAA;AAAA,EACR,MAAA,EAAQ,QAAA;AAAA,EACR,IAAA,EAAM,MAAA;AAAA,EACN,OAAA,EAAS,SAAA;AAAA,EACT,aAAA,EAAe,gBAAA;AAAA,EACf,YAAA,EAAc,eAAA;AAAA,EACd,sBAAA,EAAwB;AAC1B,CAAA;AAIA,MAAM,mBAAA,0BAA4C,oCAAoC,CAAA;AACtF,MAAM,uBAAA,0BAAgD,wCAAwC,CAAA;AAC9F,MAAM,6BAAA,0BAAsD,8CAA8C,CAAA;AAC1G,MAAM,qBAAA,0BAA8C,sCAAsC,CAAA;AAC1F,MAAM,6BAAA,0BAAsD,8CAA8C,CAAA;AAC1G,MAAM,qBAAA,0BAA8C,sCAAsC,CAAA;AAuE1F,MAAM,OAAO,MAAY;AAAC,CAAA;AAM1B,IAAI,UAAA,GAAa,KAAA;AAEjB,MAAM,8BAA8B,MAAM;AACxC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,kBAAA,CAAmB,cAAA,IAAkB,UAAA,EAAY;AACpD,QAAA;AAAA,MACF;AACA,MAAA,UAAA,GAAa,IAAA;AAEb,MAAAA,sBAAA,IAAeC,UAAA,CAAM,IAAI,+DAA+D,CAAA;AAExF,MAAAC,iCAAA,CAA6B,MAAM;AACjC,QAAA,gBAAA,EAAiB;AACjB,QAAA,gBAAA,EAAiB;AACjB,QAAA,uBAAA,EAAwB;AACxB,QAAA,gBAAA,EAAiB;AACjB,QAAA,iBAAA,EAAkB;AAClB,QAAA,eAAA,EAAgB;AAAA,MAClB,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAOA,SAAS,gBAAA,GAAyB;AAChC,EAAAC,uCAAA,CAAyB,kBAAA,CAAmB,cAAA,CAAmCC,iBAAA,CAAS,eAAe,GAAG,CAAA,IAAA,KAAQ;AAChH,IAAA,IAAI,IAAA,CAAK,IAAA,GAAO,6BAA6B,CAAA,EAAG;AAC9C,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAO,iBAAiB,IAAI,CAAA;AAAA,EAC9B,CAAC,CAAA;AACH;AAOA,SAAS,uBAAA,GAAgC;AACvC,EAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,cAAA,CAAmCA,iBAAA,CAAS,uBAAuB,CAAA;AAEtG,EAAAD,uCAAA,CAAyB,SAAS,CAAA,IAAA,KAAQ;AACxC,IAAA,IAAI,KAAK,IAAA,EAAM;AACb,MAAA,IAAA,CAAK,IAAA,CAAK,6BAA6B,CAAA,GAAI,IAAA;AAAA,IAC7C;AACA,IAAA,OAAO,iBAAiB,IAAI,CAAA;AAAA,EAC9B,CAAC,CAAA;AAID,EAAA,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA,OAAA,KAAW;AAC/B,IAAA,MAAM,OAAQ,OAAA,CAA+B,IAAA;AAC7C,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,IAAA,CAAK,6BAA6B,CAAA,GAAI,KAAA;AAAA,IACxC;AAAA,EACF,CAAC,CAAA;AACH;AAMA,SAAS,gBAAA,GAAyB;AAChC,EAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,cAAA,CAAmCC,iBAAA,CAAS,eAAe,CAAA;AAG9F,EAAA,OAAA,CAAQ,KAAA,CAAM,UAAU,IAAI,CAAA;AAC5B,EAAA,OAAA,CAAQ,QAAA,CAAS,UAAU,CAAA,OAAA,KAAW;AACpC,IAAA,MAAM,IAAA,GAAO,OAAA;AACb,IAAA,MAAM,kBAAkB,IAAA,CAAK,IAAA;AAC7B,IAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,IAAA,MAAM,cAAc,MAAA,EAAQ,WAAA;AAC5B,IAAA,IAAI,CAAC,eAAA,IAAmB,CAAC,WAAA,EAAa;AACpC,MAAA;AAAA,IACF;AAEA,IAAA,kBAAA,CAAmB,eAAe,CAAA;AAClC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AACjC,IAAA,MAAM,KAAA,GAAQ,OAAO,QAAA,KAAa,QAAA,GAAW,QAAA,GAAW,WAAA;AACxD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AAChC,IAAA,eAAA,CAAgB,qBAAqB,CAAA,EAAG,GAAA,CAAI,WAAA,EAAa,EAAE,KAAA,EAAO,CAAC,CAAC,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,CAAA;AAAA,EAC7F,CAAC,CAAA;AACH;AAOA,SAAS,iBAAA,GAA0B;AACjC,EAAAD,uCAAA;AAAA,IACE,kBAAA,CAAmB,cAAA,CAAoCC,iBAAA,CAAS,gBAAgB,CAAA;AAAA,IAChF,CAAA,IAAA,KAAQ;AACN,MAAA,MAAM,UAAU,IAAA,CAAK,IAAA;AACrB,MAAA,MAAM,MAAA,GAAS,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AAC/B,MAAA,MAAM,GAAA,GAAM,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AAE5B,MAAA,IAAI,CAAC,OAAA,IAAW,CAAC,GAAA,EAAK;AACpB,QAAA,OAAO,MAAA;AAAA,MACT;AAEA,MAAA,kBAAA,CAAmB,OAAO,CAAA;AAC1B,MAAA,MAAM,IAAA,GAAO,QAAQ,WAAA,GAAc,OAAA,CAAQ,qBAAqB,CAAA,EAAG,GAAA,CAAI,MAAA,CAAO,WAAW,CAAA,GAAI,MAAA;AAC7F,MAAA,MAAM,KAAA,GAAQ,IAAA,EAAM,KAAA,IAAS,GAAA,CAAI,QAAQ,UAAA,IAAc,WAAA;AACvD,MAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,IAAS,KAAA;AAE7B,MAAA,MAAM,OAAA,GAAU,IAAI,UAAA,EAAY,OAAA;AAChC,MAAA,MAAM,WAAA,GAAc,iBAAA,CAAkB,OAAA,EAAS,cAAc,CAAA;AAC7D,MAAA,MAAM,OAAA,GAAU,iBAAA,CAAkB,OAAA,EAAS,SAAS,CAAA;AAGpD,MAAA,MAAM,IAAA,GAAOC,kBAAA,CAAc,EAAE,WAAA,EAAa,OAAA,EAAQ,EAAG,MAAM,gBAAA,CAAiB,KAAA,EAAO,GAAA,EAAK,OAAO,CAAC,CAAA;AAEhG,MAAA,IAAI,CAAC,KAAA,EAAO;AAGV,QAAA,OAAA,CAAQ,uBAAuB,GAAG,IAAA,CAAK,EAAE,KAAK,aAAA,EAAeC,uBAAA,IAAsB,CAAA;AACnF,QAAA,GAAA,CAAI,mBAAmB,CAAA,GAAI,IAAA;AAAA,MAC7B;AAEA,MAAA,IAAA,CAAK,YAAA,GAAe,KAAA;AACpB,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,MAIE,YAAA,CAAa,EAAE,IAAA,EAAK,EAAG;AACrB,QAAA,OAAO,CAAC,IAAA,CAAK,YAAA;AAAA,MACf;AAAA;AACF,GACF;AACF;AAGA,SAAS,eAAA,GAAwB;AAC/B,EAAA,kBAAA,CACG,cAAA,CAAmCF,iBAAA,CAAS,WAAW,CAAA,CACvD,KAAA,CAAM,SAAA,CAAU,CAAA,OAAA,KAAW,SAAA,CAAU,OAAA,EAA+B,KAAA,EAAO,MAAA,CAAO,GAAG,CAAC,CAAA;AACzF,EAAA,kBAAA,CACG,cAAA,CAAmCA,iBAAA,CAAS,YAAY,CAAA,CACxD,KAAA,CAAM,SAAA,CAAU,CAAA,OAAA,KAAW,SAAA,CAAU,OAAA,EAA+B,IAAA,EAAM,MAAA,CAAO,IAAI,CAAC,CAAA;AACzF,EAAA,kBAAA,CACG,cAAA,CAAmCA,iBAAA,CAAS,cAAc,CAAA,CAC1D,KAAA,CAAM,SAAA,CAAU,CAAA,OAAA,KAAW,SAAA,CAAU,OAAA,EAA+B,IAAA,EAAM,MAAA,CAAO,MAAM,CAAC,CAAA;AAC3F,EAAA,kBAAA,CAAmB,eAAmCA,iBAAA,CAAS,eAAe,CAAA,CAAE,KAAA,CAAM,UAAU,CAAA,OAAA,KAAW;AACzG,IAAA,MAAM,IAAA,GAAO,OAAA;AACb,IAAA,IAAI,KAAK,IAAA,EAAM;AACb,MAAA,oBAAA,CAAqB,IAAA,CAAK,IAAA,EAAM,KAAA,EAAO,MAAA,CAAO,QAAQ,MAAS,CAAA;AAAA,IACjE;AAAA,EACF,CAAC,CAAA;AACD,EAAA,kBAAA,CAAmB,eAAmCA,iBAAA,CAAS,gBAAgB,CAAA,CAAE,KAAA,CAAM,UAAU,CAAA,OAAA,KAAW;AAC1G,IAAA,MAAM,IAAA,GAAO,OAAA;AACb,IAAA,IAAI,KAAK,IAAA,EAAM;AACb,MAAA,oBAAA,CAAqB,IAAA,CAAK,MAAM,IAAA,EAAM,MAAA,CAAO,SAAS,IAAA,CAAK,SAAA,CAAU,CAAC,CAAwB,CAAA;AAAA,IAChG;AAAA,EACF,CAAC,CAAA;AACH;AAGA,SAAS,gBAAA,GAAyB;AAChC,EAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,cAAA,CAAmCA,iBAAA,CAAS,eAAe,CAAA;AAE9F,EAAA,OAAA,CAAQ,KAAA,CAAM,UAAU,IAAI,CAAA;AAC5B,EAAA,OAAA,CAAQ,QAAA,CAAS,UAAU,CAAA,OAAA,KAAW;AACpC,IAAA,MAAM,IAAA,GAAO,OAAA;AACb,IAAA,MAAM,OAAO,IAAA,CAAK,MAAA;AAClB,IAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACrC,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,qBAAqB,CAAA,GAAI;AAAA,MAC5B,GAAG,8BAAA,CAA+B,IAAA,CAAK,SAAA,GAAY,CAAC,CAAC,CAAA;AAAA,MACrD,GAAG,kCAAkC,IAAI;AAAA,KAC3C;AAAA,EACF,CAAC,CAAA;AACH;AAEA,SAAS,SAAA,CAAU,IAAA,EAA0B,UAAA,EAAqB,YAAA,EAA2B;AAC3F,EAAA,MAAM,UAAU,IAAA,CAAK,IAAA;AACrB,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA;AAAA,EACF;AACA,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AAChC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA;AAAA,EACF;AAGA,EAAA,MAAM,gBAAA,GAAmB,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AACzC,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AAChC,EAAA,MAAM,eAAA,GAAkB,YAAA,KAAiB,MAAA,CAAO,MAAA,GAAS,gBAAA,GAAmB,OAAA;AAE5E,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,uBAAuB,CAAA,IAAK,EAAC;AAC3D,EAAA,MAAM,WAAW,aAAA,CAAc,SAAA,CAAU,CAAA,UAAA,KAAc,UAAA,CAAW,QAAQ,OAAO,CAAA;AACjF,EAAA,IAAI,WAAW,CAAA,EAAG;AAEhB,IAAA,eAAA,CAAgB,OAAA,EAAS,UAAA,EAAY,YAAA,EAAc,eAAe,CAAA;AAAA,EACpE,CAAA,MAAA,IAAW,YAAA,KAAiB,MAAA,CAAO,MAAA,IAAU,gBAAA,EAAkB;AAC7D,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,IAAK,QAAA,EAAU,CAAA,EAAA,EAAK;AAClC,MAAA,eAAA,CAAgB,cAAc,CAAC,CAAA,CAAG,GAAA,EAAK,UAAA,EAAY,cAAc,eAAe,CAAA;AAAA,IAClF;AACA,IAAA,aAAA,CAAc,MAAA,CAAO,CAAA,EAAG,QAAA,GAAW,CAAC,CAAA;AAAA,EACtC,CAAA,MAAO;AACL,IAAA,eAAA,CAAgB,OAAA,EAAS,UAAA,EAAY,YAAA,EAAc,eAAe,CAAA;AAClE,IAAA,aAAA,CAAc,MAAA,CAAO,UAAU,CAAC,CAAA;AAAA,EAClC;AACF;AAEA,SAAS,mBAAmB,OAAA,EAA4B;AACtD,EAAA,IAAI,OAAO,SAAA,CAAU,cAAA,CAAe,IAAA,CAAK,OAAA,EAAS,uBAAuB,CAAA,EAAG;AAC1E,IAAA;AAAA,EACF;AAEA,EAAA,OAAA,CAAQ,uBAAuB,IAAI,EAAC;AACpC,EAAA,OAAA,CAAQ,qBAAqB,CAAA,mBAAI,IAAI,GAAA,EAAI;AAEzC,EAAA,MAAM,QAAQ,WAAA,CAAY,MAAM,4BAAA,CAA6B,OAAO,GAAG,kBAAkB,CAAA;AACzF,EAAA,KAAA,CAAM,KAAA,IAAQ;AACd,EAAA,OAAA,CAAQ,6BAA6B,CAAA,GAAI,KAAA;AAKzC,EAAA,IAAI,OAAO,OAAA,CAAQ,EAAA,KAAO,UAAA,EAAY;AACpC,IAAA,OAAA,CAAQ,EAAA,CAAG,SAAS,MAAM;AACxB,MAAA,oBAAA,CAAqB,OAAA,EAAS,IAAA,EAAM,MAAA,CAAO,aAAA,EAAe,MAAS,CAAA;AACnE,MAAA,wBAAA,CAAyB,OAAO,CAAA;AAAA,IAClC,CAAC,CAAA;AACD,IAAA,OAAA,CAAQ,EAAA,CAAG,SAAS,MAAM;AACxB,MAAA,oBAAA,CAAqB,OAAA,EAAS,IAAA,EAAM,MAAA,CAAO,YAAA,EAAc,MAAS,CAAA;AAClE,MAAA,wBAAA,CAAyB,OAAO,CAAA;AAAA,IAClC,CAAC,CAAA;AAAA,EACH;AACF;AAGA,SAAS,yBAAyB,OAAA,EAA4B;AAC5D,EAAA,MAAM,WAAA,GAAc,QAAQ,6BAA6B,CAAA;AACzD,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,aAAA,CAAc,WAAW,CAAA;AACzB,IAAA,OAAA,CAAQ,6BAA6B,CAAA,GAAI,MAAA;AAAA,EAC3C;AACF;AAEA,SAAS,6BAA6B,OAAA,EAA4B;AAChE,EAAA,MAAM,cAAcE,uBAAA,EAAmB;AACvC,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,uBAAuB,CAAA,IAAK,EAAC;AAC3D,EAAA,IAAI,CAAA;AACJ,EAAA,KAAK,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,aAAA,CAAc,QAAQ,CAAA,EAAA,EAAK;AACzC,IAAA,MAAM,WAAA,GAAc,cAAc,CAAC,CAAA;AACnC,IAAA,MAAM,iBAAA,GAAA,CAAqB,WAAA,GAAc,WAAA,CAAY,aAAA,IAAiB,GAAA;AACtE,IAAA,IAAI,oBAAoB,kBAAA,EAAoB;AAC1C,MAAA;AAAA,IACF;AACA,IAAA,eAAA,CAAgB,WAAA,CAAY,GAAA,EAAK,IAAA,EAAM,MAAA,CAAO,wBAAwB,IAAI,CAAA;AAAA,EAC5E;AACA,EAAA,aAAA,CAAc,MAAA,CAAO,GAAG,CAAC,CAAA;AAC3B;AAEA,SAAS,oBAAA,CACP,OAAA,EACA,UAAA,EACA,SAAA,EACA,OAAA,EACM;AACN,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,uBAAuB,CAAA,IAAK,EAAC;AAC3D,EAAA,aAAA,CAAc,QAAQ,CAAA,UAAA,KAAc;AAClC,IAAA,eAAA,CAAgB,UAAA,CAAW,GAAA,EAAK,UAAA,EAAY,SAAA,EAAW,OAAO,CAAA;AAAA,EAChE,CAAC,CAAA;AACD,EAAA,OAAA,CAAQ,uBAAuB,IAAI,EAAC;AACtC;AAEA,SAAS,eAAA,CACP,OAAA,EACA,UAAA,EACA,SAAA,EACA,OAAA,EACM;AACN,EAAA,MAAM,UAAA,GAAa,QAAQ,mBAAmB,CAAA;AAC9C,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA;AAAA,EACF;AACA,EAAA,IAAI,eAAe,KAAA,EAAO;AACxB,IAAA,UAAA,CAAW,SAAA,CAAU;AAAA,MACnB,IAAA,EAAMC,sBAAA;AAAA,MACN,SACE,SAAA,KAAc,MAAA,CAAO,aAAA,IAAiB,SAAA,KAAc,OAAO,YAAA,GACvD,CAAA,EAAG,SAAS,CAAA,kBAAA,EACV,YAAY,IAAA,GAAO,eAAA,GAAkB,YAAY,KAAA,GAAQ,kBAAA,GAAqB,EAChF,CAAA,CAAA,GACA;AAAA,KACP,CAAA;AAAA,EACH;AACA,EAAA,UAAA,CAAW,GAAA,EAAI;AACf,EAAA,OAAA,CAAQ,mBAAmB,CAAA,GAAI,MAAA;AACjC;AAGA,SAAS,iBAAiB,IAAA,EAAgC;AACxD,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AACpC,EAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AACtC,EAAA,MAAM,QAAA,GAAW,OAAO,WAAA,KAAgB,QAAA,GAAW,WAAA,GAAc,EAAA;AACjE,EAAA,MAAM,UAAA,GAAa,OAAO,aAAA,KAAkB,QAAA,GAAW,aAAA,GAAgB,EAAA;AACvE,EAAA,IAAI,OAAA,GAAU,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AAE9B,EAAA,MAAM,OAAOC,sBAAA,CAAkB;AAAA,IAC7B,IAAA,EAAM,CAAA,QAAA,EAAW,iBAAA,CAAkB,QAAQ,CAAC,CAAA,CAAA;AAAA,IAC5C,EAAA,EAAI,SAAA;AAAA,IACJ,MAAMC,cAAA,CAAU,QAAA;AAAA,IAChB,UAAA,EAAY;AAAA,MACV,GAAG,6BAAA,CAA8B,IAAA,CAAK,IAAI,CAAA;AAAA,MAC1C,CAAC,0BAA0B,GAAG,QAAA;AAAA;AAAA,MAC9B,CAACC,qCAA0B,GAAG,QAAA;AAAA,MAC9B,CAAC,+BAA+B,GAAG,sCAAA;AAAA;AAAA,MACnC,CAAC,mCAAmC,GAAG,UAAA;AAAA;AAAA,MACvC,CAAC,+CAA+C,GAAG,UAAA;AAAA,MACnD,CAACC,mCAAwB,GAAG,8BAAA;AAAA,MAC5B,CAAC,yBAAyB,GAAG,OAAA,EAAS,SAAA;AAAA;AAAA,MACtC,CAACC,+BAAoB,GAAG,OAAA,EAAS,SAAA;AAAA,MACjC,CAAC,qCAAqC,GAAG,OAAA,EAAS,aAAA;AAAA;AAAA,MAClD,CAAC,8BAA8B,GAAG,OAAA,EAAS,aAAA;AAAA,MAC3C,CAACC,qCAAgC,GAAG;AAAA;AACtC,GACD,CAAA;AAED,EAAA,IAAI,CAAC,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,EAAU;AAC3C,IAAA,OAAA,GAAU,EAAC;AACX,IAAA,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA,GAAI,OAAA;AAAA,EACtB;AACA,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,OAAO,OAAA,CAAQ,OAAA,KAAY,QAAA,GAAW,OAAA,CAAQ,OAAA,GAAW,OAAA,CAAQ,OAAA,GAAU,EAAC;AAC/G,EAAA,MAAM,SAAA,GAAYC,iBAAA,CAAa,EAAE,IAAA,EAAM,CAAA;AACvC,EAAA,IAAI,SAAA,CAAU,cAAc,CAAA,EAAG;AAC7B,IAAA,OAAA,CAAQ,cAAc,CAAA,GAAI,SAAA,CAAU,cAAc,CAAA;AAAA,EACpD;AACA,EAAA,IAAI,UAAU,OAAA,EAAS;AACrB,IAAA,OAAA,CAAQ,SAAS,IAAI,SAAA,CAAU,OAAA;AAAA,EACjC;AAEA,EAAA,OAAO,IAAA;AACT;AAGA,SAAS,gBAAA,CAAiB,KAAA,EAAe,GAAA,EAAqB,OAAA,EAA4B;AACxF,EAAA,OAAON,sBAAA,CAAkB;AAAA,IACvB,IAAA,EAAM,GAAG,KAAK,CAAA,QAAA,CAAA;AAAA,IACd,EAAA,EAAI,SAAA;AAAA,IACJ,MAAMC,cAAA,CAAU,QAAA;AAAA,IAChB,UAAA,EAAY;AAAA,MACV,GAAG,8BAA8B,OAAO,CAAA;AAAA,MACxC,CAAC,0BAA0B,GAAG,GAAA,CAAI,MAAA,EAAQ,QAAA;AAAA;AAAA,MAC1C,CAACC,qCAA0B,GAAG,GAAA,CAAI,MAAA,EAAQ,QAAA;AAAA,MAC1C,CAAC,+BAA+B,GAAG,sCAAA;AAAA;AAAA,MACnC,CAAC,mCAAmC,GAAG,GAAA,CAAI,MAAA,EAAQ,UAAA;AAAA;AAAA,MACnD,CAAC,+CAA+C,GAAG,GAAA,CAAI,MAAA,EAAQ,UAAA;AAAA,MAC/D,CAAC,wBAAwB,GAAG,iCAAA;AAAA;AAAA,MAC5B,CAACC,mCAAwB,GAAG,iCAAA;AAAA,MAC5B,CAAC,yBAAyB,GAAG,GAAA,CAAI,UAAA,EAAY,SAAA;AAAA;AAAA,MAC7C,CAACC,+BAAoB,GAAG,GAAA,CAAI,UAAA,EAAY,SAAA;AAAA,MACxC,CAAC,qCAAqC,GAAG,GAAA,CAAI,UAAA,EAAY,aAAA;AAAA;AAAA,MACzD,CAAC,8BAA8B,GAAG,GAAA,CAAI,UAAA,EAAY,aAAA;AAAA,MAClD,CAACC,qCAAgC,GAAG;AAAA;AACtC,GACD,CAAA;AACH;AAOA,SAAS,8BAA8B,OAAA,EAAkD;AACvF,EAAA,MAAM,aAAa,OAAA,EAAS,UAAA;AAC5B,EAAA,MAAM,MAAA,GAAS,aAAa,qBAAqB,CAAA;AACjD,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,UAAU,UAAA,EAAY,gBAAA,EAAkB,OAAA,IAAW,UAAA,EAAY,YAAY,gBAAA,EAAkB,OAAA;AACnG,EAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,EAAS;AAC1C,IAAA,OAAO,EAAE,CAACE,2BAAgB,GAAG,OAAA,CAAQ,aAAY,EAAE;AAAA,EACrD;AACA,EAAA,OAAO,EAAC;AACV;AAEA,SAAS,kCAAkC,IAAA,EAAsC;AAC/E,EAAA,MAAM,UAAU,IAAA,CAAK,gBAAA,EAAkB,OAAA,IAAW,IAAA,CAAK,YAAY,gBAAA,EAAkB,OAAA;AACrF,EAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,EAAS;AAC1C,IAAA,OAAO,EAAE,CAACA,2BAAgB,GAAG,OAAA,CAAQ,aAAY,EAAE;AAAA,EACrD;AACA,EAAA,OAAO,EAAC;AACV;AAEA,SAAS,+BAA+B,GAAA,EAA8B;AACpE,EAAA,MAAMC,YAAA,GAA6B;AAAA;AAAA,IAEjC,CAAC,sCAAsC,GAAG,OAAA;AAAA;AAAA,IAC1C,CAACC,mCAAwB,GAAG;AAAA,GAC9B;AAEA,EAAA,MAAM,cAAc,GAAA,IAAO,kBAAA;AAC3B,EAAA,IAAI,OAAO,gBAAgB,QAAA,EAAU;AACnC,IAAA,MAAM,cAAA,GAAiB,WAAA;AACvB,IAAA,MAAM,QAAA,GAAW,WAAA,CAAY,cAAA,CAAe,QAAQ,CAAA;AACpD,IAAA,MAAM,QAAA,GAAW,WAAA,CAAY,cAAA,CAAe,QAAQ,CAAA;AACpD,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,cAAA,CAAe,IAAA,EAAM,QAAQ,CAAA;AAElD,IAAAD,YAAA,CAAW,uBAAuB,CAAA,GAAI,QAAA;AACtC,IAAAA,YAAA,CAAWE,gCAAqB,CAAA,GAAI,QAAA;AAEpC,IAAAF,YAAA,CAAWG,yBAAc,CAAA,GAAI,QAAA;AAC7B,IAAAH,YAAA,CAAWI,sBAAW,CAAA,GAAI,IAAA;AAG1B,IAAAJ,YAAA,CAAWK,wBAAa,CAAA,GAAI,QAAA;AAE5B,IAAAL,YAAA,CAAWM,wBAAa,CAAA,GAAI,IAAA;AAAA,EAC9B,CAAA,MAAA,IAAW,OAAO,WAAA,KAAgB,QAAA,EAAU;AAC1C,IAAA,MAAM,WAAA,GAAc,eAAe,WAAW,CAAA;AAC9C,IAAAN,YAAA,CAAW,kBAAkB,CAAA,GAAI,WAAA;AACjC,IAAAA,YAAA,CAAWO,mBAAQ,CAAA,GAAI,WAAA;AAEvB,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,WAAW,CAAA;AACpC,MAAA,MAAM,QAAA,GAAW,WAAA,CAAY,QAAA,CAAS,QAAQ,CAAA;AAC9C,MAAA,MAAM,QAAA,GAAW,WAAA,CAAY,QAAA,CAAS,QAAQ,CAAA;AAC9C,MAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,QAAA,CAAS,IAAA,GAAO,QAAA,CAAS,SAAS,IAAA,EAAM,EAAE,CAAA,GAAI,KAAA,CAAA,EAAW,QAAQ,CAAA;AAEtF,MAAAP,YAAA,CAAW,uBAAuB,CAAA,GAAI,QAAA;AACtC,MAAAA,YAAA,CAAWE,gCAAqB,CAAA,GAAI,QAAA;AAEpC,MAAAF,YAAA,CAAWG,yBAAc,CAAA,GAAI,QAAA;AAC7B,MAAAH,YAAA,CAAWI,sBAAW,CAAA,GAAI,IAAA;AAE1B,MAAAJ,YAAA,CAAWK,wBAAa,CAAA,GAAI,QAAA;AAE5B,MAAAL,YAAA,CAAWM,wBAAa,CAAA,GAAI,IAAA;AAAA,IAC9B,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACA,EAAA,OAAON,YAAA;AACT;AAEA,SAAS,kBAAkB,YAAA,EAA8B;AACvD,EAAA,OAAO,YAAA,KAAiB,KAAK,YAAA,GAAe,WAAA;AAC9C;AAEA,SAAS,eAAe,GAAA,EAAqB;AAC3C,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,WAAA,EAAa,OAAO,CAAA;AACzC;AAEA,SAAS,OAAA,CAAQ,aAAiC,gBAAA,EAAkC;AAElF,EAAA,OAAO,WAAA,KAAgB,gBAAA,KAAqB,MAAA,GAAS,IAAA,GAAO,IAAA,CAAA;AAC9D;AAEA,SAAS,YAAY,eAAA,EAA6C;AAChE,EAAA,MAAM,mBAAmB,eAAA,IAAmB,MAAA;AAC5C,EAAA,MAAM,aAAA,GAAgB,gBAAA,CAAiB,QAAA,CAAS,GAAG,CAAA,GAC/C,gBAAA,CAAiB,SAAA,CAAU,CAAA,EAAG,gBAAA,CAAiB,MAAA,GAAS,CAAC,CAAA,GACzD,gBAAA;AACJ,EAAA,OAAO,cAAc,WAAA,EAAY;AACnC;AAEA,SAAS,YAAY,eAAA,EAA6C;AAEhE,EAAA,OAAO,eAAA,IAAmB,WAAA;AAC5B;AAEA,SAAS,iBAAA,CAAkB,SAA8C,GAAA,EAAiC;AACxG,EAAA,MAAM,KAAA,GAAQ,UAAU,GAAG,CAAA;AAC3B,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,MAAA,CAAO,MAAM,CAAC,CAAC,CAAA,GAAI,MAAA,CAAO,KAAK,CAAA;AAC/D;AASO,MAAM,yBAAA,GAA4BQ,uBAAkB,0BAA0B;;;;"}
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":"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"}
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":"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"}
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":";;;;;;AAyBA,MAAM,gBAAA,GAAmB,cAAA;AAIzB,MAAM,MAAA,GAAS,kCAAA;AAGf,MAAM,qBAAA,GAAwB;AAAA,EAC5B,EAAE,OAAA,EAAS,QAAA,CAAS,6BAAA,EAA+B,WAAW,kBAAA,EAAmB;AAAA,EACjF,EAAE,OAAA,EAAS,QAAA,CAAS,0BAAA,EAA4B,WAAW,YAAA,EAAa;AAAA,EACxE,EAAE,OAAA,EAAS,QAAA,CAAS,iBAAA,EAAmB,WAAW,MAAA;AACpD,CAAA;AAUA,IAAI,UAAA,GAAa,KAAA;AAEjB,MAAM,8BAAA,IAAkC,CAAC,OAAA,GAA8B,EAAC,KAAM;AAC5E,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,kBAAA,CAAmB,cAAA,IAAkB,UAAA,EAAY;AACpD,QAAA;AAAA,MACF;AACA,MAAA,UAAA,GAAa,IAAA;AAIb,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,KAAA,MAAW,EAAE,OAAA,EAAS,SAAA,EAAU,IAAK,qBAAA,EAAuB;AAC1D,UAAA,WAAA,IAAe,KAAA,CAAM,GAAA,CAAI,CAAA,mDAAA,EAAsD,OAAO,CAAA,CAAA,CAAG,CAAA;AACzF,UAAA,wBAAA;AAAA,YACE,kBAAA,CAAmB,eAA0C,OAAO,CAAA;AAAA,YACpE,CAAA,IAAA,KAAQ,eAAA,CAAgB,IAAA,EAAM,SAAA,EAAW,OAAO,CAAA;AAAA,YAChD;AAAA,cACE,aAAA,EAAe,CAAC,IAAA,EAAM,IAAA,KAAS;AAE7B,gBAAA,IAAI,cAAc,YAAA,EAAc;AAC9B,kBAAA,gCAAA;AAAA,oBACE,IAAA;AAAA,oBACA,IAAA,CAAK,MAAA;AAAA,oBACL,yBAAA,CAA0B,OAAO,CAAA,CAAE;AAAA,mBACrC;AAAA,gBACF;AAAA,cACF,CAAA;AAAA,cACA,YAAA,EAAc,CAAC,EAAE,IAAA,EAAM,MAAK,KAAM,gBAAA,CAAiB,IAAA,EAAM,IAAA,EAAM,OAAO;AAAA;AACxE,WACF;AAAA,QACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAMA,SAAS,eAAA,CACP,IAAA,EACA,SAAA,EACA,OAAA,EACkB;AAGlB,EAAA,IAAI,sCAAA,CAAuC,gBAAgB,CAAA,EAAG;AAC5D,IAAA,OAAO,MAAA;AAAA,EACT;AAKA,EAAA,IAAI,cAAc,MAAA,EAAQ;AACxB,IAAA,MAAM,aAAa,aAAA,EAAc;AACjC,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,MAAM,EAAE,EAAA,EAAI,MAAA,EAAO,GAAI,WAAW,UAAU,CAAA;AAC5C,MAAA,IAAI,MAAA,KAAW,MAAA,IAAU,EAAA,KAAO,aAAA,EAAe;AAC7C,QAAA,OAAO,MAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,IAAa,EAAC;AAChC,EAAA,MAAM,MAAA,GAAS,KAAK,CAAC,CAAA;AAErB,EAAA,MAAM,EAAE,YAAA,EAAa,GAAI,yBAAA,CAA0B,OAAO,CAAA;AAC1D,EAAA,MAAM,gBAAA,GAAmB,sBAAA,CAAuB,OAAA,CAAQ,gBAAgB,CAAA;AAExE,EAAA,MAAM,UAAA,GAAa,mCAAA,CAAoC,SAAA,EAAW,MAAA,EAAQ,KAAK,IAAI,CAAA;AACnF,EAAA,MAAM,KAAA,GAAS,UAAA,CAAW,8BAA8B,CAAA,IAAgB,SAAA;AACxE,EAAA,UAAA,CAAW,gCAAgC,CAAA,GAAI,MAAA;AAE/C,EAAA,MAAM,OAAO,iBAAA,CAAkB;AAAA,IAC7B,IAAA,EAAM,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA;AAAA,IAC3B,EAAA,EAAI,UAAU,SAAS,CAAA,CAAA;AAAA,IACvB;AAAA,GACD,CAAA;AAED,EAAA,IAAI,gBAAgB,MAAA,EAAQ;AAC1B,IAAA,+BAAA,CAAgC,IAAA,EAAM,MAAA,EAAQ,SAAA,EAAW,gBAAgB,CAAA;AAAA,EAC3E;AAEA,EAAA,OAAO,IAAA;AACT;AAIA,SAAS,gBAAgB,KAAA,EAA8C;AACrE,EAAA,OAAO,CAAC,CAAC,KAAA,IAAS,OAAQ,KAAA,CAA8B,MAAA,CAAO,aAAa,CAAA,KAAM,UAAA;AACpF;AAOA,SAAS,gBAAA,CAAiB,IAAA,EAAY,IAAA,EAAiC,OAAA,EAAsC;AAC3G,EAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,EAAA,IAAI,CAAC,eAAA,CAAgB,MAAM,CAAA,EAAG;AAC5B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,yBAAA,CAA0B,OAAO,CAAA;AAC3D,EAAA,MAAM,UAAU,MAAA,CAAO,MAAA,CAAO,aAAa,CAAA,CAAE,KAAK,MAAM,CAAA;AACxD,EAAA,MAAM,YAAA,GAAe,2BAAA,CAA4B,EAAE,CAAC,MAAA,CAAO,aAAa,GAAG,OAAA,EAAQ,EAAG,IAAA,EAAM,aAAA,IAAiB,KAAK,CAAA;AAClH,EAAA,MAAA,CAAO,MAAA,CAAO,aAAa,CAAA,GAAI,MAAM,YAAA;AAErC,EAAA,OAAO,IAAA;AACT;AASO,MAAM,6BAAA,GAAgC,kBAAkB,8BAA8B;;;;"}
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":"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"}
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":";;AAKO,SAAS,SAAS,KAAA,EAAoC;AAC3D,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,MAAA;AAC7C;AAGO,SAAS,SAAS,KAAA,EAAoC;AAC3D,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,KAAK,IAAI,KAAA,GAAQ,MAAA;AAC9D;AAGO,SAAS,GAAA,CAAI,GAAuB,CAAA,EAA2C;AACpF,EAAA,OAAO,MAAM,MAAA,IAAa,CAAA,KAAM,SAAY,MAAA,GAAA,CAAa,CAAA,IAAK,MAAM,CAAA,IAAK,CAAA,CAAA;AAC3E;AAGO,SAAS,cAAc,KAAA,EAAwB;AACpD,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;AAgDO,SAAS,iBAAiB,KAAA,EAAkD;AACjF,EAAA,OACE,YAAA,CAAa,KAAK,CAAA,IAClB,OAAQ,MAAoC,WAAA,KAAgB,UAAA,IAC5D,OAAQ,KAAA,CAAkC,SAAA,KAAc,UAAA;AAE5D;AAWO,SAAS,kBAAA,CACd,MAAA,EACA,OAAA,EACA,OAAA,EACyB;AACzB,EAAA,MAAM,MAAA,GAAS,OAAO,SAAA,EAAU;AAChC,EAAA,MAAM,KAAA,GAAiC,EAAE,SAAA,EAAW,EAAC,EAAE;AACvD,EAAA,IAAI,IAAA,GAAO,EAAA;AACX,EAAA,IAAI,OAAA,GAAU,KAAA;AAEd,EAAA,MAAM,WAAW,MAAY;AAC3B,IAAA,IAAI,OAAA,EAAS;AACX,MAAA;AAAA,IACF;AACA,IAAA,OAAA,GAAU,IAAA;AACV,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,KAAA,CAAM,IAAA,GAAO,IAAA;AAAA,IACf;AACA,IAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,EACf,CAAA;AAEA,EAAA,MAAM,IAAA,GAAO,CAAC,KAAA,KAAyB;AACrC,IAAA,IAAI,OAAA,EAAS;AACX,MAAA;AAAA,IACF;AACA,IAAA,OAAA,GAAU,IAAA;AACV,IAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,EACf,CAAA;AAEA,EAAA,OAAO,IAAI,cAAA,CAAwB;AAAA,IACjC,MAAM,KAAK,UAAA,EAAY;AACrB,MAAA,IAAI;AACF,QAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,OAAO,IAAA,EAAK;AAC1C,QAAA,IAAI,IAAA,EAAM;AACR,UAAA,QAAA,EAAS;AACT,UAAA,UAAA,CAAW,KAAA,EAAM;AAEjB,UAAA;AAAA,QACF;AACA,QAAA,IAAA,IAAQ,eAAA,CAAgB,KAAA,EAAO,KAAK,CAAA,IAAK,EAAA;AACzC,QAAA,UAAA,CAAW,QAAQ,KAAK,CAAA;AAAA,MAC1B,SAAS,KAAA,EAAO;AACd,QAAA,IAAA,CAAK,KAAK,CAAA;AACV,QAAA,UAAA,CAAW,MAAM,KAAK,CAAA;AAAA,MACxB;AAAA,IACF,CAAA;AAAA,IACA,OAAO,MAAA,EAAQ;AAGb,MAAA,QAAA,EAAS;AAET,MAAA,OAAO,MAAA,CAAO,OAAO,MAAM,CAAA;AAAA,IAC7B;AAAA,GACD,CAAA;AACH;AAMA,SAAS,eAAA,CAAgB,OAAgC,KAAA,EAAoC;AAC3F,EAAA,IAAI,CAAC,YAAA,CAAa,KAAK,CAAA,EAAG;AACxB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM;AAAA,IACJ,IAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA,EAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAA;AAAA,IACA,KAAA;AAAA,IACA,IAAA;AAAA,IACA,YAAA;AAAA,IACA,KAAA;AAAA,IACA;AAAA,GACF,GAAI,KAAA;AAEJ,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,YAAA,EAAc;AACjB,MAAA,MAAM,YAAY,KAAA,IAAS,SAAA;AAC3B,MAAA,OAAO,OAAO,SAAA,KAAc,QAAA,GAAW,SAAA,GAAY,MAAA;AAAA,IACrD;AAAA,IACA,KAAK,WAAA;AACH,MAAA,KAAA,CAAM,SAAA,CAAU,KAAK,EAAE,UAAA,EAAY,UAAU,KAAA,EAAO,KAAA,IAAS,MAAM,CAAA;AAEnE,MAAA,OAAO,MAAA;AAAA,IACT,KAAK,mBAAA;AACH,MAAA,IAAI,OAAO,OAAO,QAAA,EAAU;AAC1B,QAAA,KAAA,CAAM,UAAA,GAAa,EAAA;AAAA,MACrB;AACA,MAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,QAAA,KAAA,CAAM,aAAA,GAAgB,OAAA;AAAA,MACxB;AAEA,MAAA,OAAO,MAAA;AAAA,IACT,KAAK,QAAA;AACH,MAAA,KAAA,CAAM,YAAA,GAAe,YAAA;AACrB,MAAA,KAAA,CAAM,KAAA,GAAQ,KAAA;AACd,MAAA,IAAI,qBAAqB,MAAA,EAAW;AAClC,QAAA,KAAA,CAAM,gBAAA,GAAmB,gBAAA;AAAA,MAC3B;AAEA,MAAA,OAAO,MAAA;AAAA,IACT;AACE,MAAA,OAAO,MAAA;AAAA;AAEb;;;;"}
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":"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"}
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":"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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":["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":";;;;;;;;AAmBA,MAAM,gBAAA,GAAmB,OAAA;AAKzB,MAAM,cAAA,GAAiB,WAAA;AACvB,MAAM,yBAAA,GAA4B,sBAAA;AAClC,MAAM,YAAA,GAAe,SAAA;AACrB,MAAM,YAAA,GAAe,SAAA;AACrB,MAAM,iBAAA,GAAoB,cAAA;AAC1B,MAAM,kBAAA,GAAqB,eAAA;AAC3B,MAAM,kBAAA,GAAqB,eAAA;AA8B3B,MAAM,4BAA4B,MAAM;AACtC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,mBAAmB,cAAA,EAAgB;AACtC,QAAA;AAAA,MACF;AAEA,MAAAA,sBAAA,IAAeC,UAAA,CAAM,GAAA,CAAI,CAAA,4CAAA,EAA+CC,iBAAA,CAAS,WAAW,CAAA,CAAA,CAAG,CAAA;AAE/F,MAAAC,iCAAA,CAA6B,MAAM;AACjC,QAAAC,uCAAA;AAAA,UACE,kBAAA,CAAmB,cAAA,CAAyCF,iBAAA,CAAS,WAAW,CAAA;AAAA,UAChF,CAAA,IAAA,KAAQ;AACN,YAAA,MAAM,GAAA,GAAM,UAAA,CAAW,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,CAAA;AACxC,YAAA,MAAM,EAAE,MAAM,IAAA,EAAM,QAAA,EAAU,MAAK,GAAI,mBAAA,CAAoB,KAAK,IAAI,CAAA;AACpE,YAAA,MAAM,aAAa,OAAO,IAAA,KAAS,WAAW,QAAA,CAAS,IAAA,EAAM,EAAE,CAAA,GAAI,IAAA;AACnE,YAAA,MAAM,eAAe,OAAO,UAAA,KAAe,QAAA,IAAY,CAAC,MAAM,UAAU,CAAA;AAIxE,YAAA,IAAA,CAAK,qBAAqBG,oBAAA,EAAgB;AAE1C,YAAA,OAAOC,sBAAA,CAAkB;AAAA,cACvB,MAAM,GAAA,IAAO,aAAA;AAAA,cACb,MAAMC,cAAA,CAAU,MAAA;AAAA,cAChB,EAAA,EAAI,IAAA;AAAA,cACJ,UAAA,EAAY;AAAA,gBACV,CAAC,cAAc,GAAG,OAAA;AAAA,gBAClB,CAACC,qCAAgC,GAAG,2BAAA;AAAA,gBACpC,CAAC,yBAAyB,GAAG,aAAA,CAAc,MAAM,YAAA,GAAe,UAAA,GAAa,QAAW,QAAQ,CAAA;AAAA,gBAChG,GAAI,WAAW,EAAE,CAAC,YAAY,GAAG,QAAA,KAAa,EAAC;AAAA,gBAC/C,GAAI,OAAO,EAAE,CAAC,YAAY,GAAG,IAAA,KAAS,EAAC;AAAA,gBACvC,GAAI,MAAM,EAAE,CAAC,iBAAiB,GAAG,GAAA,KAAQ,EAAC;AAAA,gBAC1C,GAAI,OAAO,EAAE,CAAC,kBAAkB,GAAG,IAAA,KAAS,EAAC;AAAA,gBAC7C,GAAI,eAAe,EAAE,CAAC,kBAAkB,GAAG,UAAA,KAAe;AAAC;AAC7D,aACD,CAAA;AAAA,UACH,CAAA;AAAA,UACA;AAAA;AAAA;AAAA,YAGE,YAAA,CAAa,EAAE,IAAA,EAAM,GAAA,EAAI,EAAG;AAC1B,cAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,cAAA,IAAI,CAAC,UAAU,OAAO,MAAA,KAAW,YAAY,CAAC,WAAA,CAAY,MAAM,CAAA,EAAG;AACjE,gBAAA,OAAO,KAAA;AAAA,cACT;AAGA,cAAA,MAAM,cAAc,IAAA,CAAK,kBAAA;AACzB,cAAA,IAAI,WAAA,EAAa;AACf,gBAAAC,uBAAA,CAAmB,QAAQ,WAAW,CAAA;AAAA,cACxC;AAEA,cAAA,MAAA,CAAO,EAAA,CAAG,OAAA,EAAS,CAAA,GAAA,KAAO,GAAA,CAAI,GAAG,CAAC,CAAA;AAClC,cAAA,MAAA,CAAO,EAAA,CAAG,KAAA,EAAO,MAAM,GAAA,EAAK,CAAA;AAE5B,cAAA,OAAO,IAAA;AAAA,YACT;AAAA;AACF,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,YAAY,GAAA,EAA2F;AAC9G,EAAA,OAAO,IAAA,IAAQ,GAAA,IAAO,OAAQ,GAAA,CAAyB,EAAA,KAAO,UAAA;AAChE;AAEA,SAAS,WAAW,QAAA,EAAuC;AACzD,EAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,IAAIC,iBAAA,CAAa,QAAQ,CAAA,IAAK,KAAA,IAAS,QAAA,EAAU;AAC/C,IAAA,MAAM,MAAO,QAAA,CAA+B,GAAA;AAC5C,IAAA,OAAO,OAAO,GAAA,KAAQ,QAAA,GAAW,GAAA,GAAM,MAAA;AAAA,EACzC;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,oBAAoB,UAAA,EAK3B;AAGA,EAAA,MAAM,SAAS,UAAA,EAAY,MAAA,EAAQ,gBAAA,IAAoB,UAAA,EAAY,UAAU,EAAC;AAC9E,EAAA,OAAO;AAAA,IACL,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,MAAM,MAAA,CAAO;AAAA,GACf;AACF;AAEA,SAAS,aAAA,CAAc,IAAA,EAA0B,IAAA,EAA0B,QAAA,EAAsC;AAC/G,EAAA,IAAI,CAAA,GAAI,CAAA,aAAA,EAAgB,IAAA,IAAQ,WAAW,CAAA,CAAA;AAC3C,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC5B,IAAA,CAAA,IAAK,IAAI,IAAI,CAAA,CAAA;AAAA,EACf;AACA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,CAAA,IAAK,IAAI,QAAQ,CAAA,CAAA;AAAA,EACnB;AACA,EAAA,OAAO,CAAA;AACT;AAUO,MAAM,uBAAA,GAA0BC,uBAAkB,wBAAwB;;;;"}

@@ -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":";;;;;;AAgBO,MAAM,wBAAA,GAA2B;AACjC,MAAM,sBAAA,GAAyB;AAC/B,MAAM,wBAAA,GAA2B;AACjC,MAAM,0BAAA,GAA6B;AACnC,MAAM,0BAAA,GAA6B;AAE1C,MAAM,MAAA,GAAS,kCAAA;AACf,MAAM,0BAAA,GAA6B,OAAA;AA6F5B,SAAS,gCAAA,CACd,gBACA,YAAA,EACM;AAGN,EAAA,mBAAA;AAAA,IACE,cAAA;AAAA,IACA,wBAAA;AAAA,IACA,CAAA,IAAA,KAAQ,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA;AAAA,IACzB;AAAA,GACF;AACA,EAAA,iBAAA;AAAA,IAAkB,cAAA;AAAA,IAAgB,sBAAA;AAAA,IAAwB,CAAA,IAAA,KACxD,IAAA,CAAK,SAAA,KAAc,UAAA,GAAa,UAAA,GAAa;AAAA,GAC/C;AACA,EAAA,mBAAA,CAAoB,gBAAgB,wBAAwB,CAAA;AAI5D,EAAA,mBAAA,CAAwC,cAAA,EAAgB,0BAAA,EAA4B,CAAA,IAAA,KAAQ,IAAA,CAAK,MAAM,YAAY,CAAA;AACnH,EAAA,mBAAA,CAAoB,gBAAgB,0BAA0B,CAAA;AAChE;AAEA,SAAS,mBAAA,CACPA,gBAAA,EACA,WAAA,EACA,cAAA,EACA,YAAA,EACM;AACN,EAAAC,uCAAA;AAAA,IACED,iBAAkB,WAAW,CAAA;AAAA,IAC7B,CAAA,IAAA,KAAQ;AAIN,MAAA,MAAM,IAAA,GAAO,eAAe,IAAI,CAAA;AAChC,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,CAAA,EAAI,IAAA,CAAK,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,GAAK,IAAA,CAAK,OAAA;AAC3E,MAAA,OAAOE,sBAAA,CAAkB;AAAA,QACvB,IAAA,EAAM,CAAA,MAAA,EAAS,IAAA,CAAK,OAAO,CAAA,CAAA;AAAA,QAC3B,UAAA,EAAY;AAAA,UACV,CAACC,qCAAgC,GAAG,MAAA;AAAA,UACpC,CAACC,iCAA4B,GAAG,UAAA;AAAA,UAChC,CAACC,yBAAc,GAAG,0BAAA;AAAA,UAClB,CAACC,wBAAa,GAAG,SAAA;AAAA,UACjB,GAAI,IAAA,CAAK,aAAA,IAAiB,IAAA,GAAO,EAAE,CAACC,yBAAc,GAAG,IAAA,CAAK,aAAA,EAAc,GAAI,EAAC;AAAA,UAC7E,GAAI,IAAA,CAAK,UAAA,IAAc,IAAA,GAAO,EAAE,CAACC,sBAAW,GAAG,IAAA,CAAK,UAAA,EAAW,GAAI;AAAC;AACtE,OACD,CAAA;AAAA,IACH,CAAA;AAAA,IACA;AAAA;AAAA;AAAA,MAGE,YAAA,EAAc,KAAA;AAAA,MACd,aAAA,CAAc,MAAM,IAAA,EAAM;AACxB,QAAA,IAAI,WAAW,IAAA,EAAM;AACrB,QAAA,eAAA,CAAgB,YAAA,EAAc,MAAM,IAAA,CAAK,OAAA,EAAS,eAAe,IAAI,CAAA,EAAG,KAAK,MAAM,CAAA;AAAA,MACrF;AAAA;AACF,GACF;AACF;AAEA,SAAS,iBAAA,CACPR,gBAAA,EACA,WAAA,EACA,gBAAA,EACM;AACN,EAAAC,uCAAA;AAAA,IACED,iBAA+B,WAAW,CAAA;AAAA,IAC1C,CAAA,IAAA,KAAQ;AACN,MAAA,OAAOE,sBAAA,CAAkB;AAAA,QACvB,IAAA,EAAM,iBAAiB,IAAI,CAAA;AAAA,QAC3B,UAAA,EAAY;AAAA,UACV,CAACC,qCAAgC,GAAG,MAAA;AAAA,UACpC,CAACC,iCAA4B,GAAG,UAAA;AAAA,UAChC,CAACC,yBAAc,GAAG,0BAAA;AAAA;AAAA;AAAA,UAGlB,GAAI,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA,GAAI,CAAA,GAAI,EAAE,CAACI,kCAAuB,GAAG,IAAA,CAAK,SAAA,KAAc,EAAC;AAAA,UAClF,GAAI,IAAA,CAAK,aAAA,IAAiB,IAAA,GAAO,EAAE,CAACF,yBAAc,GAAG,IAAA,CAAK,aAAA,EAAc,GAAI,EAAC;AAAA,UAC7E,GAAI,IAAA,CAAK,UAAA,IAAc,IAAA,GAAO,EAAE,CAACC,sBAAW,GAAG,IAAA,CAAK,UAAA,EAAW,GAAI;AAAC;AACtE,OACD,CAAA;AAAA,IACH,CAAA;AAAA,IACA,EAAE,cAAc,KAAA;AAAM,GACxB;AACF;AAEA,SAAS,mBAAA,CAAoBR,kBAA4C,WAAA,EAA2B;AAClG,EAAAC,uCAAA;AAAA,IACED,iBAAiC,WAAW,CAAA;AAAA,IAC5C,CAAA,IAAA,KAAQ;AACN,MAAA,OAAOE,sBAAA,CAAkB;AAAA,QACvB,IAAA,EAAM,eAAA;AAAA,QACN,UAAA,EAAY;AAAA,UACV,CAACC,qCAAgC,GAAG,MAAA;AAAA,UACpC,CAACC,iCAA4B,GAAG,kBAAA;AAAA,UAChC,CAACC,yBAAc,GAAG,0BAAA;AAAA,UAClB,GAAI,IAAA,CAAK,aAAA,IAAiB,IAAA,GAAO,EAAE,CAACE,yBAAc,GAAG,IAAA,CAAK,aAAA,EAAc,GAAI,EAAC;AAAA,UAC7E,GAAI,IAAA,CAAK,UAAA,IAAc,IAAA,GAAO,EAAE,CAACC,sBAAW,GAAG,IAAA,CAAK,UAAA,EAAW,GAAI;AAAC;AACtE,OACD,CAAA;AAAA,IACH,CAAA;AAAA,IACA,EAAE,cAAc,KAAA;AAAM,GACxB;AACF;AAEA,SAAS,eAAA,CACP,IAAA,EACA,IAAA,EACA,OAAA,EACA,MACA,MAAA,EACM;AACN,EAAA,IAAI,CAAC,IAAA,EAAM;AACX,EAAA,IAAI;AACF,IAAA,IAAA,CAAK,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,MAAM,CAAA;AAAA,EAClC,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;;;;;;;;;"}

@@ -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":";;;;;;;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,CACdA,gBAAA,EACA,OAAA,GAAkC,EAAC,EAC7B;AACN,EAAA,IAAI,UAAA,EAAY;AACd,IAAA;AAAA,EACF;AACA,EAAA,UAAA,GAAa,IAAA;AAEb,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;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,CAACE,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;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,CAACC,kCAAuB,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,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,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,CAAeG,gCAA6B,UAAA,EAAY;AAAA,IAC7D,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;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,CAACC,yCAA8B,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,CAAeC,gCAA6B,QAAA,EAAU;AAAA,IAC3D,CAACX,qCAAgC,GAAG,MAAA;AAAA,IACpC,CAACY,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,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,aAAaC,6BAAA,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,CAAaC,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,aAAaC,yCAAA,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,CAAaC,+BAAoB,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,CAAaC,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,IACpCC,eAAA,CAAW,IAAI,CAAA,CAAE,IAAA,CAAKD,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,CAAaE,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,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,EAAMC,sBAAA;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,EAAAC,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,MAAM3B,eAA8C,EAAC;AAKrD,EAAA,MAAM,YAAA,GAAe,QAAA,CAAS,KAAA,CAAM,YAAY,CAAA;AAChD,EAAA,IAAI,YAAA,EAAc;AAChB,IAAAA,YAAA,CAAW4B,yCAAoC,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,IAAA5B,YAAA,CAAW6B,gCAAqB,CAAA,GAAI,gBAAA,GAAmBC,4BAAuB,QAAQ,CAAA,GAAI,cAAc,QAAQ,CAAA;AAEhH,IAAA9B,YAAA,CAAW+B,oDAA+C,CAAA,GAAI,KAAA,CAAM,QAAQ,QAAQ,CAAA,GAAI,SAAS,MAAA,GAAS,CAAA;AAAA,EAC5G;AAEA,EAAA,OAAO/B,YAAA;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":["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":"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"}

@@ -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":"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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":["instrumentFastify"],"mappings":";;;;;AAoCA,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,YAAA,CAAa,GAAG,CAAA,IAAK,CAAC,CAAC,GAAA,CAAI,MAAA,IAAU,CAAC,CAAC,IAAI,GAAA,KAAQ,CAAC,CAAC,GAAA,CAAI,gBAAgB,YAAA,IAAgB,GAAA,CAAA;AAClG;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;;;;"}
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":";;;;;;;;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,CAAC,SAAS,GAAG,OAAA;AAAA,IACb,CAAC,yBAAyB,GAAG,CAAA,QAAA,EAAW,IAAI,IAAI,IAAI,CAAA,CAAA;AAAA,IACpD,CAAC,aAAa,GAAG,IAAA;AAAA,IACjB,CAAC,aAAa,GAAG,IAAA;AAAA,IACjB,CAAC,gCAAgC,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,MAAA,WAAA,IACE,KAAA,CAAM,IAAI,CAAA,sCAAA,EAAyC,QAAA,CAAS,eAAe,CAAA,GAAA,EAAM,QAAA,CAAS,eAAe,CAAA,CAAA,CAAG,CAAA;AAE9G,MAAA,MAAM,iBAAiB,kBAAA,CAAmB,cAAA;AAAA,QACxC,QAAA,CAAS;AAAA,OACX;AACA,MAAA,MAAM,iBAAiB,kBAAA,CAAmB,cAAA;AAAA,QACxC,QAAA,CAAS;AAAA,OACX;AAKA,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,wBAAA;AAAA,UACE,cAAA;AAAA,UACA,CAAA,IAAA,KAAQ;AAEN,YAAA,IAAI,CAAC,eAAc,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,YAAY,4BAAA,CAA6B,OAAA,CAAQ,MAAM,OAAA,CAAQ,IAAA,IAAQ,EAAE,CAAA;AAC/E,YAAA,OAAO,iBAAA,CAAkB;AAAA,cACvB,IAAA,EAAM,SAAA;AAAA,cACN,EAAA,EAAI,IAAA;AAAA,cACJ,UAAA,EAAY,EAAE,GAAG,oBAAA,CAAqB,IAAA,EAAM,IAAI,CAAA,EAAG,CAAC,YAAY,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,QAAA,wBAAA,CAAyB,gBAAgB,CAAA,IAAA,KAAQ;AAC/C,UAAA,IAAI,CAAC,eAAc,EAAG;AACpB,YAAA,OAAO,MAAA;AAAA,UACT;AACA,UAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,oBAAA,CAAqB,KAAK,IAAI,CAAA;AACrD,UAAA,OAAO,iBAAA,CAAkB;AAAA,YACvB,IAAA,EAAM,SAAA;AAAA,YACN,EAAA,EAAI,IAAA;AAAA,YACJ,UAAA,EAAY,EAAE,GAAG,oBAAA,CAAqB,IAAA,EAAM,IAAI,CAAA,EAAG,CAAC,YAAY,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,GAA4B,kBAAkB,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":[],"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":";;;;;;AAmBA,MAAM,gBAAA,GAAmB,OAAA;AAKzB,MAAM,cAAA,GAAiB,WAAA;AACvB,MAAM,yBAAA,GAA4B,sBAAA;AAClC,MAAM,YAAA,GAAe,SAAA;AACrB,MAAM,YAAA,GAAe,SAAA;AACrB,MAAM,iBAAA,GAAoB,cAAA;AAC1B,MAAM,kBAAA,GAAqB,eAAA;AAC3B,MAAM,kBAAA,GAAqB,eAAA;AA8B3B,MAAM,4BAA4B,MAAM;AACtC,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,4CAAA,EAA+C,QAAA,CAAS,WAAW,CAAA,CAAA,CAAG,CAAA;AAE/F,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,wBAAA;AAAA,UACE,kBAAA,CAAmB,cAAA,CAAyC,QAAA,CAAS,WAAW,CAAA;AAAA,UAChF,CAAA,IAAA,KAAQ;AACN,YAAA,MAAM,GAAA,GAAM,UAAA,CAAW,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,CAAA;AACxC,YAAA,MAAM,EAAE,MAAM,IAAA,EAAM,QAAA,EAAU,MAAK,GAAI,mBAAA,CAAoB,KAAK,IAAI,CAAA;AACpE,YAAA,MAAM,aAAa,OAAO,IAAA,KAAS,WAAW,QAAA,CAAS,IAAA,EAAM,EAAE,CAAA,GAAI,IAAA;AACnE,YAAA,MAAM,eAAe,OAAO,UAAA,KAAe,QAAA,IAAY,CAAC,MAAM,UAAU,CAAA;AAIxE,YAAA,IAAA,CAAK,qBAAqB,eAAA,EAAgB;AAE1C,YAAA,OAAO,iBAAA,CAAkB;AAAA,cACvB,MAAM,GAAA,IAAO,aAAA;AAAA,cACb,MAAM,SAAA,CAAU,MAAA;AAAA,cAChB,EAAA,EAAI,IAAA;AAAA,cACJ,UAAA,EAAY;AAAA,gBACV,CAAC,cAAc,GAAG,OAAA;AAAA,gBAClB,CAAC,gCAAgC,GAAG,2BAAA;AAAA,gBACpC,CAAC,yBAAyB,GAAG,aAAA,CAAc,MAAM,YAAA,GAAe,UAAA,GAAa,QAAW,QAAQ,CAAA;AAAA,gBAChG,GAAI,WAAW,EAAE,CAAC,YAAY,GAAG,QAAA,KAAa,EAAC;AAAA,gBAC/C,GAAI,OAAO,EAAE,CAAC,YAAY,GAAG,IAAA,KAAS,EAAC;AAAA,gBACvC,GAAI,MAAM,EAAE,CAAC,iBAAiB,GAAG,GAAA,KAAQ,EAAC;AAAA,gBAC1C,GAAI,OAAO,EAAE,CAAC,kBAAkB,GAAG,IAAA,KAAS,EAAC;AAAA,gBAC7C,GAAI,eAAe,EAAE,CAAC,kBAAkB,GAAG,UAAA,KAAe;AAAC;AAC7D,aACD,CAAA;AAAA,UACH,CAAA;AAAA,UACA;AAAA;AAAA;AAAA,YAGE,YAAA,CAAa,EAAE,IAAA,EAAM,GAAA,EAAI,EAAG;AAC1B,cAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,cAAA,IAAI,CAAC,UAAU,OAAO,MAAA,KAAW,YAAY,CAAC,WAAA,CAAY,MAAM,CAAA,EAAG;AACjE,gBAAA,OAAO,KAAA;AAAA,cACT;AAGA,cAAA,MAAM,cAAc,IAAA,CAAK,kBAAA;AACzB,cAAA,IAAI,WAAA,EAAa;AACf,gBAAA,kBAAA,CAAmB,QAAQ,WAAW,CAAA;AAAA,cACxC;AAEA,cAAA,MAAA,CAAO,EAAA,CAAG,OAAA,EAAS,CAAA,GAAA,KAAO,GAAA,CAAI,GAAG,CAAC,CAAA;AAClC,cAAA,MAAA,CAAO,EAAA,CAAG,KAAA,EAAO,MAAM,GAAA,EAAK,CAAA;AAE5B,cAAA,OAAO,IAAA;AAAA,YACT;AAAA;AACF,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,YAAY,GAAA,EAA2F;AAC9G,EAAA,OAAO,IAAA,IAAQ,GAAA,IAAO,OAAQ,GAAA,CAAyB,EAAA,KAAO,UAAA;AAChE;AAEA,SAAS,WAAW,QAAA,EAAuC;AACzD,EAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,IAAI,YAAA,CAAa,QAAQ,CAAA,IAAK,KAAA,IAAS,QAAA,EAAU;AAC/C,IAAA,MAAM,MAAO,QAAA,CAA+B,GAAA;AAC5C,IAAA,OAAO,OAAO,GAAA,KAAQ,QAAA,GAAW,GAAA,GAAM,MAAA;AAAA,EACzC;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,oBAAoB,UAAA,EAK3B;AAGA,EAAA,MAAM,SAAS,UAAA,EAAY,MAAA,EAAQ,gBAAA,IAAoB,UAAA,EAAY,UAAU,EAAC;AAC9E,EAAA,OAAO;AAAA,IACL,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,MAAM,MAAA,CAAO;AAAA,GACf;AACF;AAEA,SAAS,aAAA,CAAc,IAAA,EAA0B,IAAA,EAA0B,QAAA,EAAsC;AAC/G,EAAA,IAAI,CAAA,GAAI,CAAA,aAAA,EAAgB,IAAA,IAAQ,WAAW,CAAA,CAAA;AAC3C,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC5B,IAAA,CAAA,IAAK,IAAI,IAAI,CAAA,CAAA;AAAA,EACf;AACA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,CAAA,IAAK,IAAI,QAAQ,CAAA,CAAA;AAAA,EACnB;AACA,EAAA,OAAO,CAAA;AACT;AAUO,MAAM,uBAAA,GAA0B,kBAAkB,wBAAwB;;;;"}
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":";;;;;;AAsBA,MAAM,gBAAA,GAAmB,QAAA;AAIzB,MAAM,MAAA,GAAS,4BAAA;AAGf,MAAM,qBAAA,GAAwB;AAAA,EAC5B,EAAE,OAAA,EAAS,QAAA,CAAS,WAAA,EAAa,WAAW,MAAA,EAAO;AAAA,EACnD,EAAE,OAAA,EAAS,QAAA,CAAS,gBAAA,EAAkB,WAAW,MAAA,EAAO;AAAA,EACxD,EAAE,OAAA,EAAS,QAAA,CAAS,iBAAA,EAAmB,WAAW,YAAA,EAAa;AAAA,EAC/D,EAAE,OAAA,EAAS,QAAA,CAAS,oBAAA,EAAsB,WAAW,MAAA;AACvD,CAAA;AAYA,IAAI,UAAA,GAAa,KAAA;AAEjB,MAAM,yBAAA,IAA6B,CAAC,OAAA,GAAyB,EAAC,KAAM;AAClE,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,kBAAA,CAAmB,cAAA,IAAkB,UAAA,EAAY;AACpD,QAAA;AAAA,MACF;AACA,MAAA,UAAA,GAAa,IAAA;AAIb,MAAA,4BAAA,CAA6B,MAAM;AACjC,QAAA,KAAA,MAAW,EAAE,OAAA,EAAS,SAAA,EAAU,IAAK,qBAAA,EAAuB;AAC1D,UAAA,WAAA,IAAe,KAAA,CAAM,GAAA,CAAI,CAAA,6CAAA,EAAgD,OAAO,CAAA,CAAA,CAAG,CAAA;AACnF,UAAA,wBAAA;AAAA,YACE,kBAAA,CAAmB,eAAyC,OAAO,CAAA;AAAA,YACnE,CAAA,IAAA,KAAQ,eAAA,CAAgB,IAAA,EAAM,SAAA,EAAW,OAAO,CAAA;AAAA,YAChD;AAAA,cACE,aAAA,EAAe,CAAC,IAAA,EAAM,IAAA,KAAS;AAC7B,gBAAA,2BAAA,CAA4B,MAAM,IAAA,CAAK,MAAA,EAAQ,yBAAA,CAA0B,OAAO,EAAE,aAAa,CAAA;AAAA,cACjG,CAAA;AAAA;AAAA,cAEA,YAAA,EAAc,CAAC,EAAE,IAAA,EAAM,MAAK,KAAM,gBAAA,CAAiB,IAAA,EAAM,IAAA,EAAM,OAAO;AAAA;AACxE,WACF;AAAA,QACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAA,CAAA;AAMA,SAAS,eAAA,CAAgB,IAAA,EAAgC,SAAA,EAAmB,OAAA,EAA0C;AAGpH,EAAA,IAAI,sCAAA,CAAuC,gBAAgB,CAAA,EAAG;AAC5D,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,IAAa,EAAC;AAChC,EAAA,MAAM,MAAA,GAAS,KAAK,CAAC,CAAA;AAErB,EAAA,MAAM,EAAE,YAAA,EAAa,GAAI,yBAAA,CAA0B,OAAO,CAAA;AAC1D,EAAA,MAAM,gBAAA,GAAmB,sBAAA,CAAuB,OAAA,CAAQ,gBAAgB,CAAA;AAExE,EAAA,MAAM,UAAA,GAAa,8BAAA,CAA+B,IAAA,EAAM,SAAS,CAAA;AACjE,EAAA,UAAA,CAAW,gCAAgC,CAAA,GAAI,MAAA;AAC/C,EAAA,MAAM,KAAA,GAAS,QAAQ,KAAA,IAAoB,SAAA;AAE3C,EAAA,MAAM,OAAO,iBAAA,CAAkB;AAAA,IAC7B,IAAA,EAAM,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA;AAAA,IAC3B,EAAA,EAAI,UAAU,SAAS,CAAA,CAAA;AAAA,IACvB;AAAA,GACD,CAAA;AAED,EAAA,IAAI,gBAAgB,MAAA,EAAQ;AAC1B,IAAA,0BAAA,CAA2B,IAAA,EAAM,MAAA,EAAQ,SAAA,EAAW,gBAAgB,CAAA;AAAA,EACtE;AAEA,EAAA,OAAO,IAAA;AACT;AAIA,SAAS,gBAAgB,KAAA,EAA8C;AACrE,EAAA,OAAO,CAAC,CAAC,KAAA,IAAS,OAAQ,KAAA,CAA8B,MAAA,CAAO,aAAa,CAAA,KAAM,UAAA;AACpF;AAWA,SAAS,gBAAA,CAAiB,IAAA,EAAY,IAAA,EAAgC,OAAA,EAAiC;AACrG,EAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,EAAA,IAAI,CAAC,eAAA,CAAgB,MAAM,CAAA,EAAG;AAC5B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,yBAAA,CAA0B,OAAO,CAAA;AAC3D,EAAA,MAAM,UAAU,MAAA,CAAO,MAAA,CAAO,aAAa,CAAA,CAAE,KAAK,MAAM,CAAA;AACxD,EAAA,MAAM,YAAA,GAAe,sBAAA,CAAuB,EAAE,CAAC,MAAA,CAAO,aAAa,GAAG,OAAA,EAAQ,EAAG,IAAA,EAAM,aAAA,IAAiB,KAAK,CAAA;AAC7G,EAAA,MAAA,CAAO,MAAA,CAAO,aAAa,CAAA,GAAI,MAAM,YAAA;AAErC,EAAA,OAAO,IAAA;AACT;AAOO,MAAM,wBAAA,GAA2B,kBAAkB,yBAAyB;;;;"}
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":"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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":[],"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":"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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":[],"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"}
{
"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"}