@sentry/node 10.21.0 → 10.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/build/cjs/index.js +10 -7
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/cjs/integrations/node-fetch.js +2 -0
  4. package/build/cjs/integrations/node-fetch.js.map +1 -1
  5. package/build/cjs/integrations/tracing/firebase/firebase.js +18 -0
  6. package/build/cjs/integrations/tracing/firebase/firebase.js.map +1 -1
  7. package/build/cjs/integrations/tracing/firebase/otel/firebaseInstrumentation.js +3 -0
  8. package/build/cjs/integrations/tracing/firebase/otel/firebaseInstrumentation.js.map +1 -1
  9. package/build/cjs/integrations/tracing/firebase/otel/patches/functions.js +265 -0
  10. package/build/cjs/integrations/tracing/firebase/otel/patches/functions.js.map +1 -0
  11. package/build/cjs/integrations/tracing/index.js +3 -0
  12. package/build/cjs/integrations/tracing/index.js.map +1 -1
  13. package/build/cjs/integrations/tracing/langchain/index.js +112 -0
  14. package/build/cjs/integrations/tracing/langchain/index.js.map +1 -0
  15. package/build/cjs/integrations/tracing/langchain/instrumentation.js +201 -0
  16. package/build/cjs/integrations/tracing/langchain/instrumentation.js.map +1 -0
  17. package/build/esm/index.js +2 -1
  18. package/build/esm/index.js.map +1 -1
  19. package/build/esm/integrations/node-fetch.js +2 -0
  20. package/build/esm/integrations/node-fetch.js.map +1 -1
  21. package/build/esm/integrations/tracing/firebase/firebase.js +19 -1
  22. package/build/esm/integrations/tracing/firebase/firebase.js.map +1 -1
  23. package/build/esm/integrations/tracing/firebase/otel/firebaseInstrumentation.js +3 -0
  24. package/build/esm/integrations/tracing/firebase/otel/firebaseInstrumentation.js.map +1 -1
  25. package/build/esm/integrations/tracing/firebase/otel/patches/functions.js +262 -0
  26. package/build/esm/integrations/tracing/firebase/otel/patches/functions.js.map +1 -0
  27. package/build/esm/integrations/tracing/index.js +3 -0
  28. package/build/esm/integrations/tracing/index.js.map +1 -1
  29. package/build/esm/integrations/tracing/langchain/index.js +109 -0
  30. package/build/esm/integrations/tracing/langchain/index.js.map +1 -0
  31. package/build/esm/integrations/tracing/langchain/instrumentation.js +199 -0
  32. package/build/esm/integrations/tracing/langchain/instrumentation.js.map +1 -0
  33. package/build/esm/package.json +1 -1
  34. package/build/types/index.d.ts +2 -1
  35. package/build/types/index.d.ts.map +1 -1
  36. package/build/types/integrations/node-fetch.d.ts +2 -1
  37. package/build/types/integrations/node-fetch.d.ts.map +1 -1
  38. package/build/types/integrations/tracing/firebase/firebase.d.ts.map +1 -1
  39. package/build/types/integrations/tracing/firebase/otel/firebaseInstrumentation.d.ts.map +1 -1
  40. package/build/types/integrations/tracing/firebase/otel/patches/functions.d.ts +23 -0
  41. package/build/types/integrations/tracing/firebase/otel/patches/functions.d.ts.map +1 -0
  42. package/build/types/integrations/tracing/firebase/otel/types.d.ts +34 -0
  43. package/build/types/integrations/tracing/firebase/otel/types.d.ts.map +1 -1
  44. package/build/types/integrations/tracing/index.d.ts.map +1 -1
  45. package/build/types/integrations/tracing/langchain/index.d.ts +93 -0
  46. package/build/types/integrations/tracing/langchain/index.d.ts.map +1 -0
  47. package/build/types/integrations/tracing/langchain/instrumentation.d.ts +29 -0
  48. package/build/types/integrations/tracing/langchain/instrumentation.d.ts.map +1 -0
  49. package/build/types-ts3.8/index.d.ts +2 -1
  50. package/build/types-ts3.8/integrations/node-fetch.d.ts +2 -1
  51. package/build/types-ts3.8/integrations/tracing/firebase/otel/patches/functions.d.ts +23 -0
  52. package/build/types-ts3.8/integrations/tracing/firebase/otel/types.d.ts +34 -0
  53. package/build/types-ts3.8/integrations/tracing/langchain/index.d.ts +93 -0
  54. package/build/types-ts3.8/integrations/tracing/langchain/instrumentation.d.ts +29 -0
  55. package/package.json +4 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing/langchain/index.ts"],"sourcesContent":["import type { IntegrationFn, LangChainOptions } from '@sentry/core';\nimport { defineIntegration, LANGCHAIN_INTEGRATION_NAME } from '@sentry/core';\nimport { generateInstrumentOnce } from '@sentry/node-core';\nimport { SentryLangChainInstrumentation } from './instrumentation';\n\nexport const instrumentLangChain = generateInstrumentOnce<LangChainOptions>(\n LANGCHAIN_INTEGRATION_NAME,\n options => new SentryLangChainInstrumentation(options),\n);\n\nconst _langChainIntegration = ((options: LangChainOptions = {}) => {\n return {\n name: LANGCHAIN_INTEGRATION_NAME,\n setupOnce() {\n instrumentLangChain(options);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for LangChain.\n *\n * This integration is enabled by default.\n *\n * When configured, this integration automatically instruments LangChain runnable instances\n * to capture telemetry data by injecting Sentry callback handlers into all LangChain calls.\n *\n * @example\n * ```javascript\n * import * as Sentry from '@sentry/node';\n * import { ChatOpenAI } from '@langchain/openai';\n *\n * Sentry.init({\n * integrations: [Sentry.langChainIntegration()],\n * sendDefaultPii: true, // Enable to record inputs/outputs\n * });\n *\n * // LangChain calls are automatically instrumented\n * const model = new ChatOpenAI();\n * await model.invoke(\"What is the capital of France?\");\n * ```\n *\n * ## Manual Callback Handler\n *\n * You can also manually add the Sentry callback handler alongside other callbacks:\n *\n * @example\n * ```javascript\n * import * as Sentry from '@sentry/node';\n * import { ChatOpenAI } from '@langchain/openai';\n *\n * const sentryHandler = Sentry.createLangChainCallbackHandler({\n * recordInputs: true,\n * recordOutputs: true\n * });\n *\n * const model = new ChatOpenAI();\n * await model.invoke(\n * \"What is the capital of France?\",\n * { callbacks: [sentryHandler, myOtherCallback] }\n * );\n * ```\n *\n * ## Options\n *\n * - `recordInputs`: Whether to record input messages/prompts (default: respects `sendDefaultPii` client option)\n * - `recordOutputs`: Whether to record response text (default: respects `sendDefaultPii` client option)\n *\n * ### Default Behavior\n *\n * By default, the integration will:\n * - Record inputs and outputs ONLY if `sendDefaultPii` is set to `true` in your Sentry client options\n * - Otherwise, inputs and outputs are NOT recorded unless explicitly enabled\n *\n * @example\n * ```javascript\n * // Record inputs and outputs when sendDefaultPii is false\n * Sentry.init({\n * integrations: [\n * Sentry.langChainIntegration({\n * recordInputs: true,\n * recordOutputs: true\n * })\n * ],\n * });\n *\n * // Never record inputs/outputs regardless of sendDefaultPii\n * Sentry.init({\n * sendDefaultPii: true,\n * integrations: [\n * Sentry.langChainIntegration({\n * recordInputs: false,\n * recordOutputs: false\n * })\n * ],\n * });\n * ```\n *\n * ## Supported Events\n *\n * The integration captures the following LangChain lifecycle events:\n * - LLM/Chat Model: start, end, error\n * - Chain: start, end, error\n * - Tool: start, end, error\n *\n */\nexport const langChainIntegration = defineIntegration(_langChainIntegration);\n"],"names":["generateInstrumentOnce","LANGCHAIN_INTEGRATION_NAME","SentryLangChainInstrumentation","defineIntegration"],"mappings":";;;;;;AAKO,MAAM,mBAAA,GAAsBA,+BAAsB;AACzD,EAAEC,+BAA0B;AAC5B,EAAE,WAAW,IAAIC,8CAA8B,CAAC,OAAO,CAAC;AACxD;;AAEA,MAAM,qBAAA,IAAyB,CAAC,OAAO,GAAqB,EAAE,KAAK;AACnE,EAAE,OAAO;AACT,IAAI,IAAI,EAAED,+BAA0B;AACpC,IAAI,SAAS,GAAG;AAChB,MAAM,mBAAmB,CAAC,OAAO,CAAC;AAClC,KAAK;AACL,GAAG;AACH,CAAC,CAAA;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;MACa,oBAAA,GAAuBE,sBAAiB,CAAC,qBAAqB;;;;;"}
@@ -0,0 +1,201 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const instrumentation = require('@opentelemetry/instrumentation');
4
+ const core = require('@sentry/core');
5
+
6
+ const supportedVersions = ['>=0.1.0 <1.0.0'];
7
+
8
+ /**
9
+ * Augments a callback handler list with Sentry's handler if not already present
10
+ */
11
+ function augmentCallbackHandlers(handlers, sentryHandler) {
12
+ // Handle null/undefined - return array with just our handler
13
+ if (!handlers) {
14
+ return [sentryHandler];
15
+ }
16
+
17
+ // If handlers is already an array
18
+ if (Array.isArray(handlers)) {
19
+ // Check if our handler is already in the list
20
+ if (handlers.includes(sentryHandler)) {
21
+ return handlers;
22
+ }
23
+ // Add our handler to the list
24
+ return [...handlers, sentryHandler];
25
+ }
26
+
27
+ // If it's a single handler object, convert to array
28
+ if (typeof handlers === 'object') {
29
+ return [handlers, sentryHandler];
30
+ }
31
+
32
+ // Unknown type - return original
33
+ return handlers;
34
+ }
35
+
36
+ /**
37
+ * Wraps Runnable methods (invoke, stream, batch) to inject Sentry callbacks at request time
38
+ * Uses a Proxy to intercept method calls and augment the options.callbacks
39
+ */
40
+ function wrapRunnableMethod(
41
+ originalMethod,
42
+ sentryHandler,
43
+ _methodName,
44
+ ) {
45
+ return new Proxy(originalMethod, {
46
+ apply(target, thisArg, args) {
47
+ // LangChain Runnable method signatures:
48
+ // invoke(input, options?) - options contains callbacks
49
+ // stream(input, options?) - options contains callbacks
50
+ // batch(inputs, options?) - options contains callbacks
51
+
52
+ // Options is typically the second argument
53
+ const optionsIndex = 1;
54
+ let options = args[optionsIndex] ;
55
+
56
+ // If options don't exist or aren't an object, create them
57
+ if (!options || typeof options !== 'object' || Array.isArray(options)) {
58
+ options = {};
59
+ args[optionsIndex] = options;
60
+ }
61
+
62
+ // Inject our callback handler into options.callbacks (request time callbacks)
63
+ const existingCallbacks = options.callbacks;
64
+ const augmentedCallbacks = augmentCallbackHandlers(existingCallbacks, sentryHandler);
65
+ options.callbacks = augmentedCallbacks;
66
+
67
+ // Call original method with augmented options
68
+ return Reflect.apply(target, thisArg, args);
69
+ },
70
+ }) ;
71
+ }
72
+
73
+ /**
74
+ * Sentry LangChain instrumentation using OpenTelemetry.
75
+ */
76
+ class SentryLangChainInstrumentation extends instrumentation.InstrumentationBase {
77
+ constructor(config = {}) {
78
+ super('@sentry/instrumentation-langchain', core.SDK_VERSION, config);
79
+ }
80
+
81
+ /**
82
+ * Initializes the instrumentation by defining the modules to be patched.
83
+ * We patch the BaseChatModel class methods to inject callbacks
84
+ *
85
+ * We hook into provider packages (@langchain/anthropic, @langchain/openai, etc.)
86
+ * because @langchain/core is often bundled and not loaded as a separate module
87
+ */
88
+ init() {
89
+ const modules = [];
90
+
91
+ // Hook into common LangChain provider packages
92
+ const providerPackages = [
93
+ '@langchain/anthropic',
94
+ '@langchain/openai',
95
+ '@langchain/google-genai',
96
+ '@langchain/mistralai',
97
+ '@langchain/google-vertexai',
98
+ '@langchain/groq',
99
+ ];
100
+
101
+ for (const packageName of providerPackages) {
102
+ // In CJS, LangChain packages re-export from dist/index.cjs files.
103
+ // Patching only the root module sometimes misses the real implementation or
104
+ // gets overwritten when that file is loaded. We add a file-level patch so that
105
+ // _patch runs again on the concrete implementation
106
+ modules.push(
107
+ new instrumentation.InstrumentationNodeModuleDefinition(
108
+ packageName,
109
+ supportedVersions,
110
+ this._patch.bind(this),
111
+ exports => exports,
112
+ [
113
+ new instrumentation.InstrumentationNodeModuleFile(
114
+ `${packageName}/dist/index.cjs`,
115
+ supportedVersions,
116
+ this._patch.bind(this),
117
+ exports => exports,
118
+ ),
119
+ ],
120
+ ),
121
+ );
122
+ }
123
+
124
+ return modules;
125
+ }
126
+
127
+ /**
128
+ * Core patch logic - patches chat model methods to inject Sentry callbacks
129
+ * This is called when a LangChain provider package is loaded
130
+ */
131
+ _patch(exports) {
132
+ const client = core.getClient();
133
+ const defaultPii = Boolean(client?.getOptions().sendDefaultPii);
134
+
135
+ const config = this.getConfig();
136
+
137
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
138
+ const recordInputs = config?.recordInputs ?? defaultPii;
139
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
140
+ const recordOutputs = config?.recordOutputs ?? defaultPii;
141
+
142
+ // Create a shared handler instance
143
+ const sentryHandler = core.createLangChainCallbackHandler({
144
+ recordInputs,
145
+ recordOutputs,
146
+ });
147
+
148
+ // Patch Runnable methods to inject callbacks at request time
149
+ // This directly manipulates options.callbacks that LangChain uses
150
+ this._patchRunnableMethods(exports, sentryHandler);
151
+
152
+ return exports;
153
+ }
154
+
155
+ /**
156
+ * Patches chat model methods (invoke, stream, batch) to inject Sentry callbacks
157
+ * Finds a chat model class from the provider package exports and patches its prototype methods
158
+ */
159
+ _patchRunnableMethods(exports, sentryHandler) {
160
+ // Known chat model class names for each provider
161
+ const knownChatModelNames = [
162
+ 'ChatAnthropic',
163
+ 'ChatOpenAI',
164
+ 'ChatGoogleGenerativeAI',
165
+ 'ChatMistralAI',
166
+ 'ChatVertexAI',
167
+ 'ChatGroq',
168
+ ];
169
+
170
+ // Find a chat model class in the exports by checking known class names
171
+ const chatModelClass = Object.values(exports).find(exp => {
172
+ if (typeof exp !== 'function') {
173
+ return false;
174
+ }
175
+ return knownChatModelNames.includes(exp.name);
176
+ }) ;
177
+
178
+ if (!chatModelClass) {
179
+ return;
180
+ }
181
+
182
+ // Patch directly on chatModelClass.prototype
183
+ const targetProto = chatModelClass.prototype ;
184
+
185
+ // Patch the methods (invoke, stream, batch)
186
+ // All chat model instances will inherit these patched methods
187
+ const methodsToPatch = ['invoke', 'stream', 'batch'] ;
188
+
189
+ for (const methodName of methodsToPatch) {
190
+ const method = targetProto[methodName];
191
+ if (typeof method === 'function') {
192
+ targetProto[methodName] = wrapRunnableMethod(
193
+ method ,
194
+ sentryHandler);
195
+ }
196
+ }
197
+ }
198
+ }
199
+
200
+ exports.SentryLangChainInstrumentation = SentryLangChainInstrumentation;
201
+ //# sourceMappingURL=instrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing/langchain/instrumentation.ts"],"sourcesContent":["import {\n type InstrumentationConfig,\n type InstrumentationModuleDefinition,\n InstrumentationBase,\n InstrumentationNodeModuleDefinition,\n InstrumentationNodeModuleFile,\n} from '@opentelemetry/instrumentation';\nimport type { LangChainOptions } from '@sentry/core';\nimport { createLangChainCallbackHandler, getClient, SDK_VERSION } from '@sentry/core';\n\nconst supportedVersions = ['>=0.1.0 <1.0.0'];\n\ntype LangChainInstrumentationOptions = InstrumentationConfig & LangChainOptions;\n\n/**\n * Represents the patched shape of LangChain provider package exports\n */\ninterface PatchedLangChainExports {\n [key: string]: unknown;\n}\n\n/**\n * Augments a callback handler list with Sentry's handler if not already present\n */\nfunction augmentCallbackHandlers(handlers: unknown, sentryHandler: unknown): unknown {\n // Handle null/undefined - return array with just our handler\n if (!handlers) {\n return [sentryHandler];\n }\n\n // If handlers is already an array\n if (Array.isArray(handlers)) {\n // Check if our handler is already in the list\n if (handlers.includes(sentryHandler)) {\n return handlers;\n }\n // Add our handler to the list\n return [...handlers, sentryHandler];\n }\n\n // If it's a single handler object, convert to array\n if (typeof handlers === 'object') {\n return [handlers, sentryHandler];\n }\n\n // Unknown type - return original\n return handlers;\n}\n\n/**\n * Wraps Runnable methods (invoke, stream, batch) to inject Sentry callbacks at request time\n * Uses a Proxy to intercept method calls and augment the options.callbacks\n */\nfunction wrapRunnableMethod(\n originalMethod: (...args: unknown[]) => unknown,\n sentryHandler: unknown,\n _methodName: string,\n): (...args: unknown[]) => unknown {\n return new Proxy(originalMethod, {\n apply(target, thisArg, args: unknown[]): unknown {\n // LangChain Runnable method signatures:\n // invoke(input, options?) - options contains callbacks\n // stream(input, options?) - options contains callbacks\n // batch(inputs, options?) - options contains callbacks\n\n // Options is typically the second argument\n const optionsIndex = 1;\n let options = args[optionsIndex] as Record<string, unknown> | undefined;\n\n // If options don't exist or aren't an object, create them\n if (!options || typeof options !== 'object' || Array.isArray(options)) {\n options = {};\n args[optionsIndex] = options;\n }\n\n // Inject our callback handler into options.callbacks (request time callbacks)\n const existingCallbacks = options.callbacks;\n const augmentedCallbacks = augmentCallbackHandlers(existingCallbacks, sentryHandler);\n options.callbacks = augmentedCallbacks;\n\n // Call original method with augmented options\n return Reflect.apply(target, thisArg, args);\n },\n }) as (...args: unknown[]) => unknown;\n}\n\n/**\n * Sentry LangChain instrumentation using OpenTelemetry.\n */\nexport class SentryLangChainInstrumentation extends InstrumentationBase<LangChainInstrumentationOptions> {\n public constructor(config: LangChainInstrumentationOptions = {}) {\n super('@sentry/instrumentation-langchain', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the modules to be patched.\n * We patch the BaseChatModel class methods to inject callbacks\n *\n * We hook into provider packages (@langchain/anthropic, @langchain/openai, etc.)\n * because @langchain/core is often bundled and not loaded as a separate module\n */\n public init(): InstrumentationModuleDefinition | InstrumentationModuleDefinition[] {\n const modules: InstrumentationModuleDefinition[] = [];\n\n // Hook into common LangChain provider packages\n const providerPackages = [\n '@langchain/anthropic',\n '@langchain/openai',\n '@langchain/google-genai',\n '@langchain/mistralai',\n '@langchain/google-vertexai',\n '@langchain/groq',\n ];\n\n for (const packageName of providerPackages) {\n // In CJS, LangChain packages re-export from dist/index.cjs files.\n // Patching only the root module sometimes misses the real implementation or\n // gets overwritten when that file is loaded. We add a file-level patch so that\n // _patch runs again on the concrete implementation\n modules.push(\n new InstrumentationNodeModuleDefinition(\n packageName,\n supportedVersions,\n this._patch.bind(this),\n exports => exports,\n [\n new InstrumentationNodeModuleFile(\n `${packageName}/dist/index.cjs`,\n supportedVersions,\n this._patch.bind(this),\n exports => exports,\n ),\n ],\n ),\n );\n }\n\n return modules;\n }\n\n /**\n * Core patch logic - patches chat model methods to inject Sentry callbacks\n * This is called when a LangChain provider package is loaded\n */\n private _patch(exports: PatchedLangChainExports): PatchedLangChainExports | void {\n const client = getClient();\n const defaultPii = Boolean(client?.getOptions().sendDefaultPii);\n\n const config = this.getConfig();\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const recordInputs = config?.recordInputs ?? defaultPii;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const recordOutputs = config?.recordOutputs ?? defaultPii;\n\n // Create a shared handler instance\n const sentryHandler = createLangChainCallbackHandler({\n recordInputs,\n recordOutputs,\n });\n\n // Patch Runnable methods to inject callbacks at request time\n // This directly manipulates options.callbacks that LangChain uses\n this._patchRunnableMethods(exports, sentryHandler);\n\n return exports;\n }\n\n /**\n * Patches chat model methods (invoke, stream, batch) to inject Sentry callbacks\n * Finds a chat model class from the provider package exports and patches its prototype methods\n */\n private _patchRunnableMethods(exports: PatchedLangChainExports, sentryHandler: unknown): void {\n // Known chat model class names for each provider\n const knownChatModelNames = [\n 'ChatAnthropic',\n 'ChatOpenAI',\n 'ChatGoogleGenerativeAI',\n 'ChatMistralAI',\n 'ChatVertexAI',\n 'ChatGroq',\n ];\n\n // Find a chat model class in the exports by checking known class names\n const chatModelClass = Object.values(exports).find(exp => {\n if (typeof exp !== 'function') {\n return false;\n }\n return knownChatModelNames.includes(exp.name);\n }) as { prototype: unknown; name: string } | undefined;\n\n if (!chatModelClass) {\n return;\n }\n\n // Patch directly on chatModelClass.prototype\n const targetProto = chatModelClass.prototype as Record<string, unknown>;\n\n // Patch the methods (invoke, stream, batch)\n // All chat model instances will inherit these patched methods\n const methodsToPatch = ['invoke', 'stream', 'batch'] as const;\n\n for (const methodName of methodsToPatch) {\n const method = targetProto[methodName];\n if (typeof method === 'function') {\n targetProto[methodName] = wrapRunnableMethod(\n method as (...args: unknown[]) => unknown,\n sentryHandler,\n methodName,\n );\n }\n }\n }\n}\n"],"names":["InstrumentationBase","SDK_VERSION","InstrumentationNodeModuleDefinition","InstrumentationNodeModuleFile","getClient","createLangChainCallbackHandler"],"mappings":"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"}
@@ -27,6 +27,7 @@ export { vercelAIIntegration } from './integrations/tracing/vercelai/index.js';
27
27
  export { openAIIntegration } from './integrations/tracing/openai/index.js';
28
28
  export { anthropicAIIntegration } from './integrations/tracing/anthropic-ai/index.js';
29
29
  export { googleGenAIIntegration } from './integrations/tracing/google-genai/index.js';
30
+ export { langChainIntegration } from './integrations/tracing/langchain/index.js';
30
31
  export { buildLaunchDarklyFlagUsedHandlerShim as buildLaunchDarklyFlagUsedHandler, launchDarklyIntegrationShim as launchDarklyIntegration } from './integrations/featureFlagShims/launchDarkly.js';
31
32
  export { OpenFeatureIntegrationHookShim as OpenFeatureIntegrationHook, openFeatureIntegrationShim as openFeatureIntegration } from './integrations/featureFlagShims/openFeature.js';
32
33
  export { statsigIntegrationShim as statsigIntegration } from './integrations/featureFlagShims/statsig.js';
@@ -37,6 +38,6 @@ export { getDefaultIntegrations, getDefaultIntegrationsWithoutPerformance, init,
37
38
  export { initOpenTelemetry, preloadOpenTelemetry } from './sdk/initOtel.js';
38
39
  export { getAutoPerformanceIntegrations } from './integrations/tracing/index.js';
39
40
  export { setOpenTelemetryContextAsyncContextStrategy as setNodeAsyncContextStrategy } from '@sentry/opentelemetry';
40
- export { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, Scope, addBreadcrumb, addEventProcessor, addIntegration, captureCheckIn, captureConsoleIntegration, captureEvent, captureException, captureFeedback, captureMessage, captureSession, close, consoleIntegration, consoleLoggingIntegration, continueTrace, createConsolaReporter, createTransport, dedupeIntegration, endSession, eventFiltersIntegration, extraErrorDataIntegration, featureFlagsIntegration, flush, functionToStringIntegration, getActiveSpan, getClient, getCurrentScope, getGlobalScope, getIsolationScope, getRootSpan, getSpanDescendants, getSpanStatusFromHttpCode, getTraceData, getTraceMetaTags, httpHeadersToSpanAttributes, inboundFiltersIntegration, instrumentSupabaseClient, isEnabled, isInitialized, lastEventId, linkedErrorsIntegration, parameterize, profiler, requestDataIntegration, rewriteFramesIntegration, setContext, setCurrentClient, setExtra, setExtras, setHttpStatus, setMeasurement, setTag, setTags, setUser, spanToBaggageHeader, spanToJSON, spanToTraceHeader, startInactiveSpan, startNewTrace, startSession, startSpan, startSpanManual, supabaseIntegration, suppressTracing, trpcMiddleware, updateSpanName, winterCGHeadersToDict, withActiveSpan, withIsolationScope, withMonitor, withScope, wrapMcpServerWithSentry, zodErrorsIntegration } from '@sentry/core';
41
+ export { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, Scope, addBreadcrumb, addEventProcessor, addIntegration, captureCheckIn, captureConsoleIntegration, captureEvent, captureException, captureFeedback, captureMessage, captureSession, close, consoleIntegration, consoleLoggingIntegration, continueTrace, createConsolaReporter, createLangChainCallbackHandler, createTransport, dedupeIntegration, endSession, eventFiltersIntegration, extraErrorDataIntegration, featureFlagsIntegration, flush, functionToStringIntegration, getActiveSpan, getClient, getCurrentScope, getGlobalScope, getIsolationScope, getRootSpan, getSpanDescendants, getSpanStatusFromHttpCode, getTraceData, getTraceMetaTags, httpHeadersToSpanAttributes, inboundFiltersIntegration, instrumentSupabaseClient, isEnabled, isInitialized, lastEventId, linkedErrorsIntegration, parameterize, profiler, requestDataIntegration, rewriteFramesIntegration, setContext, setCurrentClient, setExtra, setExtras, setHttpStatus, setMeasurement, setTag, setTags, setUser, spanToBaggageHeader, spanToJSON, spanToTraceHeader, startInactiveSpan, startNewTrace, startSession, startSpan, startSpanManual, supabaseIntegration, suppressTracing, trpcMiddleware, updateSpanName, winterCGHeadersToDict, withActiveSpan, withIsolationScope, withMonitor, withScope, wrapMcpServerWithSentry, zodErrorsIntegration } from '@sentry/core';
41
42
  export { NODE_VERSION, NodeClient, SentryContextManager, anrIntegration, childProcessIntegration, contextLinesIntegration, createGetModuleFromFilename, createSentryWinstonTransport, cron, defaultStackParser, disableAnrDetectionForCallback, generateInstrumentOnce, getSentryRelease, httpServerIntegration, httpServerSpansIntegration, localVariablesIntegration, logger, makeNodeTransport, metrics, modulesIntegration, nodeContextIntegration, onUncaughtExceptionIntegration, onUnhandledRejectionIntegration, pinoIntegration, spotlightIntegration, systemErrorIntegration, validateOpenTelemetrySetup } from '@sentry/node-core';
42
43
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -79,6 +79,8 @@ function getConfigWithDefaults(options = {}) {
79
79
  [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.node_fetch',
80
80
  };
81
81
  },
82
+ requestHook: options.requestHook,
83
+ responseHook: options.responseHook,
82
84
  } ;
83
85
 
84
86
  return instrumentationConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"node-fetch.js","sources":["../../../src/integrations/node-fetch.ts"],"sourcesContent":["import type { UndiciInstrumentationConfig } from '@opentelemetry/instrumentation-undici';\nimport { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';\nimport type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, getClient, hasSpansEnabled, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\nimport type { NodeClient } from '@sentry/node-core';\nimport { generateInstrumentOnce, SentryNodeFetchInstrumentation } from '@sentry/node-core';\nimport type { NodeClientOptions } from '../types';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\ninterface NodeFetchOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * If set to false, do not emit any spans.\n * This will ensure that the default UndiciInstrumentation from OpenTelemetry is not setup,\n * only the Sentry-specific instrumentation for breadcrumbs & trace propagation is applied.\n *\n * If `skipOpenTelemetrySetup: true` is configured, this defaults to `false`, otherwise it defaults to `true`.\n */\n spans?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing fetch requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst instrumentOtelNodeFetch = generateInstrumentOnce(\n INTEGRATION_NAME,\n UndiciInstrumentation,\n (options: NodeFetchOptions) => {\n return getConfigWithDefaults(options);\n },\n);\n\nconst instrumentSentryNodeFetch = generateInstrumentOnce(\n `${INTEGRATION_NAME}.sentry`,\n SentryNodeFetchInstrumentation,\n (options: NodeFetchOptions) => {\n return options;\n },\n);\n\nconst _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {\n return {\n name: 'NodeFetch',\n setupOnce() {\n const instrumentSpans = _shouldInstrumentSpans(options, getClient<NodeClient>()?.getOptions());\n\n // This is the \"regular\" OTEL instrumentation that emits spans\n if (instrumentSpans) {\n instrumentOtelNodeFetch(options);\n }\n\n // This is the Sentry-specific instrumentation that creates breadcrumbs & propagates traces\n // This must be registered after the OTEL one, to ensure that the core trace propagation logic takes presedence\n // Otherwise, the sentry-trace header may be set multiple times\n instrumentSentryNodeFetch(options);\n },\n };\n}) satisfies IntegrationFn;\n\nexport const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);\n\n// Matching the behavior of the base instrumentation\nfunction getAbsoluteUrl(origin: string, path: string = '/'): string {\n const url = `${origin}`;\n\n if (url.endsWith('/') && path.startsWith('/')) {\n return `${url}${path.slice(1)}`;\n }\n\n if (!url.endsWith('/') && !path.startsWith('/')) {\n return `${url}/${path.slice(1)}`;\n }\n\n return `${url}${path}`;\n}\n\nfunction _shouldInstrumentSpans(options: NodeFetchOptions, clientOptions: Partial<NodeClientOptions> = {}): boolean {\n // If `spans` is passed in, it takes precedence\n // Else, we by default emit spans, unless `skipOpenTelemetrySetup` is set to `true` or spans are not enabled\n return typeof options.spans === 'boolean'\n ? options.spans\n : !clientOptions.skipOpenTelemetrySetup && hasSpansEnabled(clientOptions);\n}\n\nfunction getConfigWithDefaults(options: Partial<NodeFetchOptions> = {}): UndiciInstrumentationConfig {\n const instrumentationConfig = {\n requireParentforSpans: false,\n ignoreRequestHook: request => {\n const url = getAbsoluteUrl(request.origin, request.path);\n const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;\n const shouldIgnore = _ignoreOutgoingRequests && url && _ignoreOutgoingRequests(url);\n\n return !!shouldIgnore;\n },\n startSpanHook: () => {\n return {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.node_fetch',\n };\n },\n } satisfies UndiciInstrumentationConfig;\n\n return instrumentationConfig;\n}\n"],"names":[],"mappings":";;;;AAQA,MAAM,gBAAA,GAAmB,WAAW;;AAyBpC,MAAM,uBAAA,GAA0B,sBAAsB;AACtD,EAAE,gBAAgB;AAClB,EAAE,qBAAqB;AACvB,EAAE,CAAC,OAAO,KAAuB;AACjC,IAAI,OAAO,qBAAqB,CAAC,OAAO,CAAC;AACzC,GAAG;AACH,CAAC;;AAED,MAAM,yBAAA,GAA4B,sBAAsB;AACxD,EAAE,CAAC,EAAA,gBAAA,CAAA,OAAA,CAAA;AACA,EAAA,8BAAA;AACA,EAAA,CAAA,OAAA,KAAA;AACA,IAAA,OAAA,OAAA;AACA,GAAA;AACA,CAAA;;AAEA,MAAA,2BAAA,IAAA,CAAA,OAAA,GAAA,EAAA,KAAA;AACA,EAAA,OAAA;AACA,IAAA,IAAA,EAAA,WAAA;AACA,IAAA,SAAA,GAAA;AACA,MAAA,MAAA,eAAA,GAAA,sBAAA,CAAA,OAAA,EAAA,SAAA,EAAA,EAAA,UAAA,EAAA,CAAA;;AAEA;AACA,MAAA,IAAA,eAAA,EAAA;AACA,QAAA,uBAAA,CAAA,OAAA,CAAA;AACA;;AAEA;AACA;AACA;AACA,MAAA,yBAAA,CAAA,OAAA,CAAA;AACA,KAAA;AACA,GAAA;AACA,CAAA,CAAA;;AAEA,MAAA,0BAAA,GAAA,iBAAA,CAAA,2BAAA;;AAEA;AACA,SAAA,cAAA,CAAA,MAAA,EAAA,IAAA,GAAA,GAAA,EAAA;AACA,EAAA,MAAA,GAAA,GAAA,CAAA,EAAA,MAAA,CAAA,CAAA;;AAEA,EAAA,IAAA,GAAA,CAAA,QAAA,CAAA,GAAA,CAAA,IAAA,IAAA,CAAA,UAAA,CAAA,GAAA,CAAA,EAAA;AACA,IAAA,OAAA,CAAA,EAAA,GAAA,CAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AACA;;AAEA,EAAA,IAAA,CAAA,GAAA,CAAA,QAAA,CAAA,GAAA,CAAA,IAAA,CAAA,IAAA,CAAA,UAAA,CAAA,GAAA,CAAA,EAAA;AACA,IAAA,OAAA,CAAA,EAAA,GAAA,CAAA,CAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AACA;;AAEA,EAAA,OAAA,CAAA,EAAA,GAAA,CAAA,EAAA,IAAA,CAAA,CAAA;AACA;;AAEA,SAAA,sBAAA,CAAA,OAAA,EAAA,aAAA,GAAA,EAAA,EAAA;AACA;AACA;AACA,EAAA,OAAA,OAAA,OAAA,CAAA,KAAA,KAAA;AACA,MAAA,OAAA,CAAA;AACA,MAAA,CAAA,aAAA,CAAA,sBAAA,IAAA,eAAA,CAAA,aAAA,CAAA;AACA;;AAEA,SAAA,qBAAA,CAAA,OAAA,GAAA,EAAA,EAAA;AACA,EAAA,MAAA,qBAAA,GAAA;AACA,IAAA,qBAAA,EAAA,KAAA;AACA,IAAA,iBAAA,EAAA,OAAA,IAAA;AACA,MAAA,MAAA,GAAA,GAAA,cAAA,CAAA,OAAA,CAAA,MAAA,EAAA,OAAA,CAAA,IAAA,CAAA;AACA,MAAA,MAAA,uBAAA,GAAA,OAAA,CAAA,sBAAA;AACA,MAAA,MAAA,YAAA,GAAA,uBAAA,IAAA,GAAA,IAAA,uBAAA,CAAA,GAAA,CAAA;;AAEA,MAAA,OAAA,CAAA,CAAA,YAAA;AACA,KAAA;AACA,IAAA,aAAA,EAAA,MAAA;AACA,MAAA,OAAA;AACA,QAAA,CAAA,gCAAA,GAAA,2BAAA;AACA,OAAA;AACA,KAAA;AACA,GAAA;;AAEA,EAAA,OAAA,qBAAA;AACA;;;;"}
1
+ {"version":3,"file":"node-fetch.js","sources":["../../../src/integrations/node-fetch.ts"],"sourcesContent":["import type { UndiciInstrumentationConfig } from '@opentelemetry/instrumentation-undici';\nimport { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';\nimport type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, getClient, hasSpansEnabled, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\nimport type { NodeClient } from '@sentry/node-core';\nimport { generateInstrumentOnce, SentryNodeFetchInstrumentation } from '@sentry/node-core';\nimport type { NodeClientOptions } from '../types';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\ninterface NodeFetchOptions extends Pick<UndiciInstrumentationConfig, 'requestHook' | 'responseHook'> {\n /**\n * Whether breadcrumbs should be recorded for requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * If set to false, do not emit any spans.\n * This will ensure that the default UndiciInstrumentation from OpenTelemetry is not setup,\n * only the Sentry-specific instrumentation for breadcrumbs & trace propagation is applied.\n *\n * If `skipOpenTelemetrySetup: true` is configured, this defaults to `false`, otherwise it defaults to `true`.\n */\n spans?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing fetch requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst instrumentOtelNodeFetch = generateInstrumentOnce(\n INTEGRATION_NAME,\n UndiciInstrumentation,\n (options: NodeFetchOptions) => {\n return getConfigWithDefaults(options);\n },\n);\n\nconst instrumentSentryNodeFetch = generateInstrumentOnce(\n `${INTEGRATION_NAME}.sentry`,\n SentryNodeFetchInstrumentation,\n (options: NodeFetchOptions) => {\n return options;\n },\n);\n\nconst _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {\n return {\n name: 'NodeFetch',\n setupOnce() {\n const instrumentSpans = _shouldInstrumentSpans(options, getClient<NodeClient>()?.getOptions());\n\n // This is the \"regular\" OTEL instrumentation that emits spans\n if (instrumentSpans) {\n instrumentOtelNodeFetch(options);\n }\n\n // This is the Sentry-specific instrumentation that creates breadcrumbs & propagates traces\n // This must be registered after the OTEL one, to ensure that the core trace propagation logic takes presedence\n // Otherwise, the sentry-trace header may be set multiple times\n instrumentSentryNodeFetch(options);\n },\n };\n}) satisfies IntegrationFn;\n\nexport const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);\n\n// Matching the behavior of the base instrumentation\nfunction getAbsoluteUrl(origin: string, path: string = '/'): string {\n const url = `${origin}`;\n\n if (url.endsWith('/') && path.startsWith('/')) {\n return `${url}${path.slice(1)}`;\n }\n\n if (!url.endsWith('/') && !path.startsWith('/')) {\n return `${url}/${path.slice(1)}`;\n }\n\n return `${url}${path}`;\n}\n\nfunction _shouldInstrumentSpans(options: NodeFetchOptions, clientOptions: Partial<NodeClientOptions> = {}): boolean {\n // If `spans` is passed in, it takes precedence\n // Else, we by default emit spans, unless `skipOpenTelemetrySetup` is set to `true` or spans are not enabled\n return typeof options.spans === 'boolean'\n ? options.spans\n : !clientOptions.skipOpenTelemetrySetup && hasSpansEnabled(clientOptions);\n}\n\nfunction getConfigWithDefaults(options: Partial<NodeFetchOptions> = {}): UndiciInstrumentationConfig {\n const instrumentationConfig = {\n requireParentforSpans: false,\n ignoreRequestHook: request => {\n const url = getAbsoluteUrl(request.origin, request.path);\n const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;\n const shouldIgnore = _ignoreOutgoingRequests && url && _ignoreOutgoingRequests(url);\n\n return !!shouldIgnore;\n },\n startSpanHook: () => {\n return {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.node_fetch',\n };\n },\n requestHook: options.requestHook,\n responseHook: options.responseHook,\n } satisfies UndiciInstrumentationConfig;\n\n return instrumentationConfig;\n}\n"],"names":[],"mappings":";;;;AAQA,MAAM,gBAAA,GAAmB,WAAW;;AAyBpC,MAAM,uBAAA,GAA0B,sBAAsB;AACtD,EAAE,gBAAgB;AAClB,EAAE,qBAAqB;AACvB,EAAE,CAAC,OAAO,KAAuB;AACjC,IAAI,OAAO,qBAAqB,CAAC,OAAO,CAAC;AACzC,GAAG;AACH,CAAC;;AAED,MAAM,yBAAA,GAA4B,sBAAsB;AACxD,EAAE,CAAC,EAAA,gBAAA,CAAA,OAAA,CAAA;AACA,EAAA,8BAAA;AACA,EAAA,CAAA,OAAA,KAAA;AACA,IAAA,OAAA,OAAA;AACA,GAAA;AACA,CAAA;;AAEA,MAAA,2BAAA,IAAA,CAAA,OAAA,GAAA,EAAA,KAAA;AACA,EAAA,OAAA;AACA,IAAA,IAAA,EAAA,WAAA;AACA,IAAA,SAAA,GAAA;AACA,MAAA,MAAA,eAAA,GAAA,sBAAA,CAAA,OAAA,EAAA,SAAA,EAAA,EAAA,UAAA,EAAA,CAAA;;AAEA;AACA,MAAA,IAAA,eAAA,EAAA;AACA,QAAA,uBAAA,CAAA,OAAA,CAAA;AACA;;AAEA;AACA;AACA;AACA,MAAA,yBAAA,CAAA,OAAA,CAAA;AACA,KAAA;AACA,GAAA;AACA,CAAA,CAAA;;AAEA,MAAA,0BAAA,GAAA,iBAAA,CAAA,2BAAA;;AAEA;AACA,SAAA,cAAA,CAAA,MAAA,EAAA,IAAA,GAAA,GAAA,EAAA;AACA,EAAA,MAAA,GAAA,GAAA,CAAA,EAAA,MAAA,CAAA,CAAA;;AAEA,EAAA,IAAA,GAAA,CAAA,QAAA,CAAA,GAAA,CAAA,IAAA,IAAA,CAAA,UAAA,CAAA,GAAA,CAAA,EAAA;AACA,IAAA,OAAA,CAAA,EAAA,GAAA,CAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AACA;;AAEA,EAAA,IAAA,CAAA,GAAA,CAAA,QAAA,CAAA,GAAA,CAAA,IAAA,CAAA,IAAA,CAAA,UAAA,CAAA,GAAA,CAAA,EAAA;AACA,IAAA,OAAA,CAAA,EAAA,GAAA,CAAA,CAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AACA;;AAEA,EAAA,OAAA,CAAA,EAAA,GAAA,CAAA,EAAA,IAAA,CAAA,CAAA;AACA;;AAEA,SAAA,sBAAA,CAAA,OAAA,EAAA,aAAA,GAAA,EAAA,EAAA;AACA;AACA;AACA,EAAA,OAAA,OAAA,OAAA,CAAA,KAAA,KAAA;AACA,MAAA,OAAA,CAAA;AACA,MAAA,CAAA,aAAA,CAAA,sBAAA,IAAA,eAAA,CAAA,aAAA,CAAA;AACA;;AAEA,SAAA,qBAAA,CAAA,OAAA,GAAA,EAAA,EAAA;AACA,EAAA,MAAA,qBAAA,GAAA;AACA,IAAA,qBAAA,EAAA,KAAA;AACA,IAAA,iBAAA,EAAA,OAAA,IAAA;AACA,MAAA,MAAA,GAAA,GAAA,cAAA,CAAA,OAAA,CAAA,MAAA,EAAA,OAAA,CAAA,IAAA,CAAA;AACA,MAAA,MAAA,uBAAA,GAAA,OAAA,CAAA,sBAAA;AACA,MAAA,MAAA,YAAA,GAAA,uBAAA,IAAA,GAAA,IAAA,uBAAA,CAAA,GAAA,CAAA;;AAEA,MAAA,OAAA,CAAA,CAAA,YAAA;AACA,KAAA;AACA,IAAA,aAAA,EAAA,MAAA;AACA,MAAA,OAAA;AACA,QAAA,CAAA,gCAAA,GAAA,2BAAA;AACA,OAAA;AACA,KAAA;AACA,IAAA,WAAA,EAAA,OAAA,CAAA,WAAA;AACA,IAAA,YAAA,EAAA,OAAA,CAAA,YAAA;AACA,GAAA;;AAEA,EAAA,OAAA,qBAAA;AACA;;;;"}
@@ -1,4 +1,4 @@
1
- import { defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
1
+ import { defineIntegration, captureException, flush, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
2
2
  import { generateInstrumentOnce, addOriginToSpan } from '@sentry/node-core';
3
3
  import { FirebaseInstrumentation } from './otel/firebaseInstrumentation.js';
4
4
 
@@ -10,6 +10,24 @@ const config = {
10
10
 
11
11
  span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'db.query');
12
12
  },
13
+ functions: {
14
+ requestHook: span => {
15
+ addOriginToSpan(span, 'auto.firebase.otel.functions');
16
+
17
+ span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.request');
18
+ },
19
+ errorHook: async (_, error) => {
20
+ if (error) {
21
+ captureException(error, {
22
+ mechanism: {
23
+ type: 'auto.firebase.otel.functions',
24
+ handled: false,
25
+ },
26
+ });
27
+ await flush(2000);
28
+ }
29
+ },
30
+ },
13
31
  };
14
32
 
15
33
  const instrumentFirebase = generateInstrumentOnce(INTEGRATION_NAME, () => new FirebaseInstrumentation(config));
@@ -1 +1 @@
1
- {"version":3,"file":"firebase.js","sources":["../../../../../src/integrations/tracing/firebase/firebase.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';\nimport { addOriginToSpan, generateInstrumentOnce } from '@sentry/node-core';\nimport { type FirebaseInstrumentationConfig, FirebaseInstrumentation } from './otel';\n\nconst INTEGRATION_NAME = 'Firebase';\n\nconst config: FirebaseInstrumentationConfig = {\n firestoreSpanCreationHook: span => {\n addOriginToSpan(span, 'auto.firebase.otel.firestore');\n\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'db.query');\n },\n};\n\nexport const instrumentFirebase = generateInstrumentOnce(INTEGRATION_NAME, () => new FirebaseInstrumentation(config));\n\nconst _firebaseIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentFirebase();\n },\n };\n}) satisfies IntegrationFn;\n\nexport const firebaseIntegration = defineIntegration(_firebaseIntegration);\n"],"names":[],"mappings":";;;;AAKA,MAAM,gBAAA,GAAmB,UAAU;;AAEnC,MAAM,MAAM,GAAkC;AAC9C,EAAE,yBAAyB,EAAE,IAAA,IAAQ;AACrC,IAAI,eAAe,CAAC,IAAI,EAAE,8BAA8B,CAAC;;AAEzD,IAAI,IAAI,CAAC,YAAY,CAAC,4BAA4B,EAAE,UAAU,CAAC;AAC/D,GAAG;AACH,CAAC;;AAEM,MAAM,kBAAA,GAAqB,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC;;AAEpH,MAAM,oBAAA,IAAwB,MAAM;AACpC,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB,MAAM,kBAAkB,EAAE;AAC1B,KAAK;AACL,GAAG;AACH,CAAC,CAAA;;MAEY,mBAAA,GAAsB,iBAAiB,CAAC,oBAAoB;;;;"}
1
+ {"version":3,"file":"firebase.js","sources":["../../../../../src/integrations/tracing/firebase/firebase.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { captureException, defineIntegration, flush, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';\nimport { addOriginToSpan, generateInstrumentOnce } from '@sentry/node-core';\nimport { type FirebaseInstrumentationConfig, FirebaseInstrumentation } from './otel';\n\nconst INTEGRATION_NAME = 'Firebase';\n\nconst config: FirebaseInstrumentationConfig = {\n firestoreSpanCreationHook: span => {\n addOriginToSpan(span, 'auto.firebase.otel.firestore');\n\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'db.query');\n },\n functions: {\n requestHook: span => {\n addOriginToSpan(span, 'auto.firebase.otel.functions');\n\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.request');\n },\n errorHook: async (_, error) => {\n if (error) {\n captureException(error, {\n mechanism: {\n type: 'auto.firebase.otel.functions',\n handled: false,\n },\n });\n await flush(2000);\n }\n },\n },\n};\n\nexport const instrumentFirebase = generateInstrumentOnce(INTEGRATION_NAME, () => new FirebaseInstrumentation(config));\n\nconst _firebaseIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentFirebase();\n },\n };\n}) satisfies IntegrationFn;\n\nexport const firebaseIntegration = defineIntegration(_firebaseIntegration);\n"],"names":[],"mappings":";;;;AAKA,MAAM,gBAAA,GAAmB,UAAU;;AAEnC,MAAM,MAAM,GAAkC;AAC9C,EAAE,yBAAyB,EAAE,IAAA,IAAQ;AACrC,IAAI,eAAe,CAAC,IAAI,EAAE,8BAA8B,CAAC;;AAEzD,IAAI,IAAI,CAAC,YAAY,CAAC,4BAA4B,EAAE,UAAU,CAAC;AAC/D,GAAG;AACH,EAAE,SAAS,EAAE;AACb,IAAI,WAAW,EAAE,IAAA,IAAQ;AACzB,MAAM,eAAe,CAAC,IAAI,EAAE,8BAA8B,CAAC;;AAE3D,MAAM,IAAI,CAAC,YAAY,CAAC,4BAA4B,EAAE,cAAc,CAAC;AACrE,KAAK;AACL,IAAI,SAAS,EAAE,OAAO,CAAC,EAAE,KAAK,KAAK;AACnC,MAAM,IAAI,KAAK,EAAE;AACjB,QAAQ,gBAAgB,CAAC,KAAK,EAAE;AAChC,UAAU,SAAS,EAAE;AACrB,YAAY,IAAI,EAAE,8BAA8B;AAChD,YAAY,OAAO,EAAE,KAAK;AAC1B,WAAW;AACX,SAAS,CAAC;AACV,QAAQ,MAAM,KAAK,CAAC,IAAI,CAAC;AACzB;AACA,KAAK;AACL,GAAG;AACH,CAAC;;AAEM,MAAM,kBAAA,GAAqB,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC;;AAEpH,MAAM,oBAAA,IAAwB,MAAM;AACpC,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB,MAAM,kBAAkB,EAAE;AAC1B,KAAK;AACL,GAAG;AACH,CAAC,CAAA;;MAEY,mBAAA,GAAsB,iBAAiB,CAAC,oBAAoB;;;;"}
@@ -1,9 +1,11 @@
1
1
  import { InstrumentationBase } from '@opentelemetry/instrumentation';
2
2
  import { SDK_VERSION } from '@sentry/core';
3
3
  import { patchFirestore } from './patches/firestore.js';
4
+ import { patchFunctions } from './patches/functions.js';
4
5
 
5
6
  const DefaultFirebaseInstrumentationConfig = {};
6
7
  const firestoreSupportedVersions = ['>=3.0.0 <5']; // firebase 9+
8
+ const functionsSupportedVersions = ['>=6.0.0 <7']; // firebase-functions v2
7
9
 
8
10
  /**
9
11
  * Instrumentation for Firebase services, specifically Firestore.
@@ -30,6 +32,7 @@ class FirebaseInstrumentation extends InstrumentationBase {
30
32
  const modules = [];
31
33
 
32
34
  modules.push(patchFirestore(this.tracer, firestoreSupportedVersions, this._wrap, this._unwrap, this.getConfig()));
35
+ modules.push(patchFunctions(this.tracer, functionsSupportedVersions, this._wrap, this._unwrap, this.getConfig()));
33
36
 
34
37
  return modules;
35
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"firebaseInstrumentation.js","sources":["../../../../../../src/integrations/tracing/firebase/otel/firebaseInstrumentation.ts"],"sourcesContent":["import { type InstrumentationNodeModuleDefinition, InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { SDK_VERSION } from '@sentry/core';\nimport { patchFirestore } from './patches/firestore';\nimport type { FirebaseInstrumentationConfig } from './types';\n\nconst DefaultFirebaseInstrumentationConfig: FirebaseInstrumentationConfig = {};\nconst firestoreSupportedVersions = ['>=3.0.0 <5']; // firebase 9+\n\n/**\n * Instrumentation for Firebase services, specifically Firestore.\n */\nexport class FirebaseInstrumentation extends InstrumentationBase<FirebaseInstrumentationConfig> {\n public constructor(config: FirebaseInstrumentationConfig = DefaultFirebaseInstrumentationConfig) {\n super('@sentry/instrumentation-firebase', SDK_VERSION, config);\n }\n\n /**\n * sets config\n * @param config\n */\n public override setConfig(config: FirebaseInstrumentationConfig = {}): void {\n super.setConfig({ ...DefaultFirebaseInstrumentationConfig, ...config });\n }\n\n /**\n *\n * @protected\n */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n protected init(): InstrumentationNodeModuleDefinition | InstrumentationNodeModuleDefinition[] | void {\n const modules: InstrumentationNodeModuleDefinition[] = [];\n\n modules.push(patchFirestore(this.tracer, firestoreSupportedVersions, this._wrap, this._unwrap, this.getConfig()));\n\n return modules;\n }\n}\n"],"names":[],"mappings":";;;;AAKA,MAAM,oCAAoC,GAAkC,EAAE;AAC9E,MAAM,0BAAA,GAA6B,CAAC,YAAY,CAAC,CAAA;;AAEjD;AACA;AACA;AACO,MAAM,uBAAA,SAAgC,mBAAmB,CAAgC;AAChG,GAAS,WAAW,CAAC,MAAM,GAAkC,oCAAoC,EAAE;AACnG,IAAI,KAAK,CAAC,kCAAkC,EAAE,WAAW,EAAE,MAAM,CAAC;AAClE;;AAEA;AACA;AACA;AACA;AACA,IAAkB,SAAS,CAAC,MAAM,GAAkC,EAAE,EAAQ;AAC9E,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,GAAG,oCAAoC,EAAE,GAAG,MAAA,EAAQ,CAAC;AAC3E;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAY,IAAI,GAAuF;AACvG,IAAI,MAAM,OAAO,GAA0C,EAAE;;AAE7D,IAAI,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,0BAA0B,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;;AAErH,IAAI,OAAO,OAAO;AAClB;AACA;;;;"}
1
+ {"version":3,"file":"firebaseInstrumentation.js","sources":["../../../../../../src/integrations/tracing/firebase/otel/firebaseInstrumentation.ts"],"sourcesContent":["import { type InstrumentationNodeModuleDefinition, InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { SDK_VERSION } from '@sentry/core';\nimport { patchFirestore } from './patches/firestore';\nimport { patchFunctions } from './patches/functions';\nimport type { FirebaseInstrumentationConfig } from './types';\n\nconst DefaultFirebaseInstrumentationConfig: FirebaseInstrumentationConfig = {};\nconst firestoreSupportedVersions = ['>=3.0.0 <5']; // firebase 9+\nconst functionsSupportedVersions = ['>=6.0.0 <7']; // firebase-functions v2\n\n/**\n * Instrumentation for Firebase services, specifically Firestore.\n */\nexport class FirebaseInstrumentation extends InstrumentationBase<FirebaseInstrumentationConfig> {\n public constructor(config: FirebaseInstrumentationConfig = DefaultFirebaseInstrumentationConfig) {\n super('@sentry/instrumentation-firebase', SDK_VERSION, config);\n }\n\n /**\n * sets config\n * @param config\n */\n public override setConfig(config: FirebaseInstrumentationConfig = {}): void {\n super.setConfig({ ...DefaultFirebaseInstrumentationConfig, ...config });\n }\n\n /**\n *\n * @protected\n */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n protected init(): InstrumentationNodeModuleDefinition | InstrumentationNodeModuleDefinition[] | void {\n const modules: InstrumentationNodeModuleDefinition[] = [];\n\n modules.push(patchFirestore(this.tracer, firestoreSupportedVersions, this._wrap, this._unwrap, this.getConfig()));\n modules.push(patchFunctions(this.tracer, functionsSupportedVersions, this._wrap, this._unwrap, this.getConfig()));\n\n return modules;\n }\n}\n"],"names":[],"mappings":";;;;;AAMA,MAAM,oCAAoC,GAAkC,EAAE;AAC9E,MAAM,0BAAA,GAA6B,CAAC,YAAY,CAAC,CAAA;AACjD,MAAM,0BAAA,GAA6B,CAAC,YAAY,CAAC,CAAA;;AAEjD;AACA;AACA;AACO,MAAM,uBAAA,SAAgC,mBAAmB,CAAgC;AAChG,GAAS,WAAW,CAAC,MAAM,GAAkC,oCAAoC,EAAE;AACnG,IAAI,KAAK,CAAC,kCAAkC,EAAE,WAAW,EAAE,MAAM,CAAC;AAClE;;AAEA;AACA;AACA;AACA;AACA,IAAkB,SAAS,CAAC,MAAM,GAAkC,EAAE,EAAQ;AAC9E,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,GAAG,oCAAoC,EAAE,GAAG,MAAA,EAAQ,CAAC;AAC3E;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAY,IAAI,GAAuF;AACvG,IAAI,MAAM,OAAO,GAA0C,EAAE;;AAE7D,IAAI,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,0BAA0B,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;AACrH,IAAI,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,0BAA0B,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;;AAErH,IAAI,OAAO,OAAO;AAClB;AACA;;;;"}
@@ -0,0 +1,262 @@
1
+ import { SpanKind, context, trace, diag } from '@opentelemetry/api';
2
+ import { InstrumentationNodeModuleDefinition, InstrumentationNodeModuleFile, isWrapped, safeExecuteInTheMiddle } from '@opentelemetry/instrumentation';
3
+
4
+ /**
5
+ * Patches Firebase Functions v2 to add OpenTelemetry instrumentation
6
+ * @param tracer - Opentelemetry Tracer
7
+ * @param functionsSupportedVersions - supported versions of firebase-functions
8
+ * @param wrap - reference to native instrumentation wrap function
9
+ * @param unwrap - reference to native instrumentation unwrap function
10
+ * @param config - Firebase instrumentation config
11
+ */
12
+ function patchFunctions(
13
+ tracer,
14
+ functionsSupportedVersions,
15
+ wrap,
16
+ unwrap,
17
+ config,
18
+ ) {
19
+ let requestHook = () => {};
20
+ let responseHook = () => {};
21
+ const errorHook = config.functions?.errorHook;
22
+ const configRequestHook = config.functions?.requestHook;
23
+ const configResponseHook = config.functions?.responseHook;
24
+
25
+ if (typeof configResponseHook === 'function') {
26
+ responseHook = (span, err) => {
27
+ safeExecuteInTheMiddle(
28
+ () => configResponseHook(span, err),
29
+ error => {
30
+ if (!error) {
31
+ return;
32
+ }
33
+ diag.error(error?.message);
34
+ },
35
+ true,
36
+ );
37
+ };
38
+ }
39
+ if (typeof configRequestHook === 'function') {
40
+ requestHook = (span) => {
41
+ safeExecuteInTheMiddle(
42
+ () => configRequestHook(span),
43
+ error => {
44
+ if (!error) {
45
+ return;
46
+ }
47
+ diag.error(error?.message);
48
+ },
49
+ true,
50
+ );
51
+ };
52
+ }
53
+
54
+ const moduleFunctionsCJS = new InstrumentationNodeModuleDefinition('firebase-functions', functionsSupportedVersions);
55
+ const modulesToInstrument = [
56
+ { name: 'firebase-functions/lib/v2/providers/https.js', triggerType: 'function' },
57
+ { name: 'firebase-functions/lib/v2/providers/firestore.js', triggerType: 'firestore' },
58
+ { name: 'firebase-functions/lib/v2/providers/scheduler.js', triggerType: 'scheduler' },
59
+ { name: 'firebase-functions/lib/v2/storage.js', triggerType: 'storage' },
60
+ ] ;
61
+
62
+ modulesToInstrument.forEach(({ name, triggerType }) => {
63
+ moduleFunctionsCJS.files.push(
64
+ new InstrumentationNodeModuleFile(
65
+ name,
66
+ functionsSupportedVersions,
67
+ moduleExports =>
68
+ wrapCommonFunctions(
69
+ moduleExports,
70
+ wrap,
71
+ unwrap,
72
+ tracer,
73
+ { requestHook, responseHook, errorHook },
74
+ triggerType,
75
+ ),
76
+ moduleExports => unwrapCommonFunctions(moduleExports, unwrap),
77
+ ),
78
+ );
79
+ });
80
+
81
+ return moduleFunctionsCJS;
82
+ }
83
+
84
+ /**
85
+ * Patches Cloud Functions for Firebase (v2) to add OpenTelemetry instrumentation
86
+ *
87
+ * @param tracer - Opentelemetry Tracer
88
+ * @param functionsConfig - Firebase instrumentation config
89
+ * @param triggerType - Type of trigger
90
+ * @returns A function that patches the function
91
+ */
92
+ function patchV2Functions(
93
+ tracer,
94
+ functionsConfig,
95
+ triggerType,
96
+ ) {
97
+ return function v2FunctionsWrapper(original) {
98
+ return function ( ...args) {
99
+ const handler = typeof args[0] === 'function' ? args[0] : args[1];
100
+ const documentOrOptions = typeof args[0] === 'function' ? undefined : args[0];
101
+
102
+ if (!handler) {
103
+ return original.call(this, ...args);
104
+ }
105
+
106
+ const wrappedHandler = async function ( ...handlerArgs) {
107
+ const functionName = process.env.FUNCTION_TARGET || process.env.K_SERVICE || 'unknown';
108
+ const span = tracer.startSpan(`firebase.function.${triggerType}`, {
109
+ kind: SpanKind.SERVER,
110
+ });
111
+
112
+ const attributes = {
113
+ 'faas.name': functionName,
114
+ 'faas.trigger': triggerType,
115
+ 'faas.provider': 'firebase',
116
+ };
117
+
118
+ if (process.env.GCLOUD_PROJECT) {
119
+ attributes['cloud.project_id'] = process.env.GCLOUD_PROJECT;
120
+ }
121
+
122
+ if (process.env.EVENTARC_CLOUD_EVENT_SOURCE) {
123
+ attributes['cloud.event_source'] = process.env.EVENTARC_CLOUD_EVENT_SOURCE;
124
+ }
125
+
126
+ span.setAttributes(attributes);
127
+ functionsConfig?.requestHook?.(span);
128
+
129
+ // Can be changed to safeExecuteInTheMiddleAsync once following is merged and released
130
+ // https://github.com/open-telemetry/opentelemetry-js/pull/6032
131
+ return context.with(trace.setSpan(context.active(), span), async () => {
132
+ let error;
133
+ let result;
134
+
135
+ try {
136
+ result = await handler.apply(this, handlerArgs);
137
+ } catch (e) {
138
+ error = e ;
139
+ }
140
+
141
+ functionsConfig?.responseHook?.(span, error);
142
+
143
+ if (error) {
144
+ span.recordException(error);
145
+ }
146
+
147
+ span.end();
148
+
149
+ if (error) {
150
+ await functionsConfig?.errorHook?.(span, error);
151
+ throw error;
152
+ }
153
+
154
+ return result;
155
+ });
156
+ };
157
+
158
+ if (documentOrOptions) {
159
+ return original.call(this, documentOrOptions, wrappedHandler);
160
+ } else {
161
+ return original.call(this, wrappedHandler);
162
+ }
163
+ };
164
+ };
165
+ }
166
+
167
+ function wrapCommonFunctions(
168
+ moduleExports,
169
+ wrap,
170
+ unwrap,
171
+ tracer,
172
+ functionsConfig,
173
+ triggerType,
174
+ ) {
175
+ unwrapCommonFunctions(moduleExports, unwrap);
176
+
177
+ switch (triggerType) {
178
+ case 'function':
179
+ wrap(moduleExports, 'onRequest', patchV2Functions(tracer, functionsConfig, 'http.request'));
180
+ wrap(moduleExports, 'onCall', patchV2Functions(tracer, functionsConfig, 'http.call'));
181
+ break;
182
+
183
+ case 'firestore':
184
+ wrap(moduleExports, 'onDocumentCreated', patchV2Functions(tracer, functionsConfig, 'firestore.document.created'));
185
+ wrap(moduleExports, 'onDocumentUpdated', patchV2Functions(tracer, functionsConfig, 'firestore.document.updated'));
186
+ wrap(moduleExports, 'onDocumentDeleted', patchV2Functions(tracer, functionsConfig, 'firestore.document.deleted'));
187
+ wrap(moduleExports, 'onDocumentWritten', patchV2Functions(tracer, functionsConfig, 'firestore.document.written'));
188
+ wrap(
189
+ moduleExports,
190
+ 'onDocumentCreatedWithAuthContext',
191
+ patchV2Functions(tracer, functionsConfig, 'firestore.document.created'),
192
+ );
193
+ wrap(
194
+ moduleExports,
195
+ 'onDocumentUpdatedWithAuthContext',
196
+ patchV2Functions(tracer, functionsConfig, 'firestore.document.updated'),
197
+ );
198
+
199
+ wrap(
200
+ moduleExports,
201
+ 'onDocumentDeletedWithAuthContext',
202
+ patchV2Functions(tracer, functionsConfig, 'firestore.document.deleted'),
203
+ );
204
+
205
+ wrap(
206
+ moduleExports,
207
+ 'onDocumentWrittenWithAuthContext',
208
+ patchV2Functions(tracer, functionsConfig, 'firestore.document.written'),
209
+ );
210
+ break;
211
+
212
+ case 'scheduler':
213
+ wrap(moduleExports, 'onSchedule', patchV2Functions(tracer, functionsConfig, 'scheduler.scheduled'));
214
+ break;
215
+
216
+ case 'storage':
217
+ wrap(moduleExports, 'onObjectFinalized', patchV2Functions(tracer, functionsConfig, 'storage.object.finalized'));
218
+ wrap(moduleExports, 'onObjectArchived', patchV2Functions(tracer, functionsConfig, 'storage.object.archived'));
219
+ wrap(moduleExports, 'onObjectDeleted', patchV2Functions(tracer, functionsConfig, 'storage.object.deleted'));
220
+ wrap(
221
+ moduleExports,
222
+ 'onObjectMetadataUpdated',
223
+ patchV2Functions(tracer, functionsConfig, 'storage.object.metadataUpdated'),
224
+ );
225
+ break;
226
+ }
227
+
228
+ return moduleExports;
229
+ }
230
+
231
+ function unwrapCommonFunctions(
232
+ moduleExports,
233
+ unwrap,
234
+ ) {
235
+ const methods = [
236
+ 'onSchedule',
237
+ 'onRequest',
238
+ 'onCall',
239
+ 'onObjectFinalized',
240
+ 'onObjectArchived',
241
+ 'onObjectDeleted',
242
+ 'onObjectMetadataUpdated',
243
+ 'onDocumentCreated',
244
+ 'onDocumentUpdated',
245
+ 'onDocumentDeleted',
246
+ 'onDocumentWritten',
247
+ 'onDocumentCreatedWithAuthContext',
248
+ 'onDocumentUpdatedWithAuthContext',
249
+ 'onDocumentDeletedWithAuthContext',
250
+ 'onDocumentWrittenWithAuthContext',
251
+ ];
252
+
253
+ for (const method of methods) {
254
+ if (isWrapped(moduleExports[method])) {
255
+ unwrap(moduleExports, method);
256
+ }
257
+ }
258
+ return moduleExports;
259
+ }
260
+
261
+ export { patchFunctions, patchV2Functions };
262
+ //# sourceMappingURL=functions.js.map